SandCastle - Automatizzare la costruzione del .chm
Dopo il post di Marco che annuncia una prima uscita pubblica, per chi volesse iniziare a usare SandCastle (July CTP 2006) senza impazzire lanciando tutti i comandi a manina (ovviamente pià avanti nello sviluppo il tutto verrà integrato con VS 2005) ho creato un .bat da lanciare come Post-Build Event (occhio ai parametri da passare) che appunto automatizza il processo. Ecco i passi da seguire:
Dopo l'installazione di SandCastle troverete una directory c:\program files\sandcastle al cui interno sono presenti i vari tools per la generazione dei file .chm, diversi .xsl per le trasformazioni dei vari file xml che devono essere generati duranti il processo. Creare il .bat che automatizza i 10 passi per creare l'help è abbastanza semplice. Quello che volevo ottenere però è la parametrizzazione del .bat per evitare di dover creare a mano un .bat per ogni progetto.
1) Installare SandCastle: download http://www.microsoft.com/downloads/details.aspx?familyid=E82EA71D-DA89-42EE-A715-696E3A4873B2&displaylang=en
2) Installare HTML Workshop: download http://msdn.microsoft.com/library/default.asp?url=/library/en-us/htmlhelp/html/hwMicrosoftHTMLHelpDownloads.asp
N.B.Per prima cosa occorre copiare la directory di SandCastle fuori dalla Program Files in quanto sviluppando come non-admin (vero che lo siete tutti !!!) non si può accedere a tale directory. Per creare l'help occorre anche il HtmlWorkshop versione 1.4 e anch'esso si installa in Program Files. Nel mio caso ho spostato entrambi i tool il tutto sotto: c:\applications\download. Ovviamente mettete il tutto dove volete sul vostro pc: se cambiate questa dir fate un find/replace sia nel .bat allegato che nel .config allegato.
3) All'interno del progetto Visual Studio create un file sandcastle.config il cui contenuto è allegato in fondo a questo post.
4) All'interno del progetto Visual Studio create un file SandCastleJuly2006.bat (ho creato la versione perchè sicuramente cambierà qualcosa). Il file accetta alcuni parametri da inserire come Post-Build Event nelle proprietà del progetto: questo consente di avere un bat comune a tutti i progetti. Ecco il contenuto del file .bat
cd %1
MRefBuilder %2.dll /out:reflection.org
pause
XslTransform "c:\applications\downloaded\Sandcastle\ProductionTransforms\AddOverloads.xsl" reflection.org | XslTransform "c:\applications\downloaded\Sandcastle\ProductionTransforms\AddGuidFilenames.xsl" /out:reflection.xml
pause
XslTransform "c:\applications\downloaded\Sandcastle\ProductionTransforms\ReflectionToManifest.xsl" reflection.xml /out:manifest.xml
pause
if not exist Output mkdir Output
if not exist Output\html mkdir Output\html
if not exist Output\art mkdir Output\art
if not exist Output\scripts mkdir Output\scripts
if not exist Output\styles mkdir Output\styles
copy "c:\applications\downloaded\Sandcastle\Presentation\art\*" Output\art
copy "c:\applications\downloaded\Sandcastle\Presentation\scripts\*" Output\scripts
copy "c:\applications\downloaded\Sandcastle\Presentation\styles\*" Output\styles
pause
copy %3sandcastle.config .
pause
BuildAssembler /config:sandcastle.config manifest.xml
pause
XslTransform "c:\applications\downloaded\Sandcastle\ProductionTransforms\ReflectionToChmContents.xsl" reflection.xml /arg:html=Output\html /out:%2.hhc
pause
move *.hhc Output
copy %3%2.hhp Output\.
cd Output
pause
"c:\applications\downloaded\HTML Help Workshop\hhc" %2.hhp
cd %3
I vari "pause" servono come controllo per verificare l'output dei vari step.
Come si nota al file .bat vegono passati alcuni parametri. Se volete lanciare il bat a mano occorre passare DirectoryTarget (la bin\debug o bin\release), il Nome della dll o exe da utilizzare, la directory del progetto VS.
Per automatizzare questi parametri e soprattutto ottenere la creazione automatica ho utilizzato un post-build event sul progetto VS 2005: il comando per lanciare il .bat dal post build event è:
$(ProjectDir)SandCastleJuly2006.bat $(TargetDir) $(TargetName) $(ProjectDir)
In questo modo vengono passati i parametri corretti senza doverli impostare a mano: soprattutto spostando il progetto VS, rinominandolo oppure modificando qualunque parametro in VS non occorre diventare pazzi ad aggiornare i .bat.
5) Creare un file (purtroppo a mano) nomeprogetto.hhp all'interno del progetto Visual Studio. Questo file server per il lancio di hhc.exe e contiene il path del file .chm da creare e il path al file .hhc da utilizzare come riferimento (questo file viene creato durante il processo dal file .bat). Ecco il contenuto del file .hhp (è un normalissimo file di testo):
[OPTIONS]
Compatibility=1.1 or later
Compiled file=DevLeap.Library.Mobile.Dal.Sql2005Mobile.chm
Contents file=DevLeap.Library.Mobile.Dal.Sql2005Mobile.hhc
Display compile progress=No
Language=0x409 English (United States)
[FILES]
art\*.gif
html\*.htm
scripts\*.js
styles\*.css
[INFOTYPES]
6) Abilitare nelle proprietà del progetto la generazione dei commenti: per default VS crea un file nomeprogetto.xml. Il mio bat utilizza questo nome: se preferite modificare la sezione del file sandcastle.config evidenziata in giallo (il cui contenuto per intero è allegato più avanti):
<!--
Copy in comments -->
<component type="Microsoft.Ddue.Tools.CopyFromIndexComponent" assembly="c:\applications\downloaded\Sandcastle\ProductionTools\BuildComponents\BuildComponents.dll">
<index name="comments" value="/doc/members/member" key="@name" cache="100">
<data files="DevLeap.Library.Mobile.Dal.Sql2005Mobile.XML" />
<data files="%SystemRoot%\Microsoft.NET\Framework\v2.0.50727\*.xml" />
</index>
<copy name="comments" source="*" target="/document/comments" />
</component>
Infine, ecco il contenuto del file sandcastle.config con i riferimenti alla mia directory c:\applications\downloaded dove ho copiato SandCastle:
<
configuration>
<
dduetools>
<
builder>
<
components>
<!--
Create skeleton document -->
<
component type="Microsoft.Ddue.Tools.CopyFromFileComponent" assembly="c:\applications\downloaded\Sandcastle\ProductionTools\BuildComponents\BuildComponents.dll">
<
data file="c:\applications\downloaded\Sandcastle\Presentation\transforms\skeleton.xml" />
<
copy source="/*" target="/" />
</
component>
<!--
Copy in reflection data -->
<
component type="Microsoft.Ddue.Tools.CopyFromIndexComponent" assembly="c:\applications\downloaded\Sandcastle\ProductionTools\BuildComponents\BuildComponents.dll">
<
index name="reflection" value="/reflection/apis/api" key="@id" cache="10">
<
data files="reflection.xml" />
<
data files="c:\applications\downloaded\Sandcastle\Examples\Cpref_reflection\*.xml" />
</
index>
<
copy name="reflection" source="*" target="/document/reference" />
</
component>
<!--
Copy in container data -->
<
component type="Microsoft.Ddue.Tools.CopyFromIndexComponent" assembly="c:\applications\downloaded\Sandcastle\ProductionTools\BuildComponents\BuildComponents.dll">
<
copy name="reflection" key="string(/document/reference/containers/namespace/@api)" source="*[not(local-name()='elements')]" target="/document/reference/containers/namespace" />
</
component>
<
component type="Microsoft.Ddue.Tools.CopyFromIndexComponent" assembly="c:\applications\downloaded\Sandcastle\ProductionTools\BuildComponents\BuildComponents.dll">
<
copy name="reflection" key="string(/document/reference/containers/type/@api)" source="*[not(local-name()='elements')]" target="/document/reference/containers/type" />
</
component>
<!--
Generate syntax -->
<
component type="Microsoft.Ddue.Tools.IfThenComponent" assembly="c:\applications\downloaded\Sandcastle\ProductionTools\BuildComponents\BuildComponents.dll">
<
if condition="not(starts-with($key,'Overload:') or starts-with($key,'R:'))" />
<
then>
<
component type="Microsoft.Ddue.Tools.SyntaxComponent" assembly="c:\applications\downloaded\Sandcastle\ProductionTools\BuildComponents\BuildComponents.dll">
<
syntax input="/document/reference" output="/document/syntax" />
<
generators>
<
generator type="Microsoft.Ddue.Tools.CSharpDeclarationSyntaxGenerator" assembly="c:\applications\downloaded\Sandcastle\ProductionTools\BuildComponents\SyntaxGenerators.dll" />
<
generator type="Microsoft.Ddue.Tools.VisualBasicDeclarationSyntaxGenerator" assembly="c:\applications\downloaded\Sandcastle\ProductionTools\BuildComponents\SyntaxGenerators.dll" />
<
generator type="Microsoft.Ddue.Tools.CPlusPlusDeclarationSyntaxGenerator" assembly="c:\applications\downloaded\Sandcastle\ProductionTools\BuildComponents\SyntaxGenerators.dll" />
</
generators>
</
component>
</
then>
</
component>
<!--
Copy in comments -->
<
component type="Microsoft.Ddue.Tools.CopyFromIndexComponent" assembly="c:\applications\downloaded\Sandcastle\ProductionTools\BuildComponents\BuildComponents.dll">
<
index name="comments" value="/doc/members/member" key="@name" cache="100">
<
data files="DevLeap.Library.Mobile.Dal.Sql2005Mobile.XML" />
<
data files="%SystemRoot%\Microsoft.NET\Framework\v2.0.50727\*.xml" />
</
index>
<
copy name="comments" source="*" target="/document/comments" />
</
component>
<!--
Copy in reflection data and comments for members -->
<
component type="Microsoft.Ddue.Tools.ForEachComponent" assembly="c:\applications\downloaded\Sandcastle\ProductionTools\BuildComponents\BuildComponents.dll">
<
variable expression="/document/reference/elements/element/@api" />
<
components>
<
component type="Microsoft.Ddue.Tools.CopyFromIndexComponent" assembly="c:\applications\downloaded\Sandcastle\ProductionTools\BuildComponents\BuildComponents.dll">
<
copy name="reflection" source="*[not(local-name()='elements')]" target="/document/reference/elements/element[@api=$key]" />
</
component>
<
component type="Microsoft.Ddue.Tools.CopyFromIndexComponent" assembly="c:\applications\downloaded\Sandcastle\ProductionTools\BuildComponents\BuildComponents.dll">
<
copy name="comments" source="summary" target="/document/reference/elements/element[@api=$key]" />
</
component>
</
components>
</
component>
<!--
transform -->
<
component type="Microsoft.Ddue.Tools.TransformComponent" assembly="c:\applications\downloaded\Sandcastle\ProductionTools\BuildComponents\BuildComponents.dll">
<
transform file="c:\applications\downloaded\Sandcastle\Presentation\transforms\main_sandcastle.xsl" />
</
component>
<!--
resolve shared content -->
<
component type="Microsoft.Ddue.Tools.SharedContentComponent" assembly="c:\applications\downloaded\Sandcastle\ProductionTools\BuildComponents\BuildComponents.dll">
<
content file="c:\applications\downloaded\Sandcastle\Presentation\content\shared_content.xml" />
<
content file="c:\applications\downloaded\Sandcastle\Presentation\content\reference_content.xml" />
</
component>
<!--
resolve reference links -->
<
component type="Microsoft.Ddue.Tools.ResolveReferenceLinksComponent" assembly="c:\applications\downloaded\Sandcastle\ProductionTools\BuildComponents\BuildComponents.dll">
<
targets files="reflection.xml" type="local" />
<
targets files="c:\applications\downloaded\Sandcastle\Examples\Cpref_reflection\*.xml" type="none" />
</
component>
<!--
save the result -->
<
component type="Microsoft.Ddue.Tools.SaveComponent" assembly="c:\applications\downloaded\Sandcastle\ProductionTools\BuildComponents\BuildComponents.dll">
<
save path="concat('Output\html\',/html/head/meta[@name='guid']/@content,'.htm')" indent="false" omit-xml-declaration="true" />
</
component>
</
components>
</
builder>
</
dduetools>
</
configuration>
Scusate l'impaginazione di questo file, ma non avevo veramente tempo di riallineare il tutto sull'editor html.
Spero utile a tutti coloro che vogliono iniziare senza impazzire con i 10 passi manuali in cui spesso si commettono errori.