Reference version from catalog in plugin

I’m writing a convention plugin that basically just provides convenient defaults for another plugin, namely de.aaschmid.cpd. As part of this, I’d like to provide a value for cpd.toolVersion (defined in CodeQualityExtension, a superclass of CpdExtension).

Instead of hardcoding this value, I’d like to use a version that I get from a separate version catalog plugin. A naive approach would be:

cpd {
    minimumTokenCount = 123
    toolVersion = libs.versions.cpd.get() // or something like this
}

However, while I’m able to use my version catalog to define the version of the de.aaschmid.cpd plugin, this catalog is not available inside the plugin that I’d like to export.

I’ve seen Give access to catalogs from binary plugins · Issue #15382 · gradle/gradle · GitHub and Make version catalogs accessible from precompiled script plugins · Issue #15383 · gradle/gradle · GitHub but, to be honest, I don’t see how this helps.

I managed to compile my plugin with unsafe/untyped access to my catalog (val libs = extensions.getByType<VersionCatalogsExtension>().named("libs") etc.), but haven’t managed to include this plugin in my project so that the libs catalog is known (even though I included it in that project, too):

An exception occurred applying plugin request [id: 'de.c-otto.java-conventions']
> Failed to apply plugin 'de.c-otto.java-conventions.cpd'.
   > Extension of type 'VersionCatalogsExtension' does not exist. Currently registered extension types: [ExtraPropertiesExtension, BasePluginExtension, DefaultArtifactPublicationSet, SourceSetContainer, ReportingExtension, JavaToolchainService, JavaPluginExtension, TestingExtension, CpdExtension, NullAwayExtension, CheckstyleExtension]

How can I reference a version defined in an imported version catalog inside a plugin so that this version is inlined and used when I publish the plugin?

(Thanks to @Vampire who’s very helpful, both here and in the linked GitHub issues)

Can you show how you “included it in that project”?
Because when the catalog is there, it should work with that string-y API or with my hack-around from the issue you mentioned.

The version catalog is published to my local Maven repository (de.c-otto:version-catalog:2023.01.21).

Conventions plugin project

published to my local maven repository (de.c-otto:java-conventions:2021.01.21)

settings.gradle.kts:

dependencyResolutionManagement {
    repositories {
        mavenLocal()
    }
    versionCatalogs {
        create("libs") {
            from("de.c-otto:version-catalog:2023.01.21")
        }
    }
}

build.gradle.kts:

repositories {
    mavenCentral()
    mavenLocal()
    gradlePluginPortal()
}

val platformVersion = libs.versions.platform.get()

dependencies {
    implementation(platform("de.c-otto:java-platform:$platformVersion")) // unrelated, I think
    implementation(files(libs.javaClass.superclass.protectionDomain.codeSource.location))
    implementation(libs.gradleplugins.cpd) // this resolves to de.aaschmid.cpd:gradle-cpd-plugin:3.3 and works as expected
}

src/main/kotlin/de.c-otto.de.java-conventions.gradle.kts

plugins {
    java
    `java-test-fixtures`
    id("de.c-otto.java-conventions.cpd")
}

java {
    sourceCompatibility = JavaVersion.VERSION_17
    targetCompatibility = JavaVersion.VERSION_17
    consistentResolution {
        useCompileClasspathVersions()
    }
}

val libs = extensions.getByType<VersionCatalogsExtension>().named("libs")
val platformVersion = libs.findVersion("platform").get()

dependencies {
    implementation(platform("de.c-otto:java-platform:$platformVersion")) // unrelated?
}

repositories {
    mavenCentral()
    mavenLocal()
}

src/main/kotlin/de.c-otto.java-conventions.cpd.gradle.kts

import de.aaschmid.gradle.plugins.cpd.Cpd

val libs = extensions.getByType<VersionCatalogsExtension>().named("libs")

plugins {
    id("de.aaschmid.cpd")
}

cpd {
    toolVersion = libs.findVersion("pmdCpd").get().strictVersion // `pmdCpd = "6.53.0"` is defined in the `[versions]` block in my TOML file
}

Consuming Java project

both settings.gradle.kts and buildSrc/settings.gradle.kts include:

dependencyResolutionManagement {
    repositories {
        mavenCentral()
        mavenLocal()
    }
    versionCatalogs {
        create("libs") {
            from("de.c-otto:version-catalog:2023.01.21")
        }
    }
}

buildSrc/build.gradle.kts:

plugins {
    `kotlin-dsl`
}

repositories {
    gradlePluginPortal()
    mavenLocal()
}

val platformVersion = libs.versions.platform.get()

dependencies {
    implementation("de.c-otto:java-conventions:$platformVersion")
    implementation(files(libs.javaClass.superclass.protectionDomain.codeSource.location))
}

buildSrc/src/main/kotlin/de.c-otto.playground.java-conventions.gradle.kts:

plugins {
    java
    id("de.c-otto.java-conventions")
}

group = "de.c-otto.playground"

repositories {
    mavenLocal()
}

Failure:

An exception occurred applying plugin request [id: 'de.c-otto.java-conventions']
> Failed to apply plugin 'de.c-otto.java-conventions.cpd'.
   > Extension of type 'VersionCatalogsExtension' does not exist. Currently registered extension types: [ExtraPropertiesExtension, BasePluginExtension, DefaultArtifactPublicationSet, SourceSetContainer, ReportingExtension, JavaToolchainService, JavaPluginExtension, TestingExtension]

I think I know the problem, can you please show more of the previous output?

Here’s the output with -i --stacktrace:
https://c-otto.de/gradle-out.txt

Yeah, as I thought.
It happens during :buildSrc:generatePrecompiledScriptPluginAccessors.
In that step the plugins { ... } blocks of the precompiled Kotlin DSL script plugins are extracted, copied over to a dummy project and then executed to find out which type-safe accessors to generate.
In that dummy project the version catalog is not available and thus this step fails.

I guess you should report this as issue if there is none yet, but remove the traces of my hack-around to show that it has nothing to do with the problem. :slight_smile:

As a work-around, do not use the plugins { ... } block, but the legacy apply way to apply the plugin, then it should work I think.

Sidenote: Why do you only take the version for the platform from the version catalog and not the whole platform like libs.platform?

1 Like

Will do, thanks a lot!

I haven’t used libs.platform because I noticed the same thing after I published and tagged the platform/catalog project. I’ll change that soon, though - thanks for the hint :slight_smile:

Looks like someone beat me to it: `VersionCatalogExtension` not added to the `gradle-kotlin-dsl-accessors` during `generatePrecompiledScriptPluginAccessors` · Issue #22468 · gradle/gradle · GitHub

1 Like