Visual Studio Build-Prozesse automatisieren

Es ist oft das gleiche Problem: Ein Build wird erstellt und nun soll unter bestimmten Bedingungen das Ergebnis irgendwo hin gesynct werden. Viele benutzen immer noch altehrwürdige Script-Dateien, die sie mehr oder weniger erfolgreich und reibungslos in die Eigenschaften des Build-Tabs im Visual Studio einbinden. Mit dem „MSBuild Extension Pack“ gibt es aber bereits seit Längerem eine bessere Alternative.

Vorbemerkungen

Die Aufgabe, die ich hier dargelegen möchte, besteht darin, direkt während bzw. nach dem Build im Visual Studio Prozesse anzustoßen, die etwas mit dem Build-Ergebnis tun. Es ist wichtig, zu erwähnen, dass die hier gezeigten Techniken eigentlich nicht auf dem Client (also dem Developer-Rechner) durchgeführt werden sollten. Heutzutage nutzt man Continious Integration oder ähnliche Prozesse auf zentralen Build-Servern. In unserer schönen .NET-Welt bringt der Team Foundation Server solche Techniken in Form von Build-Controllern mit. Diese beruhen technisch genau wie das VS auf MSBuild, sodass das hier gezeigte ohne Weiteres dorthin übertragen werden kann.

Die alte Welt

Befor das VS so erweiterbar wurde, wie es heute zum Glück ist, musste man sich mit den gebotenen Mitteln behelfen. Abb. 1 zeigt die Projekteigenschaften eines VS2010-Projektes im Tab Buildereignisse:

Abb. 1: Buildereignisse im VS

Hier wurde im Bereich „Postbuildereignisse“ ein call-Befehl hinterlegt, der ein Script startete. Das Script zeige ich jetzt nicht, aber meist war es irgendwas mit xopy oder robocopy, das dafür verwendet wurde, den Build-Output in irgendeinen zentralen Ordner zu syncen.

Das Problem daran ist aber, dass wir hier quasi unsere schöne Welt verlassen und einen Medienbruch begehen. Das Script hatte keine Ahnung von MSBuild oder anderen VS-Eigenheiten und kopierte stumpf von A nach B. Sind Fehler aufgetreten, hat man meist den %errorlevel% abgefragt und doch wieder ignoriert. Also eher schlecht, als recht.

MSBuild ExtensionPack

Wie das mittlerweile so ist bei Microsoft, findet sich auch hier die Lösung unserer Probleme bei codeplex. Es handelt sich hier um ein Projekt, dass sich ganz der eleganten Erweiterung von MSBuild-basierten Prozessen widmet. Die Dokumentation ist sehr gut gelungen und beim Download kommen sogar Installer mit.

Installation

Die Installation ist eigentlich selbst erklärend. Nur ein paar Dinge sollte man beachten.

Abb. 2: Der Ordner für die Installer

In Abb. 2 habe ich den Ordner für die Installer der .NET-Version 4 markiert. Diese sollte man benutzen, wenn man das entsprechende Framework einsetzt. Entpackt man diesen Ordner findet man je eine Datei für x64 und x86. Da die meisten Entwickler heutzutage auf einem 64-bit-Windows arbeiten, ist die Versuchung groß, nur die x64-Version zu installieren.

Bei mir führte das allerdings später zu einer Fehlermeldung, weil das VS selbst ja keine x64-App ist und er immer wieder versucht hat, aus dem x86-Programme-Ordner Dateien zu laden. Ich habe dann einfach die x86-Version dazu installiert und alles hat geklappt.

Ansonsten bringt die Installation selbst erstmal nichts Neues im optischen Sinne. Das VS sieht immer noch genauso aus.

Einsatz

Für unseren Einsatzzweck findet die Magie komplett innerhalb der *.csproj-Datei des Projektes statt, dessen Build-Vorgang wir automatisieren wollen. Hat man mehrere Projekte (z.B. in einer geschichteten Anwendung), ist es fast immer das Startprojekt mit der UI, das automatisiert werden soll.

