Inleiding

Doelpubliek : Gradle-ontwikkelaars die geconfronteerd worden met het beheren van afhankelijkheden in multi-build-projecten.

In onze zoektocht om de plugin te maken`site-baker`, hebben we geprobeerd een schone en gecentraliseerde codebasis te behouden. Een van de beste praktijken in het moderne Gradle-ecosysteem is het gebruik van decatalogus van versies(libs.versions.toml) om de afhankelijkheden te beheren. Maar wat gebeurt er wanneer je project complexer wordt, zoals eeneen composiet bouwenwaar een onafhankelijke build (onze plugin) moet worden opgenomen in een ander ?

Hier is waar ons avontuur een onverwachte wending nam. We wilden dat onze plugin`site-baker`, dat zelf ook een Gradle-project is, gebruikt hetzelfde versiecatalogus als ons hoofproject. Dit artikel, het vijfde van onze serie, vertelt hoe we dit probleem hebben opgelost.

1. De Versiecatalogus (libs.versions.toml): Een herinnering

Het bestand`gradle/libs.versions.toml`is een functie van Gradle die het mogelijk maakt om de versies en coördinaten van de afhankelijkheden van je project centraal te beheren. Het is meestal gestructureerd in vier secties :

  • [versions]: Definieert aliassen voor de versienummers (bijv:`kotlin = "1.9.20"`).

  • [libraries]: Stelt aliassen in voor de volledige afhankelijkheden, met behulp van de bovenstaand gedefinieerde versies (bijv:`kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" }`).

  • [bundles]: Bundelt meerdere bibliotheken onder één alias (bijv:`jackson = ["jackson-core", "jackson-databind"]`).

  • [plugins]: Definieert aliassen voor de Gradle-plugins.

Eenmaal gedefinieerd genereert Gradle automatisch getypte toegangs­methoden, wat uw`build.gradle.kts`veel leesbaarder :

dependencies {
    // Au lieu de : implementation("org.jetbrains.kotlin:kotlin-stdlib:1.9.20")
    implementation(libs.kotlin.stdlib)
}
Dataflux van de versiecatalogus
@startuml
!theme plain
file "gradle/libs.versions.toml" as Toml
node "Gradle" as G
file "build.gradle.kts" as BuildScript

Toml --> G : "wordt gelezen door"
G --> BuildScript : "genereer de accessor `libs"
BuildScript -> G : "Gebruik `libs` om de afhankelijkheden te declareren"
@enduml

2. Het probleem: een samengestelde build en twee gescheiden werelden

Onze projectstructuur is eenbouw composiet. Het hoofdproject (thymeleaf.cheroliv.com) omvat het project van de plugin (site-baker) via de opdracht`includeBuild("site-baker")in het bestand`settings.gradle.kts.

Structuur van de samenstelling
@startuml
!theme plain
package "Hoofdproject" {
  file "settings.gradle.kts" as MainSettings
  folder "gradle" {
    file "libs.versions.toml" as MainToml
  }
}

package "Plugin `site-baker` (Build Inclus)" {
  file "settings.gradle.kts" as PluginSettings
  file "build.gradle.kts" as PluginBuild
}

MainSettings -> PluginSettings : `includeBuild("site-baker")`
PluginBuild ..> MainToml : Wilde toegang krijgen tot...
note right on link: ...maar kon niet !
@enduml

Het probleem is dat, standaard, een ingebouwde build eengescheiden wereld. Het heeft zijn eigen configuratie, zijn eigen levenscyclus, en het ziet het bestand niet`libs.versions.toml`van de hoofd build. Onze`plugin/build.gradle.kts`probeerde te gebruiken`libs.plugins.kotlin.jvm`, maar het object`libs`werd niet gegenereerd vanuit het juiste TOML-bestand, wat bouwfouten veroorzaakte.

3. De oplossing: Delen van de afhankelijkheidsresolutie

Na meerdere onderzoeken bleek de oplossing een functionaliteit van Gradle specifiek ontworpen voor dit geval:`dependencyResolutionManagement`.

In het bestand`settings.gradle.kts` du bouw inclusief (site-baker/settings.gradle.kts), kunnen we Gradle vertellen waar het versiecatalogus te vinden is dat we moeten gebruiken.

Hier is de configuratie die we hebben toegevoegd aan`site-baker/settings.gradle.kts`:

// site-baker/settings.gradle.kts

dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        gradlePluginPortal()
        mavenCentral()
    }
    versionCatalogs {
        create("libs") {
            from(files("../gradle/libs.versions.toml"))
        }
    }
}

rootProject.name = "site-baker"
include("plugin")

Laten we het cruciale deel analyseren:

versionCatalogs {
    create("libs") { // Crée un catalogue nommé 'libs'
        from(files("../gradle/libs.versions.toml")) // En utilisant ce fichier
    }
}
  • create("libs")`Wij declareren een versiecatalogus die toegankelijk zal zijn via het alias`libs.

  • from(files("../gradle/libs.versions.toml")): Dit is de magie. We geven Gradle aan dat de bron van deze catalogus het TOML-bestand is dat zich in de map bevindt`gradle` du ouderproject(../)

Met deze configuratie, de build`site-baker`Hij weet nu dat hij de versiecatalogus van het hoofdproject moet gebruiken. Het object`libs`wordt correct gegenereerd, en de afhankelijkheden worden opgelost zoals verwacht.

4. Conclusie: De kracht van goed beheerde compositie-builds

Deze ervaring was rijk aan lessen. De versiecatalogus is een fantastisch hulpmiddel voor onderhoudbaarheid, maar het gedrag in scenario’s met samengestelde builds is niet altijd intuïtief.

De sleutel is om te onthouden dat een ingesloten build nog steeds een onafhankelijke build is. Om configuraties zoals de versie-catalogus te delen, moet je de expliciete mechanismen gebruiken die Gradle biedt, zoals het blok`dependencyResolutionManagement`in`settings.gradle.kts`.

Door dit probleem op te lossen, hebben we niet alleen onze build netter gemaakt, maar we hebben ook de consistentie van ons project vergroot door ervoor te zorgen dat de plugin en het hoofdproject één enkele bron van waarheid delen voor hun afhankelijkheden.

In het volgende artikel zullen we ons plugin verder uitbreiden door er complexere taken aan toe te voegen, nu ons afhankelijkheidsbeheer solide en gecentraliseerd is.

Gerelateerde artikelen