# Gradle cross-project configuration side effects

I was always using gradle `allprojects` and `suprojects` sections as a very handy way for multi-module projects configuration.

But, it appears, that using these shortcuts is [officially a bad practice](https://docs.gradle.org/current/userguide/sharing_build_logic_between_subprojects.html#sec:convention_plugins_vs_cross_configuration).

Quote from [gradle forum thread](https://discuss.gradle.org/t/java-toolchain-present-in-allprojects-block-is-no-applied-to-subproject-unless-apply-plugin-java-is-in-the-block/46938/2):

1. Consider switching from Groovy DSL to Kotlin DSL. This is by now the recommended default DSL, it immediately gives you type-safe build scripts, it gives you actually helpful error messages if you mess up the syntax, and you get amazingly better IDE support when using a proper IDE like IntelliJ IDEA.
    
2. Do not use `allprojects { ... }`, `subprojects { ... }`, `project(...) { ... }` or similar. Those are bad practice and immediately introduce project coupling by doing cross-project configuration. This disturbs more sophisticated Gradle features and even prevents some upcoming features to work properly. Instead you should use [convention plugins](https://docs.gradle.org/current/userguide/sharing_build_logic_between_subprojects.html), for example in `buildSrc` or and [included build](https://docs.gradle.org/current/userguide/composite_builds.html), for example implemented as [precompiled script plugins](https://docs.gradle.org/current/userguide/custom_plugins.html#sec:precompiled_plugins). Those you can then targetedly apply to the projects where their effect should be present, so that you for example only apply Java conventions to projects that are actually Java projects.
    

**Disclaimer**: I put this on top to point to official incorectness of such way of configuration. Still, I consider it as the most understandable and easy to use.

Below you'll find one of hard to debug problems appeared after configuring common staff in the root project with groovy DSL (which, eventually, lead me to the above insight).

### The problem

My problem appeared with pom configuration ([using my po](https://docs.gradle.org/current/userguide/composite_builds.html)[m plugin](https://github.com/xvik/gradle-pom-plugin)) inside multi-module project: as all modules share the same core pom information (developer, license, scm etc.), it would be logical to apply it in `allprojects` section:

```java
plugins {
    id 'java-platform'
    id 'ru.vyarus.pom'
}

allprojects {
    maven.pom {
          developers {
              developer {
                  id = 'johnd'
                  name = 'John Doe'
                  email = 'johnd@somemail.com'
              }
         }
    }
}

subprojects {
    apply plugin: 'java'
    apply plugin: 'ru.vyarus.pom'
}
```

Root project is a BOM, but I omit [related configurations](https://github.com/xvik/dropwizard-guicey/blob/master/build.gradle) to clearly show the problem.

This should put developer section in all poms, including root BOM (allprojects). But java plugin must be applied only for sub-modules because root project does not have sources. Looks logical.

But actual behaviour would be: BOM (root pom) would contain *many duplicated* developer tags and sub-modules would not contain any!

**What happend**: When allprojects is applied for sub-module, `ru.vyarus.pom` plugin *is not applied yet* and gradle goes to the root project, trying to find referenced extension (`maven`), and successfully finds it (so root pom being configured multiple times with the same chunk).

If we try to move `subprojects` section above the `allprojects` - it would work as planned. But it might be much harder to spot in the real build file.

Note that simply moving pom plugin activation (`apply plugin: 'ru.vyarus.pom'`) into `allprojects` section would not work, becuase plugin is activated only after `java` (or `java-platform`) plugin activation.

### Debugging

[Mentioned gradle forum topic](https://discuss.gradle.org/t/java-toolchain-present-in-allprojects-block-is-no-applied-to-subproject-unless-apply-plugin-java-is-in-the-block/46938/2) provides a nice way to debug such things:

```java
println("ROOT: ${System.identityHashCode(maven)}")

allprojects {
    println("$name: ${System.identityHashCode(maven)}")
    maven.pom {
        ...
    }
}
```

Here we print identity of the `maven` extension object, which would be equal if root project extension is used in sub-modules.

I didn't use it in my case (becuase pom plugin supports debug mode, showing all xml modifications), but need to mention it as a very nice alternative.

### Solution

Suppose we can't move subprojects, then the simplest solution would be in delaying this configuration (afterEvaluate usage was highly [not recommended in thread](https://discuss.gradle.org/t/java-toolchain-present-in-allprojects-block-is-no-applied-to-subproject-unless-apply-plugin-java-is-in-the-block/46938/2)):

```java
allprojects {
    afterEvaluate {
        maven.pom {
            ...
        }
    }
}
```

But, it might not be enoght if extension values are used in `afterEvaluate` in plugin, which is a common practice (and so your `afterEvaluate` block would execute after extension values were used, and would be ignored).

The best way would be to wait for required (or related) plugin activation:

```java
allprojects {
    plugins.withId('java') {
        maven.pom {
            ...
        }
    }
}
```

(or `pluginManager.withPlugin("java-base")` as suggested in thread, but this version is shorter).

Side note: when groovy closure was used in pom plugin for xml configuration, it was possible to [resolve closure declaration project](https://github.com/xvik/gradle-pom-plugin/commit/1dfc9182a5d0362a98b2f245554125e61adebaf0#diff-50333754d608fec9d17f0799a0a0558959ff3783c08d98dcf3b523c73b01b384L124) and detect such mis-use automatically. But, unfortunately, it is impossible with pure `Action` or direct model object access.
