Gradle just makes good use of features provided by Groovy to create nice DSLs.
Regardless of how Gradle does it (other answers already pointed that out), we can easily implement this ourselves using basic Groovy DSL creation tools.
The following code, for example, demonstrates how we can evaluate a script (like a Gradle file) using a base class, which means the script runs as if it were inside the class of our choice (in this case, MyScript
):
import org.codehaus.groovy.control.CompilerConfiguration
class Main {
static void main( args ) {
def compilerConfig = new CompilerConfiguration()
compilerConfig.scriptBaseClass = 'MyScript'
def shell = new GroovyShell( this.class.classLoader, compilerConfig )
shell.evaluate( '''
task helloWorld {
println "Hello world"
}
''' )
}
}
abstract class MyScript extends Script {
def task( taskObject ) {
}
}
When we evaluate this script:
task helloWorld {
println "Hello world"
}
First, by using Groovy language syntax rules, we can convert this to a more Java-like syntax to understand what it will do:
task(helloWorld(() -> {
System.out.println("Hello world");
}));
So, the task()
method call should work, as we have defined a method in the script's base class: def task( taskObject )
...
But there's no helloWorld
method, so this should fail, and guess what happens when you run it?
groovy.lang.MissingMethodException:
No signature of method: Script1.helloWorld() is applicable for argument types: (Script1$_run_closure1)...
We can see that our interpretation of the Groovy syntax above was correct: Groovy called helloWorld
with an instance of a Closure
!
Now, we can do something smart to allow a task of any name to be created... something like this:
abstract class MyScript extends Script {
def currentTaskName
def task( taskObject ) {
println "Trying to create task $taskObject"
}
def methodMissing( String name, def args ) {
[ name: name, config: args ]
}
}
methodMissing is another Groovy feature that lets you handle calls to any method of a class that does not actually exist!
Guess what this prints!
Trying to create task [name:helloWorld, config:[Script1$_run_closure1@70325e14]]
And that's all there is to it... Gradle may do some more advanced magic, like alter the AST of the compiled Groovy sources (which anyone can do in Groovy), but essentially, this is it.
How to use the configuration Closure to configure the task is left as an exercise to the reader :P
Check the Groovy DSL docs if you want to learn more.