5

I am currently developing a Gradle 5 project that imports two different Maven BOMs. Therefore, I use the native Gradle syntax without the dependency management plugin. However, both BOMs may define different versions for the same dependency.

dependencies {
    implementation platform ("org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}")
    implementation platform ("com.organisation:xyz:${otherBomVersion}")
}

As far as I know in Maven the first BOM which defines a version for a given dependency determines it. In contrast, in the Gradle dependency management plugin the last BOM which defines a version for a given dependency determines it.

How is the order of imported BOMs handled in pure Gradle 5?

M. Justin
  • 5,160
  • 1
  • 38
  • 65
Robin Ellerkmann
  • 1,979
  • 4
  • 25
  • 30

2 Answers2

2

The order of BOMs, or wherever the dependency is declared, doesn't matter at all in Gradle. Unlike Maven, which uses a nearest first approach, Gradle takes all dependency information in consideration and selects the highest. The documentation states

Gradle will consider all requested versions, wherever they appear in the dependency graph. Out of these versions, it will select the highest one.

A practical example. The following declaration will always select 2.2.5.RELEASE of Spring Cloud Gateway defined by spring-cloud-dependencies BOM version Hoxton.SR8 no matter which platform() declaration is listed first:

dependencies {
    implementation platform('org.springframework.cloud:spring-cloud-dependencies:Hoxton.SR8')
    implementation platform('org.springframework.cloud:spring-cloud-dependencies:Greenwich.SR6')
    
    implementation 'org.springframework.cloud:spring-cloud-starter-gateway'
}

The dependency insight report may look like this:

> gradlew -q dependencyInsight --dependency spring-cloud-starter-gateway
org.springframework.cloud:spring-cloud-starter-gateway:2.2.5.RELEASE (by constraint)
   variant "compile" [
      org.gradle.status              = release (not requested)
      org.gradle.usage               = java-api
      org.gradle.libraryelements     = jar (compatible with: classes)
      org.gradle.category            = library (not requested)

      Requested attributes not found in the selected variant:
         org.gradle.dependency.bundling = external
         org.gradle.jvm.version         = 8
   ]

org.springframework.cloud:spring-cloud-starter-gateway:2.2.5.RELEASE
\--- org.springframework.cloud:spring-cloud-dependencies:Hoxton.SR8
     \--- compileClasspath

org.springframework.cloud:spring-cloud-starter-gateway -> 2.2.5.RELEASE
\--- compileClasspath

You may use enforcedPlatform() should you prefer versions from a specific BOM.

thokuest
  • 2,971
  • 17
  • 26
  • 1
    Did some digging to determine whether/how this applies to platform dependencies, given that they force versions rather than the standard transitive "highest version wins". This explains it: https://docs.gradle.org/current/userguide/dependency_management.html#sub:using-platform-to-control-transitive-deps. "This platform notation ... sets the endorseStrictVersions behavior by default". "strictly" says "any version not matched by this version notation will be excluded". Each platform whitelists its own versions, so either would be legal choices. Then the "highest version wins" would take over. – M. Justin Oct 26 '20 at 21:42
  • Note that this is not true when using `mavenBom` with the [Spring dependency management plugin](https://docs.spring.io/dependency-management-plugin/docs/current/reference/html/#dependency-management-configuration-bom-import-multiple) rather than Gradle 5's `platform` dependencies. In that case, the last BOM that declares a dependency wins. "If multiple boms provide dependency management for the same dependency, the dependency management from the last bom will be used." – M. Justin Oct 27 '20 at 21:32
1

A simpler suggestion would be to use this mechanism described in the Spring Boot Gradle Plugin docs, whereby you can explicitly override versions picked by setting properties.

The example they give:

ext['slf4j.version'] = '1.7.20'

The full list of properties you can set can be found in the Spring Boot reference documentation.

In the end you would have an explicit way to override the defaults given by Spring Boot.

Ah, and now I see you are in fact not using that plugin, so this mechanism doesn't apply to you. Well, if you start using it, the above should work as intended and be predictable.

M. Justin
  • 5,160
  • 1
  • 38
  • 65
rud
  • 965
  • 7
  • 22