Lifting the blankets of Gradle

Gradle, the build system, is a DSL on top of Groovy. The build scripts (which are really configuration scripts) are Groovy code. So why do they look so weird?

Today I configured a particular obscure property of the eclipse plugin in milestone 6 of Gradle. The answer looks like:


apply plugin: 'eclipse-wtp'

eclipse {
wtp {
facet {
file {
whenMerged { config ->
config.facets.each {
if (it.name == 'jst.web') {
it.version = 3.0
}
}
config.facets.unique()
}
}
}
}
}

What is it doing? What is that “eclipse” word anyway? Is it a type? a variable? No — it is a method. The eclipse method (defined on the Project, but I’m not sure how) accepts a closure. Similarly, “wtp” is a method on EclipseModel, which accepts a closure. “facet” is a method on EclipseWtp.

But wait, how within the closure passed to the “wtp” method do we have access to a method on EclipseWtp? What determines that? The answer is: before calling our closure, Gradle sets some properties on the groovy.lang.Closure. The following snippet is modified for clarity from org.gradle.util.ConfigureUtil:


Closure copy = (Closure) configureClosure.clone();
copy.setResolveStrategy(Closure.DELEGATE_FIRST);
copy.setDelegate(delegate);
copy.call()

… where the delegate was passed in like so, in org.gradle.plugins.ide.eclipse.model.EclipseModel:


EclipseWtp wtp = new EclipseWtp()

void wtp(Closure closure) {
ConfigureUtil.configure(closure, wtp)
}

What happens is:
– wtp is a method that accepts our closure
– the wtp method sets an object of type EclipseWtp as the delegate on the closure
– the wtp method tells the closure to resolve methods on the delegate first.
– finally, the closure is called.

So, the strange scoping blocks in gradle turn out to be method calls. The way to find out what delegate you’re getting access to within each of these is to look at the DSL Reference or (for the real nitty-gritty details) the source code. For instance, the DSL Reference documentation for EclipseModel says that within the wtp {} block, you should see the documentation for EclipseWtp. (Note that the javadocs are useless. This is groovy trickery, not Java.)

This concept of a resolve strategy within a closure is interesting.