Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

(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?

like image 601
Laurence Gonsalves Avatar asked Sep 02 '25 17:09

Laurence Gonsalves


1 Answers

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"

like image 180
3ameration Avatar answered Sep 05 '25 16:09

3ameration