Build-Tools: vom Quellcode zum auslieferbaren Artefakt

https://bildung.social/@oerinformatik/

https://oer-informatik.de/build-tools

Build-Tool, Build-Management, Maven, Gradle, Abhängigkeitsverwaltung, Repository, Lifecycle, Task-Graph, Wrapper, Continuous Integration

tl/dr; (ca. 25 min Lesezeit): Build-Tools wie Maven oder Gradle machen aus Quellcode und Bibliotheken wiederholbar ein auslieferbares Programm: Sie lösen Abhängigkeiten auf, kompilieren, testen, packen und veröffentlichen. Der Artikel zeigt, was dabei von Hand passieren müsste, welche Konzepte alle Build-Tools gemeinsam haben, wie sich das Phasenmodell von Maven vom Task-Graphen von Gradle unterscheidet und wie Build-Tools in einer CI-Pipeline arbeiten [AI-Assisted]. (Zuletzt geändert am 12.11.2026)

Dieser Artikel ist Teil einer Artikelreihe über Build-Tools: Grundlagen der Build-Tools / Das Build-Tool Maven / Das Build-Tool Gradle

Schon fit in diesem Thema? Nutze die Kann-Liste und die Leitfragen unter dem Artikel als Checkliste, ob Du die relevanten Kompetenzen bereits hast.

Als Build-Tool-Grundlagen kann ich nach Durchlesen des Artikels…

  • … die Aufgaben eines Build-Tools nennen und erklären, warum ein Build wiederholbar sein muss.

  • … die Stationen eines Builds (Abhängigkeiten auflösen, kompilieren, testen, packen, prüfen, veröffentlichen) in der richtigen Reihenfolge darstellen.

  • … ein kleines Java-Programm von Hand mit javac, java und jar bauen und die Schritte einem Build-Tool zuordnen.

  • … die Begriffe Artefakt, Koordinaten, Abhängigkeit, transitive Abhängigkeit, Scope und Repository erklären.

  • … das Prinzip „Convention over Configuration“ an der Standard-Ordnerstruktur erläutern.

  • … den Zweck eines Wrappers (mvnw, gradlew) erklären.

Darüber hinaus kann ich als Build-Tool-Expertenwissen…

  • … das Phasenmodell von Maven und den Task-Graphen von Gradle unterscheiden und bewerten.

  • … die Rolle eines Build-Tools in einer CI-Pipeline beschreiben und einen Build-Ablauf als UML-Aktivitätsdiagramm modellieren.

Die Entwicklungsabteilung der CSB-Rad GmbH hat im September 2026 zwei teure Wochen erlebt: Einmal war der Onlineshop drei Stunden offline, weil das Programm auf einem Laptop mit Java 21 gebaut worden war, der Server aber nur Java 17 kannte. Zwei Wochen später ging eine ungetestete Änderung an der Versandkostenberechnung live, und manche Bestellungen wurden plötzlich versandkostenfrei verschickt. Beide Pannen haben dieselbe Ursache: Das Programm wurde von Hand gebaut, jedes Mal ein bisschen anders, und nichts hat geprüft, ob das Ergebnis wirklich in Ordnung ist.

Genau dafür gibt es Build-Tools (build tools, auch Build-Management-Werkzeuge). Sie beschreiben in einer Konfigurationsdatei, was zu einem Projekt gehört und wie daraus ein auslieferbares Programm entsteht. Jeder Build läuft danach gleich ab, egal ob auf Deinem Rechner, auf dem Rechner einer Kollegin oder auf einem Build-Server.

Überblick: Quellcode, Konfiguration und Bibliotheken aus einem Repository gehen in das Build-Tool, das Abhängigkeiten auflöst, kompiliert, testet, packt und prüft; heraus kommen Artefakt und Berichte (mit KI erstellt)

Was erledigt ein Build-Tool?

Ein Build-Tool übernimmt alle wiederkehrenden Arbeiten zwischen „Code ist geschrieben“ und „Programm läuft bei der Kundschaft“. Das folgende UML-Anwendungsfalldiagramm zeigt die typischen Aufgaben. Auslöser sind Entwickler*innen, die auf ihrem Rechner bauen, oder ein CI-Server, der nach jedem Commit automatisch baut:

