CHAPTER 5
As explained in Chapter 4, a task is the fundamental unit of build activity, and it’s identified within the build script with a name. Going a little further, a task is a named collection of build instructions that Gradle executes during a build process.
All the samples discussed in earlier chapters of this book showed how to create a task and define its behavior at the same time. However, there’s a simpler way to declare a task—all that’s needed is a name. This way is displayed in the following sample.
Code Listing 22
task onlyname |
If this task is executed with gradle -q onlyname, it will not produce any result. This is because this task hasn’t had an action assigned yet.
Earlier in this book, a task was bound to an action using the left-shift (<<) operator and a set of statements within curly braces.
Code Listing 23
task nameandaction << { println 'An action was assigned to this task' } |
But Gradle also allows you to associate action to a task by referring to the task object created with the task declaration. This is displayed in the following sample.
Code Listing 24
task nameandaction
nameandaction << { println 'An action was assigned to this task' } nameandaction << { println 'A second action was assigned to this task' } |
The output for this example should look like this.
Code Listing 25
An action was assigned to this task A second action was assigned to this task |
Even though the previous example reflects a trivial build behavior, it exposes a powerful insight: tasks aren’t one-off declarations of build activity, but first-class objects in Gradle programming.
Since build actions can be assigned to tasks over the course of the build file, there’s more you can do with tasks. This chapter will go a little further in exploring the capabilities of tasks.
Note: In Groovy, operators like << (left-shift operator from Java) can be overloaded to have different meanings, depending on the types of the objects they operate on. Gradle has overloaded the << operator to append a code block to the list of actions a task performs. The equivalent of this is the doLast() method, which will be covered later in this book.
Considering the example discussed in the previous section, take a look at the following code:
Code Listing 26
task extractDatabase
extractDatabase << { println 'Connect to database' } extractDatabase << { println 'Extracting database data' } extractDatabase << { println 'Closing database connection' } extractDatabase { println 'Setting up database connection' } |
It is expected that all code blocks were build actions snippets. So, you might expect that the message for the fourth block will be printed at the end. But instead, the following output is displayed.
Code Listing 27
Setting up database connection Connect to database Extracting database data Closing database connection |
Why is that?
In Gradle, when a code block with no left-shift operator is added to a task name, this code block is treated as a configuration block. This means that the code block will be run during Gradle’s configuration phase, according to the project lifecycle detailed in Chapter 4. This phase runs before the execution phase, when task actions are executed.
A configuration block can be additive just like action blocks, so the following code will work exactly as the example shown at the beginning of this section.
Code Listing 28
task extractDatabase
extractDatabase << { println 'Connect to database' } extractDatabase << { println 'Extracting database data' } extractDatabase << { println 'Closing database connection' } extractDatabase { print 'Setting up ' } extractDatabase { println 'database connection' } |
A configuration block is useful to set up variables and data structures that will be needed by the task action when it runs later on in the build. This gives the user an opportunity to turn a build’s tasks into a rich object model populated with information about the build.
All build configuration code runs every time Gradle executes a build script, regardless of whether any given task runs during execution.
Note: As discussed in Chapter 4, every time Gradle executes a build, it runs through three lifecycle phases: initialization, configuration, and execution. Build tasks are executed in the execution phase, following the order required by their dependency relationships. Those task objects are assembled into an internal object model during the configuration phase. This internal object model is usually called the DAG (Directed Acyclic Graph). During initialization phase, Gradle decides which projects are going to participate in the build.
Every time Gradle executes a build, it creates an internal object model beforehand, so every task declared is a task object contained within the overall project. Like any object, a task object has properties and methods.
The default type for each new task is DefaultTask. Every Gradle task descends from this object type. The DefaultTask contains the functionality required for them to interface with the Gradle project model.
An overview of task’s properties and methods will be discussed in the following sections.
This method adds a task as a dependency of the calling task. This depended-on task will always run before the task that depends on it. The following sample shows several ways to use this technique.
Code Listing 29
task setUpConnection { println 'Setting up database connection' } //Declares dependency using quotes (usually optional) task connectToDatabase { dependsOn 'setUpConnection' } //Declares dependency using the task name with no quotes task extractDatabase { dependsOn connectToDatabase } //Declares dependency using the left-shift operator task closeConnection { dependsOn << extractDatabase } |
Another way to write the previous code with the same results is:
Code Listing 30
task setUpConnection { println 'Setting up database connection' } //An explicit call on the task object task connectToDatabase connectToDatabase.dependsOn setUpConnection //Declares dependency using the task name with no quotes task extractDatabase { dependsOn connectToDatabase } //A shortcut for declaring dependencies (discussed in Chapter 4) task closeConnection(dependsOn: extractDatabase)
|
Multiple dependencies can be used. In this way, a task can depend on more than one task. The following sample shows how to achieve this.
Code Listing 31
task setUpConnection { println 'Setting up database connection' } task connectToDatabase connectToDatabase { println 'Connecting to database' } //Declares multi-dependency by listing tasks in a comma separated list task extractDatabase { dependsOn setUpConnection, connectToDatabase println 'Extracting database' } //A shortcut for declaring dependencies (discussed in Chapter 4) task closeConnection(dependsOn: extractDatabase) { println 'Database connection closed' } |
All depended-on tasks are executed following the order in which they appear in the list, starting from the left. The following figure shows the output for the previous code.

