Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Gradle task syntax: how is it explained from a Groovy perspective?

Tags:

gradle

groovy

dsl

I'm having a hard time understanding how Gradle's Groovy DSL works.

Unfortunately Gradle is the main use-case for Groovy that I come across in my day to day work, and I've noticed that for many devs, their exposure to Groovy is strictly through Gradle. And that a majority of Gradle users have a very limited grasp of Groovy as a consequence.

In my limited understanding of Groovy, the following sintax tokenA tokenB { tokenC } where all tokens are not language keywords, tokenA would be a method that we are calling with arguments tokenB and the final argument is a closure. I would like to think I'm correct, but I know I'm wrong because there probably needs to be a comma after tokenB for that analysis to be correct.

I am by no means, as you can already tell, a Groovy dev, and I think using Gradle without learning the basics of Groovy is a bad thing to do, because it limits me from fully exploiting its capabilities. But my only viable option is to learn though examples without learning the theory unfortunately.

I did check out some similar questions like this one but no answers where clear or complete enough for me.

TL;DR

  1. How are the tokens task myTask { doLast {} } interpreted in Groovy?
  2. Does Gradle use a standard Groovy interpreter?
  3. How is myTask interpreted as an identifier when there is task and not def or a type behind it?
  4. If later in the file I added myTask { dependsOn myOtherTask } how does that get interpreted?
like image 363
Anthony Avatar asked Jul 05 '19 16:07

Anthony


People also ask

How does Gradle task work?

In Gradle, Task is a single unit of work that a build performs. These tasks can be compiling classes, creating a JAR, Generating Javadoc, and publishing some archives to a repository and more. It can be considered as a single atomic piece of work for a build process.

Is Gradle written in Groovy?

gradle is a Groovy script. Thus it can execute arbitrary code and access any Java library, build-specific Gradle DSL and the Gradle API.

What is Groovy task?

A custom task type is a simple Groovy class which extends DefaultTask – the class which defines standard task implementation. There are other task types which we can extend from, but in most cases, the DefaultTask class is the appropriate choice.

What is Gradle task type?

Gradle supports two types of task. One such type is the simple task, where you define the task with an action closure. We have seen these in Build Script Basics. For this type of task, the action closure determines the behaviour of the task. This type of task is good for implementing one-off tasks in your build script.


1 Answers

I believe its all groovy and nothing special to gradle. Here's the groovy concepts you need to know.

  1. If the last argument of a method is a closure, you can put the closure after the closing bracket for the method arguments.
class MyClass {
   void doStuff(String name, Closure c) {
      c.call()
   } 
} 

def o = new MyClass() 
o.doStuff('x') {
   println "hello" 
} 
  1. You can implement method missing on your object. If someone tries to call a method that doesn't exist you can do stuff
class MyClass {
    def methodMissing(String name, args) {
        println "You invoked ${name}(${args})" 
    }
} 
def o = new MyClass() {
   o.thisMethodDoesNotExist('foo')
}
  1. You can set the delegate on a closure
class MyBean {
   void include(String pattern) {...} 
   void exclude(String pattern) {...} 
} 
class MyClass {
   private MyBean myBean = new MyBean() 
   void doStuff(Closure c) {
      c.setDelegate(myBean)
      c.call()
   } 
} 

def o = new MyClass() 
o.doStuff {
   include 'foo' 
   exclude 'bar' 
} 

These 3 groovy features pretty much explain the "magic" behaviour going on in a gradle script that have java developers scratching their heads.

So, let's break down your snippet

task myTask(type:Foo) { 
   doLast {...} 
}

Let's add some brackets and also add the implicit project references. Let's also extract the closure into a variable

Closure c = { 
   doLast {...} 
}
project.task(project.myTask([type: Foo.class], c)) 

The project.myTask(...) method doesn't exist and the behavior is ultimately implemented via methodMissing functionality. Gradle will set the delegate on the closure to the task instance. So any methods in the closure will delegate to the newly created task.

Ultimately, here's what's logically called

Action<? extends Task> action = { task ->
   task.doLast {...} 
}
project.tasks.create('myTask', Foo.class, action)

See TaskContainer.create(String, Class, Action)

like image 63
lance-java Avatar answered Sep 27 '22 23:09

lance-java