(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".
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.)
Is the discrepancy described above a bug in Gradle, IntelliJ, or both?
Why does libs.versions.foo.version
work when the version has a _version
suffix, but things behave completely differently if it does not?
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"
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With