Tricky Android


Android tips, tricks and everything I found interesting


Gradle tip #1: tasks

Part 2
Part 3

With this post I would like to start series of Gradle-related topics I wish I knew when I first started writing Gradle build scripts.

Today we will talk about Gradle tasks and specifically configuration and execution parts of the task. Since these terms might appear unknown to vast majority of readers, it will be easier to have real examples. Essentially (sorry for looking ahead), we will try to figure out what is the difference between these 3 examples:

task myTask {
    println "Hello, World!"
}

task myTask {
    doLast {
        println "Hello, World!"
    }
}

task myTask << {
    println "Hello, World!"
}

My goal - is to create a task which prints "Hello, World!" when I execute it.
When I first started, my first guess was to implement it like this:

task myTask {
    println "Hello, World!"
}

Now, let's try to execute my new task!

user$ gradle myTask
Hello, World!
:myTask UP-TO-DATE

It seems to be working! It prints "Hello, World!".
But! It doesn't work as we might expect it to work. And here is why. Let's try to call gradle tasks to see what other tasks are available:

user$ gradle tasks
Hello, World!
:tasks

------------------------------------------------------------
All tasks runnable from root project
------------------------------------------------------------

Build Setup tasks
-----------------
init - Initializes a new Gradle build. [incubating]
..........

Wait a second! Why my "Hello, World!" string is printed? I just called tasks, I didn't call my custom task!

The reason why this is happening - is that Gradle task has 2 major stages in its lifecycle:

  • Configuration stage
  • Execution stage

I might not be super precise with terminology here, but this analogy helped me to understand tasks.

The thing is that Gradle has to configure all tasks specified in build script before actual build is started. It doesn't matter if certain task will be executed - it still needs to be configured.

Knowing that, how do I know which part of my task is evaluated during configuration and which one during execution?
And the answer is - the part specified within the top-level of the task - is task configuration section. I.e:

task myTask {
    def name = "Pavel" //<-- this is evaluated during configuration
    println "Hello, World!"////<-- this is also evaluated during configuration
}

That's why when I call gradle tasks I can see "Hello, World!" - this is our configuration section is executed. But that's not really what I want - I want "Hello, World!" to be printed only when I explicitly call my task.

So how do I tell Gradle to do something when my tasks is executed?

In order to do that I need to specify task Action. The easiest way to specify task action is via Task#doLast() method:

task myTask {
    def text = 'Hello, World!' //configure my task
    doLast {
        println text //this is executed when my task is called
    }
}

Now my "Hello, World!" string will be printed only when I explicitly call gradle myTask

Cool, now I know how to configure and make my task do real work only when I invoke it. What about that third option with << symbol?:

task myTask2 << {
    println "Hello, World!" 
}

This version is just a shortcut of doLast version, i.e. it is exactly the same as I would write:

task myTask {
    doLast {
        println 'Hello, World!' //this is executed when my task is called
    }
}

However, since now everything goes into execution part, I cannot configure my task the same way I did it with doLast option (it is still possible to do, but in a slightly different way). So this option is good for really small tasks which do not require configuration, but if you have some task other than printing a "Hello, World!" - you might consider going with doLast.

Happy gradling!

comments powered by Disqus