UML-Anwendungsfalldiagramm: Entwickler*innen und CI-Server lösen Builds aus; das Build-Tool löst Abhängigkeiten auf, kompiliert, führt Unit- und Integrationstests aus, prüft den Code statisch, erstellt Pakete, veröffentlicht Artefakte und erzeugt Dokumentation (mit KI erstellt)
  • Abhängigkeiten auflösen: fremde Bibliotheken (z. B. ein Web-Framework oder ein Test-Framework) in der richtigen Version herunterladen und bereitstellen.

  • Quellcode kompilieren: aus .java-Dateien Bytecode (.class-Dateien) erzeugen.

  • Tests ausführen: Unit-Tests, Integrationstests und weitere automatische Prüfungen starten und Berichte erzeugen.

  • Code statisch prüfen: Stilregeln, typische Fehler oder Sicherheitslücken finden, ohne das Programm auszuführen.

  • Programmpaket erstellen: die kompilierten Klassen und Ressourcen in ein verteilbares Archiv packen, in Java meist eine JAR-Datei (Java Archive).

  • Artefakt veröffentlichen: das fertige Paket in ein Repository hochladen, damit andere Projekte oder Server es nutzen können.

  • Dokumentation erzeugen: API-Dokumentation (Javadoc), Testberichte und Abdeckungsberichte zusammenstellen.

Das Ergebnis eines Builds nennt man Artefakt (artifact): ein Paket mit eindeutigem Namen und eindeutiger Version, z. B. versandkosten-1.0.0.jar.

Ein Build von Hand: Was passiert eigentlich?

Um zu sehen, was ein Build-Tool automatisiert, bauen wir zunächst ein kleines Programm ganz ohne Werkzeug. Die Klasse liegt im Paket example.csbrad.hallo; die Ordner entsprechen dem Paketnamen:

package example.csbrad.hallo;

public class App {

    public static void main(String[] args) {
        System.out.println("Hallo Welt - gebaut mit Maven!");
    }
}

Mit dem JDK (hier Java 25) sind drei Schritte nötig: kompilieren, starten, packen. Die Befehle unterscheiden sich in der PowerShell und in der Bash nur bei den Pfadtrennern:

# 1. kompilieren: Bytecode landet im Ordner out
javac -d out src\main\java\example\csbrad\hallo\App.java

# 2. starten: Klassenpfad (-cp) zeigt auf den Ordner mit den .class-Dateien
java -cp out example.csbrad.hallo.App

# 3. packen: JAR mit Startklasse im Manifest erzeugen und starten
jar --create --file hallo.jar --main-class example.csbrad.hallo.App -C out .
java -jar hallo.jar

Für eine Klasse ohne Bibliotheken ist das überschaubar. In einem echten Projekt wie dem Versandkosten-Service der CSB-Rad GmbH kommen aber schnell dazu:

  • Bibliotheken: Ein kleiner REST-Dienst mit Spring Boot braucht über 30 JAR-Dateien, die alle im Klassenpfad stehen müssen, und zwar in zueinander passenden Versionen.

  • Tests: Die Testklassen müssen mit JUnit im Klassenpfad kompiliert und mit einem Test-Runner gestartet werden; schlägt ein Test fehl, darf kein Paket entstehen.

  • Reihenfolge: Erst Hauptcode, dann Testcode, dann Tests, dann packen. Wer einen Schritt vergisst, liefert veraltete Klassen aus.

  • Umgebung: Welches JDK wird benutzt? Mit welchem --release-Wert? Genau daran ist der Shop der CSB-Rad GmbH gescheitert.

Ein Build-Tool schreibt all das einmal in eine Konfigurationsdatei und führt es danach mit einem einzigen Befehl immer gleich aus.

Die Stationen eines Builds

Unabhängig vom Werkzeug läuft ein Build in ähnlichen Stationen ab. Das folgende UML-Aktivitätsdiagramm zeigt einen vereinfachten Ablauf. Schlägt ein Test fehl, wird der Build abgebrochen, es entsteht also kein Paket:

UML-Aktivitätsdiagramm eines allgemeinen Builds: Abhängigkeiten auflösen, Quellcode kompilieren, Tests ausführen; nur wenn alle Tests bestanden sind, Paket erstellen, prüfen und veröffentlichen, sonst Build abbrechen; zum Schluss Ergebnis melden (mit KI erstellt)

