Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow mavenBom to override dependency #173

Closed
rwinch opened this issue May 9, 2017 · 16 comments
Closed

Allow mavenBom to override dependency #173

rwinch opened this issue May 9, 2017 · 16 comments

Comments

@rwinch
Copy link

rwinch commented May 9, 2017

I realize this is inconsistent with Maven, but I'd love for the io.spring.dependency-management to support overriding a dependency declaration with a mavenBom. To illustrate:

Two mavenBom

Currently if there are two mavenBoms the second one wins (this is how I would expect it to work). For example:

plugins {
    id 'java'
    id 'io.spring.dependency-management' version '1.0.2.RELEASE'
}

dependencyManagement {
	imports {
		mavenBom 'org.springframework:spring-framework-bom:4.2.0.RELEASE'
		mavenBom 'org.springframework:spring-framework-bom:4.3.0.RELEASE'
	}
}

repositories {
    mavenCentral()
}

dependencies {
	compile 'org.springframework:spring-core'
}
$ ./gradlew dependencyInsight --dependency spring-core --configuration compile                 
No Gradle wrapper found, using /home/rwinch/.sdkman/candidates/gradle/current/bin/gradle
:dependencyInsight
org.springframework:spring-core:4.3.0.RELEASE (selected by rule)

org.springframework:spring-core: -> 4.3.0.RELEASE
\--- compile

BUILD SUCCESSFUL

Total time: 0.472 secs

mavenBom then dependency

If a mavenBom is followed by a dependency, the dependency wins (this is how I would expect it to win). For example:

plugins {
    id 'java'
    id 'io.spring.dependency-management' version '1.0.2.RELEASE'
}

dependencyManagement {
	imports {
		mavenBom 'org.springframework:spring-framework-bom:4.2.0.RELEASE'
	}
	dependencies {
		dependency 'org.springframework:spring-core:4.3.0.RELEASE'
	}
}

repositories {
    mavenCentral()
}

dependencies {
	compile 'org.springframework:spring-core'
}
$ gradle dependencyInsight --dependency spring-core --configuration compile 
No Gradle wrapper found, using /home/rwinch/.sdkman/candidates/gradle/current/bin/gradle
:dependencyInsight
org.springframework:spring-core:4.3.0.RELEASE (selected by rule)

org.springframework:spring-core: -> 4.3.0.RELEASE
\--- compile

BUILD SUCCESSFUL

Total time: 0.521 secs

dependency followed by mavenBom

If a dependency is followed by a mavenBom the dependency wins.

plugins {
    id 'java'
    id 'io.spring.dependency-management' version '1.0.2.RELEASE'
}

dependencyManagement {
	dependencies {
		dependency 'org.springframework:spring-core:4.3.0.RELEASE'
	}
	imports {
		mavenBom 'org.springframework:spring-framework-bom:4.2.0.RELEASE'
	}
}

repositories {
    mavenCentral()
}

dependencies {
	compile 'org.springframework:spring-core'
}
$ gradle  dependencyInsight --dependency spring-core --configuration compile 
No Gradle wrapper found, using /home/rwinch/.sdkman/candidates/gradle/current/bin/gradle
:dependencyInsight
org.springframework:spring-core:4.3.0.RELEASE (selected by rule)

org.springframework:spring-core: -> 4.3.0.RELEASE
\--- compile

BUILD SUCCESSFUL

Total time: 0.474 secs

This is how maven behaves, but to me is quite inconvenient because I would like to override an explicit dependency with a mavenBom It would be nice if the dependency could be overridden by the mavenBom.

@wilkinsona
Copy link
Contributor

wilkinsona commented May 9, 2017

Thanks for the detailed report, @rwinch. I will confess that, up until now, I've only given thought to the relative ordering of multiple boms and hadn't considered the effect that the relative ordering of a bom and an individual managed dependency should have. In short, the current behaviour isn't the way it is for any well-considered reason. That said, changing it does bring the risk of breaking someone's build so it's probably not appropriate for a 1.0.x release.

#165 is also somewhat related to this as it proposes making the behaviour described above configurable, e.g. last wins, first wins, etc.

@rwinch
Copy link
Author

rwinch commented May 9, 2017

Thanks for the fast reply @wilkinsona. I do want to ensure I point out that the way it currently behaves aligns with Maven. I'm guessing this is due to the Maven philosophy of deeper dependencies have less precedence than those explicitly listed.

