I was able to solve this problem in a relatively clean way. Improvements are certainly welcome!
Although Gradle does not support multiple settings.gradle
scripts out of the box, it is possible to create individual sub-projects each with their own settings.gradle
file. Let’s say you have multi-project A that depends on multi-project B, each with their own sub-projects. Your directory structure might look like:
A
- settings.gradle
- foo
- faz
\ B
- settings.gradle
- bar
- bap
Out of the box, Gradle expects A/settings.gradle
to look something like this:
include ':foo', ':faz', 'B:bar', 'B:bap'
The problem with this is that every time B adds a new project, A/settings.gradle
must also change even if the new project is only used by B. To avoid this situation, you could try to apply
B/settings.gradle
in A/settings.gradle
instead of adding redundant declarations:
apply from: 'B/settings.gradle'
include ':foo', ':faz'
If you try this, you’ll find that Gradle fails because it generates the wrong projectDir
for :bar
and :bap
. It mistakenly assumes that B’s includes are relative to settingsDir
which happens to be A/
when Gradle is invoked from that project root. To fix this you can add another script such as B/settings-parent.gradle
(the exact name is not significant):
apply from: 'settings.gradle'
def updateProjectPaths(Set<ProjectDescriptor> projects) {
projects.each { ProjectDescriptor project ->
String relativeProjectPath = project.projectDir.path.replace(settingsDir.path, "")
project.projectDir = new File("B/$relativeProjectPath")
// Recursively update paths for all children
updateProjectPaths(project.children)
}
}
updateProjectPaths(rootProject.children)
This strips settingsDir.path
and prefixes the path with B/
. This can be extended to multiple layers of settings[-parent].gradle
files by having each layer add itself onto the path. Now you will apply this script to A/settings.gradle
:
apply from: 'B/settings-parent.gradle'
include ':foo', ':faz'
With this scheme, new B projects do not unnecessarily break A/settings.gradle
and all projects can be used without explicitly referencing the B sub-project. For example, if ':foo'
wanted to use 'B:bap'
, it may simply declare:
compile project(':bap')