Declarative Gradle builds, is it possible?

I’ve been thinking about 100% declarative Gradle builds for quite some time now. As of June 2020 Gradle offers not one but two different DSLs based on programming languages (Groovy and Kotlin) for defining the steps that the build should take to do its thing. For years developers were used to adding whatever code was “necessary” to make the build turn its gears, regardless of the consequences. In recent times the push has been to move those pieces of code into plugins, simplifying the build files themselves, and also promising less actual code in the build files. Yet, one can still write any valid Groovy or Kotlin code, such as control flows, type declarations, local closures/functions/lambdas/whatever.

A couple of weeks ago I tweeted a pair of images with little context (on purpose) related to Gradle build files (here and here)

They sparked quite the conversation however I was surprised that most of the feedback was positive. I said surprised because I posted a similar tweet more than a year ago and most replies were negative, as in:

  • If I wanted to use XML then I’d use Maven.
  • Gradle minus Groovy plus XML is equal to Maven.

What changed in the last 18 months? I honestly have no clue but reactions to these images were now along the lines of

  • YAML? No! TOML please.
  • Yes XML, I want schema.
  • Don’t care which format as long as it can be validated.
  • Yes, make sure there’s tool support.

Now, unbeknownst to some, these images are not just for show, as it turns out I have a PoC of the YAML/TOML formats working to a limited extent

The plugin assumes that the target build file has a specific subset of elements, some provided by the standard APIs of the Project type, some provided by the java-libary, application, and org.kordamp.gradle-java-project plugins. OK, so the plugin proves it can be done (kind of), so the next question is “should it be done”. That’s the tricky part.

Supporting all features provided by Gradle core APIs and core plugins is certainly attainable, after all it’s a limited set of code compared to the broad range of plugins available in the wild. These APIs can become part of the schema used to validate build files. Wait a second, what’s the mechanism for validating YAML and TOML files? That’s right, there’s none yet; only XML has had one for decades. Schema validation is the first stumbling block. Not only do the groups that oversee the specs for YAML and TOML have to agree on a schema validation mechanism, also the tools have to embrace it and offer support. That’s going to take a while.

Say we cross that bridge and we have validating schemas for the 3 formats. The next hurdle is validating APIs provided by third party plugins. And this one is even more difficult. It would require plugin authors to provide some sort of plugin capabilities descriptor (guess what, every Maven plugin does this already) that describe every configurable bit provided by the plugin, such as extension name and type, tasks, rules, DSL objects, properties, and more. Imagine we have the means to create such descriptors. Are we done now? Not by a long shot. The next hurdle: adoption. And that requires two key elements, 1) buy in from the makers of Gradle to embrace the idea and spread the use of plugin descriptors in the plugin ecosystem (does anyone want to take bets on this one given their push for “compile-time” type safety by embracing Kotlin?), and 2) tool support.

Both of these requirements are the killers. Without buy in from the makers and the community this whole idea is dead in the water. Without tool support there may be marginal uptake. Frankly I don’t even know if it’s possible. Assume there are plugin descriptors available for the plugins you’re interested in using. You open up your favorite editor expecting to see syntax highlighting and validation, but you only get the former nor the latter. Why is this so? In order to know which plugin descriptors may be available the editor would have to know which plugins will be applied to the build, that requires running the build file (or the settings file) up to a certain point, pause the build, read the descriptors, patch the schema validator with the new descriptors, then continue. If it were that simple then I ask you this: why is it not done for Maven plugins? Have you noticed you can place literal XML gibberish (read: invalid XML) inside a plugin’s <configuration> block and the build file will still be read and will not fail validation? IntelliJ does provide some validation and code suggestions for these blocks. Do other Java IDEs do it as well? Does VS.Code? Does VIM, EMACS, or any other editor with XML validation capabilities?

I don’t have the answers to all these questions. This all started by asking myself: can a Gradle build be written with no programmatic code at all? Yes, certainly, specially if you choose Groovy over Kotlin as the latter add some constructs that must be in place to please the compiler. But neither of the two remove the option to put a control flow element or a field declaration or some other programming construct for whatever reason. Moving all the code you can to plugins is a good goal to aim for to keep build files “simple”. Once you do you’ll notice that build files rarely need updates and the benefits of a programming language based DSL are not that useful. So why have them? Round in circles we go. If you ever worked with Ant build files, or Apache Jelly in the early days of Maven then you know of what I’m talking about. Food for thought.

Keep on coding!

Image by Arek Socha from Pixabay

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

ˆ Back To Top