It just so happens that this is inconvenient for a number of scenarios, so I would like to be able to override with the BOM.

@wilkinsona
Copy link
Contributor

the way it currently behaves aligns with Maven

That isn't the case for when two boms are imported. In Maven the first import will win, whereas in this plugin (and as you have observed above) the last bom will win. That difference also wasn't intentional, but it seems to be the most intuitive and hasn't caused any problems thus far. A decision was made a while ago to stick with it. Aligning the behaviour of a single managed dependency and a bom to be last-wins makes sense to me, irrespective of what Maven does.

@rwinch
Copy link
Author

rwinch commented May 9, 2017

That isn't the case for when two boms are imported.

Sorry. I should have been more clear. If you have a dependency management section with a dependency and then a bom in Maven, the dependency wins. I believe this is due to the Maven philosophy of deeper dependencies have less precedence. For example:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <modelVersion>4.0.0</modelVersion>
  <groupId>foo</groupId>
  <artifactId>bar</artifactId>
  <version>1.0.0.BUILD-SNAPSHOT</version>
  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>4.2.0.RELEASE</version>
      </dependency>
      <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-framework-bom</artifactId>
        <version>4.3.0.RELEASE</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <scope>compile</scope>
    </dependency>
  </dependencies>
</project>
$ mvn dependency:list                            
...
[INFO] The following files have been resolved:
[INFO]    commons-logging:commons-logging:jar:1.2:compile
[INFO]    org.springframework:spring-core:jar:4.2.0.RELEASE:compile

A decision was made a while ago to stick with it. Aligning the behaviour of a single managed dependency and a bom to be last-wins makes sense to me, irrespective of what Maven does.

This works well for me (this is what I would like it to do anyways), but wanted to be sure I communicated Maven's current behavior.

Thanks again!

@rwinch
Copy link
Author

rwinch commented May 30, 2017

@wilkinsona Would you consider adding a flag to support allowing a bom to override the properties in 1.x?

@wilkinsona
Copy link
Contributor

I'll certainly consider it, although I'm rather wary of how complex it may get. There's a somewhat similar requirement in #160.

After refreshing my memory, the current behaviour is a side-effect of wanting a version declared on a dependency to take precedence over any dependency management for that dependency (e1bb745). To implement what's being requested here, things would have to be further differentiated so that dependency management from a bom, dependency management for a single dependency, and implicit dependency management that comes from a dependency declared with a version were all tracked and considered separately.

@rwinch
Copy link
Author

rwinch commented Jun 22, 2017

It might be obvious, but I wanted to point out that this behavior prevents the spring-io-plugin from working correctly. When a project manages a dependency explicitly for its standard build, then the spring-io-plugin does not override the version.

If one is careful they can have conditional logic to disable the dependency management for dependencies in the Spring IO platform. However, this is somewhat fragile because you must keep track of which dependencies are in the Spring IO Platform and might not even know which version of the Spring IO Platform is going to be tested.

@wilkinsona
Copy link
Contributor

It might be obvious, but I wanted to point out that this behavior prevents the spring-io-plugin from working correctly. When a project manages a dependency explicitly for its standard build, then the spring-io-plugin does not override the version.

That's not my experience. REST Docs uses the dependency management plugin, and the Spring IO plugin manages to override the dependency management to use its versions.

I have the "normal" dependency management configured like this:

dependencyManagement {
		imports {
			mavenBom "org.springframework:spring-framework-bom:$springVersion"
		}
		dependencies {
			dependency 'com.fasterxml.jackson.core:jackson-databind:2.5.5'
			dependency 'com.jayway.restassured:rest-assured:2.8.0'
			dependency 'com.samskivert:jmustache:1.12'
			dependency 'commons-codec:commons-codec:1.10'
			dependency 'javax.servlet:javax.servlet-api:3.1.0'
			dependency 'javax.validation:validation-api:1.1.0.Final'
			dependency 'junit:junit:4.12'
			dependency 'io.rest-assured:rest-assured:3.0.2'
			dependency 'org.apache.pdfbox:pdfbox:2.0.6'
			dependency 'org.asciidoctor:asciidoctorj:1.5.5'
			dependency 'org.asciidoctor:asciidoctorj-pdf:1.5.0-alpha.15'
			dependency 'org.hamcrest:hamcrest-core:1.3'
			dependency 'org.hamcrest:hamcrest-library:1.3'
			dependency 'org.hibernate:hibernate-validator:5.2.2.Final'
			dependency 'org.mockito:mockito-core:1.10.19'
			dependency 'org.springframework.hateoas:spring-hateoas:0.19.0.RELEASE'
			dependency 'org.jacoco:org.jacoco.agent:0.7.7.201606060606'
		}
	}

