Automatisiertes Buildmanagement

Veröffentlicht: 24. Mai 2003 | Aktualisiert: 24. Jun 2004

Von Klaus Aschenbrenner

Dieser Artikel führt in das Buildmanagement von Softwareprodukten ein und zeigt wie mit Hilfe eines einfachen Skriptes der Build-Prozess vereinfacht werden kann. Auf den nächsten Seiten wird dem interessierten Leser gezeigt, was ein effektives Buildmanagement umfasst, wie tägliche Builds durchgeführt und archiviert werden und projektspezifische Aufgaben abgedeckt werden.

Auf dieser Seite

 Der Baukasten für ein Buildmanagement
 Das Build-Script
 Setzen der aktuellen Build-Number
 Erstellen der aktuellen Version
 Erstellen von Dokumentationsdateien
 Zusammenfassung

Was ist ein automatisiertes Buildmanagement? Warum brauchen wir eines? Warum reicht es nicht einfach aus, im Visual Studio.NET ein Build/Rebuild All durchzuführen? Das sind alles Fragen, die Sie sich vielleicht stellen, wenn Sie die Überschrift zu diesem Artikel lesen. Auf den nächsten Seiten möchte ich zeigen, warum ein effizientes und zuverlässiges Buildmanagement in unseren heutigen komplexen Softwareprojekten nicht mehr wegdenkbar ist und welche Probleme dadurch zu lösen versucht werden.

Da sich dieser Artikel mit dem Buildmanagement und dessen Automatisierung beschäftigt, möchte ich im ersten Schritt erklären, worum es sich bei einem Buildmanagement im Konkreten überhaupt handelt. Des Weiteren werden wir klären, was dieses Konzept so herausragend macht, damit dessen Aufgaben nicht mehr manuell durchgeführt werden. Grundlegend kann gesagt werden, dass nicht für alle Softwareprojekte ein Buildmanagement eingeführt werden muss. Handelt es sich um einfache Tools und Programme, die im Internet vielleicht als Shareware angeboten werden, ist es nicht unbedingt notwendig, ein Buildmanagement einzuführen.

Wie sieht es aber aus, wenn Software produziert wird, die in Masse vertrieben werden soll? Denken wir an ein konkretes Beispiel und nehmen den Windows 2003 Server, dessen Entwicklung nach mehreren Jahren in den letzten Wochen fertig gestellt wurde. Hier reicht es nicht mehr aus, wenn ein Entwickler einfach ein Build/Rebuild All durchführt und anschließend die erzeugten Dateien mit einem Installationsprogramm verpackt.

Bei solch großen Projekten müssen Ressourcen unterschiedlicher Art und Herkunftsquelle zu einem Ergebnis, dem so genannten Build zusammengeführt werden. Dieser Build wird während des Build-Prozesses eindeutig mit der so genannten Build-Number gekennzeichnet und kann anschließend mit einem Installationsprogramm verpackt und vertrieben werden. Um welche unterschiedlichen Ressourcen es sich dabei handeln kann, möchte ich anhand unseres konkreten Beispiels des Windows 2003 Server aufzeigen:

  • Quellcode aller Programmierer, die am Produkt mitgearbeitet haben. (Anmerkung: beim Windows 2003 Server sind es ungefährt 5.000)

  • Lokalisierungen für verschiedene Sprachen, die von Lokalierungsteams rund um den Globus stammen können

  • Hilfedateien, die wiederum für alle unterstützten Sprachen übersetzt vorliegen müssen

  • Das eigentliche Betriebssystem selbst

  • Gerätetreiber für Hardware, die unterstützt wird

  • Zusatzprogramme, die mit ausgeliefert werden (denken Sie an Notepad, Solitär usw…)

  • Installationsprogramm

Wie anhand der Auflistung zu erkennen ist, kann ein Build bald komplex werden und ist schwer von Hand aus durchführbar. Ein weiterer wichtiger Punkt von Builds ist, dass sie reproduzierbar sein müssen. Warum reproduzierbar? Über die regelmäßigen Builds kann die komplette Entwicklung der Software nachvollzogen werden. Darüber hinaus haben Sie mit einem regelmäßigen Build auch immer den aktuellsten Status Ihres Projektes vor Augen. Weiterhin kann mit Hilfe von regelmäßigen Builds die Testabteilung mit Arbeit versorgt werden.

