Welcome to part 3 of my Sandcastle under the hood-series. In part 2, which focused on all the different components available to use in the BuildAssembler, I promised you a walkthrough of the sandcastle.config that was delivered in the July CTP of Sandcastle. So here we go!
Input to BuildAssembler
First of all a reminder of the files we can work with (some of them created in the first steps):
- reflection.xml
Contains reflected data from all assemblies - manifest.xml
A list of all topics. - commments.xml (or similar)
One or more files that contain the xml comments assembled from your source code. These files are produced by using the /doc switch when compiling your project. - sandcastle.config
The file where we define which components to run during the BuildAssembler process, their input parameters et.c.
Also available are a bunch of xml files with reflection data for most common .NET Framework assemblies (found in Examples\Cpref_reflection). And if you have the .NET Framework SDK installed (you probably do if you’re a developer), you also have the xml comments for those assemblies (found in C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727).
Running BuildAssembler with sandcastle.config
Remember, the BuildAssembler runs once for each topic found in the manifest.xml. The input for each run is the topic id, which can look something like “P:SandcastleTestLibrary.TestClass.MyTestString”.
First, the CopyFromFileComponent is used to bring in a skeleton xml document. Nothing very fancy, it simply looks like this:
<document>
<reference />
<syntax />
<comments />
</document>
In the initialization phase, the next CopyFromIndexComponent has built up an index (called “reflection”) of all reflection xml files (reflection.xml and Examples\Cpref_reflection\*.xml). It uses the id attribute of the <api> elements as unique identifiers. This unique identifier is matched to the current topic id, and the contents of that node is copied into the document/reference element.
The next two CopyFromIndexComponents use the same index (“reflection”) to insert api information about the namspace and/or type that contains the current topic. Everything except the elements node is copied. So what previously looked like this:
<reference>
<apidata name="MyTestString" group="member" subgroup="property" />
..
<containers>
<library assembly="SandcastleTestLibrary" module="SandcastleTestLibrary" />
<namespace api="N:SandcastleTestLibrary" />
<type api="T:SandcastleTestLibrary.TestClass" ref="true" />
</containers>
</reference>
Turns into this:
<reference>
<apidata name="MyTestString" group="member" subgroup="property" />
..
<containers>
<library assembly="SandcastleTestLibrary" module="SandcastleTestLibrary" />
<namespace api="N:SandcastleTestLibrary">
<apidata name="SandcastleTestLibrary" group="namespace" />
<file name="f9552800-d515-86c9-d25b-6cdb3d52a8be" />
</namespace>
<type api="T:SandcastleTestLibrary.TestClass" ref="true">
<apidata name="TestClass" group="type" subgroup="class" />
<typedata visibility="public" abstract="false" sealed="false" serializable="false" layout="auto" format="ansi" />
<family>
<ancestors>
<type api="T:System.Object" ref="true" />
</ancestors>
</family>
<containers>
<library assembly="SandcastleTestLibrary" module="SandcastleTestLibrary" />
<namespace api="N:SandcastleTestLibrary" />
</containers>
<file name="0ee38ce4-f243-9c7b-5521-2106d1b1f4bc" />
</type>
</containers>
</reference>
Next, an IfThenComponent is used to make sure that the next step is only applied to topics that are not Overloads. For all other topics, a SyntaxComponent is used to generate the syntax strings. One string is generated for each language. The strings are placed in the <syntax> element and look something like this:
<syntax>
<div codeLanguage="CSharp">
<span class="keyword">public</span> <referenceLink target="T:System.String" prefer-overload="false" show-container="false" show-templates="false" show-parameters="false">string</referenceLink> <span class="identifier">MyTestString</span> { get; set; }</div>
<div codeLanguage="VisualBasic">
<span class="keyword">Public</span> <span class="keyword">Property</span> <span class="identifier">MyTestString</span> <span class="keyword">As</span> </div target="T:System.String" prefer-overload="false" show-container="false" show-templates="false" show-parameters="false">
<div codeLanguage="ManagedCPlusPlus">
<span class="keyword">public</span>:
<referenceLink target="T:System.String" prefer-overload="false" show-container="false" show-templates="false" show-parameters="false" />^ <span class="identifier">MyTestString</span> {
<referenceLink target="T:System.String" prefer-overload="false" show-container="false" show-templates="false" show-parameters="false" />^ <span class="keyword">get</span> ();
<span class="keyword">void</span> <span class="keyword">set</span> (, <referenceLink target="T:System.String" prefer-overload="false" show-container="false" show-templates="false" show-parameters="false" />^ <span class="parameter">value</span>);
}</div>
</syntax>
A new CopyFromIndexComponent has built up an index of all files containing xml comments. This index is called “comments” and is at first used to copy in the comments for the current topic.
Now, a ForEachComponent loops through all contained elements. For each such element, the reflected info and comments for this element are copied from the previously created “reflection” and “comments” indexes. The document now contains all data that is needed for the topic!
<document>
<reference>
<apidata name="TestClass" group="type" subgroup="class" />
..
<elements>
..
<element api="P:SandcastleTestLibrary.TestClass.MyTestString">
<apidata name="MyTestString" group="member" subgroup="property" />
..
<file name="ffff1456-643b-ea6a-547a-9ce4ebc205a4" />
<summary>
A test property
</summary>
</element>
..
</elements>
..
<file name="0ee38ce4-f243-9c7b-5521-2106d1b1f4bc" />
</reference>
<syntax>
..
</syntax>
<comments>
<summary>
This is the summary documentation for TestClass
</summary>
</comments>
</document>
It’s time for the big transformation! A TransformComponent applies the Presentation\transforms\main_sandcastle.xsl template file to the current document. The result is a complete html, but it still contains a couple of custom nodes that isn’t part of a the standard HTML specification.
The SharedContentComponent is used to replace any links to, you guessed it, shared content. Among other things, this is where the header and footer is applied. You can change it to something more to your liking by editing the file Presentation\Content\shared_content.xml.
Finally, the ResolveReferenceLinksComponent is used to create correct links to internal and external documents.
In the end, the now complete document is written to disc using the GUID filename that was generated in one of the first steps.
Next steps
I could describe the last steps of the process as well (generating a TOC, running HHC etc) but to be honest, if you’ve followed along all the way to this point you probably already understand the rest. Instead, my next step will be to make the MSBuild script work with Microsoft Help Compiler 2.0 too.
As always, I really do appreciate any comments you have, so fire away!
Leave a Reply