And the Platform's configured like this:

		dependencyManagement {
			springIoTestRuntime {
				imports {
					mavenBom "io.spring.platform:platform-bom:${platformVersion}"
				}
			}
		}

The end result is that the Platform's versions win. Here's the versions with Cairo-BUILD-SNAPSHOT:

+--- com.fasterxml.jackson.core:jackson-databind: -> 2.9.0.pr4
|    +--- com.fasterxml.jackson.core:jackson-annotations:2.9.0.pr4
|    \--- com.fasterxml.jackson.core:jackson-core:2.9.0.pr4
+--- org.springframework:spring-web: -> 5.0.0.BUILD-SNAPSHOT
|    +--- org.springframework:spring-aop:5.0.0.BUILD-SNAPSHOT
|    |    +--- org.springframework:spring-beans:5.0.0.BUILD-SNAPSHOT
|    |    |    \--- org.springframework:spring-core:5.0.0.BUILD-SNAPSHOT
|    |    |         \--- org.springframework:spring-jcl:5.0.0.BUILD-SNAPSHOT
|    |    \--- org.springframework:spring-core:5.0.0.BUILD-SNAPSHOT (*)
|    +--- org.springframework:spring-beans:5.0.0.BUILD-SNAPSHOT (*)
|    +--- org.springframework:spring-context:5.0.0.BUILD-SNAPSHOT
|    |    +--- org.springframework:spring-aop:5.0.0.BUILD-SNAPSHOT (*)
|    |    +--- org.springframework:spring-beans:5.0.0.BUILD-SNAPSHOT (*)
|    |    +--- org.springframework:spring-core:5.0.0.BUILD-SNAPSHOT (*)
|    |    \--- org.springframework:spring-expression:5.0.0.BUILD-SNAPSHOT
|    |         \--- org.springframework:spring-core:5.0.0.BUILD-SNAPSHOT (*)
|    \--- org.springframework:spring-core:5.0.0.BUILD-SNAPSHOT (*)
+--- commons-codec:commons-codec: -> 1.10
+--- javax.validation:validation-api: -> 1.1.0.Final
+--- junit:junit: -> 4.12
|    \--- org.hamcrest:hamcrest-core:1.3
+--- org.hibernate:hibernate-validator: -> 5.4.1.Final
|    +--- javax.validation:validation-api:1.1.0.Final
|    +--- org.jboss.logging:jboss-logging:3.3.0.Final -> 3.3.1.Final
|    \--- com.fasterxml:classmate:1.3.1 -> 1.3.3
+--- org.mockito:mockito-core: -> 2.8.9
|    +--- net.bytebuddy:byte-buddy:1.6.14
|    +--- net.bytebuddy:byte-buddy-agent:1.6.14
|    \--- org.objenesis:objenesis:2.5 -> 2.5.1
+--- org.hamcrest:hamcrest-core: -> 1.3
+--- org.hamcrest:hamcrest-library: -> 1.3
|    \--- org.hamcrest:hamcrest-core:1.3
+--- org.springframework:spring-test: -> 5.0.0.BUILD-SNAPSHOT
|    \--- org.springframework:spring-core:5.0.0.BUILD-SNAPSHOT (*)
\--- org.glassfish:javax.el:3.0.0

@rwinch
Copy link
Author

rwinch commented Jun 22, 2017

@wilkinsona Thanks for the reply.

My mistake on this. It appears that this does not cause issues with spring-io-plugin. Sorry for the misinformation. I'm not sure what I was doing wrong previously.

@xenoterracide
Copy link

xenoterracide commented Nov 4, 2017

same issue I believe

I have my own BOM generated, it's overriding assertj-core and a couple of other things, just by setting their property version
https://bitbucket.org/xenworks/bom/src/4e8a2c5c2f27d7d88decc862b046c3788cfda561/build.gradle

https://bitbucket.org/xenworks/util/src/f6aa3210c8c06f1d23aafcb7980edcb10d9517b3/build.gradle

howver it's not using v2.8
https://app.wercker.com/XenWorks/util/runs/build/59fd77c28e888f0001dc2a84?step=59fd77cf4267f300010c73d7

