Diese Woche hatte ich mal wieder das Thema Build Integration bei uns angehen müssen. Der Auslöser war mal wieder die zwingende Aktualisierung von einigen Dokumenten unserer Anwendung. Außerdem habe ich eine Anwendung, die schon eine Weile nicht mehr funktionierte, nun kamen auch noch einige Bug Fixes dazu.
Grundsätzlich war der Build-Prozess bei uns immer so, dass wir Build-Skripte auf dem Server liegen hatte und auch notwendige Erweiterungen. CCNet konnte in früheren Versionen SVN-Dateien nicht (korrekt) abrufen, aus diesem Grund enthielten die Build-Skripte die Anweisungen zum Abrufen der Informationen aus der Versionsverwaltung. Zusätzlich wollten wir immer ein Clean-Build, d. h. der Server wird in den Ursprungszustand zurückgesetzt.
Sowohl abrufen, als auch das ausführen von Skripten vor dem eigentlichen Build ist mit den neueren CCNet Versionen möglich. (1.3 und 1.4) Somit werden nun die Skripte ordnungsgemäß versioniert und können sich auch in den Versionen deutlich unterscheiden. Ich hatte bereits vor einiger Zeit schon Teile unserer Build-Skripte angepasst, so dass Funktionen wie das Abrufen von Daten aus dem SCM-System nicht mehr ausgeführt wurden. Manche werden sich fragen, warum Dokumente über Build-Skripte erzeugt werden müssen, die Antwort ist ganz simpel, weil bei uns die Dokumente geTeXt sind.
Dokumente mit TeX bzw. LaTeX zu erstellen ist bei umfangreichen Dokumenten sehr nützlich und gerade bei einer Integration mit der Anwendung oder Build-Prozessen sinnvoll. Die großen Vorteile aus meiner Sicht sind:
· Textbasierende Dokumente, dadurch effektives Merging möglich und geringere Größe
· Wenige Formatierungen
· Sauberes Schriftbild
· Automatisches ersetzen von Platzhaltern, Versionsnummern der Anwendung, Versionsnummern des Dokumentes
Die Vorurteile, dass die Dokumente nicht mehr für alle lesbar sind, ist durch den Einsatz des Build-Systems ausgehebelt. Jeder kann sich das Finale Dokument laden und anschauen. Anmerkungen können genau zu einer Version gemacht werden. Hier das Target für die Erstellung der Dokumente:
289 <Target Name="CreatePdf" DependsOnTargets="CreateRawFolderStructure">
290 <!-- Eventuelle die Version der Anwendung setzen s-->
291 <Message Text="Creating TexFile-Items! File is $(TexFile)" Importance="high"/>
292 <CreateItem Include="$(SourceFolder)\**\$(TexFile)" Exclude="$(SvnExcludes)">
293 <Output TaskParameter="Include" ItemName="texMaster"/>
294 </CreateItem>
295 <Message Text="Creating TexFile-Items created! Versionnumber suffix is $(VersionNumberFileSuffix)" Importance="high"/>
296 <CreateProperty Value="$(VersionNumberFile)-tex$(VersionNumberFileSuffix)">
297 <Output TaskParameter="Value" PropertyName="TexVersionNumberFile"/>
298 </CreateProperty>
299 <CreateItem Include="$(SourceFolder)\**\*.tex" Exclude="$(SvnExcludes)">
300 <Output TaskParameter="Include" ItemName="versionTexFiles"/>
301 </CreateItem>
302 <Exec Command="echo 1.0.0.0> "$(VersionNumberFile)"" Condition="!Exists('$(VersionNumberFile)')"/>
303 <Version VersionFile="$(VersionNumberFile)" RevisionType="None" BuildType="None">
304 <Output TaskParameter="Major" PropertyName="Major" />
305 <Output TaskParameter="Minor" PropertyName="Minor" />
306 <Output TaskParameter="Build" PropertyName="Build" />
307 <Output TaskParameter="Revision" PropertyName="Revision" />
308 </Version>
309 <FileUpdate Files="@(versionTexFiles)" Singleline="false" Encoding="ISO-8859-1"
310 Regex="\\newcommand{\\Version}{.+}"
311 ReplacementText="\newcommand{\Version}{$(Major).$(Minor).$(Build)}"/>
312
313 <Exec Command="echo 1.0.0.0> "$(TexVersionNumberFile)"" Condition="!Exists('$(TexVersionNumberFile)')"/>
314 <Version VersionFile="$(TexVersionNumberFile)" RevisionType="Increment" BuildType="Increment" Condition="'$(CCNetLabel)'==''">
315 <Output TaskParameter="Major" PropertyName="Major" />
316 <Output TaskParameter="Minor" PropertyName="Minor" />
317 <Output TaskParameter="Build" PropertyName="Build" />
318 <Output TaskParameter="Revision" PropertyName="Revision" />
319 </Version>
320 <Exec Command="echo $(CCNetLabel)> "$(TexVersionNumberFile)"" Condition="'$(CCNetLabel)'!=''"/>
321 <!-- immer Version herausholen -->
322 <Version VersionFile="$(TexVersionNumberFile)" RevisionType="None" BuildType="None" >
323 <Output TaskParameter="Major" PropertyName="Major" />
324 <Output TaskParameter="Minor" PropertyName="Minor" />
325 <Output TaskParameter="Build" PropertyName="Build" />
326 <Output TaskParameter="Revision" PropertyName="Revision" />
327 </Version>
328 <FileUpdate Files="@(versionTexFiles)" Singleline="false" Encoding="ISO-8859-1"
329 Regex="\\newcommand{\\DocVersion}{.+?}"
330 ReplacementText="\newcommand{\DocVersion}{$(Major).$(Minor).$(Build)}"/>
331 <CreateProperty Value="$(CCNetArtifactDirectory)\texify.log">
332 <Output PropertyName="TexifyLogFile" TaskParameter="Value"/>
333 </CreateProperty>
334 <Exec Command=""$(LatexBinFolder)\texify" -b --pdf -q --max-iterations=1 -e "@(texMaster)"" IgnoreExitCode="true" WorkingDirectory="@(texMaster->'%(RootDir)%(Directory)')" Outputs="$(TexifyLogFile)"/>
335 <Exec Command=""$(LatexBinFolder)\makeindex" "@(texMaster->'%(RootDir)%(Directory)%(Filename).nlo')" -s nomencl.ist -o "@(texMaster->'%(RootDir)%(Directory)%(Filename).nls')"" IgnoreExitCode="true" WorkingDirectory="@(texMaster->'%(RootDir)%(Directory)')" Outputs="$(TexifyLogFile)"/>
336 <Exec Command=""$(LatexBinFolder)\bibtex" "@(texMaster->'%(RootDir)%(Directory)%(Filename)')"" IgnoreExitCode="true" WorkingDirectory="@(texMaster->'%(RootDir)%(Directory)')" Outputs="$(TexifyLogFile)"/>
337 <Exec Command=""$(LatexBinFolder)\texify" -b --pdf -q --max-iterations=1 -e "@(texMaster)"" IgnoreExitCode="true" WorkingDirectory="@(texMaster->'%(RootDir)%(Directory)')" Outputs="$(TexifyLogFile)"/>
338 <!--<Exec Command=""$(LatexBinFolder)\texify" -b -pdf -e "@(texMaster)" WorkingDirectory="@(texMaster->'%(RootDir)%(Directory)')" Outputs="$(TexifyLogFile)"/>-->
339 <CreateProperty Value="@(texMaster->'%(RootDir)%(Directory)%(Filename).pdf')">
340 <Output TaskParameter="Value" PropertyName="PdfResult"/>
341 </CreateProperty>
342 <Copy SourceFiles="$(PdfResult)" DestinationFiles="@(texMaster->'$(OutputPath)\%(Filename)_$(Major).$(Minor).$(Build).pdf')"/>
343 <CreateItem Include="@(texMaster->'%(RootDir)%(Directory)Anlagen\*.*')" Exclude="$(SvnExcludes)">
344 <Output ItemName="attachments" TaskParameter="Include"/>
345 </CreateItem>
346 <Copy SourceFiles="@(attachments)" DestinationFolder="$(OutputPath)\Anlagen"/>
347 </Target>
Es ist auch möglich statt mit Regex die Dokumente anzupassen über ein Include-File alles Variable im Dokument in eine separate Datei auszulagern und diese während des Builds zu generieren. Die interessanten Zeilen in dem Skript sind die Aufrufe von Texify. Bei uns mussten die Anweisungen 2-mal durchlaufen werden, nach dem 1. Durchlauf wurden Index und Bibtex erzeugt/ausgeführt.
Das zweite Problem was auftrat war die Unterstützung von .NET Framework 3.5. Wobei ich hier einschränken muss, der Standard Logger ist gegen das .NET Framework 2.0 kompiliert, das Verursacht das Problem. Die Lösung ist eigentlich ganz einfach, Code nehmen und gegen .NET Framework 3.5 kompilieren. Leider habe ich die Quellen nicht gefunden. Nach etwas suchen habe ich mich dann für den „Improved MsBuild Logger“, allerdings habe ich zusätzlich noch die Lösung um einen detailierteren Build-Status zu erhalten gewählt "Viewing build progress". Beide Lösungen zusammen geworfen und angepasst und herausgekommen ist ein neuer Logger, ich wollte noch einige Infos über die Tasks haben. Eventuell werde ich noch ein separates Xslt schreiben, um die Timings besser auszuwerten. Den Fortschritt des Build zu sehen ist echt klasse, so hat man wenigstens einige Details zum Fortschritt. Der Logger wird wie gewöhnlich beim Aufruf angegeben, heraus kommt das Xml-File mit den Status-Infos.
Der Build-Prozess bei uns läuft nun so ab:
1. Löschen aller Dateien – CCNET prebuild-Task mit MsBuild-Skript
2. Abrufen der Sourcen aus der Versionsverwaltung – CCNET Sourcen abrufen
3. Anstossen des eigentlichen Builds (kompilieren, Documente erstellen, FxCop, MSI erstellen, …) – MSBuild-Task
4. Publishen der Sourcen – Über eigenen MsBuild-Task oder Publisher-Task
5. Statistiken refreshen usw.
Ich habe leider kein vollständiges Projekt zur Hand, so dass es leider ohne Code gehen muss. L
Keine Kommentare:
Kommentar veröffentlichen