Wichtig ist das Prinzip „schnell scheitern“ (fail fast): Billige Prüfungen (Stilregeln, Versionsprüfungen, Unit-Tests) laufen früh, teure Prüfungen (Integrationstests, Abdeckungsanalyse) später. Sobald eine Station scheitert, bricht der Build ab, und Du bekommst die Fehlermeldung, bevor etwas Kaputtes ausgeliefert wird.

Kernkonzepte, die alle Build-Tools teilen

Convention over Configuration: Die Standard-Ordnerstruktur

Maven hat eine Ordnerstruktur eingeführt, die heute auch Gradle und viele Entwicklungsumgebungen erwarten. Wer sich an diese Konvention hält, muss fast nichts konfigurieren (Convention over Configuration). Nur Abweichungen werden in der Konfigurationsdatei beschrieben:

projekt/
├── pom.xml  bzw.  build.gradle.kts    Konfiguration des Builds
├── src/
│   ├── main/
│   │   ├── java/                      Quellcode der Anwendung
│   │   └── resources/                 Konfigurationsdateien, Texte, Bilder
│   └── test/
│       ├── java/                      Quellcode der Tests (gleiche Pakete)
│       └── resources/                 Testdaten
└── target/  bzw.  build/              Ausgaben des Builds (nie ins Git-Repository!)

Haupt- und Testcode liegen in zwei spiegelbildlichen Bäumen. Eine Testklasse VersandkostenRechnerTest liegt im selben Paket wie die getestete Klasse VersandkostenRechner, aber unter src/test/java. So darf der Test auch paketsichtbare Elemente nutzen, und trotzdem landet der Testcode nicht im ausgelieferten Paket.

Deklarativ statt imperativ

Ein Shell-Skript wie oben beschreibt imperativ, wie gebaut wird: Befehl für Befehl. Build-Tools arbeiten überwiegend deklarativ: Du beschreibst, was das Projekt ist (Name, Version, Java-Version, Abhängigkeiten, gewünschte Prüfungen), und das Build-Tool weiß, welche Schritte dafür nötig sind. Maven ist dabei sehr streng deklarativ (XML-Datei pom.xml). Gradle erlaubt in seinem Build-Skript (build.gradle.kts, geschrieben in Kotlin) zusätzlich eigene Logik. Das ist mächtiger, aber auch fehleranfälliger.

Koordinaten und Abhängigkeiten

Jedes Artefakt wird über drei Koordinaten (coordinates) eindeutig benannt, ähnlich wie eine Postanschrift:

Koordinaten eines Artefakts
Koordinate Bedeutung Beispiel
groupId (Gradle: group) Organisation oder Projektgruppe, meist umgekehrter Domainname org.springframework.boot
artifactId (Gradle: name) Name des Artefakts spring-boot-starter-webmvc
version Version, oft nach Semantic Versioning; -SNAPSHOT kennzeichnet einen Entwicklungsstand 4.1.2

In Kurzform schreibt man org.springframework.boot:spring-boot-starter-webmvc:4.1.2. Für die Beispielprojekte der CSB-Rad GmbH nutzen wir die Gruppe example.csbrad, abgeleitet von der (erfundenen) Domain csb-rad.example.

Ein Projekt deklariert nur seine direkten Abhängigkeiten (dependencies). Diese bringen ihrerseits Abhängigkeiten mit, die das Build-Tool automatisch mitlädt: die transitiven Abhängigkeiten. Der Versandkosten-Service deklariert nur eine einzige Laufzeitbibliothek; das Build-Tool löst daraus über 30 JAR-Dateien auf (Webserver Tomcat, JSON-Bibliothek Jackson, Logging …).

Ein Scope (Gültigkeitsbereich) legt fest, wofür eine Abhängigkeit gebraucht wird:

Gültigkeitsbereiche von Abhängigkeiten in Maven und Gradle
Zweck Maven-Scope Gradle-Konfiguration
zum Kompilieren und zur Laufzeit compile (Vorgabe) implementation
nur für die Tests test testImplementation
nur zur Laufzeit, nicht zum Kompilieren runtime runtimeOnly
stellt die Laufzeitumgebung bereit (z. B. Applikationsserver) provided compileOnly

So landet z. B. JUnit nie im ausgelieferten Programm.

Repositories: Woher kommen die Bibliotheken?

Artefakte liegen in Repositories (Ablagen). Das wichtigste öffentliche Repository ist Maven Central (https://repo.maven.apache.org/maven2/); Maven und Gradle nutzen es gleichermaßen. Auf Deinem Rechner gibt es außerdem ein lokales Repository als Zwischenspeicher (Maven: ~/.m2/repository, Gradle: ~/.gradle/caches). Firmen betreiben oft ein eigenes Repository, in dem sie ihre internen Bibliotheken ablegen und Maven Central spiegeln.

Das folgende UML-Sequenzdiagramm zeigt vereinfacht, wie ein Build-Tool eine Abhängigkeit auflöst: Es sucht zuerst lokal, lädt nur Fehlendes aus dem entfernten Repository und speichert es für den nächsten Build. Danach geht es mit den transitiven Abhängigkeiten aus der pom-Datei des Artefakts genauso weiter:

UML-Sequenzdiagramm: Das Build-Tool sucht eine Abhängigkeit im lokalen Repository, lädt sie bei Bedarf aus dem zentralen Repository, speichert sie lokal, löst die weiteren Abhängigkeiten auf und kompiliert anschließend (mit KI erstellt)

Deshalb dauert der erste Build eines Projekts oft lange, und deshalb brauchst Du beim ersten Build Internetzugang (bzw. einen korrekt eingetragenen Proxy).

Der Wrapper: Gleiche Werkzeugversion für alle

Selbst wenn die Konfiguration im Git-Repository liegt, bauen zwei Rechner unterschiedlich, wenn dort verschiedene Versionen von Maven oder Gradle installiert sind. Abhilfe schafft ein Wrapper: ein kleines Skript im Projekt (mvnw/mvnw.cmd bzw. gradlew/gradlew.bat) plus eine Eigenschaftsdatei, in der die gewünschte Werkzeugversion steht. Beim ersten Aufruf lädt der Wrapper genau diese Version herunter. Danach rufst Du statt mvn bzw. gradle immer den Wrapper auf. Die Wrapper-Dateien werden mit ins Git-Repository eingecheckt, die Ordner target/ bzw. build/ dagegen nie.

Phasen oder Task-Graph?

Maven und Gradle unterscheiden sich vor allem darin, wie sie die Schritte eines Builds ordnen.

Maven kennt einen festen Lebenszyklus (lifecycle) aus aufeinanderfolgenden Phasen (phases). Rufst Du eine Phase auf, laufen alle Phasen davor mit. mvn package führt also validate, compile und test aus, bevor gepackt wird. Plugins hängen ihre Arbeitsschritte (goals) an einzelne Phasen an:

UML-Aktivitätsdiagramm der Hauptphasen des Maven-Lifecycles: validate, compile, test, package, verify, install und deploy in fester Reihenfolge (mit KI erstellt)

Gradle dagegen kennt Tasks (Aufgaben), die voneinander abhängen. Aus diesen Abhängigkeiten berechnet Gradle bei jedem Aufruf einen gerichteten, kreisfreien Graphen (directed acyclic graph, DAG) und führt nur die Tasks aus, die für das Ziel nötig sind. gradlew build braucht jar und test, beide brauchen classes, und classes braucht compileJava. Wie sie untereinander angeordnet sind, ist nicht festgelegt. Das UML-Aktivitätsdiagramm zeigt das mit einer Gabelung:

UML-Aktivitätsdiagramm eines vereinfachten Gradle-Task-Graphen: compileJava, dann classes, dann parallel jar und test, danach build (mit KI erstellt)

Zwei Eigenschaften des Task-Graphen sparen viel Zeit:

  • Inkrementelle Builds: Gradle merkt sich Ein- und Ausgaben jedes Tasks. Hat sich nichts geändert, wird der Task übersprungen (UP-TO-DATE).

  • Build-Cache: Ergebnisse können sogar zwischen Rechnern geteilt werden.

Maven baut dagegen bei jedem Aufruf die Phasen der Reihe nach ab. Dafür ist sein Modell sehr leicht zu verstehen und in jedem Maven-Projekt gleich.

Eine Landkarte der Build-Tools

Build-Tools gibt es für fast jede Sprache. Die Konzepte von oben (Koordinaten, Repositories, Lifecycle oder Task-Graph) findest Du überall wieder:

Verbreitete Build-Tools und ihre Merkmale
Werkzeug Sprache / Plattform Konfiguration Stil Repository
Make C, C++, allgemein Makefile imperativ, Regeln mit Abhängigkeiten keines
Apache Ant Java (historisch) build.xml imperativ (Targets) keines (später Ivy)
Apache Maven Java, JVM pom.xml deklarativ, feste Phasen Maven Central
Gradle Java, Kotlin, Android build.gradle.kts deklarativ mit Skript, Task-Graph Maven Central, Gradle Plugin Portal
npm JavaScript, TypeScript package.json Skripte und Abhängigkeiten npm Registry
pip, Poetry Python pyproject.toml Abhängigkeiten, Build-Backends PyPI
dotnet CLI, MSBuild C#, .NET *.csproj deklarativ (XML), Targets NuGet
Cargo Rust Cargo.toml deklarativ crates.io

Maven und Gradle im direkten Vergleich

Im Java-Umfeld sind Maven und Gradle die beiden großen Build-Tools. Für das Hello-World-Programm von oben sieht die vollständige Konfiguration so aus. Beide erzeugen ein ausführbares JAR mit Java 25 und brauchen keine einzige Bibliothek:

Datei build.gradle.kts (Kotlin DSL) plus eine Zeile in settings.gradle.kts (rootProject.name = "hallo-gradle"):

plugins {
    application
}

group = "example.csbrad"
version = "1.0.0-SNAPSHOT"

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(25)
    }
}

application {
    mainClass = "example.csbrad.hallo.App"
}

tasks.jar {
    manifest {
        attributes("Main-Class" to application.mainClass)
    }
}

Bauen und starten: gradlew build und java -jar build/libs/hallo-gradle-1.0.0-SNAPSHOT.jar (oder direkt gradlew run)

Die wichtigsten Befehle im Vergleich (mit Wrapper: mvnw statt mvn, gradlew statt gradle):

Gegenüberstellung wichtiger Maven- und Gradle-Befehle
Aufgabe Maven Gradle
kompilieren mvn compile gradlew classes
Tests ausführen mvn test gradlew test
Paket erstellen (mit Tests) mvn package gradlew build
alle Prüfungen inkl. Integrationstests mvn verify gradlew check
ins lokale Repository installieren mvn install gradlew publishToMavenLocal
Ausgaben löschen mvn clean gradlew clean
Abhängigkeitsbaum anzeigen mvn dependency:tree gradlew dependencies
Ausgabeordner target/ build/

Welches Werkzeug ist besser? Beide sind ausgereift. Maven punktet mit seiner Einheitlichkeit: Wer ein Maven-Projekt kennt, kennt alle. Gradle ist schneller (inkrementelle Builds, Cache) und flexibler, verlangt aber mehr Disziplin, damit Build-Skripte nicht zu kleinen Programmen wuchern. Android-Projekte nutzen Gradle, viele Unternehmensanwendungen Maven. In der Praxis übernimmst Du meist das Werkzeug, das im Projekt schon verwendet wird.

Build-Tools in der CI-Pipeline

Ihren vollen Nutzen entfalten Build-Tools in einer CI-Pipeline (Continuous Integration): Nach jedem git push startet ein Build-Server automatisch denselben Build, den Du auch lokal ausführst. Erst wenn alle Prüfungen bestanden sind, wird das Artefakt veröffentlicht. Die CSB-Rad GmbH plant genau das auf ihrem GitLab-Server:

UML-Sequenzdiagramm einer CI-Pipeline: Push auf den Git-Server startet asynchron eine Pipeline; der CI-Runner ruft das Build-Tool auf und veröffentlicht das Artefakt nur bei erfolgreichem Build, sonst meldet er den Fehler (mit KI erstellt)

Damit das klappt, muss der Build ohne Benutzereingaben laufen (Maven: Option -B für batch mode), mit dem Wrapper immer dieselbe Werkzeugversion nutzen und bei einem Fehler mit einem Rückgabewert ungleich 0 enden. Den Rückgabewert setzen Maven und Gradle von selbst. Wie die Prüfungen konkret in den Build eingehängt werden, zeigen die beiden Folgeartikel zu Maven und Gradle am Beispiel des Versandkosten-Service.

Übungsaufgaben

Die Aufgaben spielen bei der CSB-Rad GmbH. Die Beispielprojekte liegen im Repository zu dieser Artikelreihe.

Aufgabe 1.1: Fachbegriffe rund um den Build (Basiskompetenz)

Ordne jedem Begriff eine kurze Erklärung zu (ein Satz genügt). (6 Punkte)

Fachbegriffe zum Ausfüllen
Begriff Erklärung
Artefakt
Koordinaten
transitive Abhängigkeit
lokales Repository
Wrapper
Convention over Configuration
Fachbegriffe, ausgefüllt
Begriff Erklärung
Artefakt Ergebnis eines Builds mit eindeutigem Namen und eindeutiger Version, z. B. eine JAR-Datei.
Koordinaten groupId, artifactId und version, die ein Artefakt in einem Repository eindeutig bezeichnen.
transitive Abhängigkeit Abhängigkeit einer Abhängigkeit; sie wird nicht deklariert, sondern vom Build-Tool automatisch mitgeladen.
lokales Repository Zwischenspeicher auf dem eigenen Rechner (z. B. ~/.m2/repository), damit Bibliotheken nur einmal heruntergeladen werden.
Wrapper Skript im Projekt (mvnw, gradlew), das eine festgelegte Version des Build-Tools lädt und benutzt.
Convention over Configuration Wer sich an Standards (z. B. Ordnerstruktur src/main/java) hält, muss nichts konfigurieren; nur Abweichungen werden beschrieben.

Je Zeile 1 Punkt.

Aufgabe 2.1: Einen Abhängigkeitsbaum lesen (Kernkompetenz)

Eine Auszubildende der CSB-Rad GmbH hat für den Versandkosten-Service mvn dependency:tree ausgeführt. Hier ein gekürzter Ausschnitt der Ausgabe:

example.csbrad:versandkosten-maven:jar:1.0.0-SNAPSHOT
+- org.springframework.boot:spring-boot-starter-webmvc:jar:4.1.2:compile
|  +- org.springframework.boot:spring-boot-starter:jar:4.1.2:compile
|  |  +- org.springframework.boot:spring-boot-starter-logging:jar:4.1.2:compile
|  |  |  \- ch.qos.logback:logback-classic:jar:1.5.38:compile
|  |  \- org.yaml:snakeyaml:jar:2.6:compile
|  +- org.springframework.boot:spring-boot-starter-tomcat:jar:4.1.2:compile
|  |  ...
|  \- org.springframework.boot:spring-boot-webmvc:jar:4.1.2:compile
\- org.springframework.boot:spring-boot-starter-webmvc-test:jar:4.1.2:test
   ...
  1. Nenne die Koordinaten des Projekts selbst (Gruppe, Name, Version) und erkläre, was SNAPSHOT bedeutet. (2 Punkte)

  2. Welche Abhängigkeiten hat die Auszubildende in ihrer Konfiguration selbst eingetragen? Woran erkennst Du das? (2 Punkte)

  3. Ist logback-classic eine direkte oder eine transitive Abhängigkeit? Begründe. (2 Punkte)

  4. Erkläre, was das letzte Wort compile bzw. test in jeder Zeile bedeutet und welche Folge test für das ausgelieferte Programm hat. (2 Punkte)

  1. Gruppe example.csbrad, Name versandkosten-maven, Version 1.0.0-SNAPSHOT. SNAPSHOT kennzeichnet einen Entwicklungsstand, der sich noch ändern kann; erst ein Release bekommt eine feste Versionsnummer ohne diesen Zusatz.

  2. spring-boot-starter-webmvc und spring-boot-starter-webmvc-test: Sie hängen direkt unter dem Projekt (erste Einrückungsebene, +- bzw. \- ganz links).

  3. Transitiv: logback-classic steht auf der vierten Ebene des Baums. Es hängt unter spring-boot-starter-logging, das wiederum über spring-boot-starter vom spring-boot-starter-webmvc mitgebracht wird. Eingetragen hat es niemand.

  4. Es ist der Scope. compile: wird zum Kompilieren und zur Laufzeit gebraucht und landet im Programm. test: wird nur zum Kompilieren und Ausführen der Tests gebraucht; diese Bibliotheken (JUnit, Mockito …) kommen nicht ins ausgelieferte JAR.

Aufgabe 2.2: Vom Shell-Skript zum Build-Tool (Kernkompetenz)

Bisher baut Cem Sarıkaya das Shop-Backend der CSB-Rad GmbH alle zwei Wochen mit einem selbst geschriebenen Skript auf seinem Laptop:

javac -d out -cp "lib\*" (Get-ChildItem -Recurse src\*.java).FullName
jar --create --file shop.jar --main-class example.csbrad.shop.ShopApp -C out .
scp shop.jar deploy@shop.csb-rad.example:/opt/shop/
  1. Ordne jede Zeile einer Station eines Builds zu. Welche Stationen fehlen ganz? (4 Punkte)

  2. Nenne zwei Risiken dieses Vorgehens, die mit einem Build-Tool und Wrapper vermieden werden, und erkläre jeweils kurz, wie. (4 Punkte)

  1. Zeile 1: Quellcode kompilieren (die Bibliotheken liegen von Hand im Ordner lib). Zeile 2: Programmpaket erstellen. Zeile 3: Artefakt veröffentlichen bzw. ausliefern. Es fehlen: Abhängigkeiten auflösen (Versionen werden nicht verwaltet), Tests ausführen, Paket prüfen (z. B. Integrationstests, Abdeckung, Stilprüfung).

  2. Mögliche Antworten (je 2 Punkte):

  • Falsche Java-Version: Das Skript nutzt das JDK, das gerade auf dem Laptop installiert ist. Mit Build-Tool wird die Ziel-Version festgelegt (maven.compiler.release bzw. Toolchain) und z. B. mit dem Enforcer-Plugin geprüft.

  • Ungetestete Auslieferung: Nichts verhindert, dass fehlerhafter Code ausgeliefert wird. Im Build-Tool laufen die Tests vor dem Packen; schlägt einer fehl, entsteht kein Paket.

  • Unklare Bibliotheksversionen: Die JARs im Ordner lib sind nicht nachvollziehbar. Das Build-Tool lädt deklarierte Versionen inklusive transitiver Abhängigkeiten aus einem Repository.

  • Abhängigkeit von einer Person und einem Rechner: Mit Konfiguration und Wrapper im Git-Repository kann jede Person und jeder CI-Server identisch bauen.

Aufgabe 3.1: Eine Build-Pipeline modellieren (Profilkompetenz)

Sophie Bertram möchte, dass neue Versionen des Shops der CSB-Rad GmbH künftig automatisch gebaut werden. Cem Sarıkaya beschreibt den gewünschten Ablauf:

„Zuerst lädt der Build-Server die Bibliotheken und prüft, ob Java- und Werkzeugversion stimmen. Passen sie nicht, bricht er sofort ab und benachrichtigt das Team. Sonst wird kompiliert, dann laufen die Unit-Tests. Gleichzeitig mit den Unit-Tests darf die Stilprüfung laufen. Wenn beides fertig ist und alles bestanden hat, wird das JAR gebaut und mit den Integrationstests geprüft. Erst wenn auch die bestanden sind, landet das JAR im Firmen-Repository. Bei jedem Fehlschlag wird das Team benachrichtigt.“

Modelliere den Ablauf als UML-Aktivitätsdiagramm. Achte auf Start- und Endknoten, Verzweigungen mit Bedingungen und Zusammenführungen sowie Gabelung und Vereinigung. (10 Punkte)

UML-Aktivitätsdiagramm der Beispiellösung: Bibliotheken laden, Java- und Werkzeugversion prüfen; passen sie nicht, Team benachrichtigen; sonst kompilieren, dann parallel Unit-Tests und Stilprüfung; bei Erfolg JAR bauen und Integrationstests ausführen; bei Erfolg JAR im Firmen-Repository ablegen, sonst jeweils Team benachrichtigen (mit KI erstellt)

