We’re looking to switch to using version catalogs,...
# general
e
We’re looking to switch to using version catalogs, but there’s an interesting case I’m not sure how to solve. We have a dependency X and another dependency Y, which is dependent upon X and is versioned accordingly. So if X is version “1.1.4”, Y will be version “Y.1.1.4". They’re distributed by different developers (BOMs not an option), but they have to keep in lock-step. Today we solve this using properties. Is there a way to handle such a case with catalogs?
c
this would be solvable with a variable when using the version catalog API; don’t see how the TOML file could address this (short of manually ensuring that the dependencies line up).
e
Do you have an example of what this could look like with a variable?
c
In settings.gradle.kts:
Copy code
dependencyResolutionManagement {
    versionCatalogs {
        create("libs") {
            val xVer = "1.1.4"
            version("xVer", xVer)
            library("xLib", "org.x", "lib").versionRef("xVer")
            library("yLib", "org.y:lib:y.${xVer}")
        }
    }
}
you can combine the API with reading from the TOML file using
from
in the API to read a TOML file, allowing to use both where appropriate. Personally I prefer the type-safety, expressiveness and flexibility of the API.
g
Catalog will only give you recommendations either way, so if you have several dependencies moving in lockstep you'll need to either use strict versions in catalog (which's not recommended for use in libraries) or platform alongside. Platform of course could use catalog itself.
e
Thank you!
hmm is there a way to programmatically read the existing version catalog? i have everything in a .toml file. ideally i’d like to do something along the lines of
Copy code
versionCatalogs {
    create("libs") {
        library("libraryX", "com.dep:libraryx:1.6." + readExistingVersion("libraryY"))
    }
}
but i’m not seeing any read apis available
g
the only way I found is to use
VersionCatalogExtension
on projects
it's not available in settings though
you could declare group+artifact coords in the toml file and augment it in the
settings.gradle[.kts]
(or relevant project if you publish catalog later)
e
yeah i guess augmenting it is what i’m unsure how to do
this is only used in one module. perhaps i could use VersionCatalogExtension in combination with
resolutionStrategy
to force resolution of combined versions
not a super robust solution, but this is also a very niche need
g
i'm not sure if resolution strategy or just a local platform would be easiest way in your case iirc you could just use
dependencyResolutionManagement { versionCatalogs { create("libs") { version("some-alias", "x.y.z"); version ("another-alias", "w.x.y.z") } } }
alongside autoimport from
libs.versions.toml
and then add another subproject with
java-platform
plugin and adding relevant dependencies like
dependencies { constaints { api(libs.fisrt.dep) ; api(libs.second.dep) } }
one thing to be careful: if you publish library from such project -- you have to either publish the platform or modify library's pom+module otherwise you'll get issues with dependency resolution on downstream projects
e
ah yeah, no worries about publishing here
thanks for all the tips! super helpful.
g
just sharing some pitfalls i already checked for myself 😁
v
Another option might be too use a virtual platform to align the versions to also make sure they don't become different by conflict resolution. See the chapter about version alignment in the user guide.
e
Thank you all. In the interest of getting us cut over to version catalogs quickly I’ve opted to do a dumb hack where I just throw an exception at build time if the versions of the 2 libs don’t match. These libs haven’t been updated in 7 months so it’s probably fine for now. Our long-term solution is going to be platform. I hadn’t really considered the benefits of using a platform here but your points have been great.
e
in a large project I work on, I create a platform based on the version catalog https://gradle-community.slack.com/files/U019L3NM4PK/F036U1L6HGE/build.gradle.kts.kt
e
^ this is exactly what i was thinking. Thanks for sharing code to achieve it. I was imagining it being much more work.
g
if you have naming convention for other boms/platforms you can expand @ephemient's fragment to put non-boms in constraints and boms into dependencies (wrapping them as platform)
if you have catalog elements without versions you should exclude them from platform, I'm not sure if gradle will fail build or just emit faulty nodes in pom.xml/module
e
BOM/platform dependencies require
Copy code
javaPlatform {
    allowDependencies()
}
so that they can be added as normally dependencies and not constraints, as you should be able to find in the documentation
Gradle doesn't fail with unversioned constraints (even enough it doesn't make a ton of sense), I haven't tried publishing to see what the generated POM looks like though
g
Yeah, I know about
javaPlatform.allowDependencies()
. And the same bom in dependencies and constraints have different semantics.
dependencies { api(platform(libs.jackson.bom)) }
will effectively add constraints from it to your platform and
dependencies { constraints { api(platform(libs.jackson.bom)) } }
will constraint bom itself but wouldn't add its contents to constraints. In case of publishing pom would have something like
Copy code
<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>your.group.id</groupId>
      <artifactId>artifact.id</artifactId>
      <scope>compile</scope>
    </dependency>
  </dependencies>
</dependencyManagement>
which does seem wrong tbh. IIRC maven doesn't like unversioned deps in
dependencyManagement
section though I couldn't point where I read that.