here's the generated pom, in case I've made a mistake somewhere

<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.xenoterracide</groupId>
  <artifactId>bom</artifactId>
  <version>0.1.0-SNAPSHOT</version>
  <packaging>pom</packaging>
  <licenses>
    <license>
      <name>The Apache Software License, Version 2.0</name>
      <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
      <distribution>repo</distribution>
    </license>
  </licenses>
  <properties>
    <jcabi-matchers.version>1.3</jcabi-matchers.version>
    <s3mock.version>0.2.2</s3mock.version>
    <mbassador.version>1.3.1</mbassador.version>
    <tika.version>1.15</tika.version>
    <aws.version>1.11.207</aws.version>
    <immutables.version>2.5.5</immutables.version>
    <assertj.version>3.8.0</assertj.version>
    <slf4j-test.version>1.1.0</slf4j-test.version>
    <equalsverifier.version>2.3.3</equalsverifier.version>
  </properties>
  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>nl.jqno.equalsverifier</groupId>
        <artifactId>equalsverifier</artifactId>
        <version>${equalsverifier.version}</version>
      </dependency>
      <dependency>
        <groupId>net.engio</groupId>
        <artifactId>mbassador</artifactId>
        <version>${mbassador.version}</version>
      </dependency>
      <dependency>
        <groupId>org.apache.tika</groupId>
        <artifactId>tika-core</artifactId>
        <version>${tika.version}</version>
      </dependency>
      <dependency>
        <groupId>org.immutables</groupId>
        <artifactId>builder</artifactId>
        <version>${immutables.version}</version>
      </dependency>
      <dependency>
        <groupId>org.immutables</groupId>
        <artifactId>value</artifactId>
        <version>${immutables.version}</version>
      </dependency>
      <dependency>
        <groupId>io.findify</groupId>
        <artifactId>s3mock_2.12</artifactId>
        <version>${s3mock.version}</version>
      </dependency>
      <dependency>
        <groupId>com.jcabi</groupId>
        <artifactId>jcabi-matchers</artifactId>
        <version>${jcabi-matchers.version}</version>
      </dependency>
      <dependency>
        <groupId>uk.org.lidalia</groupId>
        <artifactId>slf4j-test</artifactId>
        <version>${slf4j-test.version}</version>
      </dependency>
    </dependencies>
  </dependencyManagement>
</project>

my test compile deps

testCompileClasspath - Compile classpath for source set 'test'.
+--- org.slf4j:slf4j-api: -> 1.7.25
+--- com.google.guava:guava: -> 20.0
+--- org.immutables:value: -> 2.5.5
+--- junit:junit: -> 4.12
|    \--- org.hamcrest:hamcrest-core:1.3
+--- org.assertj:assertj-core: -> 2.6.0
+--- org.mockito:mockito-core: -> 1.10.19
|    +--- org.hamcrest:hamcrest-core:1.1 -> 1.3
|    \--- org.objenesis:objenesis:2.1 -> 2.5.1
\--- org.hamcrest:hamcrest-library: -> 1.3
     \--- org.hamcrest:hamcrest-core:1.3

@wilkinsona
Copy link
Contributor

@xenoterracide Your bom doesn't appear to use the assertj.version property. It's not used in any of its own dependency management and there's no parent that could be using the property.

@xenoterracide
Copy link

right, but shouldn't the properties I set in my bom affect the versions set in brussels
snippet from linked source

dependencyManagement {
    imports {
        mavenBom 'io.spring.platform:platform-bom:Brussels-SR5'
        mavenBom 'com.xenoterracide:bom:0.1.0-SNAPSHOT'
    }
}

maybe maven doesn't work that way either, originally I did have my bom extend platform bom, I'm still working out the best way to dedupe my migration.

@wilkinsona
Copy link
Contributor

Properties in a bom are isolated to that bom and its ancestors

@liaomengge
Copy link

i have the same problem, could i deal with it ?

@MorielV
Copy link

MorielV commented Aug 20, 2020

Maybe can help https://docs.spring.io/dependency-management-plugin/docs/current/reference/html/

look for "Overriding the Dependency Management"

@wilkinsona
Copy link
Contributor

Superseded by #330 and #332.

@wilkinsona wilkinsona closed this as not planned Won't fix, can't repro, duplicate, stale Jun 30, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants