https://gradle.com/ logo
#dependency-management
Title
# dependency-management
j

John

09/14/2022, 3:07 PM
What is the proper way to apply a BOM in a library project in order to manage the versions resolved in that project, but in a way that will not cause an error if a consumer of the library applies a different version of the same BOM? I tried doing
implementation(platform("bom:1"))
in the library and
implementation(enforcedPlatform("bom:2"))
in the consumer, but that causes an error. i used to do
implementation(enforcedPlatform("bom:1"))
in the library, but in gradle 7 that cause the maven publish to have a validation error about publishing BOM dependencies (thread)
my error looks like:
Copy code
Cannot select module with conflict on capability 'com.grubhub.roux5:roux-bom-derived-platform:5.26.5' also provided by [com.grubhub.roux5:roux-bom:5.26.5(platform-runtime), com.grubhub.roux5:roux-bom:5.26.5(enforced-platform-runtime)]
f

Fleshgrinder

09/14/2022, 3:18 PM
A BOM is not meant for version management, it is meant for version alignment. Use a version catalog to manage your versions, as this has no effect on the publishing and it does not interfere with the alignment.
Copy code
[versions]
dependency = '1.2.3'

[libraries]
org-group-module-a = { module = 'org.group:module-a', version.ref = 'dependency' }
org-group-module-b = { module = 'org.group:module-b', version.ref = 'dependency' }
Copy code
dependencies {
    api(libs.org.group.module.a)
    api(libs.org.group.module.b)
}
If you actually want to expose the BOM (which sometimes sadly is necessary because too many vendors do not know how to work correctly with BOMs) then
api(platform("..."))
is the way to go, even in a library.
See also https://docs.gradle.org/current/userguide/platforms.html#sub:platforms-vs-catalog which is about exactly the differences (and explains all the rest too).
j

John

09/14/2022, 3:40 PM
thanks!
actually, what I was trying to do works. The person who reported the issue had their project on gradle 5 still, so I just had to have them upgrade to gradle 6+
by " manage" i mean align as well as allow dependencies to be declared without a version since the BOM will auto-align it.
f

Fleshgrinder

09/14/2022, 3:43 PM
As long as you use
platform
and not
enforcedPlatform
it should work, yes. Gradle should also warn or even refuse it publish things with an
enforcedPlatform
. This really is only for edge cases. I would still recommend to strictly use version catalogs for managing versions and BOMs for alignment, and to not mix them. In a perfect world nobody would ever have to have a BOM in any build script, unless it is their own.
j

Jean Helou

09/14/2022, 3:50 PM
In a perfect world nobody would ever have to have a BOM in any build script, unless it is their own.In a perfect world nobody would ever have to have a BOM in any build script, unless it is their own.
hi, I find this discussion very interesting as I am investigating how to replace spring's dependency management plugin with more native gradle constructs. Am I correct to assume that such a case is one of the edge case where depending on a bom is inevitable ? I have been unable to find a version catalog published by the spring boot project but I may have missed things
f

Fleshgrinder

09/14/2022, 3:54 PM
Spring suffers from the problem that the dependencies they depend on are broken in many ways. This forces them to publish a BOM for third-party libraries. Hence, yes, this is such a special case. You can replace the Spring dependency management with a simple
implementation(platform(SpringBootPlugin.BOM_COORDINATES))
(and if you need it in other configurations add it there too, e.g.
kapt(platform(SpringBootPlugin.BOM_COORDINATES))
). The only reason why the Spring team still provides the plugin is for users to easily change versions of broken transitive dependencies without knowing the ins and outs of how Gradle resolution works. Well, this applies to newer Gradle versions. Old Gradle versions lacked a lot of features and thus forced Spring to provide the plugin, but those days are gone. See also https://github.com/spring-projects/spring-boot/issues/29588#issuecomment-1028861122
j

Jean Helou

09/14/2022, 4:08 PM
thanks, that confirms part of my research. Of course our situation is slightly more involved since the build is a few years old and overrides multiple dependency versions provided by spring for various reasons. This was done by creating our own bom which declares spring's bom as its parent pom and overrides various properties there. For now we
import
that bom using spring's dependency plugin then override the dependencies that didn't resolve to the correct versions using
dependencies
entries. I must say I am unsure what a proper idiomatic gradle override would look like and how version catalogs and platforms fit in there. I am trying to search this slack, as well as the forums and internet at large but without luck until now
f

Fleshgrinder

09/14/2022, 4:10 PM
This https://docs.gradle.org/current/userguide/resolution_rules.html should actually answer all your questions. The reason why you still need the Spring BOM even with your customization is because it has exclude rules and you don't want to recreate those.
j

