Das Gradle Feature „Composite Build“ ermöglicht es getrennt entwickelte Projekte als eines zu betrachten. Die Projekte werden dabei automatisch in der richtigen Reihenfolge gebaut. Abhängigkeiten werden ohne den Zwischenschritt eines Maven-Repositories aufgelöst. Auch in IDEs bietet es große Vorteile, indem z. B. Refactorings projektübergreifend durchgeführt werden können. Wir haben es vor kurzem zum ersten Mal auf unseren Projekten angewandt und berichten hier von unseren Erfahrungen.

Das Gradle Build Tool ist ein wunderbares Werkzeug, um Software-Builds zu beschreiben. Features wie der Incremental Build und der Remote Build Cache können den Entwicklungsprozess stark beschleunigen. Bei Problemen bieten Build Scans auf der Develocity Plattform einen umfangreichen Einblick und viele Analysemöglichkeiten. Mit dem Composite Build haben wir jetzt ein weiteres Feature in unseren Build integriert, welches sich (fast) nahtlos in das Ökosystem einfügt.

Helge Biel & Lennart Schwahn, DevOps-Engineers, intersoft GmbhHelge Biel & Lennart Schwahn, DevOps-Engineers, intersoft GmbH

Kontext

Unsere monolithische Anwendung lifestream classic soll in kleinere Projekte aufgeteilt werden. Die Anwendung bleibt bestehen, aber Teilkomponenten sollen herauswandern und dadurch besser isoliert und wartbarer werden. So werden aus Gradle Subprojekten eigenständige Projekte, die als Modul-Abhängigkeiten in lifestream classic eingebunden werden.

Den ersten Schritt sind wir vor kurzem gegangen und haben einige Module der untersten Schicht in ein eigenes Projekt verschoben. Wir nennen es die technische Plattform intersoft, kurz TPI.

Für den Buildprozess haben wir bereits einige Gradle Convention-Plugins eingesetzt. Convention Plugins in Gradle ermöglichen es, Buildlogik global wiederverwendbar zur Verfügung zu stellen. Davon haben wir bereits vor der Umstrukturierung Gebrauch gemacht, bis dahin aber im selben Projekt unter dem buildSrc-Subprojekt verwaltet. Um die Convention-Plugins in beiden Projekten anwenden zu können, haben wir sie ebenfalls ausgelagert. Sie liegen jetzt im Projekt lifestream-gradle-conventions.

Zusammengefasst haben wir jetzt also drei Projekte:

  • lifestream Classic – unser Hauptprodukt
  • die technische Plattform – die Basis für lifestream und die herauszulösenden Teilkomponenten
  • Eine Sammlung an Convention-Plugins, die den Build gleichartiger Module in den Projekten beschreibt.

Grundlagen des Composite Build Features

Die herausgelösten Projekte sind jetzt klein und gut wartbar. Durch kleinere Projekte sind Builds schneller durchführbar. Doch was ist, wenn wir in einem der Projekte etwas ändern, was zu Anpassungen in einem nachfolgenden Projekt führt? Der übliche Weg wäre, die Anpassungen in Projekt A durchzuführen, eine neue (Snapshot-)Version zu bauen und diese in Projekt B als Abhängigkeit einzutragen. Anschließend wären in Projekt B die nötigen Anpassungen vorzunehmen. Fallen noch Fehler auf, wiederholen wir die Schritte so oft, bis das gewünschte Ergebnis erreicht ist. Klingt ineffizient? Ist es auch …

Hier kommt das Composite Build Feature von Gradle ins Spiel. Mit diesem Feature ist es möglich, Modul-Dependencies durch ein lokales Gradle-Projekt zu substituieren. Gradle erkennt dann automatisch anhand der GAV (Maven-Koordinaten), dass Abhängigkeiten durch die Artefakte eines lokal eingebundenen Projekts aufgelöst werden können.

Im Folgenden erklären wir, wie wir dieses Feature auf unsere eigenen Projekte angewandt haben. Wir nutzen es, um projektübergreifende Refactorings einfacher durchführen zu können.

Einsatz des Composite Builds

Die einfachste Möglichkeit, einen Composite Build durchzuführen, ist über die Kommandozeile:

Copy to Clipboard

Alternativ können wir das auch fest in der settings.gradle eines Projekts konfigurieren:

Copy to Clipboard

Das bietet sich an, wenn das gleiche Set an Projekten immer zusammen gebaut werden soll.

In unserem Projekt soll der Composite Build jedoch nur eine Option sein. Dazu verknüpfen wir das Inkludieren der Projekte an Properties, welche in einer separaten Konfigurationsdatei im Workspace zu setzen sind. Die Properties definieren, wo diese Projekte liegen. Ist die Property nicht gesetzt, findet kein Composite Build statt. Wir ermöglichen den Entwickler:innen, so die Projekte in einem lokalen Workspace fest zu inkludieren, ohne dafür Dateien unter Versionskontrolle anpassen zu müssen.

Das haben wir in einem Settings-Plugin umgesetzt und (zunächst – wir kommen gleich noch einmal drauf zurück) ebenfalls über das Convention-Plugin bereitgestellt.

Öffnen Entwickler:innen das Projekt in einer IDE mit Gradle-Support (getestet mit IntelliJ und Eclipse), nimmt die IDE die inkludierten Projekte automatisch in den Workspace auf. Refactorings wirken sich dann projektübergreifend aus.

