In Gradle, how to access the version of a dependency from the version catalog so it can be passed to a task configuration?

115 Views Asked by At

(This is quite long, so scroll to the bottom for a shorter summary.)

I've run into some weird behavior with Gradle version catalogs. I'm not sure if I'm doing things the right way, but I'm also pretty sure that I found a bug in Gradle and/or IntelliJ.

Our Gradle build currently uses gradle.properties to store all of the version information for all of our dependencies. We've been doing this since before version catalog existed. I am now migrating our build to use version catalogs instead. Most of this migration was fairly painless.

The one tricky part of this migration has to do with gradle-jooq-plugin. We need to pass it the version of the jOOQ library that we’re using as a parameter, so that it will use the matching version of the jOOQ code generator. With the properties file, this was pretty straightforward (though somewhat verbose):

# All gradle build snippets are using Kotlin DSL
jooq {
    version = project.property("jooq_version") as String

I wasn't able to find any documentation on how to do the equivalent with version catalogs. However, with help from IntelliJ's autocomplete, I found this, which seems to do the right thing:

jooq {
    version = libs.versions.jooq.version

In our gradle.properties file we had a _version suffix on all of our versions:

jooq_version = 3.19.4

At first, I used the same names in our version catalog:

[versions]
jooq_version = "3.19.4"
...
[libraries]
jooq = { module = "org.jooq:jooq", version.ref = "jooq_version" }

However, after I got everything working, I realized that this suffix isn't really needed, as the versions are in their own namespace. Looking at example version catalogs confirmed that the convention seems to be to name the versions after what they are the version of, without anything in the name saying it's a version.

Here's where things start to get kind of weird. I tried removing the _version suffixes...

...
jooq = "3.19.4"
...
jooq = { module = "org.jooq:jooq", version.ref = "jooq" }
...

...and everything seemed to work except for passing the jooq version to the plugin. Gradle fails with:

Script compilation errors:

  Line 68:     version = libs.versions.jooq.version
                                            ^ Function invocation 'version(...)' expected

  Line 68:     version = libs.versions.jooq.version
                                            ^ Unresolved reference. None of the following candidates is applicable because of receiver type mismatch: 
                                                public infix fun PluginDependencySpec.version(version: String?): PluginDependencySpec defined in org.gradle.kotlin.dsl
                                                public infix fun PluginDependencySpec.version(version: Provider<String>): PluginDependencySpec defined in org.gradle.kotlin.dsl

Strangely, IntelliJ does not highlight any issues on that line.

I tried removing the .version suffix, to mirror the removeal of the _version suffix in the toml file...

jooq {
    version = libs.versions.jooq

...but then IntelliJ highlights the line as an error, and Gradle still complains, but with a different error:

  Line 68:     version = libs.versions.jooq
               ^ Val cannot be reassigned

  Line 68:     version = libs.versions.jooq
                       ^ No applicable 'assign' function found for '=' overload

  Line 68:     version = libs.versions.jooq
                         ^ Type mismatch: inferred type is LibrariesForLibs.JooqVersionAccessors! but Property<String!>! was expected

In IntelliJ, if I navigate to libs.versions.jooq and from there navigate to its return type, JooqVersionAccessors, I see:

    public static class JooqVersionAccessors extends VersionFactory  {
        private final JooqGradleVersionAccessors vaccForJooqGradleVersionAccessors = new JooqGradleVersionAccessors(providers, config);
        public JooqVersionAccessors(ProviderFactory providers, DefaultVersionCatalog config) { super(providers, config); }

            /**
             * Returns the version associated to this alias: jooq.version (3.19.4)
             * If the version is a rich version and that its not expressible as a
             * single version string, then an empty string is returned.
             * This version was declared in catalog libs.versions.toml
             */
            public Provider<String> getVersion() { return getVersion("jooq.version"); }

        /**
         * Returns the group of versions at versions.jooq.gradle
         */
        public JooqGradleVersionAccessors getGradle() {
            return vaccForJooqGradleVersionAccessors;
        }

    }

So there is a public getVersion() there, at least according to IntelliJ, but Gradle isn't seeing it. I also tried method syntax just to be sure...

jooq {
    version = libs.versions.jooq.getVersion()

Again, IntelliJ is happy with this, Gradle is not:

Cannot access 'getVersion': it is protected in 'JooqVersionAccessors'

Since it seems like somehow IntelliJ and Gradle have different ideas about what the type of libs.versions.jooq looks like, to get better insight about what's going on I tried using reflection to print the methods on libs.versions.jooq:

methods of class org.gradle.accessors.dm.LibrariesForLibs$JooqVersionAccessors

asProvider(param: org.gradle.accessors.dm.LibrariesForLibs.JooqVersionAccessors): org.gradle.api.provider.Provider<kotlin.String!>!
getGradle(param: org.gradle.accessors.dm.LibrariesForLibs.JooqVersionAccessors): org.gradle.accessors.dm.LibrariesForLibs.JooqGradleVersionAccessors!
equals(param: org.gradle.accessors.dm.LibrariesForLibs.JooqVersionAccessors, other: kotlin.Any?): kotlin.Boolean
hashCode(param: org.gradle.accessors.dm.LibrariesForLibs.JooqVersionAccessors): kotlin.Int
toString(param: org.gradle.accessors.dm.LibrariesForLibs.JooqVersionAccessors): kotlin.String
getVersion(param: org.gradle.accessors.dm.LibrariesForLibs.JooqVersionAccessors, name: kotlin.String!): org.gradle.api.provider.Provider<kotlin.String!>!
findVersionConstraint(param: org.gradle.accessors.dm.LibrariesForLibs.JooqVersionAccessors, name: kotlin.String!): org.gradle.api.internal.artifacts.ImmutableVersionConstraint!

There's an asProvider() method there that looks promising, so I tried:

jooq {
        version = libs.versions.jooq.asProvider().get()

This works, and I confirmed that it's passing the correct value, but IntelliJ highlights asProvider as an "Unresolved reference".

tl;dr:

  1. Within build.gradle.kts, I need to get the version (and only the version) of a specific dependency defined in the version catalog so it can be passed as a parameter to a task configuration. What is the correct way to do this? (Links to official docs would be appreciated.)

  2. Is the discrepancy described above a bug in Gradle, IntelliJ, or both?

  3. Why does libs.versions.foo.version work when the version has a _version suffix, but things behave completely differently if it does not?

2

There are 2 best solutions below

0
Simon Jacobs On

The type of version in the Gradle Jooq Plugin extension is Property<String> (see GitHub), and the type of libs.versions.jooq from your catalog is Provider<String>.

Given that, in Gradle you would set version, not by assigning it, but by writing:

jooq {
    version.set(libs.versions.jooq)
}

Regarding the errors, I don't think I can explain all your problems, but maybe some of the Kotlin code generated from your earlier tries at the TOML catalog remained in the IntelliJ indexes somehow.

0
3ameration On

If you're working with a build.gradle.kts file and have a Compose version defined in a versions.toml file like this:

[versions]
composeVersion = "1.5.10"

To access this version in your build.gradle.kts, you can use the following:

val composeVersion: String by libs.versions

However, by doing this, you'll obtain the version in Provider format. If you need to retrieve the raw version string, you can simply call:

val rawComposeVersion: String = libs.versions.composeVersion.get()

This will provide you with the plain version string "1.5.10"