Build-Automatisierung für RCP-Projekte

Die RCP-Anwendung am laufenden Band

Karsten Panier und Lars Gehrken

Ein Erfolgsfaktor von Eclipse ist die Verwendung der Continuous-Integration-Technik. Grundlegend für eine kontinuierliche Integration ist der automatisierte Build auf Basis des aktuellen Repository-Standes. Bei Eclipse RCP-Anwendungen ist diese Automatisierung nicht trivial, da weder Ant noch Maven ausreichen. Es werden daher die Eclipse PDE-Build-Tools selbst benötigt. Wie man diese einsetzt, zeigen wir Ihnen am Beispiel der RCP-Anwendung StIXDB (Structure Information Cross Databases) [1], bei der zunächst mit dem Bau eines einzelnen Plug-ins gestartet wird, um am Ende eine komplette RCP-Anwendung rein Script-gesteuert veröffentlichen zu können.

Wie Eclipse selbst bestehen RCP-Anwendungen aus Plug-ins, die in einer OSGi Runtime installiert werden. Die Abhängigkeiten eines Plug-ins von anderen Komponenten werden innerhalb derManifest.MF des Plug-ins konfiguriert. Somit finden wir darin auch die für den Build benötigten Classpath-Informationen. Diese Informationen redundant im Build-Prozess zu pflegen, wäre fehleranfällig und unnötig aufwendig. Daher übernimmt die Eclipse IDE diese Arbeit, indem sie es uns ermöglicht, ein Ant-Build-Script für ein Plug-in über das Kontextmenü zu generieren.

Der entsprechende Menüpunkt PDE-TOOLS | CREATE ANT BUILD FILE steht nach der Selektion von plugin.xml, build.properties oder Manifest.MF zur Verfügung. Ein Blick auf das generierte Skript zeigt, dass Eclipse die OSGi-Bundle-Abhängigkeiten ausgewertet und im Skript aufgelöst hat. Starten können wir das Skript über RUN AS ANT BUILD.

Wenn wir dabei als Target zip.plugin wählen, bekommen wir ein fertig paketiertes Plug-in. Das Zielverzeichnis für dieses Plug-in können wir mit der Property plugin.destination= an das Script übergeben.

Abb. 1: Run an Ant Build-Script-Dialog

Feature Build

Dieser Mechanismus funktioniert ebenfalls auf Feature-Ebene. Wie bei einem Plug-in kann man das Script über das Kontext-Menü generieren. Man erhält ein Ant Script, welches die Ant Scripts der enthaltenen Plug-ins aufruft. Falls die Plug-in-Scripts noch nicht existieren, werden diese jetzt automatisch erzeugt. Wenn wir das Ant Script eines Features aufrufen, wird das Feature mit seinen Plug-ins gemäß den Angaben in der Manifest.MF und build.properties gebaut.

Headless Build

Bisher haben wir den Build aus der IDE heraus gestartet und damit noch keinen wirklichen Vorteil gegenüber der Wizard-gestützten Export-Funktion erreicht. Jetzt soll der Build aber „headless“ ohne GUI-Tools ausgeführt werden, um ihn beispielsweise nachts automatisch auszuführen. Soweit scheinbar kein Problem: Wir haben ja ein Ant Script – und Ant selbst kann man bei Apache [2] herunterladen und installieren.

Beim Aufruf des Scripts mit Ant von der Kommandozeile aus startet der Build-Prozess, wie zuvor gesehen, bricht aber unvermittelt ab. Unser frisch installiertes Ant liefert folgende Fehlermeldung:Could not create task or type of type: eclipse.idReplacer. Was ist passiert? Nach kurzer Recherche stellen wir fest, dass die Ursache fehlende Ant-Tasks sind. Diese Ant-Tasks sind Teil der Eclipse-Plattform und können nicht losgelöst von dieser verwendet werden. Doch auch für dieses Problem haben die Entwickler von Eclipse eine Lösung parat, den Ant-gesteuerten PDE Build.

[ header = Seite 2: Eclipse PDE Build ]

Eclipse PDE Build