Leider gibt es derzeit noch keine elegente Methode, die VS-Projektdateien zu bearbeiten, während es im Studio geladen ist. Ein wenig Unterstützung gibt es aber doch:

  1. Projekt im VS öffnen.
  2. Rechtsklick auf das entsprechende Projekt und Auswahl von „Projekt entladen“ (Abb. 3).
  3. (Das Projekt wird jetzt ausgegraut gezeigt)
  4. Rechtsklick auf das ausgegraute Projekt und Auswahl von „Bearbeiten {Projektname}“ (Abb. 4)
  5. (Die csproj-Datei ist im XML-Editor geöffnet und kann geändert werden.)
  6. Änderungen in csproj-Datei durchführen und speichern.
  7. Rechtsklick auf das ausgegraute Projekt und Auswahl von „Projekt erneut laden“.
  8. (Sollte die csproj-Datei noch im Editor geöffnet sein, fragt das VS nach, und schließt sie automatisch
Abb. 3: Entladen eines Projektes
Abb. 4: csproj-Datei bearbeiten

Mit diesem Wissen bewaffnet können wir uns nun den Anpassungen widmen.

Da wir hier lediglich ein kleines Beispiel unter Zuhilfenahme von robocopy (ist bei Windows 7 immer dabei) entwickeln wollen, interessiert uns auch erstmal nur der Teil, der unter diesem Link einzeln beschrieben ist.

Zunächst einmal müssen wir dem Projekt bekannt machen, dass wir das ExtensionPack zu nutzen gedenken. Das erledigt man sehr einfach durch einfügen des folgenden Snippets in die csproj-Datei unterhalb des einleitenden <?xml…>:

<PropertyGroup>
  <TPath>$(MSBuildProjectDirectory)\..\MSBuild.ExtensionPack.tasks</TPath>
  <TPath Condition="Exists('$(MSBuildProjectDirectory)\..\..\Common\MSBuild.ExtensionPack.tasks')">$(MSBuildProjectDirectory)\..\..\Common\MSBuild.ExtensionPack.tasks</TPath>
</PropertyGroup>
<Import Project="$(TPath)"/>

Jetzt geht es weiter im unteren Bereich der Datei. Dort findet man einen auskommentierten Teil mit im Standard 2 <Target>-Tags. Eins der Tags kümmert sich um Aktionen vor dem Build (BeforeBuild) und das andere für diejenigen nach einem erfolgreichen Build (AfterBuild).

Uns interessiert hier das AfterBuild-Target, weil wir ja nur dann kopieren wollen, wenn unser Ergebnis kompiliert vorliegt. Hier die exemplarischen Anpassungen:

<Target Name="AfterBuild">
  <MSBuild.ExtensionPack.FileSystem.RoboCopy Source="C:\Temp\MSBuildExtensionTest\bin\Release" Destination="c:\temp\test" Files="*.*" Options="/MIR /XO /XF *.xml OASE.vshost* *.pdb">
    <Output TaskParameter="ExitCode" PropertyName="Exit" />
    <Output TaskParameter="ReturnCode" PropertyName="Return" />
  </MSBuild.ExtensionPack.FileSystem.RoboCopy>
  <Message Text="ExitCode = $(Exit)" />
  <Message Text="ReturnCode = $(Return)" />
</Target>

Die einzige Anpassung, die man hier noch einbringen sollte, ist das Ändern der Attribute „Source“ und „Destination“ im Robocopy-Tag. Als Source gibt man das Ausgabeverzeichnis des Projektes an, als Target das gewünschte Zielverzeichnis.

Die Optionen von Robocopy beudeuten im einzelnen:

  • /MIR: Spiegeln des kompletten Verzeichnisses und aller seiner Unterordner
  • /XO: Nur überschreiben, wenn in Source keine neuere Datei gleichen Namens ist
  • /XF: Eine durch Leerzeichen getrennte Liste von Dateien und Dateipattern, die NICHT übertragen werden soll

Das Attribut Options orientiert sich also an der CommandLine-Referenz von Robocopy und kann beliebig angepasst werden.

Das schöne ist nun u.a., dass man über die Output- und Message-Tags Robocopy-Warnungen usw. abfangen kann, sodass das VS nicht in Build-Fehlern endet, wenn Robocopy fehlschlägt.

Wenn man nun das Projekt neu lädt und den Build-Prozess startet, entsteht im Ordner (im Beispiel C:\temp\test) die gewünschte Ausgabe der Dateien.

Aber nur bei Release, bitte!

Das letzte verbleibende Problem ist nun, dass diese Aktion immer ausgeführt wird. Egal, ob Debug- oder Release-Konfiguration: Es wird immer alles in den Target-Ordner syncronisiert, was in den meisten Fällen eher sinnlos sein dürfte.

Die Lösung bringt das VS-Schema der csproj-Datei bereits mit. Das Target-Tag verfügt neben vielen anderen auch über ein Conditions-Attribut, das wir wie folgt nutzen können:

<Target Name="AfterBuild" Condition="'$(Configuration)' == 'Release'">

Jetzt wird das Robocopy nur noch ausgeführt, wenn man als Konfiguration Release auswählt. Man kann hier übrigens mit logischen Operatoren arbeiten, wenn man muss:

<Target Name="AfterBuild" Condition="'$(Configuration)' == 'Release' or '$(Configuration)' == 'Release2'">

Zu achten ist vor allem auf das richtige Setzen der einfachen Hochkommata!

Resumé

Wer sich die volle Dokumentation des MSBuild Extension Pack ansieht, wird feststellen, dass wir hier wirklich fast nichts dazu gesehen haben. Die Möglichkeiten sind vielfältig und sehr gut durchdacht. Exemplarisch seien die Anbindung an SQL-Server, Subversion, TFS oder auch CodeQuality genannt. Man gerät schnell ins Träumen ob der Perspektiven, die sich auftun. Aber bitte nicht vergessen: Sowas sollte immer auf zentralen MSBuild-Servern konfiguriert werden und nicht im VS!

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.