No hate here. I just need to vent, and maybe be educated I have been writing a suite of custom Gradle plugins for my company to help standardize and simplify our builds. We have literally hundreds of Java repositories that build with Gradle, so any standardization we can provide, or configuration we can simplify, really helps. Ensuring all projects are enforcing code coverage, Checkstyle, Sonar analysis, etc.. Setting up Spring Boot, Lombok, DataDog and the rest.
I think I’ve become pretty well versed in extending Gradle at this point. I’ve written over a dozen plugins, and overall, I have found it to be pretty straightforward to work with. But I have to ask..
Did Gradle miss the boat with the whole lazy properties thing? As a plugin developer, the complexity and race conditions it imposes upon even the simplest task configuration is so painful. And, to be honest, it kinda of smacks of premature optimization.
Maybe the problem is that not everyone uses Properties and Providers for their configuration, and for this pattern to work correctly, you really need universal support for deferred configuration across the board? One rogue plugin that doesn’t use Properties and Providers will basically break deferred configuration for the entire build.
This really comes into focus when trying to write and then use your own custom Extensions. You end up doing extra work to support deferred configuration, but then, in order for your build to actually use that configuration via build.gradle
and DSL, you end up slapping the processing of it into a project.afterEvaluate()
Without doing so, nothing that a user provides in
build.gradle
is actually available at configuration time.
Consider a simple example:
// build.gradle
plugins {
id 'mycompany.java-application'
}
mycompany {
jacoco {
coverageRequirement = 0.75
}
}
Here, the mycompany.java-application
plugin is itself applying a bunch of Gradle and 3rd party plugins, such as Jacoco, and providing sane defaults to those plugins for convenience. The few levers that we want teams to have easy control over, like test coverage thresholds, are configured on our custom extension. But the Jacoco plugin does not support setting coverage requirement via lazy property – rather, it requires a BigDecimal. And so this will not work.
And there are countless other examples of this problem I’ve encountered. For a long time, I had an project.afterEvaluate
block in our custom Extension that would iterate all of our custom Plugins and reconfigure them. And this mostly worked, but there were still challenges and edge cases (for example dynamically defining tasks based on configuration).
After battling this pattern for so long, I finally gave up. Instead, I’ve moved all user-defined configuration to gradle.properties
. It’s not as friendly or obvious as the Groovy or Kotlin DSL, e.g.:
# gradle.properties
jacoco.coverageRequirement=0.75
It lacks any sort of IDE support / code completion. It’s not necessarily where a developer would look for these options. But honestly, it’s by far the sanest solution. My Extension can easily configure itself when it is instantiated by interrogating project.getExtensions().getExtraProperties()
, and then do all of the right things without deferred configuration, race conditions or other headaches.
It’s really a shame. I would love to use the Gradle DSL for this kind of configuration. But the fact that it’s not available until after the build has been evaluated is just … ugh. And why? To save 1 second of processing at startup? Oof.
Okay. Flame suit on. Tell me why I’m wrong – please If I’m a dummy and there’s an elegant way to do this, I would love to know.