Um mit PDE Build zu starten, werfen wir ein Blick in die Eclipse-Hilfe. Hier finden wir unter PLUGIN DEVELOPMENT ENVIRONMENT GUIDE | TASKS [3] Anleitungen zum Bauen von Eclipse-Plug-ins, Features und RCP-Anwendungen.

Schauen wir uns zunächst den Artikel zum Feature an. Hier wird auf die Vorlagen im Eclipse PDE-Build-Plug-in org.eclipse.pde.build verwiesen. Dieses Plug-in enthält eine Applikation zum Starten von Eclipse im Headless Mode als Ant-Anwendung ohne GUI. Im Unterverzeichnis scripts/templates/headless-build stehen Vorlagen zur Konfiguration des PDE-Build-Systems bereit. Diese Dateien müssen wir an unser Projekt anpassen.

Es hat sich bewährt, diese Dateien in einem extra Eclipse-Projekt zu verwalten. Eine gängige Bezeichnung für ein solches Projekt ist build. Also in unserem Beispiel de.aysada.stixdb.build für die Anwendung mit dem Haupt-Plug-in de.aysada.stixdb. In dieses Projekt kopieren wir die Vorlage allElements.xml und passen sie an unsere Anwendung an. Dafür müssen lediglich das zu bauende Feature im Target allElementsDelegator angegeben und der Name des assemble-Targets angepasst werden.

<target name="allElementsDelegator">
<ant antfile="${genericTargets}" target="${target}">
<property name="type" value="feature" />
<property name="id" value="de.aysada.stixdb.feature" />
</ant>
</target>
<target name="assemble.de.aysada.stixdb.feature">
<ant antfile="${assembleScriptName}" dir="${buildDirectory}/">
</target>

Die zweite Datei, die wir aus dem scripts/templates/headless-build-Ordner kopieren, ist die build.properties. Hier werden grundsätzliche Einstellungen und zum Teil auch Pfade konfiguriert. Für den Feature Build sind folgende Properties relevant:

  • buildDirectory: zeigt auf den Quellcode des zu bauenden Features
  • baseLocation: zeigt auf eine vollständige Eclipse-Installation, die den Build durchführt
  • buildfile: verweist auf die generische build.xml vom PDE Build
  • builder: verweist auf die Wurzel des Build-Projektes
  • archivePrefix: gibt den Namen des Verzeichnisses an, in das die Build-Ergebnisse geschrieben werden.

Es hat sich als vorteilhaft erwiesen, vom System abhängige Pfade beim Start des Builds per Parameter mit -D zu übergeben. So ist die Property-Datei unabhängig von ihrer Umgebung und das Build-Projekt kann auf unterschiedlichen Systemen genutzt werden.

Nachdem wir die beiden Dateien angepasst haben, können wir das Eclipse-Build-System aufrufen, indem wir die Eclipse-Anwendung org.eclipse.ant.core.antRunner starten. Im nachfolgenden Listing ist der vollständige Aufruf als Batch-Datei realisiert.

java -cp %ECLIPSE%startup.jar org.eclipse.core.launcher.Main
-application org.eclipse.ant.core.antRunner
-DbaseLocation=%ECLIPSE%
-buildfile %ECLIPSE%/plugins/org.eclipse.pde.build_%PDE_BUILD%/scripts/build.xml
-DbuildDirectory=c:buildworking
-Dbuilder=C:buildbuilderde.aysada.stixdb.build

Damit dieser Aufruf erfolgreich ist, müssen die Sourcen des Features und die zu bauenden Plug-ins wie folgt im buildDirectory bereitliegen:

buildDirectory
features
de.aysada.stixdb.feature
plugins
de.aysada.stixdb
de.aysada.stixdb.core

Nun passt diese Struktur nicht zu der des üblichen Workspaces, da dort in der Regel Features und Plug-ins auf gleicher Ebene im Root des Workspaces liegen. Dieser Umstand stört uns nicht weiter, da wir uns die Sourcen bei einem Integrations-Build am besten immer aktuell aus dem Repository holen. Hierfür gibt es im Eclipse-Build-System das Konzept der Map-Dateien. Diese Map-Dateien beschreiben, wo im Repository sich die zu bauenden Features und Plug-ins befinden. Für diese Dateien muss ein Unterverzeichnis maps im Build-Verzeichnis angelegt werden, und es muss dort mindestens eine Datei mit der Endung .map liegen. Mithilfe dieser Datei kann der Build-Prozess die Sourcen automatisch aus einem Repository beziehen und in die für den Build notwendige Struktur im Build-Verzeichnis kopieren. Eclipse liefert von Haus aus lediglich eine Implementierung für CVS mit. Für andere Repositorys müsste ein entsprechendes Plug-in verwendet werden. Eine CVS-Map-Datei wird nach folgendem Schema aufgebaut:

<elementType>@<elementID> = CVS, <TAG>, <CVSROOT>[,<PASSWORD>[,<PATH>[,<CVSPASSFILE>]]]

Für das gewählte Beispiel sieht die Map-Datei folgendermaßen aus:

feature@de.aysada.stixdb.feature=HEAD,:ext:stixdb.aysada.de:/cvsroot/develop/projects/stixdb,
,de.aysada.stixdb.feature
plugin@de.aysada.stixdb=HEAD,:ext:stixdb.aysada.de:/cvsroot/develop/projects/stixdb,,de.aysada.stixdb
plugin@de.aysada.stixdb.core=HEAD,:ext:stixdb.aysada.de:/cvsroot/develop/projects/stixdb,,de.aysada.stixdb.core

In unserem Beispielprojekt nutzen wir das alternative Versionierungssystem Subversion. Daher haben wir den Fetch-Mechanismus mit der Property skipFetch deaktiviert und das Auschecken der Sourcen in ein eigenes Script ausgelagert. Einen kurzen Exkurs zur Verwendung anderer Versionierungssysteme im PDE-Build-Prozess finden Sie im Kasten „Andere Versionierungssysteme im Build-Prozess“.

[ header = Seite 3: Andere Versionierungssysteme ]

Andere Versionierungssysteme im Build-Prozess
Da die Eclipse IDE standardmäßig nur CVS unterstützt, stehen Nutzer der „wenigen“ Alternativen zumeist ein wenig im Regen, wenn sie diese mit dem für CVS gegebenen Komfort verwenden möchten. Damit wir dennoch im Build ein alternatives Versionierungssystem wie Subversion verwenden können, gibt es verschiedene Möglichkeiten, dieses in den Build-Prozess zu integrieren. Die sicher eleganteste Lösung wäre die Entwicklung eines Plug-ins für Eclipse, in dem wir den Extension Point org.eclipse.pde.build.fetchFactories erweitern. Hierdurch würde dem Build-Prozess das alternative System genau wie CVS zur Verfügung stehen. Allerdings ist der Aufwand hierfür nicht unbeträchtlich, sodass man dieses besser den Projekten überlässt, die sich damit beschäftigen, Teamprovider für die alternativen Systeme zu entwickeln. In unserem Beispiel Projekt StIXDB wären die Projekte Subversive und Subclipse die passenden Ansprechpartner. Wenn man sich dennoch die Arbeit macht, wären die Entwickler dieser Team-Provider und andere Nutzer dankbar für den Quellcode, bevor dieselbe Funktionalität von vielen entwickelt wird. Für das eigene Projekt, das den Build-Prozess nur verwenden will, ohne ihn grundlegend zu erweitern, gibt es zwei vergleichsweise unaufwendige Lösungen.

  • Man bindet ein eigenes Ant Target in die CustomTargets.xml ein. Dieses kann man dann mit einem passenden Hook Ant Call aufrufen.
  • Man verwendet ein Script, welches den PDE Build selbst als Java-Execute-Aufruf startet. In diesem Script kann man dann vor dem Start des PDE Build die Sourcen aus dem Repository auschecken.

In unserem Beispielprojekt haben wir uns für die letzte Möglichkeit entscheiden und checken bereits vor dem Aufruf des Eclipse-Build-Systems die Sourcen mit einem eigenen Ant Script in die richtigen Verzeichnisse aus. Anschließend startet das Ant Script das Build-System wie im Artikel beschrieben mit den systemabhängigen Pfaden als Parameter. Nach dieser Anpassung informieren wir den PDE Build darüber, dass er sich um das Auschecken der Sourcen nicht kümmern muss, indem wir in den build.properties die Property skipFetch auf true setzen.

<target name="fetch-nightly" depends="clean-nightly">
<svn>
<checkout url="${stixdb.trunk.url}/de.aysada.stixdb.feature" revision="HEAD" 
destPath="${nightlybuild.dir}/features/de.aysada.stixdb.feature" /> ... </svn> </target>

Nach erfolgreichem Build finden wir in unserer Build Directory ein neues Verzeichnis, das gemäß der Property archivePrefix in den build.properties benannt ist. In diesem Verzeichnis befindet sich ein Zip-Archiv, das aus dem Feature und den dazugehörigen Plug-ins besteht. Über die Property outputUpdateJars kann man steuern, ob die Plug-ins als JAR-Archive für eine Update Site oder für die direkte Installation in Eclipse gebaut werden sollen.

Bei der RCP-Entwicklung kann es notwendig sein, einige Plug-ins plattformspezifisch zu bauen. Das bekannteste Beispiel hierfür ist SWT, da hier je nach Plattform andere native Bibliotheken nötig sind. Ob ein Plug-in oder Fragment für eine bestimmte Plattform entwickelt ist, und wenn für welche, wird in dem Feature angegeben. Der Build muss nun diese Information auswerten, um dem Client das für ihn maßgeschneiderte Feature zu bauen. Dieses Problem lässt sich mit geringem Aufwand lösen. Zunächst müssen wir die Property configs in den build.properties erweitern. Bei dem Wert dieser Property handelt es sich um eine Liste von Tripeln, die die Zielplattform nach dem Schema , , angeben. Man ersetzt die plattformunabhängige Konfigurationsangabe „*, *, *“ durch beispielsweise win32,win32,x86 oder macosx,carbon, ppc. Nach dieser Änderung stellen wir fest, dass der erneute Build wie zuvor fehlende Ant-Targets reklamiert. Es ist nun nötig, in derallElements.xml für das angegebene System einen assembly-Task wie folgt zu erstellen:

<target name="assemble.de.aysada.stixdb.feature.win32.win32.x86">
<ant antfile="${assembleScriptName}" dir="${buildDirectory}/">
</target>

Um in einem Build unser Feature für mehrere Plattformen zu erzeugen, müssen wir in der Property configs für jede gewünschte Plattform ein solches Tripel angeben. Die einzelnen Tripels werden dann durch „&“ getrennt aufgeführt. Damit der Build nicht wieder abbricht, müssen wir zusätzlich für jedes Tripel ein entsprechendes assemble Target in allElements.xml hinzufügen. Damit die Liste in der Property leichter lesbar wird, kann man jede Konfiguration in eine eigene Zeile schreiben, wobei hierfür die vorhergehende Zeile mit „“ abgeschlossen wird. Damit erhalten wir nachfolgenden Eintrag für unser Beispielprojekt in den build.properties.

configs=win32, win32, x86 & 
macosx, carbon, ppc &
linux, gtk, ppc

[ header = Seite 4: Produkt-Build ]

Produkt-Build

Mit dem, was wir bisher umgesetzt haben, sind wir für fast alle Anforderungen an ein Build-System gerüstet, solange nur Features für eine existierende Plattform gebaut werden müssen. Möchten wir aber eine eigene RCP-Anwendung inklusive Launcher und Startdatei mit eigenem Icon erstellen, sind noch einige weitere Schritte zu tun, denn hier ist ein so genannter Produkt-Build nötig. Dieser Produkt-Build übernimmt das Branding der Applikation und erstellt ein Zip-Archiv, das eine ausführbare Anwendung enthält.

Bei einem Produkt-Build muss beachtet werden, dass Eclipse selbst immer nur für eine Zielplattform zur Verfügung gestellt wird. Dies gilt somit auch für RCP-Anwendungen auf Basis der Eclipse-Technologie. Damit nun der Build die unterschiedlichen Zielplattformen berücksichtigen kann, werden die entsprechenden, nativen Komponenten zusätzlich benötigt. Daher müssen wir diese Fragmente und das Launcher-Plug-in installieren. Die befinden sich im Eclipse RCP Delta Pack [4]. Nachdem unsere Eclipse-Installation nun über die benötigten Plug-ins verfügt, müssen wir unser Projekt anpassen. Der Produkt-Build kann die Anwendung nur dann vollständig paketieren, wenn neben dem eigenen Feature auch die von ihm benötigten Features oder Plug-ins in der .product-Datei konfiguriert sind. Jetzt müssen wir in build.properties noch die Property product auf die gerade bearbeitete product-Datei unserer RCP-Anwendung ändern.

product=/de.aysada.stixdb/stixdb.product

Anschließend ändern wir im Script für den Aufruf des Ant Runner noch den Wert der Property buildfile auf den Pfad zur ProductBuild.xml. Diese Build-Datei finden wir im Unterverzeichnis product des PDE-Build-Plug-ins. Wenn jetzt der Build gestartet wird, wird jeweils ein Archiv für jedes System erzeugt, das in der Property Configs angegeben ist. Die Archive werden in einem neuen Unterverzeichnis abgelegt. Der Name dieses Verzeichnisses ergibt sich aus den Properties ..

Eigenanpassungen des Builds

Für eigene Erweiterungen des Builds kann man sich aus dem Ordner scripts/templates/headless-build die customTargets.xml ins Build-Projekt kopieren. Diese Datei enthält Ant Targets, die als Pre- oder Post-Hooks für die unterschiedlichen Phasen des Builds fungieren. So lässt sich hier z.B. der Aufruf von Tests im Hook postBuild integrieren, was in einem späteren Artikel näher beleuchtet wird. Ein weiteres Beispiel wäre die Verwendung des Targets preFetch für das Auschecken der Sourcen, wie im Kasten beschrieben.

Fazit

Das Eclipse-Build-Framework erlaubt es, mit geringem Konfigurationsaufwand Anwendungen für verschiedene Plattformen automatisiert zu bauen. Für Anpassungen, die über das gezeigte Verhalten hinaus gehen, kann man mithilfe der customTarget.xml individuell vorgehen. Dazu sollte man aber das Build-System von Eclipse verstanden haben. Im Zweifelsfall kann man den Eclipse-Build auch im Debug-Modus starten, wie im Online-Artikel auf eclipse.org [5] beschrieben.

Dem Entwickler steht mit dem PDE-Build-System ein flexibles und für den Leistungsumfang gut handhabbares System zur Verfügung. Leider können fehlerhafte oder unterschiedliche Einstellungen in den Features und der Produktkonfiguration dafür sorgen, dass das Verhalten des automatisierten Builds von dem der Exportfunktion abweicht. Diese Fehler zu finden ist nicht gerade einfach, da die Fehlermeldungen des Build-Systems hier oft irreführend sind. Wie die Anforderung 93011 im Bugzilla [6] zeigt, arbeitet die Eclipse-Community weiter daran, den Build für RCP-Anwendungen zu vereinfachen.

Man sollte ebenfalls ein kritisches Auge auf die Plug-ins von Drittanbietern haben, die man in Eclipse für den Build verwendet. Da nicht alle Plug-ins eine saubere Trennung von Logik und User Interface (UI) vornehmen, kann es vorkommen, dass ein Plug-in im Headless Mode auf UI-Elemente zugreifen möchte und so das Build-System zum Absturz bringt. Das hier gezeigte Beispiel kann als komplette Umsetzung mit Tests in einer Cruise-Control-Installation auf der StIXDB-Projekt-Seite [1] nachgelesen werden.

Dipl.-Inform. (FH) Karsten Panier arbeitet als Softwareentwickler für die Signal Iduna Gruppe im Bereich Methoden, Standards und Architekturen. Sein Aufgabenschwerpunkt liegt im Application-Lifecycle-Management.
Dipl.-Inform. (FH) Lars Gehrken arbeitet als Gruppenleiter Basis-Technologien für die intersoft AG in der Abteilung Methoden und Verfahren. Sein Aufgabenschwerpunkt liegt im Build- und Konfigurationsmanagement.
Geschrieben von
Karsten Panier und Lars Gehrken
Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.