Subproject injection-based configuration based on overridden ext properties

In our build we follow the guidelines in https://docs.gradle.org/current/userguide/organizing_build_logic.html#sec:injected_configuration

However, I hate the unreadable and difficult to maintain massive subprojects {...} block this leads to. So I was trying to break down the configuration done in this subprojects block into a series of modular xyz.gradle files I could apply (apply from: xyz.gradle) to specific sub-projects.

Initially I tried to add the apply statements to the specific sub-projects that needed them (in the subproject’s .gradle file) but this led to all kinds of “ordering problems” that I could never quite resolve.

Next I tried to leverage ext-properties defined in the subprojects block that subprojects could override to indicate that certain xyz.gradle files should be applied to it. This again led to ordering problems.

For example, some of our modules are not Java projects - for the ones that are we have a lot of common configuration that we apply. I tried moving all of that logic into a java-module-config.gradle file. My initial attempt was to go into each Java-based subproject and directly add the `apply from: java-module-config.gradle’ line. This approach ran into tons of “task already defined” type errors". The second approach I mentioned was what is outlined in that doc section - basically in the root project I added:

subprojects { subproject ->
    ext {
        isJavaModule = false
    }
    ...
    if ( subproject.isJavaModule ) {
        apply from: 'java-module-config.gradle'
    }
}

apply from: java-module-config.gradle defines apply plugin: 'java', etc.

The subproject, e.g., defines:

isJavaModule = true

dependencies {
    compile( libraries.jpa )
    ...
}

But Gradle complains:

> Could not find method compile() for arguments [javax.persistence:javax.persistence-api:2.2] on object of type org.gradle.api.internal.artifacts.dsl.dependencies.DefaultDependencyHandler.

I’m really confused as to good ways to simplify, organize these subprojects blocks. Anything I can think of to try has not worked - even things that seem like they should work based on the documentation. Help please?

https://hibernate.atlassian.net/browse/HHH-12190Linked Hibernate Jira

Most of our configuration is in separate, granular scripts that are applied by the project that need them. It works quite well as long as the configuration in each is very specific, non-intersecting, and most of the time completely mutually exclusive.

Having not really seen the code, I would guess you probably most of the way there, but hadn’t purged enough. Ordering and conflicts are pretty hard to resolve unless you commit fully. If you’re going to implement standard script plugins to apply to other projects, get rid of the allprojects/subprojects entirely and configure a project in its specific build script after that. Your root project should be pretty empty. Otherwise, you’ll be fighting to get things that are evaluated at different times configured correctly.

Probably best to just start from scratch… Thanks

James, would you mind sharing a project that uses this approach?

I’ll put together a sharable example project. I don’t have anything ready to go and public that can clearly cover all the points.

That seems like a lot of work on your part. As an alternative… I am planning on writing a scaled-back version of the Hibernate project where I can play with these ideas. I can share that project with you when I have set it up.

And who knows, in the process of doing this clean-room I may actually just “see what you mean”.

Still working on it, but the initial project : https://github.com/sebersole/gradle-clean-room/tree/master

@jjustinic, et al. Any thoughts?

I looked at the project when you started and saw a few updates, but I’m unclear on the status. Does it represent the MVP of the build functionality for the real project, and the rest is just what’s built up over time? Are you just wanting some input before you incrementally add more? Is something not working as expected?

It looks like you naturally did a couple of things that I meant to convey with my earlier replies, but there’s also a few things I would change. I think the most significant was the release project interaction with other projects. In a clean room approach, I don’t see any reason you can’t expose what you need to expose without breaking project encapsulation (i.e. evaluationDependsOn should be avoidable).

What helps you? Just some comments here? Issues or PRs to the repo?

I ended up working on a slightly different approach that is ultimately the one I will move upstream for Hibernate : https://github.com/sebersole/gradle-clean-room2

I did the release thing a little differently here. In this one I just expect each subproject that has any release-related activities to define a release task.

Ignore the Bintray stuff. We won’t be able to use it - I had not noticed the 10G storage limit. We’d hit that in no time over all Hibernate projects.

Thanks for your suggestions.