This method adds a block of executable code to the beginning of a task’s action.
Notice that the term closure appears within the parentheses. This term is inherited from Groovy, and it refers to a block of code between two curly braces. A closure can function like an object, and it can be passed as a parameter to a method or assigned to a variable, and then executed later.
The doFirst method can be invoked directly on the task object, passing a closure to the method. This closure contains the code to run before the task action.
Code Listing 32
task extractDatabase << { //This is the task action println 'Extracting database metadata.' } extractDatabase.doFirst { println 'Connecting to the database.' } |
The output for the previous example should look like this.

The user can invoke doFirst from within the task’s configuration block. As discussed earlier in this chapter, all code located in those configuration blocks runs before any task action occurs, during the configuration phase of the build.
Code Listing 33
task extractDatabase << { //This is the task action println 'Extracting database metadata.' } extractDatabase{ doFirst { println 'Connecting to the database.' } } |
In this example, the doFirst method is invoked within the extractDatabase configuration block. So when Gradle executes the build script, the code associated to the doFirst method is executed in the configuration phase, prior to any task action.
Repeated calls to the doFirst method can be made. These calls are additive, meaning that each previous call is retained, and the new closure is appended to the start of the list. As a result, all calls to the doFirst method will be executed in reverse order (starting from the last call and ending with the first one).
Code Listing 34
task extractDatabase << { //This is the task action println 'Extracting database metadata.' } extractDatabase{ doFirst { println 'Connecting to the database.' } doFirst { println 'Setting up database connection.' } } |
The output for the previous code is displayed in the following figure.

This method is similar to the doFirst method, except that the closure is appended to the end of an action. This is useful when it’s needed to execute a code block after all of a task’s activities.
Code Listing 35
task extractDatabase << { //This is the task action println 'Extracting database metadata.' } extractDatabase.doLast { println 'Closing database connection.' } extractDatabase{ doFirst { println 'Connecting to the database.' } doFirst { println 'Setting up database connection.' } } |

The output displayed in Figure 19 shows that the code assigned to the doLast method of the extractDatabase task is executed after all of the task’s activities. In this case, it is supposed that a database connection must be closed after extracting all data from the database itself.
The doLast method also is additive. In this way, all additive calls append their closure to the end of the execution’s list, so the calls will be executed starting from the first one, and ending with the last closure added.
Code Listing 36
task extractDatabase << { //This is the task action println 'Extracting database metadata.' } extractDatabase.doLast { println 'Zipping extracted data to backupdata.zip' } extractDatabase.doLast { println 'Closing database connection.' } extractDatabase{ doFirst { println 'Connecting to the database.' } doFirst { println 'Setting up database connection.' } } |
As explained in the “Configuration blocks” section of this chapter, another way to express a call to the doLast method is by using the left-shift (<<) operator.
Code Listing 37
task extractDatabase << { //This is the task action println 'Extracting database metadata.' } extractDatabase << { println 'Zipping extracted data to backupdata.zip' } extractDatabase << { println 'Closing database connection.' } extractDatabase{ doFirst { println 'Connecting to the database.' } doFirst { println 'Setting up database connection.' } } |
The output for both previous examples should look like this.

This method allows you to express a predicate that determines whether a task should be executed. The predicate corresponds to a closure with a returning value, which is the value that will be assigned to the predicate. Gradle uses a switch logic (on or off) to execute the task, or to ignore it. So, the value assigned to the predicate should be a Boolean value.
Code Listing 38
/* This is the task that receives the predicate. */ task extractMetadata << { println 'Extacting metadata.' } /* The predicate is assigned to extractMetadata. */ /* The task will be executed if the property extract.metadata is equal to true. */ extractMetadata.onlyIf { System.properties['extract.metadata'] == 'true' } task extractData << { println 'Extracting database data.' } /* This is the main task. */ task extractDatabase << { //This is the task action } /* Code that will be executed after extractDatabase action ends. */ extractDatabase << { println 'Zipping extracted data to backupdata.zip' } extractDatabase << { println 'Closing database connection.' } /* extractDatabase will depend on extractData and extractMetadata. */ /* extractData is executed first, then extractMetadata will be executed depending on the predicate. */ extractDatabase { dependsOn extractData, extractMetadata } /* Code that will be executed before extractData task action begins is set up at configuration phase. */ extractData { doFirst { println 'Connecting to the database.' } doFirst { println 'Setting up database connection.' } } |
The previous example can be called in two ways. For the purposes of the onlyIf property explanation, the -q (quiet mode) option will be omitted in both calls. The first one looks like this:
Code Listing 39T
gradle extractDatabase |
The following figure shows the output displayed.

Notice that the extractMetadata task was skipped for this call. The reason for this relies on the absence of a value for the extract.metadata property defined in the predicate closure. Gradle assumes that the property has a value of false, and omits the execution of the extractMetadata task.
To assign a value to the extract.metadata property, the -D option of Gradle’s command line should be used. The call to extractDatabase task should look like the following sample.
Code Listing 40
gradle -Dextract.metadata=true extractDatabase |
The output for this call is shown in the following figure.

The predicate for the onlyIf method can use any kind of code in order to return a true or a false value. So, this is not limited to System properties to make the test—you can read files, call a web service, check credentials, or anything else.
This Boolean property indicates whether a task was completed successfully. You can set the didWork property in its own task actions, in order to reflect the results of build code.
In the following example, an implementation of the Java Compiler will return a didWork value of true if at least one file was successfully compiled.
Code Listing 41
/* Applying the Java plugin for this Gradle build. */ apply plugin: 'java' /* Making emailMe task depend on Java compilation. The task compileJava will return true for didWork property if at least one file was successfully compiled. */ task emailMe(dependsOn: compileJava) << { if (tasks.compileJava.didWork) { println 'Send Email announcing success' }
} |
The output for this build should look like this.

This Boolean property indicates whether the task will be executed. You can set any task’s enabled property value to false, which will prevent Gradle from executing the task. If there are any dependencies, they’re still going to execute as if the task were enabled.
The example for the onlyIf method, explained in the “Methods” section of this chapter, will be taken to demonstrate the enabled property. This example will be modified with a few changes.
Code Listing 42
/* This is the task that will be enabled on demand. */ task extractMetadata << { println 'Extacting metadata.' } task extractData << { println 'Extracting database data.' } /* Code that will be executed before extractData task action begins, is set up at configuration phase. */ extractData { doFirst { println 'Connecting to the database.' } doFirst { println 'Setting up database connection.' } } /* This is the main task. */ task extractDatabase (dependsOn: [extractMetadata, extractData]) << { //This is the task action } /* A closure is assigned to the extractDatabase task at configuration phase, so the code will be executed before Gradle enters to the execution phase.
The tasks collection is used to access the extractMetadata task object, in order to set the value for enabled property.
The value for enabled property depends on the extract.metadata System property. If this propety is set to true, the extractMetadata task will be executed. */ extractDatabase { tasks['extractMetadata'].enabled = (System.properties['extract.metadata'] == 'true') } /* Code that will be executed after extractDatabase action ends. */ extractDatabase << { println 'Zipping extracted data to backupdata.zip' } extractDatabase << { println 'Closing database connection.' } |
Like the onlyIf example, this build script can be called in two ways—one of them assigning the value of true to the extractMetadata property. The output for both ways is displayed in the following figure.

This string property contains the fully qualified path of the task object. A task’s path is, by default, simply the name of the task with a leading colon. The leading colon indicates that the task is located in the top-level build script file. Since Gradle supports dependent subprojects or nested builds, if the task existed in a nested build, then the path would be :nestedBuild:taskName.
The enabled property example was modified to display all tasks’ paths along the build execution.
Code Listing 43
/* This is the task that will be enabled on demand. */ task extractMetadata << { println "This Task's path is ${path}" //Displaying task's path println 'Extacting metadata.' } task extractData << { println "This Task's path is ${path}" //Displaying task's path println 'Extracting database data.' } /* Code that will be executed before extractData task action begins, is set up at configuration phase. */ extractData { doFirst { println 'Connecting to the database.' } doFirst { println 'Setting up database connection.' } } /* This is the main task. */ task extractDatabase (dependsOn: [extractMetadata, extractData]) << { println "This Task's path is ${path}" //Displaying task's path //This is the task action } /* A closure is assigned to the extractDatabase task at configuration phase, so the code will be executed before Gradle enters to the execution phase.
The tasks collection is used to access the extractMetadata task object, in order to set the value for enabled property.
The value for enabled property depends on the extract.metadata System property. If this propety is set to true, the extractMetadata task will be executed. */ extractDatabase { tasks['extractMetadata'].enabled = (System.properties['extract.metadata'] == 'true') } /* Code that will be executed after extractDatabase action ends. */ extractDatabase << { println 'Zipping extracted data to backupdata.zip' } extractDatabase << { println 'Closing database connection.' } |
The output should look like this:

This string property is a small piece of human-readable metadata, used to document the purpose of a task. The following sample shows some ways to set a description.
Code Listing 44
task extractMetadata(description: 'Extracts database metadata') << { println "This Task's path is ${path}" //Displaying task's path println 'Extacting metadata.' } task extractData << { println "This Task's path is ${path}" //Displaying task's path println 'Extracting database data.' } /* Code that will be executed before extractData task action begins is set up at configuration phase. */ extractData { description = 'Extracts data from the database' doFirst { println 'Connecting to the database.' } doFirst { println 'Setting up database connection.' } } /* This is the main task. */ task extractDatabase (dependsOn: [extractMetadata, extractData]) << { println "This Task's path is ${path}" //Displaying task's path //This is the task action } extractDatabase.description = 'Entry point for extractDatabase build' /* A closure is assigned to the extractDatabase task at configuration phase, so the code will be executed before Gradle enters the execution phase.
The tasks collection is used to access the extractMetadata task object, in order to set the value for enabled property.
The value for enabled property depends on the extract.metadata System property. If this property is set to true, the extractMetadata task will be executed. */ extractDatabase { tasks['extractMetadata'].enabled = (System.properties['extract.metadata'] == 'true') } /* Code that will be executed after extractDatabase action ends. */ extractDatabase << { println 'Zipping extracted data to backupdata.zip' } extractDatabase << { println 'Closing database connection.' } |
Since the description is used for documentation purposes only, the output is the same as was shown in the section on the path property, previously discussed in this chapter.
According to the “Task object model basics” section of this chapter, every task has a type. The first type for a task is the DefaultTask, but there are also task types for copying, archiving, executing programs, and many more.
A complete task reference is beyond the scope of this book, but a few important types will be discussed in this section.
A copy task copies files from one place into another. Its most basic form copies files from one directory to another. There are optional restrictions for performing this action, like file patterns to be included or to be excluded.
Code Listing 45
task copyFiles(type: Copy){ from 'source' into 'target' include '**/*.txt' } task backupFiles(dependsOn: copyFiles) << { println 'Backup completed' } |
This example creates a copy of all text files located in a directory named source, and places that copy in a directory named target. If the destination directory (target, in this case) doesn’t exist, the copy task will create that directory.
This example should be executed with the following command.
Code Listing 46
gradle backupFiles |
The from, into, and include methods are inherited from Copy.
A zip task creates a compressed file in .zip format. The from method can be used to specify the folder where the files to be compressed are saved.
Code Listing 47
task customZip(type: Zip){ from 'source' baseName = 'backupSet' } /* Using Groovy string interpolation to display zip file name. */ println "The ${customZip.archiveName} has been created" |
This code creates a .zip file from the contents of source folder, which is within the folder where build.gradle is located. The name for the .zip file will be backupSet.zip, and will be placed in the same folder as build.gradle.
Before build execution ends, the .zip filename will be displayed in a message indicating that the file was created.

Sometimes it’s useful to assign a version number to a .zip file, especially if the content of the file corresponds to an application’s source code, which is continuously modified.
To accomplish this task, a System property can be used in conjunction with the version property of the Zip type.
Code Listing 48
task versionedZip(type: Zip){ from 'source' baseName = 'backupSet' version = System.properties['zip.version'] } /* Using Groovy string interpolation to display zip file name. */ println "The ${versionedZip.archiveName} has been created" |
The build should be executed with the following command.
Code Listing 49
gradle -Dzip.version=2.5 versionedZip |

Note: The expressions surrounded with the ${} placeholder in the double-quoted strings are part of the string interpolation action. This action evaluates the expression within the placeholder, and then transforms the result to its string representation upon evaluation of the entire string.
As explained in Chapter 4, a task is the fundamental unit of build activity, and it’s identified with a name within the build script. This name is all the user needs to declare a task, but won’t produce any result because no action was assigned to the task itself.
The left-shift (<<) operator is used to bind a task to an action. An action consists of a set of statements within curly braces. Gradle also allows you to combine action code in the task by referring to the task object created with the task declaration. So, tasks are objects in Gradle programming.
When a code block is added to a task name with no left-shift operator (<<), this code block is treated as a configuration block. The code block will be run during Gradle’s configuration phase. This phase runs before the execution phase, when tasks’ actions are executed.
Every time Gradle executes a build, it creates an internal object model beforehand. Every task declared is a task object with properties and methods. The default type for each new task is DefaultTask, from which every Gradle task inherits. The control methods dependsOn(), doFirst(), doLast(), and onlyIf() were explained in this chapter, as well as the properties didWork, enabled, path, and description.
Besides the DefaultTask type, there are other special task types: the Copy and Zip types.
Finally, the term closure appeared in this chapter. This term is inherited from Groovy, and it refers to a block of code between two curly braces. You can think of a closure as an anonymous function. This block can work like an object, so it can be used as a parameter, or can be assigned to a variable to be executed later.