Gradle Multi-Project Build with local SNAPSHOT dependency resolution
Note - 2016-10-22 - I missed Gradle Composite Builds which do something very similar
I often find myself on a project with multiple applications depending on common libraries, so I tend to end up with a super project looking like this:
|
|-app1
|-app2
|-lib
All three projects are separate git projects, separately built and deployed; the apps are on a continuous deployment pipeline, the lib requires a decision to cut a release to move it from a SNAPSHOT version to a fixed version. The top level project is just a convenience to allow checking them all out in one shot and building them all with one command.
During development of a feature that requires a change to the lib, I would
update the dependency in the app that needs the feature to X.X.X-SNAPSHOT
and
work on them both at the same time.
In Maven this worked OK for development - both Maven and most IDEs would successfully resolve any SNAPSHOT dependencies locally if possible. Then after cutting a release of the app you only had to delete the -SNAPSHOT bit from the dependency version and job done.
However, Gradle does not do this by default; you have to specify the dependency as being part of your multi-module build as so:
dependencies {
compile project(':lib')
}
This is much more invasive - changing to a release version of the lib now requires replacing that with:
dependencies {
compile 'mygroup:lib:1.2.3'
}
So you have to add the group of the lib, and know which precise version to
specify, rather than just deleting -SNAPSHOT
from the version. This makes it
harder to automate changing the dependency - ideally, I would like to release
the lib automatically as part of the continuous deployment process of the app
after pushing a commit of the app which references a SNAPSHOT version of the
lib.
I’m experimenting with a way around this by manipulating the top level gradle build as so:
subprojects.each { it.evaluate() }
def allDependencies = subprojects.collect { it.configurations.collect { it.dependencies }.flatten() }.flatten()
def localDependencies = allDependencies.findAll { dependency ->
subprojects.any { it.name == dependency.name && it.version == dependency.version && it.group == dependency.group }
}
subprojects {
configurations.all {
resolutionStrategy.dependencySubstitution {
localDependencies.each {
substitute module("${it.group}:${it.name}:${it.version}") with project(":${it.name}")
}
}
}
}
This effectively gives the Maven behaviour - and IntelliJ at least respects it correctly and resolves the dependencies to the same workspace
You can play with an example here: https://github.com/lidalia-example-project/parent