0

I defined a task in build.gradle like this:

task copyConfig(type: Copy) {
    from ""
    into ""
}

In my mind, task is a method in project which takes the whole rest part as parameter,
and copyConfig is also a method which has two parameters: [type:copy] and a closure

{
    from ""
    into ""
}

but i am wondering that copyConfig is customized, there is no such method named copyConfig,
so why it works? what exactly happened.

no37
  • 13
  • 2
  • `copyConfig` is the name of the task you are creating. Task looks it up creates it for you. – cfrick Nov 28 '19 at 10:58

3 Answers3

0

Here's the equivalent java

Action<Copy> action = task -> {
   task.from("xxx");
   task.into("yyy");
}; 
project.getTasks().create("copyConfig", Copy.class, action);
lance-java
  • 25,497
  • 4
  • 59
  • 101
0

build.gradle is not pure groovy, but its expansion. gradle has an interpret stage that will adapt task definition

task copyConfig(type: Copy) {
    from ""
    into ""
}

to

Task task(Map<String, ?> args, String name, Closure configureClosure);


There are some other QAs related to this one:
Understanding the groovy syntax in a gradle task definition
gradle task method syntax in build.gradle
What are gradle task definitions in groovy language?

no37
  • 13
  • 2
  • This is not accurate. The code in a gradle file is almost 100% Groovy, and uses Groovy's biggest strength: it makes it easy to create DSLs. The fact that the DSL is implemented by delegating to a Java method, in this case, is an implementation detail... it could do something entirely different. See the DSL Groovy docs at https://docs.groovy-lang.org/docs/latest/html/documentation/core-domain-specific-languages.html to see just how powerful this is. – Renato Nov 29 '19 at 21:05
0

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.

Renato
  • 12,940
  • 3
  • 54
  • 85