Creating sbprojects dynamically without having a settings.gradle

How do you dynamically add subprojects to a project based off a map that is in the build.gradle. I can create all of the projects on the fly, but it looks like I need a settings.gradle to do the include of all of them. Can’t you do this programmatically?

‘settings.gradle’ gets evaluated before ‘build’.gradle. You can declare the map in ‘settings.gradle’ and access it from ‘build.gradle’ (if you associate it with the ‘gradle’ object), but not the other way around.

It would be nice to be able to programmatically manipulate settings so that children projects can be dynamically defined. I have a build.gradle that creates the children projects based on a map of information in build.gradle so that you only have to define these things in one place. The map is actually the dependency string as the key and a url as a value for the healthcheck (this used to be in a yaml file). Seems like lately, people are creating more multiproject builds dynamically and this issue issue with the settings.gradle keeps coming up

What exactly is the issue? You can declare the necessary information in ‘settings.gradle’. It isn’t possible to do so in ‘build.gradle’ as the latter is evaluated in a later phase (configuration rather than initialization phase).

The issue (mrampson will correct me if I’m wrong) is that enumerating the projects paths in settings.gradle is just duplication of information that’s available somewhere already. And duplication is bad.

For instance we, by convention, always have a build.gradle file in every subproject’s folder, even if it is to be empty (so far it never is).

I too am looking at the same thing as mrampson, but for now the only way I see is just generating settings.gradle with an external script, which, quite obviously, suboptimal.

Where is the information available? Why not leverage that information in settings.gradle?

Where is the information available?

Just like I described, we do have a build.gradle file in every subproject folder. Just traversing the directory tree and registering every folder with build.gradle (or even just any directory with matching sourceset) as a subproject would be sufficient. That’s exactly what I’m doing in my 5 line ruby script right now.

Why not leverage that information in settings.gradle?

Because this is duplication of settings. Every time someone adds or removes a subproject, they need to remember to modify settings.gradle - while this could be quite easily automated.

Convention over configuration, right?

It isn’t necessary to duplicate any information. Instead you can provide the information right in ‘settings.gradle’, or script ‘settings.gradle’ to get the information from elsewhere (e.g. by scanning the file system). However, keep in mind that ‘settings.gradle’ will be evaluated for each Gradle invocation, and thus has to be fast to evaluate.

or script settings.gradle to get the information from elsewhere

Ah, got it, perfect.

Thanks!

I suppose it is a matter of style, but I would rather just have a top level build.gradle and forego the settings.gradle by being able to create the subprojects on the fly and be able to manipulate the Settings.include programatically.

I’m having issues making the map available to build.gradle from settings.gradle. Is there a particular namespace that is needs to be in?

It’s not possible to forego the ‘settings.gradle’, as this information has to be provided in the initialization phase, not the configuration phase. It determines which build scripts will be evaluated in the first place.

Only the ‘gradle’ object is shared between settings script and build scripts. ‘gradle.ext.foo = “bar”’ and ‘println gradle.foo’ should work.

This is what I managed to build. Works like a charm.

import static groovy.io.FileVisitResult.*

import static groovy.io.FileType.*

def skipDirs = ~/^(build|…*|src|out)/

def preDir = {

if (skipDirs.matcher(it.name).matches())

return SKIP_SUBTREE

}

def getProjectName(String dir) {

return dir.minus(rootDir.toString() + “/”).replaceAll("/", “:”)

}

rootDir.traverse(

type: DIRECTORIES,

preDir: preDir) { dir->

def dstr = dir.toString()

if ((!skipDirs.matcher(dir.name)) &&

new File(dstr + “/build.gradle”).exists()) {

include getProjectName(dstr)

}

}

Thanks for your help Peter. I think this might be closed as answered.

1 Like

Thanks Peter. I was missing the ext part. I have this working now and it works fine.