Multiple settings gradle files for multiple projects building

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')

Leave a Comment

tech