I have a multi-project layout which uses git submodules such that developers can checkout the sub projects as standalone git repos and work on them. For eg. I have Parent gradle project which has Child1 and Child2 as sub projects.
Let me discuss the reasoning for that so that the context is laid out.
If Child1 depends on Child2 as a library, I have the Child2 dependency added in Child1 as a maven repo (implementation com.example:child2:1.0.0) so that a developer checking out only Child1 doesn’t have broken dependencies. At the same time, I want people checking out Parent to be able to use the Child2 dependency as a project (implementation project(:child2)). To implement this, I use dependency substitution in Parent only like below
allprojects {
configurations.all {
resolutionStrategy {
dependencySubstitution {
substitute module('com.example:child2') with project(':child2')
}
}
}
}
The above substitution is not working when Child1 has the spring dependency management plugin active
plugins {
id 'java'
id 'eclipse'
id 'idea'
id 'org.springframework.boot' version '2.7.5'
id 'io.spring.dependency-management' version '1.0.15.RELEASE'
}
The substitution happens as soon as the spring dependency management plugin is removed. The substitution also works if I use the substitution code in Child1 itself like
configurations.all {
resolutionStrategy {
dependencySubstitution {
substitute module('com.example:child2') with project(':child2')
}
}
}
but that’s not something that I am looking to do here.
I am able to reproduce this consistently and have created a repo with a simple reproduction (here I am replacing joda-time with the library project in the consumer, both of which are present in the dep-sub-test project, as I need an existing maven package to demonstrate`)
To reproduce:
Pull the project as is and run .\gradlew.bat :consumer:dependencies --configuration compileClasspath to see the dependency tree. The output will have
compileClasspath - Compile classpath for source set 'main'.
+--- org.springframework.boot:spring-boot-starter-web -> 2.7.5
| +--- org.springframework.boot:spring-boot-starter:2.7.5
| ...spring stuff
\--- joda-time:joda-time:2.12.0
Comment out the lines id 'io.spring.dependency-management' version '1.0.15.RELEASE' and implementation 'org.springframework.boot:spring-boot-starter-web' and run the above command once again. The output will have
compileClasspath - Compile classpath for source set 'main'.
\--- joda-time:joda-time:2.12.0 -> project :library
NOTE: the org.springframework.boot:spring-boot-starter-web dependency is only there to ensure that there is no corner case that is missed when the plugin is added but no spring dependencies are given.
The above is taken from the GitHub issue that I created. After sleeping on the issue, I have been thinking of some way that I can tell the allprojects block in the Parent project to apply the configurations in the block after the configuration in the child projects Child1 and Child2 are done, as to me, it seems like that the plugin is overwriting the substitution. Is there are Gradle only workaround for this?
The developer of the plugin has responded and quoting:
It would appear that Gradle loses the dependency substitution when another resolution strategy configures a version.