Herausforderungen

Im Normalfall sollten die oben beschriebenen Schritte ausreichen, um bereits effektiv mit dem Composite Build Feature arbeiten zu können.

In unserem Projekt haben wir aber auch einige Module, welche von der Standard Java Buildlogik in Gradle abweichen. Deshalb war der Composite Build bei uns zunächst nicht einsetzbar. Folgende Punkte sind daher zum Tragen gekommen und bedurften Anpassungen.

Problem 1 – Das projektübergreifende Auflösen von Artefakten funktioniert nur, wenn diese in der Default-Konfiguration von Gradle veröffentlicht wurden.

In unseren Projekten waren vereinzelt eigene Konfigurationen definiert, zu denen der Build Artefakte veröffentlicht. Die haben in der bisherigen Form nicht mit dem Composite-Build funktioniert. Hier ein Beispiel:

Copy to Clipboard

Abhilfe hat die Variante aware API von Gradle geschafft. Anstatt die Artefakte aus eigenen Konfigurationen zu publishen, werden sie als Variation zur Default-Konfiguration hinzugefügt und darüber gepublisht.

Dafür sind folgende Schritte notwendig:

  1. Die bestehenden Konfigurationen mit Variant-Attributen versehen.
  2. Publishing der Custom-Konfigurationen entfernen.
  3. Artefakte der Konfigurationen zur Default-Konfiguration hinzufügen.
  4. In abhängigen Projekten die Dependency-Konfiguration um das attributesSchema ergänzen.
  5. Bonus: In der Repository-Konfiguration gradleMetadata() ergänzen, wenn vom Default abgewichen wurde.
Copy to Clipboard

Problem 2 – Der Composite Build für das Convention-Plugin funktioniert nicht, wenn das anwendende Settings-Plugin im selben Artefakt releast wird.

Hintergrund ist (mutmaßlich), dass der Build bereits während der Initilization-Phase des Builds das Artefakt des Plugins auflöst. Dadurch überschreibt der Composite Build die Klassen nicht.

Zur Lösung haben wir das Projekt der Convention-Plugins aufgeteilt und das Settings-Plugin in ein eigenes Projekt verschoben. Dadurch muss Gradle nur das Artefakt der Settings-Plugins zur Initializations-Phase auflösen. Die Convention-Plugins sind in einem eigenen Artefakt und können wieder durch den Composite Build überschrieben werden.

Zusammenfassung

Das Composite Build Feature kann projektübergreifende Arbeiten stark erleichtern. Je nachdem, wie ein Projekt aufgesetzt ist, sind aber Anpassungen notwendig, um das Feature effektiv nutzen zu können.

  • Custom-Artefakte sollten mittels Variant Aware API der Default-Konfiguration hinzugefügt werden.
    Beim Publishing werden sie dadurch automatisch berücksichtigt und der Composite-Build kann die Auflösung korrekt auf lokale Artefakte umleiten.
  • Ein Settings-Plugin um den Composite-Build zu konfigurieren, sollte unabhängig von anderen Convention-Plugins entwickelt werden.
    Dadurch sind die Convention-Plugins separat publiziert und können ohne Einschränkung mittels Composite-Build live entwickelt und getestet werden.

Quellen

Helge Biel, DevOps-Engineer, intersoft AG

Über den Autor:

Hi, ich bin Helge! Ursprünglich in Hamburg geboren, wohne ich seit 2019 mit meiner Familie in Wedel.

Bei intersoft bin ich seit über 15 Jahren an der Bereitstellung und Gestaltung von Infrastruktur und Prozessen rund um Build und Deployment unserer Software-Produkte beteiligt. Der Markt hat sich in dieser Zeit stark weiterentwickelt, sodass ich das heute als DevOps-Engineer in einem Kubernetes-Umfeld tue.

Auch privat interessiere ich mich für neue Technologien und probiere gern in kleinen Projekten das eine oder andere aus.

Lennart Schwahn, DevOps-Engineer, intersoft GmbH

Über den Autor:

Ich bin Lennart, 38 Jahre alt und bei intersoft als DevOps-Engineer im Team „Developer Productivity Engineering“ (DPE) tätig.

Unser Team kümmert sich um die Prozesse und Werkzeuge, welche den Entwicklungsprozess möglich machen. Also alles von der Versionkontrollverwaltung, über den Build-Prozess bis hin zum Deployment.

Meine Firmenzugehörigkeit hat sich gerade zum 20. mal gejährt. In dieser Zeit hat sich technologisch einiges weiterentwickelt. Wir bauen unsere Software nicht mehr mit Apache Ant, sondern mit Gradle, statt CVS oder SVN nutzen wir Git für die Versionskontrolle und wir deployen unsere Software als Container in Kubernetes, statt in einem Weblogic. Solche Herausforderungen sind es, durch die die Arbeit niemals langweilig wird und es ist eine große Freude an diesen Entwicklungen mitzuwirken.

Aufgewachsen im Osnabrücker Land, bin ich 2004 nach Hamburg gezogen. Mit meiner Frau und meinen zwei Kindern leben wir inzwischen seit einigen Jahren in Quickborn im Norden Hamburgs (ja genau, da wo Mike Krüger herkommt).