
This article explains the steps to generate MSDN-style documentation with Sandcastle, using NAnt, a scripting language. The scripts were developed to be started from a CruiseControl.NET project, but you can easily start them directly from NAnt as well.
Sandcastle is Microsoft's new generator for MSDN-style documentation. The Sandcastle components gather information about your assemblies through Reflection, and combine this with the XML comments from your source code. The output is a set of HTML pages that you can optionally compile to CHM or HxS format. The NAnt scripts that I propose in this article are part of a CruiseControl.NET project, and closely follow the 12-step procedure to generate a CHM as elaborated in this official Sandcastle blog entry, and graphically represented in the image above.
The presented set of NAnt scripts will work under the following assumptions:
${bin.intern.dir} ${documentation.dir} property - they were generated by using the /doc command line argument of your favorite compiler${bin.extern.dir} property.To keep the core of the buildscripts readable and maintainable, let's first create some properties for all fixed paths to directories, executables, and XSL transformations:
<!-- Directories -->
<property name="sandcastle.dir"
value="F:\Program Files\Sandcastle" />
<property name="sandcastle.workingdir"
value="${projects.dir}\${CCNetProject}\SandcastleWorkingDir" />
<property name="sandcastle.output.dir"
value="${sandcastle.workingdir}\Output" />
<!-- Executables -->
<property name="sandcastle.mrefbuilder.exe"
value="${sandcastle.dir}\productiontools\mrefbuilder.exe" />
<property name="sandcastle.buildassembler.exe"
value="${sandcastle.dir}\productiontools\buildassembler.exe" />
<property name="sandcastle.xsltransform.exe"
value="${sandcastle.dir}\productiontools\xsltransform.exe" />
<!-- Transformations -->
<property name="sandcastle.addoverloads.xsl"
value="${sandcastle.dir}\ProductionTransforms\AddOverloads.xsl" />
<property name="sandcastle.addguidfilenames.xsl"
value="${sandcastle.dir}\ProductionTransforms\AddGuidFilenames.xsl" />
<property name="sandcastle.reflectiontomanifest.xsl"
value="${sandcastle.dir}\ProductionTransforms\ReflectionToManifest.xsl" />
<property name="sandcastle.reflectiontochmproject.xsl"
value="${sandcastle.dir}\ProductionTransforms\ReflectionToChmProject.xsl" />
<property name="sandcastle.reflectiontochmcontents.xsl"
value="${sandcastle.dir}\ProductionTransforms\ReflectionToChmContents.xsl" />
<property name="sandcastle.reflectiontochmindex.xsl"
value="${sandcastle.dir}\ProductionTransforms\ReflectionToChmIndex.xsl" />
<!-- Help Compiler -->
<property name="hhc.exe" overwrite="false"
value="F:\Program Files\HTML Help Workshop\hhc.exe" />
The first task in the NAnt Project is the creation of an empty working directory:
<!-- Create or Cleanup Working Directory -->
<mkdir dir="${sandcastle.workingdir}"
if="${not directory::exists(sandcastle.workingdir)}" />
<delete>
<fileset basedir="${sandcastle.workingdir}">
<include name="**\*" />
</fileset>
</delete>
The Sandcastle component that creates the actual HTML documentation is the so-called BuildAssembler. It takes three input files:
The following steps create these files:
This NAnt-task copies the original sandcastle.config file to the working directory. The relative paths in the original file are substituted by fixed paths, and we replace the built-in reference to a comments.xml file with a reference to the folder containing the XML documentation of our own assemblies:
<!-- Copy configuration file, and hard code references -->
<copy file="${sandcastle.dir}/Presentation/Configuration/Sandcastle.config"
tofile="${sandcastle.workingdir}/Sandcastle.config">
<filterchain>
<replacestring from=""..\..\" to=""${sandcastle.dir}\" />
<replacestring from=""..\" to=""${sandcastle.dir}\Examples\" />
<replacestring from=""comments.xml" to=""${documentation.dir}\*.xml" />
</filterchain>
</copy>
MRefBuilder is one of the core Sandcastle console applications. It uses reflection to create an XML file that describes the inner structure of a set of assemblies. Its complete command line contains:
<!-- Run MRefBuilder (introspection on assemblies) to create basic Reflection XML -->
<exec program="${sandcastle.mrefbuilder.exe}" workingdir="${sandcastle.workingdir}">
<arg value="${bin.intern.dir}/*.dll" />
<arg value="${bin.intern.dir}/*.exe" />
<arg value="/out:reflection.org1.xml" />
<arg value="/dep:${bin.extern.dir}\*.dll" />
</exec>
The file that was generated by MRefBuilder -reflection.org- contains two types of XML elements:
<assembly>: assembly metadata (version, description, company, ...) <api>: namespaces, types, members, constructors, ...These tasks create the final Reflection.xml file, containing all the necessary information, but no presentation at all. Using Sandcastle's XSLTransform tool, we apply two XSL transformations on the reflection.org file:
It is possible to apply the two transformations in one step, but I've split them to easily verify the results:
<!-- Create final Reflection XML -->
<!-- Regroup overloads -->
<exec program="${sandcastle.xsltransform.exe}" workingdir="${sandcastle.workingdir}">
<arg value="reflection.org1.xml" />
<arg value="/xsl:"${sandcastle.addoverloads.xsl}"" />
<arg value="/out:reflection.org2.xml" />
</exec>
<!-- Create filenames for html documents -->
<exec program="${sandcastle.xsltransform.exe}" workingdir="${sandcastle.workingdir}">
<arg value="reflection.org2.xml" />
<arg value="/xsl:"${sandcastle.addguidfilenames.xsl}"" />
<arg value="/out:reflection.xml" />
</exec>
If you want to expose this directory to the end-users of your application, then you would probably not use GUIDs as file name. I suggest to create your own transformation as an alternative for addguidfilenames.xsl.
The so-called Manifest is again an XML file. It's a list of Topic entries, one for each <api> -element from reflection.xml.
<!-- Create Manifest (list of Topics) -->
<exec program="${sandcastle.xsltransform.exe}" workingdir="${sandcastle.workingdir}">
<arg value="/xsl:"${sandcastle.reflectiontomanifest.xsl}"" />
<arg value="reflection.xml" />
<arg value="/out:manifest.xml" />
</exec>
The output directory has four subfolders. The BuildAssembler tool will generate its documentation in the html subfolder, the art, scripts, and styles folder are copied from the installation. They contain the necessary scripts, stylesheets, and images to give the documentation its MSDN look-and-feel.
<!-- Create Output Environment -->
<mkdir dir="${sandcastle.output.dir}" />
<mkdir dir="${sandcastle.output.dir}/html" />
<copy todir="${sandcastle.output.dir}">
<fileset basedir="${sandcastle.dir}/Presentation">
<include name="art/*" />
<include name="scripts/*" />
<include name="styles/*" />
</fileset>
</copy>
BuildAssembler is a generic console-application. It sends an XML document (reflection.xml) through a pipeline of components (like transformators, file creators, flow controllers, ...). The pipeline is constructed via the sandcastle.config, and is executed for each topic (i.e. each entry in the manifest.xml). BuildAssemblers command line consists of references to the manifest.xml file and sandcastle.config. The links to reflection.xml and the files containing the XML source code documentation are stored in the config file.
<!-- Run BuildAssembler (create html topic files) -->
<exec program="${sandcastle.buildassembler.exe}" workingdir="${sandcastle.workingdir}" >
<arg value="manifest.xml" />
<arg value="/config:Sandcastle.config" />
</exec>
The output of this process is a directory with linked HTML-file for each topic. The files in the arts, scripts, and styles folders apply an MSDN look-and-feel. The documentation is fully functional, the rest of the process is optional.
The next tasks transform the current Output directory to a single CHM file, through HTML Help Workshop.
The HTML Help compiler expects three input files:
The input files are all XML files that can be generated by applying an XSL transformation on reflection.xml:
<!-- Create html Help project -->
<exec program="${sandcastle.xsltransform.exe}" workingdir="${sandcastle.workingdir}">
<arg value="/xsl:"${sandcastle.reflectiontochmproject.xsl}"" />
<arg value="reflection.xml" />
<arg value="/out:"${sandcastle.output.dir}\test.hhp"" />
</exec>
<!-- Create html Help project Table Of Contents -->
<exec program="${sandcastle.xsltransform.exe}" workingdir="${sandcastle.workingdir}" >
<arg value="/xsl:"${sandcastle.reflectiontochmcontents.xsl}"" />
<arg value="reflection.xml" />
<arg value="/arg:html=Output\html" />
<arg value="/out:"${sandcastle.output.dir}\test.hhc"" />
</exec>
<!-- Create html Help project Index -->
<exec program="${sandcastle.xsltransform.exe}" workingdir="${sandcastle.workingdir}" >
<arg value="/xsl:"${sandcastle.reflectiontochmindex.xsl}"" />
<arg value="reflection.xml" />
<arg value="/out:"${sandcastle.output.dir}\test.hhk"" />
</exec>
Finally, we compile the project to a CHM file through the Microsoft HTML Help Compiler (v1.4). I had to put the failonerror on false to ignore the non-zero return value.
<!-- Generate CHM file -->
<exec program="${hhc.exe}"
commandline="test.hhp"
workingdir="${sandcastle.output.dir}"
failonerror="false"/>
Diederik Krols is a .NET Architect and Trainer. As a Certified Scrum Master he embraces all tools that keep the development pace and quality as high as possible, and the overhead as low as possible. He has posted many articles on CruiseControl.NET and other components of the Open Source N*Stack (NAnt, NUnit, NCover, ...) on the Real Developer Network. His personal blog contains articles on Anti-Patterns.
| You must Sign In to use this message board. | |||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||