Bewertungshinweise: Start- und Endknoten (1 Punkt), Aktionen als Tätigkeiten benannt (2 Punkte), drei Verzweigungen mit disjunkten Bedingungen in eckigen Klammern (3 Punkte), Gabelung und Vereinigung für Unit-Tests und Stilprüfung (2 Punkte), jede Verzweigung über eine Merge-Raute zusammengeführt (2 Punkte). Andere Anordnungen sind ebenfalls richtig, solange der Ablauf derselbe bleibt. Statt dreimal „Team benachrichtigen“ kannst Du die Fehlerzweige auch über eine Merge-Raute zu einer gemeinsamen Aktion führen.

Leitfragen

  1. Welche Aufgaben übernimmt ein Build-Tool?

Es löst Abhängigkeiten auf, kompiliert, führt Tests und statische Prüfungen aus, erstellt Pakete, veröffentlicht Artefakte und erzeugt Dokumentation, und zwar bei jedem Aufruf gleich. siehe Abschnitt „Was erledigt ein Build-Tool?“

  1. In welcher Reihenfolge laufen die Stationen eines Builds ab, und warum laufen billige Prüfungen zuerst?

Abhängigkeiten auflösen, kompilieren, testen, packen, prüfen, veröffentlichen. Nach dem Prinzip „schnell scheitern“ laufen schnelle Prüfungen früh, damit Du Fehler bekommst, bevor teure Schritte laufen oder Kaputtes ausgeliefert wird. siehe Abschnitt „Die Stationen eines Builds“

  1. Was unterscheidet eine direkte von einer transitiven Abhängigkeit, und wozu dient ein Scope?

Direkte Abhängigkeiten trägst Du selbst in die Konfiguration ein; transitive bringen diese mit, und das Build-Tool lädt sie automatisch. Der Scope legt fest, wofür eine Abhängigkeit gebraucht wird, z. B. test nur für Tests. siehe Abschnitt „Koordinaten und Abhängigkeiten“

  1. Woher bekommt ein Build-Tool die Bibliotheken?

Aus Repositories: zuerst aus dem lokalen Repository auf Deinem Rechner, sonst aus einem entfernten Repository wie Maven Central; Heruntergeladenes wird lokal zwischengespeichert. siehe Abschnitt „Repositories: Woher kommen die Bibliotheken?“

  1. Was bedeutet „Convention over Configuration“, und wozu dient ein Wrapper?

Wer Standards wie die Ordnerstruktur src/main/java einhält, muss nichts konfigurieren. Der Wrapper (mvnw, gradlew) sorgt dafür, dass alle mit derselben Werkzeugversion bauen. siehe Abschnitt „Kernkonzepte, die alle Build-Tools teilen“

  1. Wie unterscheiden sich das Phasenmodell von Maven und der Task-Graph von Gradle?

Maven führt feste Phasen der Reihe nach aus, beim Aufruf einer Phase alle vorherigen mit. Gradle berechnet aus den Abhängigkeiten der Tasks einen Graphen, führt nur Nötiges aus und überspringt unveränderte Tasks. siehe Abschnitt „Phasen oder Task-Graph?“

  1. Welche Rolle spielt ein Build-Tool in einer CI-Pipeline?

Der CI-Server ruft nach jedem Push denselben Build auf wie Du lokal; nur wenn alle Prüfungen bestanden sind, wird das Artefakt veröffentlicht. siehe Abschnitt „Build-Tools in der CI-Pipeline“


Hinweis zur Nachnutzung als Open Educational Resource (OER)

Dieser Artikel und seine Texte, Bilder, Grafiken, Code und sonstiger Inhalt sind - sofern nicht anders angegeben - lizenziert unter CC BY 4.0. Nennung gemäß TULLU-Regel bitte wie folgt: “Build-Tools: vom Quellcode zum auslieferbaren Artefakt” von oer-informatik.de (H. Stein), Lizenz: CC BY 4.0. Der Artikel wurde unter https://oer-informatik.de/build-tools veröffentlicht, die Quelltexte sind in weiterverarbeitbarer Form verfügbar im Repository unter https://gitlab.com/oer-informatik/java-advanced/build-tool-maven. Erstellt mit KI-Assistenz; von mir umfassend redaktionell überarbeitet, geprüft und freigegeben. Stand: 12.11.2026.

[Kommentare zum Artikel lesen, schreiben] / [Artikel teilen] / [gitlab-Issue zum Artikel schreiben]

Kommentare gerne per Mastodon, Verbesserungsvorschläge per gitlab issue (siehe oben). Beitrag teilen: