Gradle Succinctly
Gradle Succinctly
Gradle Succinctly
By
José Roberto Olivas Mendoza
If you obtained this book from any other source, please register and download a free copy from
www.syncfusion.com.
The authors and copyright holders provide absolutely no warranty for any information provided.
The authors and copyright holders shall not be liable for any claim, damages, or any other
liability arising from, out of, or in connection with the information in this book.
Please do not use this book if the listed terms are unacceptable.
3
Table of Contents
Prerequisites....................................................................................................................15
4
Getting detailed information about tasks ..........................................................................28
Tasks...................................................................................................................................................... 31
Copy ................................................................................................................................54
Zip ...................................................................................................................................54
5
Learning about the tasks of a Java project .......................................................................64
Overview ................................................................................................................................................ 70
6
Chapter summary ................................................................................................................................ 101
7
The Story Behind the Succinctly Series
of Books
Daniel Jebaraj, Vice President
Syncfusion, Inc.
Whenever platforms or tools are shipping out of Microsoft, which seems to be about every other
week these days, we have to educate ourselves, quickly.
While more information is becoming available on the Internet and more and more books are
being published, even on topics that are relatively new, one aspect that continues to inhibit us is
the inability to find concise technology overview books.
We are usually faced with two options: read several 500+ page books or scour the web for
relevant blog posts and other articles. Just as everyone else who has a job to do and customers
to serve, we find this quite frustrating.
We firmly believe, given the background knowledge such developers have, that most topics can
be translated into books that are between 50 and 100 pages.
This is exactly what we resolved to accomplish with the Succinctly series. Isn’t everything
wonderful born out of a deep desire to change things for the better?
8
Free forever
Syncfusion will be working to produce books on several topics. The books will always be free.
Any updates we publish will also be free.
As a component vendor, our unique claim has always been that we offer deeper and broader
frameworks than anyone else on the market. Developer education greatly helps us market and
sell against competing vendors who promise to “enable AJAX support with one click,” or “turn
the moon to cheese!”
We sincerely hope you enjoy reading this book and that it helps you better understand the topic
of study. Thank you for reading.
9
About the Author
I’m an IT business entrepreneur, a software developer, and a huge technology fan. My company
went to market in 1990, focused mostly in custom software development. We started with
COBOL as our main programming language, and we’ve been evolving along the years up to
.NET and Microsoft Office technologies. Nowadays, with mobile devices and the Internet being
the cutting edge of Information Technologies, applications development must be faster and
accurate in order to comply with customers’ needs and to deliver reliable products. Using tools
to automate certain tasks in the development process, such as building or testing an application,
is very important to accomplish those goals.
We heard about Gradle for the first time when we started Android applications development with
Android Studio, because it is included by default. At the beginning, we focused on creating
applications with the configuration provided by Android Studio itself, with some minor changes.
Later, a member of our development team realized this tool could be extended in order to
integrate the entire development lifecycle. When he came to me to talk about what he had found
regarding Gradle, I encouraged him to do some deep research and document how Gradle could
help us to improve our development process. He did it, and his work was so good that the entire
team was convinced of Gradle’s worth.
Now, Gradle is an essential tool for our mobile applications development, and it has improved
our development lifecycle. As a result, we’re delivering our products faster and much more
reliably, and our revenue for these kinds of projects has grown.
10
Who Is This Book For?
This book is written primarily for those IT professionals who want to learn about Gradle as an
automated build system for the Java world. The book starts with an introduction to Gradle, its
history, and a detailed overview.
The second chapter covers the Gradle installation process, including prerequisites and where to
get Gradle distributions. The rest of the book shows you how to implement and integrate Gradle
into projects, starting with some basics and ending with a detailed explanation about tasks,
which are the fundamental units for building activity.
Then, we’ll get further into Gradle, exploring some capabilities like hooks, dependency
management and, of course, testing. I’ll also explain how to deal with multi-project builds.
While Gradle is multiplatform, all examples and use cases shown throughout this book have
been developed on Windows, with a focus on Java. Gradle 2.12 (the latest release at the time
this e-book was written) is used for the purposes of this book, and Visual Studio Code is the text
editor I employed.
Visual Studio Code can be downloaded here, and all samples described in this book can be
downloaded here.
I hope that by the end of this book, IT professionals who are not familiar with Gradle feel
encouraged to use it to improve their projects’ development cycle in order to shrink delivery
times and build more reliable products.
11
Chapter 1 Introduction
What is Gradle?
Gradle is an open-source build automation system that was conceived upon a Groovy-based1
domain-specific language2 (DSL). Gradle was designed for multi-project builds, which can grow
to be quite large. It supports incremental builds by intelligently determining which parts of the
project are up-to-date, so that any task dependent upon those parts will not be re-executed.
The initial release of Gradle was published in 2007, and the project is considered to have active
development status. The current stable release (at the time of writing) was published in March
of 2016.
Gradle is being heralded as more than a build tool, but also as a means for automating the
compilation, test, and release process. Qualified by its developers as a quantum leap for
building technology in the Java world, some of Gradle’s features are:
1 Object-oriented programming language for the Java platform. It can be used as a scripting language, and is
dynamically compiled to Java Virtual Machine (JVM) bytecode.
12
Why are scripts written in Groovy?
Since Gradle’s main focus is Java projects, the team members for those projects are very
familiar with Java. Gradle developers thought that a build script should be as transparent as
possible to all team members. They didn’t use Java because its limitations prevent a nice,
expressive, and powerful build language.
Groovy was chosen because it provides the greatest transparency for Java people. Its base
syntax is the same, as well as its type system and other things. Thus, every Gradle build file is
an executable Groovy script. For Gradle beginners, being aware that they’re writing Groovy
code is not needed. But when your needs become more sophisticated, the power of the Groovy
language may become very important. In that way, Gradle’s Groovy-based build files allow the
users to do general-purpose programming tasks in their build file, offering control flow and
execution of nonstandard tasks within the build process.
Besides supporting Gradle, the company offers services related to Gradle, build automation,
and continuous integration pipelines. According to its website, some of these services are:
• Build migration: Create a seamless build migration to Gradle and replicate custom
build functionality, including Ant or Maven scripts.
• CD optimization: Leverage Gradle to automate your continuous delivery pipeline and
best practices.
• Performance tuning: Improve build performance and developer productivity.
• Standardization: Get help to centralize your Gradle build infrastructure.
• Build review: Review your current Gradle build infrastructure with a core developer, and
get recommendations on improvements.
13
• Integration: Integrate Gradle with your existing tools and platforms.
• Plugin development: Identify opportunities to leverage Gradle plugins to put in place a
manageable build infrastructure.
Chapter summary
Gradle is an open-source build automation system that is conceived upon a Groovy-based
domain-specific-language (DSL). Gradle was first released in 2007, and the project is
considered to have an active development status. This e-book uses release 2.12, published on
March 14, 2016.
Gradle is being heralded as a build tool, and also a tool for automating the compilation, test, and
release process. Gradle’s main focus is Java projects, and for that reason its developers chose
Groovy because it provides the greatest transparency for Java people. Its base syntax is the
same, as well as its type system and other things.
Although Gradle build files are Groovy scripts, Gradle itself intends to present the user not with
mere Groovy, but with a domain-specific language (DSL) specifically oriented to the task of
building code. So a new Gradle user could learn this language with no previous knowledge of
Groovy, and use Gradle effectively.
Gradle Inc. is a Silicon Valley startup that develops, distributes, and supports Gradle. It offers
services around Gradle, build automation, and continuous integration pipelines.
14
Chapter 2 Installing Gradle
Getting started
Prerequisites
Gradle requires a Java JDK or JRE version 7 or higher to be installed first. There is no need to
install Groovy because Gradle ships with its own Groovy library; any existing installation of
Groovy is ignored by Gradle.
To check the Java version, open a Command window and issue the java -version command.
Getting Gradle
Any Gradle distribution can be downloaded here. Every distribution comes packaged as a .zip
file. A full distribution contains:
15
Gradle installation process
To install Gradle on Windows, follow these steps:
1. Unzip the distribution downloaded from Gradle’s website to a folder location. It’s
suggested to name this folder location something similar to C:\gradle-2.12.
2. Right-click on the My Computer (or This PC in Windows 10) icon and select
Properties.
16
Figure 3: The System Control Panel Window
4. Click on the Environment Variables button, located at the bottom of the Advanced
System Settings dialog box.
17
Figure 5: Adding the GRADLE_HOME Environment Variable
6. Select the Path variable located under System Variables, in the same dialog box. Click
Edit and add the text ;%GRADLE_HOME%\bin at the end of the variable value list.
18
Figure 6: Editing the Path Environment Variable
Note: The screenshots shown here are taken from Windows 10, so they might
look different than your screen, depending on your Windows version.
Code Listing 1
gradle -v
19
The output for this command shows the Gradle version and the local environment configuration
(Groovy, JVM version, OS version, etc.). The displayed Gradle version should match the
distribution downloaded by the user. For the purposes of this book, the Gradle version is 2.12.
Chapter summary
Java JDK or JRE version 7 or higher must be installed prior to Gradle installation. It’s not
necessary to install Groovy, because Gradle ships with its own Groovy library.
Gradle can be downloaded from here as a .zip file. This file contains all necessary files to install
Gradle in the computer, including Gradle binary files, reference documents, and samples.
To install Gradle, the user needs to decompress the distribution .zip file into a folder. It is
suggested to name this folder to something similar to C:\ gradle-2.12. After that, an
Environment Variable named GRADLE_HOME must be added, and its value set to the name of
the installation folder (C:\ gradle-2.12). Also, the Path Environment Variable must be edited to
add the name of the GRADLE_HOME variable to its value, in the format:
;%GRADLE_HOME%.
To test the installation process, the user needs to execute the command gradle -v from a
command prompt window. The output for this command shows the Gradle version and the local
environment configuration. The displayed Gradle version should match the distribution
downloaded by the user. By the time you read this e-book, the current version of Gradle will be
different, so you can use either the 2.12 version or a newer version.
20
Chapter 3 Beginning with Gradle
Code Listing 2
Now, to run the previous code, the user needs to save the file, naming it build.gradle. For the
purposes of this book, all samples will be saved in a folder called C:\gradlesamples, and there
will be a subfolder within for each one of the samples. For this case, a subfolder named
helloworld will be created to store the file.
Figure 8: The file C:\gradlesamples\helloworld\build.gradle being edited with Visual Studio Code
21
The previously mentioned file is known as a build script, and it is the container for all Gradle
build statements. As mentioned in Chapter 1, all these statements correspond to the Groovy
language. This can be noticed in the highlighted area of Figure 8.
In this example, the build script doesn’t do much, other than printing out a sample message. To
run the Hello World sample, open a command prompt window and get into the
C:\gradlesamples\helloworld folder. After that, issue the following command.
Code Listing 3
gradle -q helloworld
For the next example, you need to create a folder called helloworldv2. Then, the file
build.gradle will be created in this folder with the following code.
Code Listing 4
// hello world v2
task hello << {
print 'hello, '
}
22
}
To run this build script, execute the second task (world) by issuing the following command.
Code Listing 5
gradle -q world
The output is similar to the first example. The difference is that two tasks are executed to
produce the same output.
When the world task is executed, the dependsOn statement (declared within the parentheses)
tells Gradle that a task named hello should be executed before the world task. Then, the
hello task produces the “hello” output. The print command leaves the cursor in the same line,
after the white space position. Now, the world task is executed producing the “world” output
and a line feed, followed by a carriage return.
The dependsOn statement declared in the code means that dependencies are being used for
this build script. Dependencies will be explained in more detail later in this book.
Option Description
--info or -i Sets the Gradle log level to INFO, which causes a few
more information messages to be displayed during the
build script execution.
23
Option Description
--dry-run or -m Evaluates and runs the build script, but does not execute
any task actions.
tasks --all Displays a list of all tasks available in the current build
script. Plugins may introduce tasks of their own, so this
list may be longer.
Code Listing 6
gradle -h
Code Listing 7
24
--configure-on-demand Only relevant projects are configured in this build
run. This means faster build for large multi-project builds. [incubating]
--console Specifies which type of console output to generate.
Values are 'plain', 'auto' (default) or 'rich'.
--continue Continues task execution after a task failure.
-D, --system-prop Set system property of the JVM (e.g. -
Dmyprop=myvalue).
-d, --debug Log in debug mode (includes normal stacktrace).
--daemon Uses the Gradle daemon to run the build. Starts the
daemon if not running.
--foreground Starts the Gradle daemon in the foreground.
[incubating]
-g, --gradle-user-home Specifies the gradle user home directory.
--gui Launches the Gradle GUI.
-I, --init-script Specifies an initialization script.
-i, --info Set log level to info.
-m, --dry-run Runs the builds with all task actions disabled.
--max-workers Configure the number of concurrent workers Gradle
is allowed to use. [incubating]
--no-color Do not use color in the console output. [deprecated
- use --console=plain instead]
--no-daemon Do not use the Gradle daemon to run the build.
--offline The build should operate without accessing network
resources.
-P, --project-prop Set project property for the build script (e.g. -
Pmyprop=myvalue).
-p, --project-dir Specifies the start directory for Gradle. Defaults
to current directory.
--parallel Build projects in parallel. Gradle will attempt to
determine the optimal number of executor threads to use. [incubating]
--parallel-threads Build projects in parallel, using the specified
number of executor threads. [deprecated - Please use --parallel, optionally
in conjunction with --max-workers.] [incubating]
--profile Profiles build execution time and generates a
report in the <build_dir>/reports/profile directory.
--project-cache-dir Specifies the project-specific cache directory.
Defaults to .gradle in the root project directory.
-q, --quiet Log errors only.
--recompile-scripts Force build script recompiling.
--refresh-dependencies Refresh the state of dependencies.
--rerun-tasks Ignore previously cached task results.
-S, --full-stacktrace Print out the full (very verbose) stacktrace for
all exceptions.
-s, --stacktrace Print out the stacktrace for all exceptions.
--stop Stops the Gradle daemon if it is running.
-t, --continuous Enables continuous build. Gradle does not exit and
will re-execute tasks when task file inputs change. [incubating]
-u, --no-search-upward Don't search in parent folders for a
settings.gradle file.
25
-v, --version Print version info.
-x, --exclude-task Specify a task to be excluded from execution.
For the helloworldv2 example, the following command can be used to run the build script.
Code Listing 8
gradle -q wo
If the abbreviation is not unique within the build script, Gradle will throw a warning. For example,
consider a build script like the following.
Code Listing 9
Code Listing 10
gradle -q h
Code Listing 11
26
Task 'h' is ambiguous in root project 'helloworldv2'. Candidates are:
'hello', 'help'.
* Try:
Run gradle tasks to get a list of available tasks. Run with --stacktrace
option to get the stack trace. Run with --info or --debug option to get
more log output.
Tip: It’s useful to name tasks in order to take advantage of task abbreviation.
Code Listing 12
Code Listing 13
------------------------------------------------------------
All tasks runnable from root project
------------------------------------------------------------
Help tasks
----------
buildEnvironment - Displays all buildscript dependencies declared in root
project 'helloworldv2'.
components - Displays the components produced by root project
'helloworldv2'. [incubating]
dependencies - Displays all dependencies declared in root project
'helloworldv2'.
dependencyInsight - Displays the insight into a specific dependency in root
project 'helloworldv2'.
help - Displays a help message.
model - Displays the configuration model of root project 'helloworldv2'.
[incubating]
projects - Displays the sub-projects of root project 'helloworldv2'.
27
properties - Displays the properties of root project 'helloworldv2'.
tasks - Displays the tasks runnable from root project 'helloworldv2'.
Other tasks
-----------
helloTask
world
hello
Code Listing 14
Code Listing 15
:help
Detailed task information for world
Path
:world
Type
Task (org.gradle.api.Task)
Description
-
Group
-
BUILD SUCCESSFUL
28
Getting a profile for the build script
The --profile command-line option allows you to get a report with information about the build.
This report is written into the build\reports\profile folder. The report will be named using the
date and time when the build script was run, and will be saved in HTML format.
This report lists summary times and detailed information for both the configuration phase and
task execution. All operations are sorted, with the most expensive operation (in terms of
consuming time) first. The results also indicate if any tasks were skipped, and the reason why
this happened, and if tasks that were not skipped didn’t work.
Chapter summary
This chapter has shown how to begin using Gradle. The traditional Hello World sample was
used to understand the basic working concepts.
For the purposes of this book, all samples will be saved in a folder called C:\gradlesamples,
and a subfolder for each example will be created within. For the Hello World sample, a subfolder
named helloworld will be created, and a file named build.gradle will be created in the
subfolder. This file is known as a build script, and it is the container for all Gradle build
statements, which are Groovy language statements.
29
The first line of code for the build.gradle file starts with a task statement. Task is one of the
two basic concepts on which Gradle sits.
A build script can contain several tasks, and a task can depend on another one to be executed.
This can be accomplished by using the dependsOn clause in a task declaration, within
parentheses. A declaration such as task world(dependsOn: hello) tells Gradle that a task
named hello must be executed prior the task named world.
The gradle command line will be used throughout this book. There are options included that
can be used to be productive right away. For example, the -h option is used to display all help
text associated with command-line options, the tasks --all option is used to get a report of all
tasks in the build script, the help --task option is used to get detailed information about a
task, and the --profile option generates a report about build execution.
You don’t have to type the full name of a task when executing it from the command-line—Gradle
allows you to provide a portion of the task name, which uniquely identifies the task within the
build script. This mechanism is known as abbreviation. It’s recommended to name tasks in a
way that avoids ambiguous references when using abbreviations, because Gradle will throw a
warning during build if this happens.
30
Chapter 4 Build Script Basics
Project lifecycle
A build.gradle file has a one-to-one relationship with a project. Every time a build script is
executed, Gradle assembles a project object for each project that will participate in the build at
build initialization. The project object is assembled in the following order:
Once these steps are complete, the build ends, and two folders named build and .gradle can
be found as a result of the process.
Tasks
A task is considered the fundamental unit of build activity, and represents a basic piece of work
that is performed by a build. This might be compiling some classes, creating a .jar file,
generating Javadoc, or storing some archives to a repository.
This chapter will focus on a simple tasks definition for a one-project build. Multi-project builds
and more about tasks will be discussed in later chapters.
Code Listing 16
task upper << {
String someString = 'I am Gradle'
31
println "Original: " + someString
println "Upper case: " + someString.toUpperCase()
}
To execute this code, create a folder named groovysample in C:\gradlesamples. After that,
save a build.gradle file with the code in the groovysample folder. The result of the build is
shown in the following figure.
Another sample can use the Groovy times loop. The code for the build.gradle file should be
created in a folder named C:\gradlesamples\groovysamplev2.
Code Listing 17
task count << {
6.times { print "$it " }
println ''
}
Task dependencies
Gradle allows you to declare tasks that depend on other tasks. This can be done by using the
dependsOn: clause after the task name, and placing the name of the dependent task after the
declaration. The following build script shows an example.
Code Listing 18
task firstTask << {
println 'First Task.'
32
}
This example shows three tasks, two of which (secondTask and thirdTask) have
dependencies declared. In this case, secondTask depends on firstTask to be executed
before it, and thirdTask depends on secondTask to be executed first. The output for the build
is displayed in the following figure.
Dependencies can be declared with no strict order. Thus, the following code works in the same
way as the example discussed at the beginning of this section.
Code Listing 19
task secondTask(dependsOn: 'firstTask') << {
println 'Second Task.'
}
33
Dynamic tasks
Due to the power of Groovy, Gradle allows you to create tasks dynamically. The following code
illustrates this capability, using the Groovy times loop statement.
Code Listing 20
9.times { index ->
task "task$index" << {
println "I'm the task number $index"
}
}
In this case, the times statement creates a loop that will iterate nine times. The index variable
receives the iteration number (between 0 and 8) every time a loop iteration is executed, and a
task is created within the loop. The name for each task is formed by the word task and the
number stored in the index variable. So, the tasks created are task0, task1, task2, and so on.
Now, this build script can be executed by calling a task with a number between 0 and 8 (for
example, task6). The output for the build script is presented in the following figure.
Task manipulation
Tasks can be accessed using an API after they’re created. The following example adds
dependencies to a task at runtime.
Code Listing 21
5.times { index ->
task "task$index" << {
println "I'm the task number $index"
}
}
task0.dependsOn task2, task3
34
In the previous example, the dependencies for task0 are declared using the dependsOn
method, which is part of the task0 object. Task objects will be discussed later in this book. The
output for this code should look like this:
Chapter summary
Everything in Gradle sits on top of two basic concepts: projects and tasks. A project is a
collection of tasks, and each task performs some actions, such as compiling classes or running
unit tests.
A build.gradle file has a one-to-one relationship with a project. Every time a build script is
executed, Gradle assembles a Project object in the build
A task is considered the fundamental unit of build activity, and represents a basic piece of work
that is performed by a build. This might be compiling some classes, creating a .jar file,
generating Javadoc, or storing some archives to a repository.
Gradle allows you to declare tasks that depend on other tasks. This can be done by using the
dependsOn: clause after the task name, and placing the name of the dependent task after the
declaration. Dependencies can be declared with no strict order.
Since Gradle’s build scripts are based on Groovy code, you can take advantage of the full
power of the Groovy language. This can be useful for creating tasks dynamically, or for task
manipulation at runtime.
35
Chapter 5 Digging into Gradle Tasks
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.
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'
}
Code Listing 25
An action was assigned to this task
36
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.
Configuration blocks
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
37
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.
38
DAG (Directed Acyclic Graph). During initialization phase, Gradle decides which
projects are going to participate in the build.
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.
Methods of DefaultTask
dependsOn(task)
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'
}
Another way to write the previous code with the same results is:
Code Listing 30
task setUpConnection {
39
println 'Setting up database connection'
}
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'
}
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.
40
Figure 16: Displayed output when closeConnection task is executed.
doFirst(closure)
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.
41
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.
42
doLast(closure)
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.'
}
43
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.
44
Figure 20: Repeated calls to the doLast method
onlyIf (closure)
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.'
}
extractDatabase << {
println 'Closing database connection.'
45
}
/* 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:
46
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
Figure 22: The result of calling extractDatabase with a given value of true for extract.metadata
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.
Properties of DefaultTask
didWork
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.
47
Code Listing 41
/* Applying the Java plugin for this Gradle build. */
apply plugin: 'java'
enabled
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.'
}
48
/* 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.'
}
}
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.
49
Figure 24: Results for enabled property example
path
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 << {
50
println "This Task's path is ${path}" //Displaying task's path
println 'Extacting metadata.'
}
/* 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.'
}
}
extractDatabase << {
println 'Closing database connection.'
}
51
The output should look like this:
description
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.'
52
}
/* 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.'
}
}
extractDatabase << {
println 'Closing database connection.'
53
}
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.
A complete task reference is beyond the scope of this book, but a few important types will be
discussed in this section.
Copy
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'
}
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.
Code Listing 46
gradle backupFiles
The from, into, and include methods are inherited from Copy.
Zip
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.
54
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.
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"
55
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.
Chapter summary
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.
56
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.
57
Chapter 6 More on Writing Build Scripts
A build script can be saved in a file with different name, however. For the next example,
another.bld will be used as the filename.
Code Listing 50
task emptyTask << {
}
This example displays the names of all tasks that are present in the build. The following
command is used to execute the build script.
Code Listing 51
gradle -b another.bld DisplayTasks
58
Code Listing 52
defaultTasks 'DisplayTasks'
The previous code defines DisplayTasks as the default task. Assuming that the build script is
saved in the build.gradle file, if the user executes gradle in the following way, DisplayTasks
will be executed automatically, because no task name was specified in the command line.
Code Listing 53
gradle
Code Listing 54
/* A list of default tasks is supplied. Each task will be executed
in order, starting from the left.
*/
defaultTasks 'SetEnvironment','extractDatabase','ResetEnvironment'
59
println 'Zipping extracted data to backupdata.zip'
}
extractDatabase << {
println 'Closing database connection.'
}
/* 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 code sets three tasks as default tasks. It will be assumed that the code is saved in
a file named severaldefaults.gradle, so when this build script is called with the following
command, SetEnvironment, extractDatabase, and ResetEnvironment will be executed, in
that order.
Code Listing 55
gradle -b severaldefaults.gradle
This section is intended to explain how to build a Java program. Most Java projects are pretty
similar: a set of Java source files needs to be compiled, some unit tests need to be run, and a
.jar file needs to be created to contain all classes. Coding all these steps for every project could
be annoying, and could result in a huge waste of time. Fortunately, Gradle solves this problem
with the use of the Java plugin. A plugin is an extension to Gradle that configures the project in
some way, typically by adding some preconfigured tasks that together do something useful.
60
To use the Java plugin, a series of conventions must be used. That means that the plugin
defines default values for many aspects of the project. These aspects include Java sources,
production resources, and tests location. Following these conventions in a project, you don’t
need to do much in the build script to get a useful build.
Code Listing 56
apply plugin: 'java'
The previous code is all you need to define a Java project. The Java plugin will be applied to the
project, and will add a number of tasks to it.
Project layout
The Java plugin is convention based. This was explained in the “Taking a Java quick start”
section of this chapter. So, the Java plugin assumes a certain layout for the project. This layout
is shown in the following table.
Directory Meaning
When the build script ends its execution, the result of the build is saved in a directory named
build. This directory contains the following relevant subdirectories.
Subdirectory Meaning
61
Subdirectory Meaning
Code Listing 57
package hello;
Now, the following command will build the Java executable .jar file in the build\libs directory.
The file will be saved as javaProgram.jar.
Code Listing 58
gradle build
62
Code Listing 59
java -jar build\libs\javaProgram.jar
Code Listing 60
no main manifest attribute, in build\libs\javaProgram.jar
This problem is the result of not configuring the main class of the .jar file. This main class can
be configured in a file known as the manifest file.
The attributes method of the manifest instance is used to set the entry point of an
application. This can be accomplished by using a map that contains key-value pairs, and these
attributes will be added to the manifest file. The value of the Main-Class attribute must be set
with the name of the class, which acts as the entry point of the program. The changes to the
build.gradle file should look like this.
Code Listing 61
apply plugin: 'java'
jar {
manifest {
attributes 'Main-Class': 'hello.HelloWorld'
}
}
A new .jar file will be created by issuing the gradle build command. After that, the user can
run the .jar file. The output displayed by the program is shown in the following figure.
63
Learning about the tasks of a Java project
The Java plugin adds many tasks to the build. The most relevant tasks for the purposes of this
book are the following:
• The assemble task: compiles the source code of the application and packages it into a
.jar file, but doesn’t run the unit tests.
• The build task: performs a full build of the project, including code compilation, unit tests
and .jar file packaging.
• The clean task: deletes the build directory.
• The compileJava task: compiles the source code of the project.
• The check task: compiles and tests the code.
It’s a good idea to get an overview of the project without reading the build script. To do so, the
following command should be used.
Code Listing 62
gradle tasks
Code Listing 63
:tasks
------------------------------------------------------------
All tasks runnable from root project
------------------------------------------------------------
Build tasks
-----------
assemble - Assembles the outputs of this project.
build - Assembles and tests this project.
buildDependents - Assembles and tests this project and all projects that
depend on it.
buildNeeded - Assembles and tests this project and all projects it depends
on.
classes - Assembles main classes.
clean - Deletes the build directory.
jar - Assembles a jar archive containing the main classes.
testClasses - Assembles test classes.
Documentation tasks
-------------------
javadoc - Generates Javadoc API documentation for the main source code.
64
Help tasks
----------
buildEnvironment - Displays all buildscript dependencies declared in root
project 'javaProgramv2'.
components - Displays the components produced by root project
'javaProgramv2'. [incubating]
dependencies - Displays all dependencies declared in root project
'javaProgramv2'.
dependencyInsight - Displays the insight into a specific dependency in root
project 'javaProgramv2'.
help - Displays a help message.
model - Displays the configuration model of root project 'javaProgramv2'.
[incubating]
projects - Displays the sub-projects of root project 'javaProgramv2'.
properties - Displays the properties of root project 'javaProgramv2'.
tasks - Displays the tasks runnable from root project 'javaProgramv2'.
Verification tasks
------------------
check - Runs all checks.
test - Runs the unit tests.
Rules
-----
Pattern: clean<TaskName>: Cleans the output files of a task.
Pattern: build<ConfigurationName>: Assembles the artifacts of a
configuration.
Pattern: upload<ConfigurationName>: Assembles and uploads the artifacts
belonging to a configuration.
To see all tasks and more detail, run gradle tasks --all
To see more detail about a task, run gradle help --task <task>
BUILD SUCCESSFUL
The previous example shows the full list of runnable tasks and their descriptions. As can be
seen at the bottom of the previous example, if the user needs more detailed information about a
particular task, the following command should be used.
Code Listing 64
gradle help --task <task>
The following example gets detailed information about the clean task.
65
Code Listing 65
gradle help --task clean
Code Listing 66
:help
Detailed task information for clean
Path
:clean
Type
Delete (org.gradle.api.tasks.Delete)
Description
Deletes the build directory.
Group
build
BUILD SUCCESSFUL
The attributes displayed for a task by the help command are the following:
• Path: the task’s path (as discussed in the “Properties of DefaultTask” section in Chapter
5).
• Type: the task type (as discussed in the “About task types” section in Chapter 5).
• Description: a brief description about the purpose of the task.
• Group: the name of the group to which the task belongs (in the example, the clean task
is part of the build group).
Header Definition
66
Header Definition
Code Listing 67
apply plugin: 'java'
jar {
manifest {
attributes 'Specification-Title' : 'Gradle Succinctly'
attributes 'Specification-Version' : '1.0'
attributes 'Specification-Vendor' : 'Syncfusion, Inc.'
attributes 'Implementation-Title' : 'hello.HelloWorld'
attributes 'Implementation-Version' : 'build02'
attributes 'Implementation-Vendor' : 'Syncfusion, Inc.'
attributes 'Main-Class': 'hello.HelloWorld'
}
}
The .jar file can be built as usual. But now, the manifest file will have all package information
after the build process.
Code Listing 68
/* Declaring default tasks will ensure the zip packaging after the build.
*/
67
defaultTasks 'build', 'packageDistribution'
This build script takes advantage of defaultTasks and the Zip task type, in order to create a
distribution package in a .zip compressed format.
As explained within the code, issuing the gradle command with no task name will automatically
execute all tasks declared by the defaultTasks statement. In this case, the build task will be
executed first, and after the build process ends, the packageDistribution task will create the
.zip file with the jar program in it.
Chapter summary
This chapter has gone beyond the simple build.gradle file used for saving build scripts.
Different names can be used for a build script file, and you can look for this named file by using
the -b option from the command line.
68
You can also execute a build script without specifying a task name. In this case, the use of the
defaultTasks statement allows you to indicate which tasks, given in a quoted and comma-
separated list, are going to be executed. The order of execution is always from left to right.
In order to demonstrate that Gradle can build something, the rest of the chapter describes how
to create a Java project using the java plugin. A plugin is an extension to Gradle that configures
the project in some way, typically by adding some pre-configured tasks.
The Java plugin is convention-based. That means that the plugin defines default values for
many aspects of the project, including Java sources, production resources, and tests location.
For creating a Java project, a specific directory layout must exist within the project’s directory,
since Gradle searches for that layout during the build process. All results of a build are saved in
the build directory. This directory is created automatically if it doesn’t exist.
The relevant tasks added by the Java plugin, for the purposes of this book, are: assemble,
clean, compileJava, build, and check. Assemble compiles Java code and creates the .jar
executable file, but doesn’t run the unit tests; clean deletes the build directory; compileJava
compiles the Java code only; build performs a full build for the project, including code
compilation, unit tests, and .jar packaging; and check compiles and tests the code.
A Java project built with Gradle needs to be configured around which class is going to be the
entry point for the application. The Java plugin exposes a jar task into the project, which has
manifest property. The attributes method of this property is used to set the entry point by
assigning to the Main-class attribute of this method the name of the class that will act as the
entry point.
The manifest property can also be used to add detailed information about a jar package. The
attributes that can hold this information are Name, Specification-Title, Specification-
Title, Specification-Version, Specification-Vendor, Implementation-Title,
Implementation-Version, and Implementation-Vendor.
A build script can take advantage of the defaultTasks statement and the Zip task type in order
to create a .zip distribution package automatically, after the build process.
69
Chapter 7 Build Hooks
Overview
Gradle is a highly customizable tool for creating custom build software. It exposes a rich set of
APIs for introducing novel functionality into a build. But the ability to customize a build doesn’t
end with plugins.
Gradle also offers the ability to modify the execution of a build by hooking a variety of events
that occur during the configuration and execution of a build. These hooks can run when tasks
are added, when projects are created, and at other times during configuration sequence.
The initialization phase lets Gradle start up and locates all the build files it must process. In this
phase, Gradle also determines if the build is a single-project or multi-project build. For a single-
project build, Gradle identifies a single build file and continues to the next phase. For a multi-
project build, Gradle locates all the build files needed for processing and continues to the next
phase.
The next phase is configuration. In this phase, Gradle executes each build file as a Groovy
script, but this is not the actual execution of build actions. This is the creation of a directed
acyclic graph (DAG) of task objects (explained in the “Configuration blocks” section of Chapter
5). Many hooks can run during the construction of this graph.
The last phase is execution. In this phase, Gradle identifies the tasks that must be executed by
looking into the DAG. Then, Gradle executes them in dependency order (as explained in the
“Task dependencies” section of Chapter 4). All build activities occur during the execution phase
and, as in the configuration phase, some hooks can run during execution.
70
AOP developed a particular vocabulary and some Java frameworks emerged with an AOP
implementation. A special name was given to the code that ran before and after the original
method: an advice action. The advice code relies on this principle: the code block that runs
before the original method has no correlation with the code that runs after, even though the
execution sequence does.
During the explanation of build hooks, the “advice” special name will be used.
The project.afterEvaluate method is used to execute code after the project has been
evaluated.
Code Listing 69
/* Declaring default tasks will ensure the zip packaging after the build.
*/
defaultTasks 'build', 'packageDistribution'
71
baseName = 'distPackage'
version = '1.0'
}
In the previous example, the project.afterEvaluate method searches for a given directory
within the project’s directory tree structure. In this case, a simple message is displayed when
that directory isn’t found. As shown in code, there is a project object, which is available at
project’s evaluation. In this case, the code uses the project object to display the project’s
name in the warning message. This is trivial, of course, but this advice can have many
applications, such as setting up a proper directory structure to be used by the execution phase,
sending notifications to the project managers, etc.
72
The result of a project’s evaluation can be inquired by means of an advice. To do this, we use
the afterProject method of the gradle object. This method takes two parameters in order to
handle the project. The first one is the project object itself, and the second parameter is the
state of the project after its evaluation.
Code Listing 70
/* Declaring default tasks will ensure the zip packaging after the build.
*/
defaultTasks 'build', 'packageDistribution'
73
}
}
The output for the previous code is displayed in the following figure.
Note the sequence of how both afterProject and afterEvaluate advices are fired in Figure
32. In this case, afterProject takes place just after project’s evaluation is ended. Then, the
code associated to this advice is executed. Once this execution ends, the afterEvaluate
advice code is executed.
74
Advices to tasks creation
An advice can be attached to notify you when a task is added to a project. This can be useful to
set some values that are needed before the task is made available for the build. The
whenTaskAdded method of the tasks object is used for that purpose. This method receives the
task object as a parameter.
Code Listing 71
/* Declaring default tasks will ensure the zip packaging after the build.
*/
defaultTasks 'packageDistribution'
packageDistribution {
dependsOn 'build'
}
75
is displayed. */
afterEvaluate {
if (!file('backuprepo').isDirectory())
{
println "There is no directory for backing up source files of
project ${project.name}"
}
}
The previous code simply displays the task’s name when it is added to the project. The output
for this build is displayed in the following figure.
76
Figure 33: Task creation advice sample output
According to the output shown in Figure 33, task creation happens after the project’s evaluation.
Task execution
Advices can be used before and after any task is executed. This can be done by using the
beforeTask and afterTask methods of the gradle.taskGraph object.
The beforeTask method receives the task object as a parameter, so the user can query any
property of a task in order to make a behavior change, before the task’s execution.
Code Listing 72
/* Declaring default tasks will ensure the zip packaging after the build.
*/
defaultTasks 'packageDistribution'
77
/* Adding the needed attributes to the manifest file. */
jar {
manifest {
attributes 'Specification-Title' : 'Gradle Succinctly'
attributes 'Specification-Version' : '1.0'
attributes 'Specification-Vendor' : 'Syncfusion, Inc.'
attributes 'Implementation-Title' : 'hello.HelloWorld'
attributes 'Implementation-Version' : 'build02'
attributes 'Implementation-Vendor' : 'Syncfusion, Inc.'
attributes 'Main-Class': 'hello.HelloWorld'
}
}
packageDistribution {
dependsOn 'build'
}
78
gradle.afterProject {project, projectState ->
if (projectState.failure) {
println "Evaluation of $project FAILED"
}
else
{
println "Evaluation of $project succeeded"
}
}
The previous code adds the beforeTask method to display every task name, before execution
begins.
79
Figure 34: The output generated by the beforeTask method
The afterTask method can be used to know the state of a task when the task’s execution
finishes. This method receives the task object and the TaskState as parameters. The following
code snippet shows its implementation.
Code Listing 73
/* The state for every task is displayed just after its execution. */
80
gradle.taskGraph.afterTask {Task task, TaskState state ->
if (state.failure) {
println 'Task FAILED'
}
else
{
println 'Task DONE'
}
}
Figure 35: The output for the build when afterTask is added
Build finished
Sometimes the user may want to know when a build is finished, and whether it ended
successfully or with an error. This can be useful to write a sort of log that keeps track of all build
executions.
81
The method buildFinished of the gradle object is used to trap a build finished notification.
The following code snippet shows the implementation.
Code Listing 74
/* If the build fails, the reason why it fails is displayed. */
gradle.buildFinished {buildResult ->
if (buildResult.failure != null)
{
println "Build has failed - ${buildResult.failure}"
}
else
{
println "BUILD SUCCESFUL AGAIN"
}
}
82
Chapter summary
Gradle is a highly customizable tool for creating custom build software. It exposes a rich set of
APIs for introducing novel functionality into a build. Gradle also offers the ability to modify the
execution of a build by hooking a variety of events that occur during the configuration and
execution of a build.
As explained in Chapter 4, a Gradle build always proceeds through three phases exactly in the
same order: initialization, configuration, and execution. The initialization phase lets Gradle start
up and locates all the build files it must process. The configuration phase executes each build
file as a Groovy script and creates a directed acyclic graph (DAG) of task objects. The execution
phase identifies the tasks that must be executed by looking into the DAG, and then executes
them in dependency order. All build activities occur during the execution phase.
The user can create advices for Project Evaluation and tasks creation in the configuration
phase. In the execution phase, advices for task execution and buildFinished can be created.
The project.afterEvaluate method is used to execute code after the project has been
evaluated. Also, the result of a project’s evaluation can be found using the afterProject
method of the gradle object. This method receives two parameters: the project object itself,
and the state of the project after its evaluation.
An advice can be attached to notify you when a task is added to a project by using the
whenTaskAdded method of the tasks object. This method receives the task object as a
parameter.
Advices can be used before and after any task is executed. This can be done by using the
beforeTask and afterTask methods of the gradle.taskGraph object. The beforeTask
method receives the task object as a parameter. The afterTask method can be used when a
task’s execution just finishes. This method receives the task object and the TaskState as
parameters.
Sometimes, the user may want to know when a build is finished, and whether it ended
successfully or with an error. The method buildFinished of the gradle object is used for this
purpose.
83
Chapter 8 Multi-Project Builds
In Gradle, a multi-project build consists of a root project, and one or more subprojects that may
also have subprojects. This scenario is useful in order to organize and understand a project as a
set of smaller, interdependent modules. In this case, all the modules are linked together.
The settings.gradle file tells Gradle how the project and subprojects are structured. The
following command displays a project’s structure.
Code Listing 75
gradle -q projects
Code Listing 76
------------------------------------------------------------
Root project
------------------------------------------------------------
This tells us that multiProject has one immediate child project, named secondproject. The
name of the subproject is displayed in a project’s path format (as explained in Chapter 5). If a
subproject has child projects, these are displayed under its name in a tree structure way. This
output maps to the directory structure for all projects. So, secondproject can be found at the
multiproject\secondproject directory.
Usually, each project has its own build.gradle file. This is not necessary in some cases (for
example, when a subproject is only a container for other subprojects), but the root project has
one.
84
The root project’s build.gradle file is often used to share common configuration between the
child projects. For example, it might be needed to apply the same sets of plugins to all of them.
It also can be used to configure individual subprojects, if it’s preferable to have all projects’
configuration in one place. This means that looking into the root build file is a good way to
discover how a particular subproject is being configured.
• You can change to the directory that corresponds to the project you want to run, and
then execute gradle <taskname> as usual.
• You can specify a qualified task name from the directory of the root project. For
example, gradle :ftputils:build will build the fptutils subproject and any
subprojects it depends on.
• You can specify a task name that can be found in the root project, or in any subprojects
it depends on, by issuing gradle <taskname> command.
The first and third options are similar to the single-project use case, but Gradle will work slightly
differently in a multi-project case. The following command…
Code Listing 77
gradle hello
…will execute the hello task in any subprojects that have the task, relative to the current
directory.
The second option gives more control over what gets executed. The following command…
Code Listing 78
gradle :secondproject:subProjectTask
…will execute the subProjectTask task of the secondproject subproject, and all its
dependencies.
Code Listing 79
------------------------------------------------------------
Root project
------------------------------------------------------------
85
To see a list of the tasks of a project, run gradle <project-path>:tasks
For example, try running gradle :secondproject:tasks
Code Listing 80
gradle subProjectTask
…the subProjectTask task will be executed in every project where it is defined. Gradle will
look for this task starting from the root project, and ending in the last project included in the
settings.gradle file.
Code Listing 81
task hello << {
println "I'm $project.name"
}
Code Listing 82
task hello << {
println "I'm $project.name"
}
Code Listing 83
task hello << {
println "I'm $project.name"
}
86
Figure 37: The output for gradle subProjectTask command
Code Listing 84
------------------------------------------------------------
Root project
------------------------------------------------------------
…and the following build script in the build.gradle file of the root project…
Code Listing 85
Closure cl = { task -> println "I'm $task.project.name" }
allprojects {
task hello << cl
}
…the output for the previous build script is displayed in the following figure.
87
Figure 38: The output for the common behavior example
Gradle provides in its API a property called allprojects. This property returns a list with the
current project and all its subprojects underneath it. If the user calls allprojects with a
closure, all the statements of the closure are delegated to the projects associated with this
property. So, despite that there’s no build.gradle file for the subprojects, the task hello of the
example is delegated for each one of them from the root project’s build.gradle file.
Code Listing 86
Closure cl = {task -> println "I am the task $task.name of project
$task.project.name"}
allprojects.each {
project("$it.path") {
task hello << cl
}
The previous code assigns a closure to an object variable. Then, when iteration via
allprojects.each is done, the closure is assigned to a task named hello, and the task is
assigned to each project using the project method, which receives the project’s path as a
parameter.
Code Listing 87
gradle hello
88
…the output displayed looks like the following figure.
Code Listing 88
------------------------------------------------------------
Root project
------------------------------------------------------------
...the code shown in the following snippet should be used to accomplish this goal.
Code Listing 89
/* Four closures are defined. */
Closure clbr = {task -> println "I am the task $task.name of project
$task.project.name. I'm in charge of Business Rules."}
Closure cldm = {task -> println "I am the task $task.name of project
$task.project.name. I'm in charge of Database Model."}
89
Closure clwf = {task -> println "I am the task $task.name of project
$task.project.name. I'm in charge of Web Front End displaying."}
Closure clroot = {task -> println "I am the task $task.name of project
$task.project.name. I'm the entry point for all process."}
project("$it.path") {
switch (it.path) {
case ":businessRules" : task hello << clbr ; break;
case ":databaseModel" : task hello << cldm ; break;
case ":webfrontend" : task hello << clwf ; break ;
default: task hello << clroot;
}
}
}
The code assigns one of the four closures declared at the beginning of the build script to the
hello task. This assignment depends on which project is being executed. The build script
evaluates the name of the project using the path property of the project object, which is
stored in the implicit it variable created by the each loop. A switch statement, which is placed
within the curly braces of the project method, makes all comparisons needed to assign the
proper closure to the task. In some way, the previous code uses the polymorphism ability of
object-oriented programming.
Code Listing 90
gradle hello
90
Figure 40: Output for multiProjectCustomBehavior
Subproject configuration
Code Listing 91
allprojects {
task hello << {task -> println "I'm $task.project.name"}
}
subprojects {
hello << {println " - and I depend on subProjectListing"}
}
91
Figure 41: SubProjectsListing output
The projects’ execution order can be determined by looking at the output displayed in Figure 41.
First, at the configuration phase, the allprojects loop assigns the hello task to the root
project and its dependents. When this action finishes, the subprojects loop assigns a new
action for every hello task belonging to each one of them. Since allprojects assigned an
action in the first place, the action added to each subproject will be executed after the first one
assigned. Then, the execution phase produced the results shown in the figure.
Code Listing 92
allprojects {
task hello << {task -> println "I'm $task.project.name"}
}
subprojects.each {
project("$it.path").hello << {
println " - and I depend on subProjectsBehavior"
}
if (it.path == ":webfrontend")
{
project("$it.path").hello << {
println " - and I manage Web User Interface, too."
}
}
}
92
This code is derived from the example discussed in the previous section. The each iteration for
the subproject property is used in order to learn the name of each dependent subproject. The
hello task for every subproject receives a closure, which displays the phrase “ - and I depend
on subProjectsBehavior,” and when the :webfrontend subproject is evaluated, only its hello
task receives a new closure, which displays the “- and I manage Web User Interface, too."
statement.
Code Listing 93
gradle -q hello
Code Listing 94
------------------------------------------------------------
Root project
------------------------------------------------------------
93
For example, try running gradle :businessRules:tasks
Code Listing 95
/* businessRules\build.gradle */
hello.doLast {
println " - and I'm in charge of Business Rules along the entire
application."
}
Code Listing 96
/* databaseModel\build.gradle */
hello.doLast {
println " - and I'm in charge of database management."
}
Code Listing 97
/* webfrontend\build.gradle */
hello.doLast {
println " - and I'm in charge of Web Responsive User Interface."
}
Code Listing 98
/* build.gradle */
allprojects {
task hello << {task -> println "I'm $task.project.name"}
}
subprojects.each {
project("$it.path").hello << {
println " - and I depend on subProjectsBehavior"
}
}
The doLast method is used in every subproject build script. This method adds a closure for the
hello task in each subproject, which will be executed after all previous closures assigned to the
same task, in the build script of the root project. Remember, the root project is the first one
evaluated in the configuration phase. Then, every subproject enters in configuration phase
following the order established in the settings.gradle file. After that, every subproject’s
build.file will enter configuration mode—that’s why the closure declared in each subproject’s
build.gradle file is added at the end of the execution chain.
94
Even if the user has access to all subprojects from the root’s build script, it is recommended to
put project-specific behavior in a separate build.gradle file for each subproject. This makes
project maintenance less complex.
Code Listing 99
gradle -q hello
Filtering by name
Another way to control a project’s behavior is by using the configure method in the build script
of the root project. This method takes a list with a series of project objects as an argument.
Then, the method applies the configuration to the projects in the list.
subprojects.each {
project("$it.path").hello << {
println " - and I depend on subProjectsFiltering"
}
}
95
hello << {println " - and I'm part of the application."}
}
This code and the project structure for this build script are derived from the sample project of the
previous section. The difference consists of a new subproject called zipPackage, which has its
own build.gradle file, too.
The build.gradle file for the zipPackage project is displayed in the following code snippet.
96
In Figure 44, you can see that the closure declared in zipPackage’s build script is not executed.
Why? The configure method is responsible for that. Again, every subproject’s build script
enters the configuration phase after the allprojects and subprojects methods are
configured. But in this build script, the configure method has been declared. So, this method is
in charge of dealing with configuration phases for all subprojects. The method receives a
parameter, which is a list with all projects to be configured, and then executes the statements
within the curly braces for each project in the list, before executing the statements contained in
the project’s build.gradle file, if there are any.
The findAll method of the subprojects object creates a sublist with all the projects, which
complies with the condition declared within the curly braces. In this case, the condition tells the
findAll method that a project should be added to this sublist when it has a name different from
zipPackage. Once the sublist is created, it is passed as a parameter to the configure method.
Due to the condition specified to the findAll method, the zipPackage project is not included.
Therefore, no configuration is applied to the project, and that includes the configuration of its
own build.gradle file.
Filtering by properties
A filter can be established using extra project properties. To define an extra property, this one
should be declared in the project’s build script with its name preceded by the keyword ext,
followed by a dot. An extra property should look like this:
In the previous code, an extra property named software is declared, and the value of true is
assigned to it.
Now, this extra property should be declared in all build scripts within the multi-project directory
structure (the example of the previous section is going to be used, with a few changes). The
value for this property will always be true, except for the zipPackage’s subproject, in which
this property will contain a false value.
The build.gradle file for the root project should look like this:
subprojects {
hello {
doLast {println " - and I depend on $rootProject.name"}
}
afterEvaluate {Project project ->
if (project.software) { hello.doLast {println " - and I'm part of
the application."} }
97
}
}
In the previous code, when the subprojects method enters the configuration phase, a closure
is added to the hello task of each subproject, one by one. This closure displays the name of
the project on which the subproject depends, using the name property of the rootProject
object. After that, the build.gradle file for the subproject is also configured, and the doLast
method adds another closure to the hello task. Then, the afterEvaluate advice action is fired
for each subproject that is being configured, passing the subproject object as a parameter.
This advice action evaluates the software extended property declared for every project in the
directory structure, and adds a closure to the hello task if the value for that property is true.
The suggestion made for the zipPackage’s build script was to set the software property’s
value to false. So, no closure will be added to zipPackage’s hello task by the
afterEvaluate advice. Finally, Gradle enters into the execution phase.
98
Code Listing 105
gradle -q hello
There’s a basic rule behind Gradle’s behavior. Gradle looks down the hierarchy, starting with
the current directory, for all tasks with the name hello and executes them. One thing should be
noted: Gradle always evaluates every project in the multi-project build, and creates all existing
task objects. Then, Gradle filters the tasks that should be executed, according to the task name
argument and the current directory. Every project has to be evaluated before any task gets
executed.
allprojects {
apply plugin: 'java'
group = 'hello'
version = '1.0'
}
project(':helloWorld').jar {
manifest {
attributes 'Specification-Title' : 'Gradle Succinctly'
attributes 'Specification-Version' : '1.0'
attributes 'Specification-Vendor' : 'Syncfusion, Inc.'
attributes 'Implementation-Title' : 'hello.HelloWorld'
attributes 'Implementation-Version' : 'build02'
attributes 'Implementation-Vendor' : 'Syncfusion, Inc.'
attributes 'Main-Class': 'hello.HelloWorld'
99
}
}
project(':sendMessage').jar {
manifest {
attributes 'Specification-Title' : 'Gradle Succinctly'
attributes 'Specification-Version' : '1.0'
attributes 'Specification-Vendor' : 'Syncfusion, Inc.'
attributes 'Implementation-Title' : 'hello.HelloWorld'
attributes 'Implementation-Version' : 'build02'
attributes 'Implementation-Vendor' : 'Syncfusion, Inc.'
attributes 'Main-Class': 'hello.SendMessage'
}
}
First, the build script declares three default tasks that will be executed in order, starting from the
left. For the purposes of this example, the build task should be executed first. Then, the
copyDist task will be executed after Gradle finishes building the two Java applications. This
task will copy the two applications to the root project’s build\libs folder. Finally, the
packageDistribution tasks will take the contents of the root project’s build\libs folder and
create a compressed .zip file with the two applications. This file will be saved in the
build\distributions folder of the root project.
In the previous example, the multi-project build consists of two subprojects: helloWorld and
sendMessage. The allprojects method applies the Java plugin for all projects included in the
build. It also assigns a version number (1.0) and a common group name for the two
applications (hello). After that, the jar object is used for both projects in order to declare the
application’s entry point and set the package information. When the user issues the gradle
command with no task name, the build task is executed first. Then, when Java applications are
built, the copyDist takes all artifacts of Jar type created by the build (in this case, the two Java
100
applications) and copies them to the root project’s build\libs directory. Now, the
packageDistribution task takes all files saved in the build\libs directory and creates a .zip
compressed file in the root project’s build\distributions directory.
Chapter summary
In Gradle, a multi-project build consists of a root project, and one or more subprojects that may
also have subprojects. In this case, all the projects are linked together.
A multi-project build should follow a consistent structure, which has a main directory known as
the root project’s directory. Every subproject consists of a subdirectory within the root. A
settings.gradle file and a build.gradle file should be in the project’s root directory. The
settings.gradle file tells Gradle how the project and subprojects are structured.
Multi-project builds are still collections of tasks Gradle can run. The difference relies on the fact
that the user may want to control which project’s tasks should be executed. The following
options apply to a multi-project build execution:
• You can change to the directory that corresponds to the project you want to run, and
then execute gradle <taskname> as usual.
• You can specify a qualified task name from the directory of the root project. For
example, gradle :ftputils:build will build the fptutils subproject and any
subprojects it depends on.
You can specify a task name that can be found in the root project, or in any subprojects it
depends on, by issuing the gradle <taskname> command.
In a multi-project build, sometimes it is not necessary for the subprojects to have their own
build.gradle file. That is, build scripts are optional in a multi-project build. Gradle provides in its
API a property called allprojects, which returns a list with the current project and all its
subprojects underneath it. If a closure is used when allprojects is called, all the statements of
the closure are delegated to the projects associated with this property.
The allprojects.each iteration can be useful for assigning a different closure to a project,
depending on its path.
There’s also a way to access the subprojects only. This can be done by using the subprojects
property. Like the allprojects property, subprojects has an each iteration, which allows you
to access subprojects individually.
Another way to add specific behavior to a subproject is by creating a build.gradle file in the
subproject directory. Also, a project’s behavior can be controlled using the configure method
in the build script of the root project. This method takes a list with a series of project objects as
an argument. Then, the method applies the configuration to the projects in the list.
A filter for the subprojects can be established using extra properties. To define an extra
property, it should be declared in the project’s build script with its name preceded by the
keyword ext followed by a dot (for example, ext.software).
101
When a task from the root project directory is executed, an intuitive behavior happens
automatically: all tasks of the different projects with the same task name are executed. There’s
a basic rule behind Gradle’s behavior. Gradle looks down the hierarchy, starting with the current
directory, for all tasks with the same name and executes them. It should be noted that Gradle
always evaluates every project in the multi-project build, and creates all existing task objects.
Then, Gradle filters the tasks that should be executed according to the task name argument and
the current directory. Every project has to be evaluated before any task gets executed.
102
Chapter 9 Running Gradle from Visual
Studio Code
All code examples of this book were created using Visual Studio Code® (VS Code), a free,
open-source code editor from Microsoft Corporation. Besides the Windows platform, the editor
is available for Linux and Mac OS X platforms. It’s extensible and customizable by installing
extensions for several programming languages, adding debuggers, and connecting to additional
services.
This chapter won’t go deep into Visual Studio Code, as it’s out of the scope of this book, but I
will give a brief explanation on how Gradle builds can be executed from VS Code.
103
Now, the Gradle project’s directory needs to be opened by VS Code. To do so, choose the
Open Folder option from the File menu.
The directory can be located using the Select Folder dialog box and clicking Select Folder.
This action will bring up the project structure to the VS Code main window.
104
Figure 49: The Select Folder dialog box
Assuming that the build script and project’s structure have been already edited, the VS Code
main window will display all of the project’s content at the left side. The following figure shows
VS Code with the project loaded.
105
Figure 50: VS Code with the build project loaded
To edit any file contained in the project’s directory, you only need to click over the name of the
desired file, and its content will be displayed in the editor window, on the right side of the
project’s navigation bar.
106
Figure 51: The Configure Task Runner option
After the Configure Task Runner option is selected, VS Code will ask for the type of Task
Runner to be configured. Choose the Others option for Gradle.
A folder named .vscode will be created in the project’s directory, and a file called tasks.json
will be displayed in the editor window. This file contains five parameters that need to be
modified to make Gradle run.
107
Figure 53: The tasks.json file
To make Gradle run, the parameters command and args need to be edited. The command
parameter’s value needs to be changed to gradle, and the args (arguments) parameter can be
an empty string if the build script has a default task defined, or can be the name of the task that
will be executed by the build script.
After these changes, the file should look like the following figure.
Now, to run Gradle, close the file and then press the Ctrl + P keys combination to bring up the
actions bar. Type task gradle into the actions bar and press the Enter key.
108
Figure 55: VS Code actions bar
After a few seconds, the output window will show the results of the build script’s execution.
109
Chapter summary
This chapter explained how to execute build scripts with Gradle from Visual Studio Code, the
free, open-source code editor from Microsoft Corporation, available for Windows, Linux, and
Mac OS X platforms.
To run Gradle from VS Code, the project’s folder must be opened with VS Code, and a
tasks.json file needs to be created in that directory. This file is created with the Configure
Task Runner option, which can be located in the search bar by typing task. To show this bar,
press the Ctrl + Shift + P keys combination.
The tasks.json file has two parameters that need to be modified: command and args. The
command parameter must be set to gradle, and the args parameter could be set to an empty
string (if the build script has a default task defined), or to the name of the task that will be
executed by Gradle.
To run the build script, you need to save and close the tasks.json file. Then, pressing the Ctrl +
P keys combination will bring up the actions bar. After you type task gradle and press the Enter
key, Gradle will run. The output window will show the results of the build script’s execution.
110
General Conclusions
Gradle is a next-generation tool for build automation. Conceived upon a Groovy-based domain-
specific-language (DSL), it uses convention-over-configuration principles to provide good
defaults. A good example for this can be found in Java applications building, which can be
accomplished with a single line of code. Its powerful DSL describes a build logic that empowers
developers to create reusable and maintainable build scripts. It also supports incremental builds
by intelligently determining which parts of the project are up-to-date, so that any task dependent
upon those parts will not be re-executed.
Gradle has a set of rich and powerful APIs that are exposed to the developer, making available
anything related with the build structure, running tasks, and their dependencies. Also, it’s
possible to customize all aspects of the build behavior.
The use of the Groovy programming language within build scripts allows them to cover most of
the developer’s needs, giving as a result a great tool for continuous delivery, continuous
integration, and continuous testing.
Qualified by its developers as a quantum leap for building technology in the Java world, Gradle
supports multiple application types and test frameworks, and a build-oriented domain model,
which allows you to create concise and readable build scripts that express their intent in a
simple way.
Above all, the guys of Gradle Inc., the startup which develops, distributes, and supports the
Gradle open-source project, have embraced the mission “to transform how software is built and
shipped.” They’re doing this by providing services related to Gradle such as build migration,
performance tuning, and plugins development. They also offer training, consulting, and support
for Gradle, in order to accelerate its adoption.
111