Gradle: neue Konkurrenz für Ant und Maven

Werkzeuge Andreas Hartmann, Halil-Cem Gürsoy  –  8 Kommentare

Nach langer Zeit der Stagnation bei den Build-Werkzeugen betritt mit Gradle ein neues System die Bühne, das halten möchte, was Maven immer versprochen hatte, aber nie einhalten konnte.

Softwareentwickler müssen sich schon bei kleinen bis mittelgroßen Projekten fragen, welche Bibliotheken sie in welcher Version wofür benötigen und welche Abhängigkeiten es bei Compile, Runtime und Test gibt. Es geht darum, wie sich Abhängigkeiten effizient verwalten und Versionskonflikte zwischen den Bibliotheken einfacher identifizieren lassen. Darüber hinaus sollte das Ausführen eines Build reproduzierbare Ergebnisse sicherstellen sowie die Build-Skripte übersichtlich und wartungsfreundlich sein. Bei weiterer konzeptioneller Betrachtung des Themas Build-Management stellen das Bauen und Deployen, das Erstellen von Reports zur Qualitätssicherung, das Erzeugen von Artefakten und die Verwaltung externer Bibliotheken nur einige Kernprozesse dar, die ein Build-Werkzeug abbilden muss.

Mit Gradle betritt nun ein Build-Management-Werkzeug die Bühne, das den konzeptionellen Schwächen von Ant und Maven im Kontext von Groovy begegnen und dessen Lösungsansatz in der Java-Welt nutzbar machen will. Die Build-Sprache Gradle wurde von Hans Dockter initiiert. Sie verwendet die auf der Java Virtual Machine laufende Programmiersprache Groovy, die stark an Java angelehnt ist. Ein einfaches Java-Build-Skript zum Kompilieren und Erstellen einer JAR-Datei besteht aus apply plugin: 'java'. Gradle setzt auf das Programmierparadigma "Convention over Configuration" und verwendet die Verzeichnisstruktur von Apache Maven. Der Benutzer sollte mit dem Paradigma eine vernünftige implizite Standardkonfiguration vorfinden und nur in Ausnahmefällen manuell in die Konfiguration eingreifen müssen. Durch ein offenes Plug-in-Konzept ist Gradle für Sprachen wie Java, Groovy und Scala einsetzbar.

Gradle unterstützt Datei- oder Maven-Repositories. Beim Hochladen der Artefakte wird das Maven POM (Project Object Model), die zentrale Stelle zum Ablegen der Metadaten eines Projekts, automatisch erstellt. Um ein Artefakt in ein Maven-Repository zu bekommen, sind in den GradleBuildscript das Maven-Plug-in zu laden sowie die Maven-Projektkonfiguration und das Repository anzugeben, gut zu sehen im folgenden Listing.

apply plugin: 'java' 
// Laden des MavenPlugins
apply plugin: 'maven'
// Maven Projektkonfiguration
version = '1.0-SNAPSHOT'
group = 'adesso'
artifactId = project.name.toLowerCase()
// Konfiguration des Upload Tasks
uploadArchives {
repositories {
deployer = mavenDeployer {
configureAuth = {
authentication(userName: 'admin', password: 'admin123')
}
snapshotRepository(url: "http://test-rep:8080/nexus/content/
repositories/snapshots/", configureAuth)
repository(url: "http://test-rep:8080/nexus/content/
repositories/releases/", configureAuth)
}
}
}

Mit der Version wird festgelegt, ob in das Snapshot- oder Release-Repository deployt wird. Je nachdem, ob der Entwickler -SNAPSHOT bei der Version weglässt oder nicht, wird er das Artefakt in das Release- oder in das Snapshot-Repository einspielen.

Die Verwaltung der Dependencies geschieht im Gradle-Buildfile selbst. Im nachfolgenden Codeschnipsel wird JUnit in der Build-Phase "Test" eingebunden.

dependencies {
testCompilegroup: 'junit', name: 'junit', version: '4.+'
}

Des Weiteren verwendet Gradle Tasks und ist leicht erweiterbar, zum Beispiel gibt es die Hooks doFirst/doLast bei den Tasks. Der folgende Code verwendet die beiden Hook-Methoden doFirst und doLast exemplarisch, um die Ausführungsdauer des JAR-Tasks Groovy zu ermitteln.

jar {
baseName=artifactId
manifest {
attributes 'Implementation-Title': 'Gradle Demo',
'Implementation-Version': version
}
long beginn
doFirst {
beginn = System.nanoTime()
}
doLast {
long ende = System.nanoTime()
longdauer = (ende - beginn)
println "=> $dauer Nanosekunden"
}
}

Im Rahmen des Build-Prozesses kann man auf Gradles Objektmodell zu- und in den Build-Ablauf eingreifen beziehungsweise diesen konfigurieren. Beispielsweise lässt sich die Versionsangabe bei der Maven-Konfiguration aus dem Objektmodell auslesen und verändern. Gradle verwendet standardmäßig als Projektname den Verzeichnisnamen des Projekts, mit artifactId = project.name.toLowerCase() wird dieser nun als Artefakt-ID für die Maven-Konfiguration verwendet. Beim Erstellen eines Build-Skripts werden vornehmlich Tasks konfiguriert, das Erstellen des Skripts erfolgt demnach deklarativ. Im folgenden Codebeispiel werden etwa der Name der JARs gleich der Artefakt-ID von Maven gesetzt und dem Manifest die Attribute Implementation-Title und Implementation-Version hinzugefügt.

jar {
baseName=artifactId
manifest {
attributes 'Implementation-Title': 'Gradle Demo',
'Implementation-Version': version
}
}

Die zur Verfügung stehenden Task-Typen definieren dabei das Prozedere. Um zu verhindern, dass bei der Ausführung eines Build-Skripts Tasks mehrfach ausgeführt werden, wird die Abhängigkeitsstruktur der Tasks zunächst als sogenannter Directed Acyclic Graph (DAG) aufgebaut und danach erst ausgeführt. Das ermöglicht das deterministische sequenzielle Abarbeiten des Build-Prozesses und inkrementelle Builds.