Im letzten Absatz habe ich öfters von regelmäßigen Builds gesprochen. Aber was ist regelmäßig? Nun, es hängt von Ihnen ab. Im Prinzip kann ein Build jede Nacht oder auch nur einmal pro Woche angestoßen werden. Der Abstand zwischen 2 Builds bestimmt aber, mit welcher Feinheit der Entwicklungszyklus der Software nachvollzogen werden kann, wie oft die Testabteilung mit neuen Builds versorgt werden kann und wie oft alle unterschiedlichen Teile eines größeren Softwareproduktes miteinander vereinigt werden. Bei größeren Projekten ist es die Regel, dass Builds meistens jede Nacht durchgeführt werden, da es einige Zeit dauern kann, bis ein solcher Build vollständig beendet ist.

Durch die Reproduzierbarkeit von Builds ergeben sich Probleme, die nur mehr über ein automatisiertes Buildmanagement behoben werden können. Dazu zählen unter anderem die Zuordnung des Quellcodes zu einer bestimmten Versionsnummer, die Archivierung von Builds und die Zurücksetzung auf eine frühere Versionsnummer.

Der eine oder andere wird sich jetzt denken, dass nach den oben genannten Punkten ein Build aus dem aktuellen Quellcode erstellt werden kann. Dem ist aber nicht so. Für einen Build gilt leider nicht die folgende, zu einfache, Formel:

  • Build = aktueller Quellcode

Richtigerweise lautet die Formel wie folgt:

  • Build = aktueller Quellcode + Build-Tools

Damit ein Build reproduzierbar ist, muss auch sichergestellt sein, dass auf alle notwendigen Build-Tools zu einem späteren Zeitpunkt zurückgegriffen werden kann. Wird ein Build auf einer Windows-Plattform durchgeführt, zählen darunter die folgenden Dinge:

  • Aktuelle Windows-Version mit allen notwendigen Service Packs

  • Aktuelle Compiler und Compiler-Tools

  • Zusätzliche Tools, die innerhalb des Build-Prozesses verwendet werden

Es ist daher nicht sehr sinnvoll, wenn ein Buildmanagement eingeführt wird, aber die notwendigen Buildtools zu einem späteren Zeitpunkt nicht mehr vorhanden sind, und daher der Build auf einer neueren Software-Plattform nicht mehr durchgeführt werden kann. Die sicherste Methode, um einem Build reproduzierbar zu machen, wäre ein komplettes Festplattenabbild des Servers zu erstellen, auf dem der Build durchgeführt wurde. Das klingt jetzt vielleicht ein wenig übertrieben, wird aber von vielen Softwarefirmen durchgeführt, wenn ein so genannter Release-Build erstellt wird. Ein solcher liegt dann vor, wenn ein Build erstellt wird, der anschließend für den Vertrieb freigegeben wird.

Der Baukasten für ein Buildmanagement

