Lesedauer : 11 Minuten.

Zielgruppe: Gradle-Entwickler, die bereits magisches oder unerwartetes Build-Verhalten erlebt haben.

Einleitung

In unserem Abenteuer beim Erstellen des Plugins`site-baker`, wir haben einen rigorosen TDD-Ansatz verfolgt. Jede Funktionalität wurde getestet, validiert und wir gingen mit Vertrauen voran. Und dann kam eines Tages das Unerwartete. Die Builds begannen, sich unregelmäßig zu verhalten. Änderungen in der Logik des Plugins oder in den Konfigurationsdateien schienen ignoriert zu werden, und unsere bisher zuverlässigen Funktionstests schlugen ohne ersichtlichen Grund fehl.

Solche Probleme können unglaublich frustrierend sein. Es stellt die Zuverlässigkeit des Tools und die Gültigkeit unseres Codes in Frage. Nach einer intensiven Debugging-Sitzung wurde der Schuldige identifiziert: derGradle-Konfigurationscache.

Das Symptom: Phantom-Builds

Das Problem äußerte sich auf verschiedene Weisen:

  1. Ich modifizierte eine Zeichenkette in einer Aufgabe.println, aber die alte Zeichenkette blieb während der Ausführung angezeigt.

  2. Ich änderte einen Wert in meiner Datei`managed-jbake-context.yml`, aber das Plugin verhielt sich, als wäre die Datei nicht geändert worden.

  3. Funktionale Tests, die Testprojekte im Flug erstellen, schlugen fehl, weil das Plugin die neu erstellten Konfigurationsdateien nicht zu erkennen schien.

Alles schien so, als würde Gradle eine \"version fantôme\" unseres Builds ausführen und unsere neuesten Änderungen ignorieren.

Die Untersuchung: Was ist der Konfigurations-Cache?

Der Konfigurations-Cache ist eine relativ moderne und äußerst leistungsstarke Funktion von Gradle, die in den neuesten Versionen standardmäßig aktiviert ist. Sein Ziel ist es, die Builds schneller zu machen.

Das Prinzip ist einfach :
  1. Bei der ersten Ausführung, Gradle führt die Phase vonConfiguration( lesen der`build.gradle.kts`, Erstellung der Aufgaben, Lösung der Abhängigkeiten) und erstellt einen Aufgabengraphen.

  2. Am Ende dieser Phase, Gradleserialisiere diesen Taskgraphenund speichert es im Cache.

  3. Bei den folgenden Ausführungen, wenn sich nichts geändert hat (Build-Skripte,gradle.properties, usw.), Gradleüberspringt die Konfigurationsphase komplettund wiederverwende den zwischengespeicherten Aufgaben-Graphen.

Der Zeitgewinn ist bei großen Projekten spektakulär. Allerdings hat diese Leistung ihren Preis: Sie legt strenge Regeln dafür fest, wie Plugins geschrieben werden müssen.

Der Lebenszyklus des Konfigurationscaches

config cache lifecycle

Die Ursache des Problems: Ein nicht konformes Plugin

Unser Plugin`site-baker`Verletzte unbemerkt mehrere Regeln des Konfigurationscaches. Damit ein Aufgabengraph serialisierbar ist, dürfen die Aufgaben keine Verweise auf komplexe Objekte wie das Objekt enthalten.`Project`oder Dateien während der Ausführungsphase beliebig zu lesen.

Unser Hauptfehler bestand darin, den Inhalt der YAML-Datei direkt innerhalb der Aufgabenausführungslogik zu lesen, indem wir auf den Pfad in unserer Erweiterung verwiesen haben. Dieser Ansatz ist mit dem Caching unvereinbar, weil Gradle nicht erkennen kann, ob sich der Dateiinhalt geändert hat, wenn dieses Lesen nicht als eineAufgabe-Eingabe(Task Input).

Die temporäre Lösung: Cache deaktivieren

Um uns zu entblockieren und ein vorhersagbares Build-Verhalten wiederherzustellen, war die schnellste Lösung, den Konfigurations-Cache zu deaktivieren. Es reicht aus, die folgende Zeile in die Datei einzufügen.gradle.properties`des Projekts, das das Plugin verwendet (oder in unserem Fall, das Testprojekt)`site-baker). .

# site-baker/gradle.properties
org.gradle.configuration-cache=false

Sofort hatten die Builds ihr normales Verhalten wiedererlangt. Jede Ausführung startete die Konfigurationsphase erneut und unsere Änderungen wurden berücksichtigt.

Allerdings ist das nur eine Umgehungslösung, keine dauerhafte Lösung. Sie opfert die Leistung und löst nicht das grundlegende Problem unseres Plugins.

Die echte Lösung: Das Plugin kompatibel machen

Damit ein Plugin ein guter Bürger des modernen Gradle-Ökosystems ist, muss es mit dem Konfigurations-Cache kompatibel sein. Dies erfordert, dass wir überdenken, wie Daten zu unseren Tasks gelangen.

Der Schlüssel ist, die zu verwendenAnbieter-APIsvon Gradle. Anstatt direkte Werte zu übergeben (wie ein`String` ou un File) für unsere Aufgaben, wir müssen verbringen`Property<T>`oder einige`Provider<T>`.

Hier ist der Refactoring-Plan :
  1. Aufgaben-Einträge deklarieren :Die Aufgabe, die die YAML-Datei parst, muss diese Datei als Eingabe deklarieren. Dazu wird die Annotation verwendet.@InputFile.

[source,kotlin] ---- @get:InputFile abstract val configFile: RegularFileProperty ----


Verwandte Artikel