Jean Helou

09/14/2022, 4:13 PM
so a version catalog that contains all the overridden versions and a resolution strategy to enforce it over what spring suggests
thanks
f

Fleshgrinder

09/14/2022, 4:17 PM
You could also look into https://github.com/nebula-plugins/gradle-resolution-rules-plugin/wiki/Consuming-Rules which provides a simple way to set up and share many resolution rules, going way beyond what you can build with version catalogs. However, it also has certain limitations (e.g. https://github.com/nebula-plugins/gradle-resolution-rules-plugin/issues/97) that Gradle itself does not have. That said, it's still trivial to use, and the default rules it provides are a gold mine. 😉
j

Jean Helou

09/14/2022, 4:19 PM
I have noted the reference, but I think I will try to stick to gradle native
p

Phanindra R

09/15/2022, 12:40 AM
@John do you want the consumers of the library to be aware of the
bom:1
at all? If not, you could exclude it from the published .pom and .module files by using a custom Gradle configuration + resolved versions in publications. Here's a sample project which demonstrates that - https://github.com/jjohannes/gradle-demos/tree/main/internal-platform
v

Vampire

09/16/2022, 10:18 AM
The only reason why the Spring team still provides the plugin is for users to easily change versions of broken transitive dependencies without knowing the ins and outs of how Gradle resolution works. Well, this applies to newer Gradle versions. Old Gradle versions lacked a lot of features and thus forced Spring to provide the plugin, but those days are gone.
That is not actually true @Fleshgrinder. Don't get me wrong, I also prefer and advice to use Gradle native functionality if it is just about using a BOM. But the plugin has additional unique functionality people might want to use like porting over imho broken-by-design features from Maven to the Gradle world and other things. So the plugin is not only maintained because of the version overwrites. ;-)
f

Fleshgrinder

09/16/2022, 11:25 AM
I just quoted the maintainer from the ticket I linked.
v

Vampire

09/16/2022, 4:47 PM
To be honest, I cannot find the origin of that quote in the ticket, not even semantically. He states that the two "key capabilities" are version management and version overwrites, but that's all. I also thought in the past that these are the only features and the plugin is obsolete with platform support, but was told otherwise. Just have a look at the documentation and you see that it has some more features which simply are not the "key capabilities".
f

Fleshgrinder

