Make Your Sandcastle Help File Builder Project Dynamic with MSBuild
When building a Sandcastle Help File Builder (SHFB) project in the GUI, you manually specify the list of assemblies you want to document. However, if you want to make the execution of it a little more flexible, you can do a bit of MSBuild magic to dynamically build up the list of documentation sources for the project prior to execution.
The reason this works is that SHFB has changed its command-line execution to run through MSBuild using build tasks rather than a standalone executable. (Though with the executable, you could go through some steps to fashion a response file, you can skip the temporary file creation now.)
First, create your SHFB project in the GUI. Set up all the various settings including a few assemblies and make sure it builds the documentation properly.
Next, run the SHFB build from the command line. SHFB projects are MSBuild projects now, so you can run them through MSBuild at a Visual Studio command prompt:
MSBuild Documentation.shfbproj
This establishes that everything is working properly from the command line. You’ll need that because when you make the project dynamic, you’ll have to run it from the command line.
Create a new MSBuild project file that you will use to dynamically build the list of documentation sources (assemblies/XML files) and add a target in it that will run the SHFB project.
If you are using SHFB 1.9.3.0 from .NET 4.0, you can use the MSBuild task directly, like this:
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
<Target Name="Build">
<MSBuild ToolsVersion="4.0" Projects="Documentation.shfbproj" />
</Target>
</Project>
On the other hand, if you’re using SHFB 1.9.1.0 from .NET 4.0, you’ll need to use the Exec task to run a .NET 3.5 MSBuild process manually, like this:
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
<Target Name="Build">
<!--
You have to use MSBuild 3.5 with SHFB or you get a warning
telling you that any parameters you pass will be ignored.
-->
<Exec
Command=""$(windir)\Microsoft.NET\Framework\v3.5\MSBuild.exe" Documentation.shfbproj"
WorkingDirectory="$(MSBuildProjectDirectory)" />
</Target>
</Project>
Execute your new MSBuild project and make sure the documentation still builds.
Open up the SHFB project in a text editor and find the
DocumentationSources
XML node. This is the bit that we’re going to
make dynamic. The node in question looks like this:
<DocumentationSources>
<DocumentationSource sourceFile="..\build_output\bin\Assembly1.dll" />
<DocumentationSource sourceFile="..\build_output\bin\Assembly1.xml" />
<DocumentationSource sourceFile="..\build_output\bin\Assembly2.dll" />
<DocumentationSource sourceFile="..\build_output\bin\Assembly2.xml" />
</DocumentationSources>
The tricky part here is that DocumentationSources
is a property,
not an item, so when you make this value dynamic you actually need
to build an XML string, not a list of files like you would for other
tasks.
Remove the DocumentationSources
node from the SHFB project file
(or comment it out). We’ll be building that dynamically and passing it
in as a parameter.
In your new MSBuild project file, use an ItemGroup
to locate all of
the .dll and .xml files that should be included in your documentation.
These are the files you will have seen in the list of
DocumentationSources
:
<ItemGroup>
<DocTarget Include="..\build_output\bin\*.dll;..\build_output\bin\*.xml;" />
</ItemGroup>
Use the CreateProperty
task to build the XML string that contains
each DocumentationSource
node:
<CreateProperty Value="@(DocTarget -> '<DocumentationSource sourceFile=%27%(FullPath)%27 />', '')">
<Output TaskParameter="Value" PropertyName="DocumentationSources" />
</CreateProperty>
Finally, pass the list of DocumentationSources to the SHFB project when you run it.
If you’re using the MSBuild task:
<MSBuild
ToolsVersion="4.0"
Projects="Documentation.shfbproj"
Properties="DocumentationSources=$(DocumentationSources)" />
If you’re using the Exec task:
<Exec
Command=""$(windir)\Microsoft.NET\Framework\v3.5\MSBuild.exe" Documentation.shfbproj /p:DocumentationSources="$(DocumentationSources)""
WorkingDirectory="$(MSBuildProjectDirectory)" />
A complete MSBuild script that uses SHFB 1.9.3.0 and the MSBuild task looks like this:
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
<Target Name="Build">
<ItemGroup>
<DocTarget Include="..\build_output\bin\*.dll;..\build_output\bin\*.xml;" />
</ItemGroup>
<CreateProperty Value="@(DocTarget -> '<DocumentationSource sourceFile=%27%(FullPath)%27 />', '')">
<Output TaskParameter="Value" PropertyName="DocumentationSources" />
</CreateProperty>
<MSBuild
ToolsVersion="4.0"
Projects="Documentation.shfbproj"
Properties="DocumentationSources=$(DocumentationSources)" />
</Target>
</Project>
Done! A dynamic Sandcastle Help File Builder project that you’ll never have to update as you add or change the assemblies you want documented.