A pointy-haired programmer

github twitter
Gradle's subprojects can't declare subprojects
Dec 27, 2017
3 minutes read

Looking at Gradle’s documentation on subprojects their support for nesting subprojects looks very powerful. I’ve glanced over their water, whale and krill examples many times when I was looking for this or that and I never really noticed that the examples were always water/bluewhale or water/krill but never water/whale/bluewhale.

While such a setup is possible it must be done in a specific way. In my opinion, it is far too easy to end up with an invalid setup without Gradle complaining - a setup that will work very poorly without the developer knowing the real reasons for this. The correct setup is given in the last section (readers should feel free to skip our failed attempts).

A fragile setup

We have a mixed Java/Kotlin project at work, let’s call it A, that is composed of several subprojects sharing pieces of code. One of these subprojects, let’s call it B, has a web interface and we decided to reuse server code in Javascript by using Kotlin’s multiplatform support. To do that Kotlin needs to use separate submodules so our plan was to have create module B1 containing common classes written in platform-independent Kotlin and module B2 to contain the Javascript Kotlin code. Module B would depend on them, and also on other sibling projects that form A.

To that effect, we created a settings.gradle file in subproject B and defined projects B1 and B2 in it. Gradle didn’t give up easily, but after much tweaking we seemed to make it work.

Except that it didn’t always work. Changes to the build files would make unexpected things break. Change detection would fail and the entire project would be rebuilt over and over again for no apparent reason. Gradle tasks would not work unless run from the correct folder. Tests would fail to work and IDEA would get the source folders mixed up, refuse to build the solution or not recognize files containing unit tests. Overall, the whole setup started looking very fragile.

We would blame it on Dagger, we would blame it on multiplaform Kotlin (we were, after all, using release candidates at the time because multiplatform had not yet been officially released). As it turned out all the problems were a result of us using Gradle for something it was not built for.

A settings.gradle too many

As it turns out any Gradle hierarchy can only contain a single settings.gradle file and all modules must be declared there with their actual hierarchy defined only by dependencies blocks in the build.gradle files.

Given the following project structure (where A, B, C and D are all modules with their own build.gradle files):

├── B
│   ├── C
│   └── D
└── settings.gradle

…​the contents of settings.gradle should be:

include 'B', 'B:C', 'B:D'

Unfortunately, this also means that project B is not free to define its own submodules. Even when included in its own Git repository, changes to the submodule structure of B require modifications in parent projects. This can be mitigated by Maven repositories or other strategies, but not via Gradle.

Back to posts