Apache Buildr – Die Maven-Alternative?

Abhängigkeiten: Eines der wichtigen Features von Maven ist die Möglichkeit, Projektabhängigkeiten automatisch verwalten lassen zu können. Buildr übernimmt dieses Feature und arbeitet mit den gleichen Repositories wie Maven zusammen. Die GAV-Koordinaten der Artefakte werden in der Form group:id:type:version angegeben und in normalen Ruby-Variablen definiert. Mit einigen Hilfsmethoden lassen sich hier elegante Schreibweisen finden, um komplexe Abhängigkeitsstrukturen einfach auszudrücken. Wichtig ist zu erwähnen, dass Buildr keine komplexe Abhängigkeitsauflösung wie Maven unterstützt. Zwar kann man mithilfe der transitive()-Methode die weiteren Abhängigkeiten herunterladen und verwenden, allerdings ist diese Funktionalität nur rudimentär implementiert. Das ist allerdings auch so gewollt, denn die automatische Auflösung der Abhängigkeiten hat auch bei Maven immer wieder zu Problemen geführt. Besser ist es, die Abhängigkeiten manuell festzulegen. Wer dennoch nicht auf dieses Feature verzichten möchte, kann die Aether- und Ivy-Plug-ins verwenden. Anders als bei Maven ist es jedoch auch sehr leicht möglich, lokale Bibliotheken, beispielsweise aus einem libs-Ordner im Projektverzeichnis, zu verwenden.

Bauen: Buildr unterstützt nicht nur Java, sondern auch Groovy, Scala und Ruby, zusammen mit ihren wichtigsten Test-Frameworks. So können Java-Projekte ohne weiteres Zutun mit JUnit, TestNG oder JBehave getestet werden. Die Kompilierung der Quelldateien lässt sich sehr leicht um zusätzliche Tasks, etwa zur Codegenerierung, erweitern. Ebenfalls Teil des Bauens ist das Kopieren und Verarbeiten der Ressourcen. Wie auch Ant und Maven erlaubt es Buildr, die Dateien während dieses Schrittes zu manipulieren, beispielsweise, um die aktuelle Versionsnummer in das Produkt einzubinden.

Paketieren: Ohne weitere Plug-ins kann Buildr ZIPs, TARs, TGZs, JARs, WARs, AARs, EARs und OSGi Bundles erzeugen. Zudem stellt die DSL Tasks zum Erzeugen von Source-Distributionen und Javadoc-Archiven bereit. Die DSL erlaubt es auch, sehr einfach Einfluss auf den Inhalt der Archive nehmen zu können. Das folgende Codebeispiel erzeugt z. B. eine ZIP-Datei namens api-docs-1.0.zip, die die generierte Dokumentation sowie eine Readme-Datei enthält.

package(:zip).path("#{id}-docs-#{version}").tap do |path|
  path.include _('target/docs')
  path.include _('README')
end

Erweiterbarkeit: Eines der wichtigsten Entwurfsziele von Buildr war die Erweiterbarkeit, um das Schaffen von Notausgängen möglichst unkompliziert zu gestalten. Buildr bietet dazu verschiedene Ansätze. Zum einen lässt sich an jeder Stelle im Build File jederzeit Ruby-Code ausführen, zum anderen können beliebige Ant-Tasks ausgeführt werden. Diese lassen sich dann in wiederverwendbare Tasks kapseln und entweder in Form eines Gems als Plug-in verwenden oder aber direkt in das tasks-Verzeichnis legen und von dort aus verwenden. Die Liste solcher Tasks wächst ständig. Auf diese Weise lassen sich Werkzeuge wie Checkstyle, Cobertura, Emma, Sonar, ANTLR, XMLBeans, OpenJPA, Hibernate, Jetty, GWT etc. einfach einbinden.

Ein Blick in die Praxis

Obwohl Buildr sowohl mit Ruby als auch mit JRuby genutzt werden kann, ist die Verwendung von Letzterem zu empfehlen. Die Installation geht recht einfach vonstatten. Falls noch nicht vorhanden, muss eine aktuelle JRuby-Version installiert und in den Path aufgenommen werden. Das gelingt am besten mit RVM (unter Linux/Mac) oder Pik (unter Windows). Die Installation von Buildr erfolgt dann mithilfe von Gem:

gem install buildr

Mit buildr –version erfahren Sie, welche Version installiert wurde. Zur Drucklegung ist Version 1.4.7 aktuell. Wechseln Sie nun in ein Projektverzeichnis Ihrer Wahl. Für den Anfang sollte es kein zu kompliziertes Projekt sein. Rufen Sie Buildr auf und lassen Sie ein Build File erstellen. Buildr versucht nun anhand einer existierenden pom.xml oder der Verzeichnisstruktur zu erkennen, um was für ein Projekt es sich handelt. Das funktioniert meist nicht besonders gut, stellt aber einen guten Startpunkt dar. Ein funktionierendes Beispielprojekt können Sie unter [4] auschecken, das soll uns nun auch als Grundlage dienen. Es handelt sich dabei um ein sehr vereinfachtes Multimodulprojekt, bestehend aus einem Modul für ein API (api) und einem Modul für die Implementierung (impl). Nachdem Buildr ein neues Build File erzeugt hat, versucht er, das Projekt direkt zu bauen. Das schlägt allerdings fehl, weil die Implementierung die Logback-Bibliothek verwendet und diese Abhängigkeit Buildr noch nicht bekannt ist.

Listing 1 zeigt, wie das vollständige Build File für dieses Projekt aussieht. Zu Beginn wird die aktuelle Versionsnummer für das Projekt festgelegt. Der Variablenname ist eine Konvention und wird von Buildrs Release-Task verwendet, um die nächsthöhere Version zu bestimmen und zu setzen. Danach wird das Maven-Central-Repository in die Liste der bekannten Repositories aufgenommen. Die Variable LOGBACK wird als Array definiert und referenziert die Core- und Classic-Artefakte der Bibliothek. Alternativ hätte man auch schreiben können:

LOGBACK = group('logback-classic', 'logback-core, 
:under=>'ch.qos.logback', :version=>'1.0.7')

Danach wird das Wurzelprojekt definiert. Das DSL-Schlüsselwort desc gibt der Projekttask eine natürlichsprachliche Beschreibung, während define die Artefakt-ID (Multi-Java) als Parameter übergeben bekommt. In dem folgenden Block werden die Group-ID und die Versionsnummer gesetzt. Damit sind alle GAV-Koordinaten bestimmt. Die Unterprojekte api und impl „erben“ diese Koordinaten, die Artefakt-IDs werden dabei zusammengesetzt. Dieses Verhalten kann aber umkonfiguriert werden. Als Nächstes wird das API-Projekt definiert. Dem Compiler wird übergeben, dass er Java-5-Bytecode erzeugen soll, danach werden ein JAR, sowie Javadocs und eine Source-Distribution erzeugt. Die Implementierung benötigt zur Compile-Zeit und zur Laufzeit die Logback-Bibliothek, deshalb wird sie in der compile.with-Direktive zusammen mit der Abhängigkeit zu dem API-Projekt angegeben. Die Methode transitive()berechnet aus den POMs der beiden Logback-Artefakte die transitiven Abhängigkeiten und übergibt sie dem Compiler. Danach werden die Tests ausgeführt und dann ein JAR sowie die Javadocs erzeugt.

Listing 1
# Version number for this release
VERSION_NUMBER = "1.0"

# Specify Maven 2.0 remote repositories here, like this:
repositories.remote  '1.5', :target => '1.5')
    package(:jar)
    package(:javadoc)
    package(:sources)
  end

  define "impl" do
    compile.with(project("api"), transitive(LOGBACK))
           .using(:source => '1.5', :target => '1.5')
    test
    package(:jar)
    package(:javadoc)
  end
end
Kommentare

Schreibe einen Kommentar

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