Für ein Konfigurationsmanagement sind nun die Grundlagen entwickelt und die notwendigen Hilfsmittel beschrieben. Im folgenden wird der daraus entwickelte Buildprozess hergeleitet. Zur freien Verfügung hier die build.xml, eine davon abgeleitete produktDolphin.xml und der XPath-Anttask.
Alle Projekte und später Projektbranches liegen flach im Eclipse-Workspace. Parallel dazu liegt ein weiteres Projekt master, in dem das Konfigurationsmanagement seine Arbeit organisiert. Es besteht im wesentlichen aus einem build-Verzeichnis (das von SVN nicht verwaltet wird), einer build.xml und allen davon erbenden produkt.xml.
workspace
+ projekt1
+ src
:
+ projekt2
+ src
:
+ projekt3
:
+ master
+ build (nicht versioniert)
+ build.xml
+ produkt1.xml (importiert build.xml)
+ produkt2.xml (importiert build.xml)
+ produkt3.xml (importiert build.xml)
:
Für jedes Produkt wird ein Ant Buildfile mit dem Produktnamen angelegt und versioniert, welches das Master-File importiert:
<import file="build.xml">
</import>
und einen Produktnamen festlegt
<property name="product.name" value="dolphin">
</property>
Nun kann ein Target angelegt werden, das alle Projekte des Produkts auf dem HEAD-Stand buildet. Die Reihenfolge spielt dabei eine Rolle - sie wird durch die Abhängigkeiten der Projekte untereinander bestimmt. Hier im Beispiel sind nur zwei Projekte zu sehen, es hängt
sub von
base ab:
<target name="buildHEAD">
<!-- zunächst Update auf alle betroffenen Projekte -->
<property name="product.release" value="HEAD">
</property>
<delete failonerror="false">
<fileset dir="build/${product.name}/${product.release}" includes="**">
</fileset>
</delete>
<_build_project projectdir="../base" />
<_build_project projectdir="../sub" />
.. mehr Projekte ..
</target>
Für
_build_project kann die Build-Strategie und eine Projektrevision explizit angegeben werden, hier im Beispiel werden die Standards benutzt (ein einfaches JAR, Revision ist HEAD, Details siehe Macrodef von
_build_project). Das Target kann nun getestet werden. Wenn alles funktioniert wird im Verzeichnis
build
ein Verzeichnis mit dem Produktnamen, dort ein Verzeichnis mit dem Releasenamen angelegt. Dort werden nacheinander die Projekte gebuildet.
Stabile Produktreleases lassen sich aber nicht auf der Grundlage der HEAD Revision der beteiligten Projekte erstellen. Für ein Release müssen feste Revisionen verwendet werden. Deshalb wird nun eine Kopie von buildHEAD angelegt, nun werden explizit die Revisionen des Projekts angegeben:
<target name="build001">
<property name="product.release" value="001">
</property>
<delete failonerror="false">
<fileset dir="build/${product.name}/${product.release}" includes="**">
</fileset>
</delete>
<_build_project projectdir="../base" projectrevision="60" />
<_build_project projectdir="../sub" projectrevision="45" />
..
</target>
Die Revisionsnummern ermittelt man per Hand, oder man nutzt das Macro
_show_head_revision in einem einmalig zu erstellenden Target:
<target name="showHeadRevisions">
<_show_head_revision projectdir="../base" />
<_show_head_revision projectdir="../sub" />
.. mehr Projekte ..
</target>
Nachdem ein Produktrelease so definiert und gebildet wurde, werden die Integrationstests durchgeführt. Gewöhnlich werden solche Tests Fehler aufdecken, die beseitigt werden müssen. Dafür kann unterschiedlich stark eskaliert werden:
- Ist das Release sehr jung, kann möglicherweise die aktuellste Revision des fehlerhaften Projekts das Problem beheben. In diesem Fall wird nur die Revision des betreffenden Projekts angepasst und der Integrationstest für das Release beginnt von vorne.
- Wenn der HEAD des fehlerhaften Projekts zu weit fortgeschritten ist, zum Beispiel weil das Produkt vor langer Zeit ausgeliefert wurde, muss auf der Grundlage der fehlerhaften Revision ein Branch des fehlerhaften Projekts angelegt werden. Auf das Branchen von Produkten wird noch ausführlich eingegangen.
In jedem Fall ist das Erstellen von stabilen Releases ein kreativer Akt. Es müssen dabei nicht automatisierbare Entscheidungen getroffen werden.
Die Wartung und Pflege ausgelieferter Produkte geschieht auf Produkt-Branches. Das Konfigurationsmanagement plant solche Branches und erstellt sie im Bedarfsfalle.
Vorbereitung eines Produkt-Branches: da jedes Produkt durch seine eigene produkt.xml repräsentiert wird, ist der erste Schritt die Anlage und Versionierung der Kopie der Stamm-produkt.xml:
- Auswahl eines geeigneten, stabilen Releases im Sinne des KM Planes.
- Kopie der produkt.xml, diese Kopie erhält den Namen des Produkt-Branches und wird nun für immer dieses Produkt repräsentieren.
- Nun in der produktBranch.xml:
- den Name des Produkts anpassen
- in buildHEAD und showHeadRevisions die Revisionsnummern des stabilen Releases übertragen
- alle anderen build-Targets löschen
- Test von buildHEAD, im Erfolgsfalle wird die produktBranch.xml versioniert.
- Anlegen eines Eclipse-Projektsets für das neue Produkt.
Beginn des Branches: nun wird für jedes Projekt an dem Änderungen durchzuführen sind ein Branch angelegt auf den verwiesen wird:
- Ausführen von buildHEAD - es erfolgt ein Update auf die Revisionen des stabilen Releases.
- SVN Kopie des zu branchenden Projekts - dazu wird Eclipse Funktionalität Team-Branch genutzt. Wichtig ist vor allem die Angabe der Revisionsnummer des Projekts. Das Projekt liegt nun als Kopie im Repository.
- Import als Eclipse Projekt direkt aus dem SVN Repository - der lokale Name des Projekts muss dabei umbenannt werden sonst wird das alte Projekt überschrieben. Der Import regelt auch gleich den Checkout der Arbeitskopie.
- Im Target buildHEAD und showHeadRevisions Umstellen des Projekts auf diesen Namen und HEAD Revision, Test.
Bestehen Abhängigkeiten anderer Projekte zu diesem so erstellten Projektbranch müssen diese in Eclipse entsprechend angepasst werden. Wenn noch nicht geschehen, sind diese Projekte dann notwendigerweise auch zu branchen. Ab nun kann auf dem Produkt-Branch normal gearbeitet werden.
Ein großer Vorteil dieses Vorgehens ist, dass nur von jenen Projekten ein Branch angelegt werden muss, in denen auch wirklich Anpassungen zu erfolgen haben (oder wenn Abhängighkeiten zu solche Projekten bestehen). Das erleichtert die Pflege von Branches ungemein - denn jeder Projektbranch muss potenziell in seinen Ursprung gemerged werden. Man kann auch testweise auf nicht gebranchten Projekten Änderungen vornehmen. Natürlich darf man diese Änderungen nicht committen - stattdessen legt man dann einfach einen Projektbranch auf Basis der Working Copy des Projekts an und verwirft anschließend die Änderungen.
- Die Modularisierung bleibt komplett in der Verantwortung der IDE - hier wird das Produkt auf Eclipse-Projekte verteilt, deren Abhängigkeiten zueindander in Buildpath-Konfiguration verwaltet werden. Die Arbeit an einem Produkt kann ohne jede Abhängigkeit zu anderen Produkten, Frameworks, Buildsystemen oder Plugins erfolgen. Die Entwicklungsarbeit auf stabilen Projektständen muss nicht über die Projekt-JARs laufen wenn versionierte Stände jederzeit reproduziert werden können. Bei der Arbeit mit Eclipse steht so zwanglos jederzeit die gesamte Infrastruktur der IDE und der Projekte zur Verfügung (Sourcen, Suchfunktionen, Debugunterstützung).
- Das Konfigurationsmanagement stellt keinerlei Anforderungen an die Projekte - nicht einmal Ant-Targets werden in den Projekten voraussgesetzt. Lediglich ein paar Konventionen sind nützlich, dann kann man Buildtargets für jeden Projekttyp gleichermaßen verwenden - praktisch ginge es aber auch ohne.
- Mit dem beschriebenen Vorgehen lassen sich Produktreleases ohne Beeinflussung der laufenden Entwicklung in einem definierten und leichtgewichtigen Verfahren durchführen. Vor, während und nach der Release-Erstellung müssen Entwickler keinerlei Änderungen an den Projekten durchführen.
- Tags für das Erstellen eines Releases exisitieren nur virtuell - als die versionierte Revisionsnummer des betreffenden Projekts. Auf Kopien (und die damit verbundenen konzeptionellen Probleme) wird komplett verzichtet. Eine Revision kann jederzeit, schnell und zuverlässig in der Working Copy rekonstruiert werden und die Projektrevisionen eines Produkts sind in der seiner produkt.xml hinterlegt und selbst versioniert.
- Über Eclipse-Projektsets (die auch per Produkt erstellt und gepflegt und versioniert werden) steht ein Mechanismus zur Verfügung, mit dem unkompliziert Produkt-Arbeitsstände für die IDE verwaltet werden können. Das Einrichten einer einsatzbereiten Entwicklungsumgebung dauert Sekunden!
- Für einen Produktbranch sind nicht notwendig alle Projekte zu branchen. Nur die Projekte, an denen wirklich Änderungen vorgenommen werden muss man branchen.
- Für das Anlegen neuer Branches kann komplett die sehr komfortable Eclipse-Funktionalität verwendet werden (Team - Branch / Import - SVN Repository).
- Die durch Produktreleases erzeugten Artefakte müssen nicht selbst versioniert werden, denn sie können jederzeit neu gebaut werden.
- Die Eindeutigkeit der Releasenummern ist über die Eindeutigkeit der Targetnamen garantiert.
- Buildautomatisierung (nightly builds etc.) ist leicht umsetzbar.
- Alle Projekte eines Produkts und alle ProjektBranches liegen gemeinsam, flach in einem Workspace-Ordner und in einem Repository-Verzeichnis.
- Alle Sources liegen in src.
- Produkte und Produkt-Branches bekommen 'poetische' Namen.
- Releases bekommen einfache Nummern, die hochgezählt werden.
- ProjektBranches bekommen den Namen des Projekts und einen Zusatz, zum Beispiel eine zweistellige Nummer.
- Eine Redundanz bleibt: Die Zusammensetzung eines Produktreleases aus Projekten ist einmal in Eclipse, zum anderen in der projekt.xml hinterlegt. Aber: diese Zusammensetzung ändert sich selten, manuelle Pflege kein Problem.
- Es ist ein Ant-Task (der XPath-Ausdrücke auswertet) bereitzustellen. Hier ist er eine Eigenentwicklung, andere Lösungen sind denkbar. Schade dass diese Basisfunktionen nicht Ant-Standard ist.
- Die gemeinsame Nutzung gleicher Projekte in verschiedenen Produkten kann zu inkonsisenten Ständen im Workspace führen. Die IDE kann natürlich immer nur ein Produkt konsistent compilieren. Das ist aber nicht spezifisch für diesen Prozess, sondern der Preis, den man zahlt wenn unterschiedliche Produkte in einem Workspace bearbeitet werden. Die einfachste Lösung: lokales Löschen der nicht benutzten Produkte - sie lassen sich sekundenschnell über Projektsets wiederherstellen. Noch konsequenter ist die Einrichtung eigener Workspaces für jedes Produktrelease.
- Lazy Build - nur die Artefakte, die sich geändert haben werden gebuildet - ist nicht ohne Schwierigkeiten umsetzbar. Hier ist anzumerken, dass das nur bei der Erstellung eines Releases, NICHT bei der täglichen Entwicklungsarbeit eine Rolle spielt. Die Entwicklung findet immer auf dem HEAD des jeweiligen Produkts statt und der inkrementelle Build wird stabil von der IDE bereitgestellt.