09/16/2022, 5:09 PM
Sorry, too many issues on this in the Spring issue tracker and my brain history. See https://github.com/spring-projects/spring-boot/issues/21723 and also https://github.com/spring-projects/spring-boot/issues/21570 as well as https://github.com/spring-gradle-plugins/dependency-management-plugin/issues/280 and probably x other issues I cannot find right now. I'm not a Spring maintainer but @Andy Wilkinson is in this Slack and he is also the one who commented on these issues, and wrote the following in the Spring Boot reference docs (see https://github.com/spring-projects/spring-boot/commit/d2926e02222f942948637bd5bb6972379eb094c8):
Copy code
To manage dependencies in your Spring Boot application, you can either apply the {dependency-management-plugin}[`io.spring.dependency-management`] plugin or, if you are using Gradle 6 or later, use Gradle's native bom support.
The primary benefit of the former is that it offers property-based customization of managed versions, while using the latter will likely result in faster builds.
I know that Gradle did not support various BOM features Maven did (e.g. exclusions), and I know that Gradle resolves dependencies differently than Maven and that some people tried to bend Gradle to resolve them the same way (not sure if this plugin did). However, Maven resolving is broken and Gradle does support all BOM features by now (well, during consumption; it is not possible for us to use all BOM features with the
platform
plugin yet https://github.com/gradle/gradle/issues/12214). Anyway, unless you have some credible sources I stick with what I've learned over the past years and do not use the dependency management plugin in any of my projects. So far I faired well with this and do not fear recommending anyone to do the same.
a

Andy Wilkinson

09/16/2022, 6:23 PM
Thanks for adding me to the thread, @Fleshgrinder.
So the plugin is not only maintained because of the version overwrites. 😉
@Vampire I'm afraid you're mistaken. That is the only reason that the plugin is still maintained. Support for Maven-style exclusion semantics will be removed in the future . In fact, it would have already happened if we'd had the bandwidth for it. I'd strongly encourage you to look at using Gradle's built-in platform support and find an alternative way of dealing with the lack of property-based version overrides.
f

Fleshgrinder

09/16/2022, 7:31 PM
@Andy Wilkinson thanks for the clarification. Quick question, is there a reason why not all Spring Boot & Cloud packages reference the Spring Boot or Cloud BOM of their release? This would tremendously simplify things for everyone. Especially the Cloud releases are complicated because of the weird versioning scheme with code names.
a

Andy Wilkinson

09/16/2022, 7:34 PM
Sorry, I'm not sure I understand the question. Also, describing something as "terrible" isn't a great way to start a constructive discussion.
f

Fleshgrinder

09/16/2022, 7:52 PM
I’m really sorry, I guess I was typing faster than thinking. I corrected that and hope you can forgive me. The main purpose of a BOM is to align versions in a dependency tree. Most end users care about it because it allows them to leave out versions, but that it actually an added bonus and not its most important feature. Suppose we have a BOM called
spring:platform
and two modules called
spring:a
and
spring:b
. All of them are properly released together with a matching version (just like in the Spring case). The
spring:platform:1
references
spring:a:1
and `spring🅱️1`; the
spring:platform:1
references
spring:a:1
and `spring🅱️1`; and so forth. Now if I do…
Copy code
dependencies {
  implementation("spring:a:1")
  implementation("spring:b:2")
}
…things are not going to be aligned, since I did not manually add the BOM. Suppose now that both modules reference the respective BOM and we would get auto-alignment (not only in Gradle, but also in Maven). I see a lot of projects where people don’t know what BOMs are and they keep on writing the versions they need manually everywhere. Having all the modules include their BOM would solve possible issues they might encounter. This is especially true if they inherit those dependencies through transitives. I also see most projects not having BOMs, and those who do are not actually referencing their own BOMs in their dependencies. Ultimately reducing the value the BOM can actually provide. A project I know of where this is done correctly is Jackson, see: https://github.com/FasterXML/jackson-annotations/blob/082ee2812f0a7019516181947e591d8ed624425a/pom.xml#L129-L137
a

Andy Wilkinson

09/16/2022, 7:55 PM
Suppose now that both modules reference the respective BOM and we would get auto-alignment (not only in Gradle, but also in Maven).
I don't believe that's the case. Assuming Maven pom metadata here as that's the lowest common denominator, I don't believe a bom that's imported or used as a parent in those modules will affect the dependency graph of a consuming project. For example, Maven will just take the version that's nearest to the root of the graph so you'll end up with
spring:a:1
and
spring:b:2
as the versions declared in the pom are only one edge from the root.
f

Fleshgrinder

09/16/2022, 7:57 PM
I’m certain you know better how Maven works than I do. I avoid it by all means and haven’t had to use it in the past two years once. It definitely works like that in Gradle. 🙂
a

Andy Wilkinson

09/16/2022, 8:02 PM
TIL. I hadn't realised that Gradle's module metadata did that. That's quite handy. We could consider it for Boot if we introduced a separate
spring-boot-bom
that was a true bill of materials for Boot's own modules. I don't think Boot's modules should reference
spring-boot-dependencies
as that's too opinionated and we intentionally allow people to choose not to use Boot's management of third-party dependencies.
j

John

09/16/2022, 8:14 PM
I for one would love a spring boot standalone BOM. Right now I build my own so that I can align boot versions without effecting other transitives. (I use the spring BOM as well)
a

Andy Wilkinson

09/16/2022, 8:15 PM
Please open an issue and we'll take a look. I can't guarantee that it'll make it into 3.0, but I think it would be a useful addition.
f

Fleshgrinder

09/16/2022, 8:19 PM
Makes perfect sense to use a dedicated Spring Boot only BOM (Cloud afaik already has one) and to not use the dependencies BOM. Although, no harm is done by using it. The versions from the BOM are only a recommendation to Gradle. So if the Spring BOM says Kotlin 1.6.10 and the user uses 1.7.10 it would resolve to 1.7.10 (although resolution would be inconsistent because JetBrains does not reference their own BOM either). In any event, I think this knowledge needs to be spread wider. I’ll open an issue. Attached is a screenshot of Gradle doing exactly what I explained.
v

Vampire

09/19/2022, 4:16 PM
@Vampire I'm afraid you're mistaken. That is the only reason that the plugin is still maintained
No need to @Andy Wilkinson, I'm happy I was outdated. That was the info I was given by some Spring guy (don't remember who it was) when I said similar things as Fleshgrinder here. So things probably have changed to the better in the meantime. 🙂
Anyway, unless you have some credible sources I stick with what I've learned over the past years and do not use the dependency management plugin in any of my projects. So far I faired well with this and do not fear recommending anyone to do the same.
@Fleshgrinder as said before, I would never recommend anything else and I'm also never using the dep-management plugin and always advise to use the built-in support when I see it used. I just said that the dep-management is not the only functionality of the plugin that someone might want to use as that was what I was told in the past by some Spring guy when I said the plugin is useless with the added native BOM support in Gradle. :-)
289 Views