Wie wird jetzt ein Buildmanagement implementiert? Hier gibt es verschiedene Lösungsansätze, aus denen Sie zu wählen haben. Aus C und C++ Zeiten kennen Sie sicherlich die make-Dateien, die Hilfe des nmake.exe-Tools abgearbeitet werden können. nmake.exe ist ein Tool, das gerne verwendet wird, wenn größere Projekte, die unter C oder C++ geschrieben sind, erstellt werden sollen. Der Buildprozess solcher Projekte kann mit Hilfe dieses Tools vereinfacht werden. Ein weiteres beliebtes Tool in .NET-Zeiten ist NAnt (http://nant.sourceforge.net), das auch beliebig erweiterbar ist.

NAnt selbst kann durch den Programmierer erweitert werden und ist daher, was das Buildmanagement betrifft sehr flexibel und leistungsfähiger. Wenn jedoch bestimmte Projekt- bzw. Produktspezifische Schritte durchgeführt werden müssen, wird es am Einfachsten sein, wenn Sie sich Ihr eigenes Build-Skript entwickeln.

Die Vorteile liegen hier ganz klar auf der Hand: Sie können den Buildprozess bis in das kleinste Detail beeinflussen und müssen sich auch in keines der oben genannten Tools einarbeiten. Sie können daher sofort mit eigenen Scripten starten. Ich habe mich dazu entschlossen, dieses Skript mit Hilfe des .NET Frameworks zu erstellen.

Der sorgfältige Leser wird sich jetzt denken: .NET und Skripting - wie soll das denn funktionieren? Genau diese Frage habe ich mir auch gestellt, bis ich auf den .NET-Scripting Host (www.dotnetframework.de) von Holger Schwichtenberg gestoßen bin. Mit Hilfe dieses Scripting-Hosts ist man in der Lage, Xml-Dateien zu schreiben, die C#-Code enthalten, und die während der Ausführung zu einer Assembly kompiliert werden.

Einfach, oder? Während der Entwicklung meiner ersten Skripte habe ich jedoch die Erfahrung gemacht, dass es sinnvoller ist, das Script im ersten Schritt als Konsolen-Anwendung in Visual Studio.NET zu entwickeln und anschließend in eine Xml-Datei für den .NET Scripting Host zu konvertieren. Gründe für dieses Vorgehen sind zum Beispiel der integrierte Debugger von Visual Studio.NET, Syntax-Highlighting und IntelliSense.

Ein weiteres wichtiges Tool für ein automatisiertes Buildmanagement ist Visual Source Safe (VSS) oder ein anderes vergleichbares Quellcodeverwaltungssystem. Das Quellcodeverwaltungssystem können Sie sich als die zentrale Speicherstelle für den Quellcode des Produktes vorstellen Diese wird während des Programmierens immer mit dem aktuellsten Code aktualisiert und dient als Basis für den Buildprozess.

 

Das Build-Script

Was genau macht eigentlich ein Build-Script? In der Einführung dieses Artikels haben sie bereits gesehen, warum ein effizientes Buildmanagement in einen Softwareentwicklungsprozess eingeführt werden sollte, und welche Probleme dadurch gelöst werden können. Das Build-Script, das ich in diesem Artikel vorstellen möchte, umfasst die folgenden Schritte:

  • Setzen der nächsten Build-Number in allen AssemblyInfo.cs Dateien des Produktes

  • Erstellen einer Debug- und Release-Version des Produktes

  • Archivieren des aktuellen Builds

  • Automatisches Generieren von Hilfedateien aus den Xml-Kommentaren von C#-Code

  • Generieren von Hilfedateien, die mit dem Produkt ausgeliefert werden

  • Markieren des Quellcodes in Visual SouceSafe mit der aktuellen Versionsnummer

Die nächste Abbildung zeigt den Workflow, der vom Build-Script abgearbeitet wird.

AutomatisiertesBuildmanagement_1.gif

Der Workflow des Build-Scripts

Wie Sie aus der Abbildung erkennen können, ist ein automatisiertes Buildmanagement ein eher komplexer Prozess, der sehr ins Detail gehen kann.

Ein wichtiger Punkt, über den man sich Gedanken machen sollte, bevor das Script erstellt wird, ist der Aufbau der Verzeichnisstruktur, die verwendet wird, und wie frühere Builds archiviert werden. Der eine oder andere wird sich fragen, warum man sich über eine Verzeichnisstruktur Gedanken machen sollte. Mit einer klaren und einfachen Verzeichnisstruktur ist es möglich, dass auf ältere Builds schnell und einfach zugegriffen werden kann, wenn diese benötigt werden, und dass sich auch alle zukünftigen Builds in einer sauberen Struktur eingliedern können. Eine mögliche Struktur, die zum Einsatz kommen könnte, zeigt die nachfolgende Abbildung.

AutomatisiertesBuildmanagement_2.gif

Eine mögliche Verzeichnisstruktur

Wie Sie aus der Abbildung entnehmen können, gibt es unter dem Wurzelverzeichnis des Projektes ein Verzeichnis mit dem Namen BuildOutput. In diesem Verzeichnis werden die kompletten Builds gespeichert, die im Laufe der Zeit erstellt werden. Dazu gibt es für jede Build-Number ein Unterverzeichnis, dessen Name gleich der Build-Number ist. Innerhalb dieser Build-Verzeichnisse gibt es dann noch jeweils ein Debug- und Release-Verzeichnis für die Debug- und Release-Versionen des Produktes.

Zusätzlich wird durch das Build-Script noch der aktuellste Build der Software, der gerade erstellt wurde, in das Verzeichnis Latest kopiert. Dieses Verzeichnis ist sehr wichtig, wenn ein Build fehlschlagen würde. Das Build-Script ist so aufgebaut, dass vor dem Erstellen der Software das Verzeichnis Latest gesichert wird. Dadurch kann durch einen fehlerhaften Buildvorgang wieder auf die letzte aktuelle Version zugegriffen werden, die ohne Probleme erstellt werden konnte.

In diesem Verzeichnis finden Sie also immer die aktuellste Version, auch wenn einmal ein Build nicht erfolgreich durchgeführt werden konnte. Daher kann sich die Qualitätssicherung z.B. in einem regelmäßigen Intervall immer die neueste Version des Produktes aus diesem Verzeichnis holen, ohne sich Gedanken über die aktuellste Build-Number machen zu müssen, oder ob ein Build fehlgeschlagen ist.

 

Setzen der aktuellen Build-Number

Der erste Schritt, der durch das Build-Script durchgeführt wird, ist das Erzeugen der nächsten Build-Number. Diese wird anschließend in das Attribut AssemblyVersion aller AssemblyInfo.cs-Dateien des aktuellen Projekts eingetragen. Eine typische Versionsnummer einer Software sieht wie folgt aus: 1.0.234.0

Wie Sie sehen, besteht die Versionsnummer insgesamt aus 4 Ziffern, die durch einen Punkt von einander getrennt sind. Die erste Ziffer ist die Hauptversionsnummer, die zweite Ziffer die Nebenversionsnummer, die dritte Ziffer die Build-Number und die 4. Ziffer die Revisionsnummer. In unserem Fall müssen wir jedes Mal, wenn das Build-Script ausgeführt wird, die Build-Number, also die 3. Ziffer um eins erhöhen.

Woher weiß jetzt aber das Build-Script, wie die aktuelle Versionsnummer lautet? Hier gibt es wiederum verschiedene Lösungsansätze: Speicherung in einer Datenbank, in einer Xml-Datei, in einer Textdatei… Ich habe mich für folgenden Lösungsansatz entschieden, der sehr oft gewählt wird: Innerhalb des Verzeichnisses BuildOutput gibt es eine einfache Textdatei. Diese hat einfach den Namen der letzten Build-Number.

Daher sind jetzt die folgenden Schritte notwendig, um die aktuelle Build-Number in allen AssemblyVersion-Attributen einzutragen:

  • Lesen der Textdatei aus dem Verzeichnis BuildOutput

  • Erhöhen der 3. Ziffer um eins

  • Umbennen der Textdatei auf die neue Build-Number

  • Auschecken aller AssemblyInfo.cs-Dateien aus VSS

  • Aktualisieren der Build-Number

  • Einchecken aller AssemblyInfo.cs-Dateien mit der aktuellen Build-Number

Um die nächste Versionsnummer zu ermitteln, habe ich mir die Funktion GetNextVersionNumber geschrieben, die die nächste Versionsnummer als String zurückliefert:

private String strBuildOutputDirectory =  
   "javascript:void(null);"; 
private String GetNextVersionNumber() 
{ 
   // Neue Build-Number ermitteln 
   Console.WriteLine("Getting next version number for current  
   build..."); 
   String [] strFiles = Directory.GetFiles( 
   strBuildOuputDirectory); 
   String strVersionNumber = strFiles[0]; 
   int nPos = strVersionNumber.LastIndexOf(@"\"); 
   strVersionNumber = strVersionNumber.Substring(nPos + 1); 
   strVersionNumber = strVersionNumber.Substring(0,  
   strVersionNumber.Length - 4); 
   String [] strNumbers = strVersionNumber.Split('.'); 
   String strBuildNumber = strNumbers[2]; 
   int nNumber = Int32.Parse(strBuildNumber); 
   nNumber++; 
   String strNewVersionNumber = strNumbers[0] + "." +  
   strNumbers[1] + "." + nNumber.ToString() + "." +  
   strNumbers[3]; 
   File.Move(strBuildOutputDirectory + strVersionNumber + ".txt",  
   strBuildOutputDirectory + strNewVersionNumber + ".txt"); 
   // Neue Versionsnummer zurückliefern 
   return strNewVersionNumber; 
}

Mit Hilfe der Funktion SetVersionNumbersOfAssemblies() werden alle angegebenen AssemblyInfo.cs-Dateien ausgecheckt, das AssemblyVersion-Attribut aktualisiert und anschließend die Dateien wieder eingecheckt.

private String strWorkingDirectory = "javascript:void(null);"; 
private void SetVersionNumberOfAssemblies(String  
   strVersionNumber) 
{ 
   String strAssemblyAttributeOld; 
   String strAssemblyAttributeNew; 
   String strNumbers = strVersionNumber.Split('.'); 
   int nNumber = Int32.Parse(strNumbers[2]); 
   nNumber--; 
   String strOldVersionNumber = strNumbers[0] + "." +  
   strNumbers[1] + "." + nNumber.ToString() + "." +  
   strNumbers[3]; 
   // AssemblyVersion-Attribute zusammenstellen 
   strAssemblyAttributeOld = "[assembly: AssemblyVersion(\"" +  
   strOldVersionNumber + "\")]"; 
   strAssemblyAttributeNew = "[assembly: AssemblyVersion\"" +  
   strNewVersionNumber + "\")]"; 
   UpdateVersionNumberOfAssemblyInfo(strAssemblyAttributeOld,  
   strAssemblyAttributeNew,  
   "$/ProjectA/DataLogic/AssemblyInfo.cs"); 
   UpdateVersionNumberOfAssemblyInfo(strAssemblyAttributeOld,  
   strAssemblyAttributeNew, 
   "$/ProjectA/BusinessLogic/AssemblyInfo.cs"); 
   UpdateVersionNumberOfAssemblyInfo(strAssemblyAttributeOld,  
 strAssemblyAttributeNew, 
 "$/ProjectA/Application/AssemblyInfo.cs"); 
} 
private void UpdateVersionNumberOfAssemblyInfo(String  
   strAssemblyAttributeOld, strAssemblyAttributeNew, String  
   strVSSFile) 
{ 
   // Datei auschecken 
   ExecuteProcess("ss.exe", "Checkout -C -I- \"" + strVSSFile  
   + "\""); 
   // Versionsnummer setzen 
   StreamReader reader = new StreamReader(strWorkingDirectory +  
   "AssemblyInfo.cs"); 
   String strContent = reader.ReadToEnd(); 
   reader.Close(); 
   strContent = strContent.Replace(strAssemblyAttributeOld,  
   strAssemblyAttributeNew); 
   StreamWriter writer = new StreamWriter(strWorkingDirectory +  
   "AssemblyInfo.cs", false, System.Text.Encoding.UTF8); 
   writer.Write(strContent); 
   writer.Close(); 
   // Datei einchecken und löschen 
   ExecuteProcess("ss.exe", "Checkin -C -I- \"" + strVSSFile +  
   "\""); 
   File.SetAttributes(strWorkingDirectory + "AssemblyInfo.cs",  
   FileAttributes.Normal); 
   File.Delete(strWorkingDirectory + "AssemblyInfo.cs"); 
}

Viele Funktionen innerhalb des Build-Scripts verwenden die Funktion ExecuteProcess(), mit deren Hilfe es möglich ist, einen externen Prozess anzustoßen. Im gesamten Verlauf des Build-Scripts wird oft das Kommandozeilentool ss.exe aufgerufen, mit dessen Hilfe es möglich ist, alle Funktionen von Visual SourceSafe von der Kommandozeile aus zu bedienen. Weitere Informationen über die möglichen Parameter und deren Bedeutung finden Sie in der Onlinehilfe zu Visual SourceSafe.

 

Erstellen der aktuellen Version

Nachdem jetzt die Build-Number für den nächsten Build erhöht wurde, steht dem eigentlichen Build-Vorgang nichts mehr im Wege. Zuerst muss jedoch noch der aktuellste Quellcode aus VSS auf dem Build-Server kopiert werden. Dazu wird der folgende Befehl ausgeführt:

ExecuteProcess("ss.exe", "GET \"$/MyProject\" -R -I-");

Dieser Befehl ruft die letzte Quellcodeversion des Projektes MyProject ab. Der Parameter -R gibt an, dass ebenfalls auch der Quellcode aller Unterordner auf den Build-Server kopiert wird. Nach diesem Schritt kann der Build-Vorgang gestartet werden.

Vorher ist es jedoch noch sehr wichtig, den letzten Build, also den Ordner Latest, zu sichern. Dadurch kann sichergestellt werden, dass bei einem fehlerhaften Build-Vorgang der letzte erfolgreiche Build wiederhergestellt werden kann. Um dies zu bewerkstelligen, wird einfach das Verzeichnis Latest in das Verzeichnis LatestBackup umbenannt.

Ein großer Vorteil von VS.NET ist, dass es ebenfalls als Konsolenanwendung verwendet werden kann. Dadurch kann mit einem einzigen Aufruf eine Release- bzw. eine Debug-Version des Projektes erstellt werden. Ein typischer Aufruf sieht wie folgt aus:

ExecuteProcess("devenv.exe", "/rebuild debug  
   \"javascript:void(null);" /out Log_Debug.txt");

Mit diesem Auruf wird eine Debug-Version der Solution MySolution.sln erstellt, und die Ausgaben des Compilers werden in die Datei Log_Debug.txt geschrieben. Mit Hilfe der Ausgaben des Compilers kann anschließend überprüft werden, ob der Build-Vorgang erfolgreich war, oder nicht. Dazu braucht nur die Ausgabedatei geparst und darauf hin überprüft werden, ob die Zeichenfolge 0 failed vorhanden ist. Wenn dies der Fall ist, kann davon ausgegangen werden, dass der Build-Vorgang erfolgreich von statten gegangen ist:

private bool WasBuildSucceeded(String strOutputFile) 
{ 
   // Lokale Variablen deklarieren 
   bool bResu< 
   // Compiler-Output ermitteln 
   StreamReader sr = new StreamReader((System.IO.Stream) 
   File.Open(strOutputFile), System.Text.Encoding.ASCII); 
   sr.BaseStream.Seek(0, SeekOrigin.Begin); 
   String strCompilerOutput = sr.ReadToEnd(); 
   sr.Close(); 
   // Überprüfen, ob der Build erfolgreich war 
   if (strCompilerOutput.IndexOf("0 failed") != -1) 
   { 
   bResult = false; 
   } 
   else 
   { 
   bResult = true; 
   } 
}

Nachdem überprüft wurde, ob der aktuelle Build erfolgreich war, werden anschließend seine Debug- und Release-Verzeichnisse in das Verzeichnis Latest kopiert. Dadurch steht für andere Projektmitglieder wieder eine aktuelle Version des Projektes zur Verfügung, die jetzt zum Beispiel verschiedenen Tests unterzogen werden kann, damit sichergestellt ist, dass sich keine logischen Fehler in den aktuellen Build eingeschlichen haben.

Sehr oft werden in Build-Prozessen auch die Log-Dateien des Kompilierungsvorgangs an den Build-Coordinator versendet. Diese können dann auf Fehler bzw. auf Warnungen untersucht und an die entsprechenden Programmierer weitergeleitet werden:

private void SendMail(String strVersion, bool bSucceeded) 
{ 
   // Neue Email erstellen 
   MailMessage msg = new MailMessage(); 
   msg.From = "<A href="mailto:buildprocess@mycompany.com">buildprocess@mycompany.com</A>"; 
   msg.To = "<A href="mailto:buildcoordinator@mycompany.com">buildcoordinator@mycompany.com</A>"; 
   if (bSucceeded) 
   { 
   msg.Subject = "Build of " + strVersion + " from " +  
   DateTime.Now.ToShortDateString() + " succeeded"; 
   } 
   else 
   { 
   msg.Subject = "Build of " + strVersion + " from " +  
   DateTime.Now.ToShortDateString() + " not succeeded"; 
   } 
   // Log-Dateien als Attachment anhängen 
   msg.Attachments.Add(new MailAttachment("Log_Debug.txt")); 
   msg.Attachments.Add(new MailAttachment("Log_Release.txt")); 
   // Email an den Build-Coordinator versenden 
   SmtpMail.Send(msg); 
}

 

Erstellen von Dokumentationsdateien

Seit dem Erscheinen des .NET Frameworks und von C# ist es möglich, dass C#-Quelltext in einer standardisierten Form dokumentiert werden kann. Diese Kommentare können anschließend während der Kompilierung in eine Xml-Datei umgewandelt werden und mit Hilfe des Tools NDoc (http://ndoc.sourceforge.net) kann daraus ein HTML Help File erstellt werden.

Daher ist es möglich, während des Build-Vorgangs automatisch Dokumentationsdateien aus dem C#-Quelltext zu erstellen. Ich habe mich dazu entschieden, diese Dokumentationsdateien ebenfalls im VSS zu speichern, damit jeder Programmierer immer auf die aktuellste Dokumentation Zugriff hat. Um ein HTML Help File erstellen zu können, muss im ersten Schritt eine Projektdatei angelegt werden. Nähere Informationen dazu finden Sie in der Dokumentation zu NDoc. Während des Build-Prozesses braucht dann nur noch diese Projektdatei der Konsolenanwendung ndocconsole.exe (Bestandteil von NDoc) übergeben werden, um daraus ein HTML Help File zu erstellen:

ExecuteProcess("ndocconsole.exe", "-project=\"MyManual.nDoc");

Nach diesem Schritt muss das erstellte HTML Help File in VSS eingecheckt werden. Um dies zu bewerkstelligen, habe ich mir die folgende Lösung überlegt:

  • Auschecken des HTML Help Files in ein entsprechendes Arbeitsverzeichnis

  • Kopieren des neu erstellten HTML Help Files in das Arbeitsverzeichnis (dadurch wird die alte Version überschrieben)

  • Einchecken des HTML Help Files in VSS

  • Löschen des HTML Help Files im Arbeitsverzeichnis

private void AddDocumentationToVSS() 
{ 
   ExecuteProcess("ss.exe", "Checkout -C -I-  
   \"$/MyProject/MyManual.chm\""); 
   File.Copy(strMyManual, "javascript:void(null);"); 
   ExecuteProcess("ss.exe", "Checkin -C -I-  
   \"$/MyProject/MyManual.chm\""); 
   File.SetAttributes("c:\\MyProject\\MyManual.chm,  
   FileAttributes.Normal); 
   File.Delete("javascript:void(null);"); 
}

Natürlich gibt es neben der Programmierdokumentation auch noch die Dokumentation, die an die Endbenutzer in Form von Hilfedateien ausgeliefert werden. Daher liegt es auf der Hand, diese Hilfedateien ebenfalls automatisch während des Build-Prozesses zu erzeugen und anschließend in die Verzeichnisse zu kopieren, in denen sich die Debug- und die Release-Versionen des Projektes befinden.

Solche Hilfedateien können mit dem HTML Help Workshop erstellt werden, der mit dem Visual Studio.NET ausgeliefert wird. Dabei wird vom HTML Help Workshop wiederum eine Projektdatei mit der Endung .hhp erstellt. Diese kann dem Help-Compiler hhc.exe als Argument übergeben werden:

ExecuteProcess("hhc.exe", "MyHelpFile.hhp");

 

Zusammenfassung

Am einfachsten wird es sein, das erstellte Build-Script über den integrierten Taskplaner von Windows 2000 bzw. Windows XP in einem regelmäßigen Intervall zu starten. Dabei kann angenommen werden, dass das Script jede Nacht laufen sollte, da durch diese Vorgehensweise Integrationsprobleme sehr schnell erkannt und beseitigt werden können.

Anhand des Artikels wurde ersichtlich, dass ein Build-Prozess ein sehr großer und komplexer Vorgang ist, den man bei der Software-Entwicklung jedoch nicht vergessen sollte. Wurde erstmals ein Build-Prozess erfolgreich eingeführt, liegen die Vorteile klar auf der Hand:

  • Es ist immer eine aktuelle Version des Projekts vorhanden

  • Es kann auf frühere Versionen zurückgegriffen werden

  • Es ist immer sichergestellt, dass keine Integrationsfehler vorliegen

  • ...

Ich hoffe, dass ich mit diesem Artikel den einen oder anderen dazu ermutigt habe, in seinen Projekten ebenfalls einen derartigen Prozess einzuführen.