Gradle Goodness Notebook

Gradle Goodness Notebook
Gradle Goodness Notebook
Buy on Leanpub

Table of Contents

About Me

I am born in 1973 and live in Tilburg, the Netherlands, with my beautiful wife and three gorgeous children. I am also known as mrhaki, which is simply the initials of his name prepended by mr. The following Groovy snippets shows how the alias comes together:

['Hubert', 'Alexander', 'Klein', 'Ikkink'].inject('mr') { alias, name ->
    alias += name[0].toLowerCase()
}

(How cool is Groovy that we can express this in a simple code sample ;-) )

I studied Information Systems and Management at the Tilburg University. After finishing my studies I started to work at a company which specialized in knowledge-based software. There I started writing my first Java software (yes, an applet!) in 1996. Over the years my focus switched from applets, to servlets, to Java Enterprise Edition applications, to Spring-based software.

In 2008 I wanted to have fun again when writing software. The larger projects I was working on were more about writing configuration XML files, tuning performance and less about real development. So I started to look around and noticed Groovy as a good language to learn about. I could still use existing Java code, libraries, and my Groovy classes in Java. The learning curve isn’t steep and to support my learning phase I wrote down interesting Groovy facts in my blog with the title Groovy Goodness. I post small articles with a lot of code samples to understand how to use Groovy. Since November 2011 I am also a DZone Most Valuable Blogger (MVB); DZone also posts my blog items on their site.

I have spoken at the Gr8Conf Europe and US editions about Groovy, Gradle, Grails and Asciidoctor topics. Other conferences where I talked are Greach in Madrid, Spain, JavaLand in Germany and JFall in The Netherlands.

I work for a company called JDriven in the Netherlands. JDriven focuses on technologies that simplify and improve development of enterprise applications. Employees of JDriven have years of experience with Java and related technologies and are all eager to learn about new technologies. I work on projects using Grails and Java combined with Groovy and Gradle.

Introduction

When I started to learn about Gradle I wrote done little code snippets with features of Gradle I found interesting. To access my notes from different locations I wrote the snippets with a short explanation in a blog: Messages from mrhaki. I labeled the post as Gradle Goodness, because I thought this is good stuff, and that is how the Gradle Goodness series began.

A while ago I bundled all my blog Groovy Goodness blog posts in a book published at Leanpub. Leanpub is very easy to use and I could use Markdown to write the content, which I really liked as a developer. So it felt natural to also bundle the Grails Goodness blog posts at Leanpub.

In this book the blog posts are bundled and categorized into sections. Within each section blog posts that cover similar features are grouped. The book is intended to browse through the subjects. You should be able to just open the book at a random page and learn more about Gradle. Maybe pick it up once in a while and learn a bit more about known and lesser known features of Gradle.

I hope you will enjoy reading the book and it will help you with learning about Gradle, so you can apply all the goodness in your projects.

Command line

Execute Tasks with Abbreviations

Gradle supports task name abbreviation to execute tasks. This means we only have to type the portion of a task name that uniquely identifies the task within the project. So we don’t have to type the complete name if we can use a shorter version. As a bonus Gradle also supports camel case to abbreviate a task name.

Suppose we have the following build file for our project:

// File: build.gradle
task helloWorld << { ant.echo 'Hello world.' }

The following statements will all invoke this task:

$ gradle helloWorld
$ gradle hello
$ gradle h
$ gradle hW

If the abbreviation is not unique for the project we get a warning from Gradle. For example if we have the tasks hello, helloTask and helloWorld and execute $ gradle h we get a failure message:

FAILURE: Could not determine which task to execute.

* What went wrong:
Task 'h' is ambigious in root project 'project'. Candidates are: 'hello', 'helloWorld', 'helloTas\
k'.

* Try:
Run with -t to get a list of available tasks.

BUILD FAILED

Code written with Gradle 0.8.

Original blog post written on November 12, 2009.

Pass Command-line Arguments to Build Script

The Gradle command-line parser will add all non-option arguments as tasks to be executed to the build. So if we want to pass an extra argument to our build script via the command-line we must use the option -P or --project-prop. With this option we can set a project property and then use it in the build script.

// File: build.gradle
task showArgs << {
    println "$word1 $word2"
}

We can run the script:

$ gradle showArgs -Pword1=hello --project-prop word2=world
:showArgs
hello world

BUILD SUCCESSFUL

Code written with Gradle 0.9-rc-1.

Original blog post written on October 08, 2010.

Run a Build Script With a Different Name

Normally Gradle looks for a build script file with the name build.gradle in the current directory to execute a build. But we can easily use a different name or directory for the build file. We only have to use the -b or --build-file command-line option and tell Gradle the name of the build script file.

$ gradle -b builder.gradle
$ gradle -b otherdir/build.gradle
$ gradle --build-file otherdir/run.it

Written with Gradle 0.9-rc-1.

Original blog post written on October 09, 2010.

Excluding Tasks for Execution

In Gradle we can create dependencies between tasks. But we can also exclude certain tasks from those dependencies. We use the command-line option -x or --exclude-task and specify the name of task we don’t want to execute. Any dependencies of this task are also excluded from execution. Unless another task depends on the same task and is executed. Let’s see how this works with an example:

task copySources << {
    println 'Copy sources.'
}

task copyResources(dependsOn: copySources) << {
    println 'Copy resources.'
}

task jar(dependsOn: [copySources, copyResources]) << {
    println 'Create JAR.'
}

task deploy(dependsOn: [copySources, jar]) << {
    println 'Deploy it.'
}

We execute the deploy task:

$ gradle -q deploy
Copy sources.
Copy resources.
Create JAR.
Deploy it.

Now we exclude the jar task. Notice how the copySources task is still executed because of the dependency in the deploy task:

$ gradle -q deploy -x jar
Copy sources.
Deploy it.

Written with Gradle 0.9-rc-1.

Original blog post written on October 18, 2010.

Display Available Tasks

To see which tasks are available for our build we can run Gradle with the task tasks. Gradle outputs the available tasks from our build script. By default only the tasks which are dependencies on other tasks are shown. To see all tasks we must add the command-line option --all.

3.times { counter ->
    task "lib$counter" {
        description = "Build lib$counter"
        if (counter > 0) {
            dependsOn = ["lib${counter - 1}"]
        }
    }
}

task compile {
    dependsOn {
        project.tasks.findAll {
            it.name.startsWith('lib')
        }
    }
    description = "Compile sources"
}
$ gradle -q tasks

------------------------------------------------------------
Root Project
------------------------------------------------------------

Tasks
-----
:compile - Compile sources
$ gradle -q tasks -all

------------------------------------------------------------
Root Project
------------------------------------------------------------

Tasks
-----
:compile - Compile sources
    :lib0 - Build lib0
    :lib1 - Build lib1
    :lib2 - Build lib2

But if we add our tasks to a group, we get even more verbose output. Gradle will group the tasks together and without the --all option we get to see all tasks belonging to the group, even those that are dependency tasks. And with the --all option we see for each task on which tasks it depends on. So by setting the group property on the task we get much better output when we ask Gradle about the available tasks.

3.times { counter ->
    task "lib$counter" {
        description = "Build lib$counter"
        if (counter > 0) {
            dependsOn = ["lib${counter - 1}"]
        }
    }
}

task compile {
    dependsOn {
        project.tasks.findAll {
            it.name.startsWith('lib')
        }
    }
    description = "Compile sources"
}

tasks*.group = 'Compile'
$ gradle -q tasks

------------------------------------------------------------
Root Project
------------------------------------------------------------

Compile tasks
-------------
:compile - Compile sources
:lib0 - Build lib0
:lib1 - Build lib1
:lib2 - Build lib2
$ gradle -q tasks -all

------------------------------------------------------------
Root Project
------------------------------------------------------------

Compile tasks
-------------
:compile - Compile sources [:lib0, :lib1, :lib2]
:lib0 - Build lib0
:lib1 - Build lib1 [:lib0]
:lib2 - Build lib2 [:lib1]

Rewritten with Gradle 2.2.1.

Original blog post written on October 20, 2010.

Getting More Help For a Task

To see which tasks are available for a Gradle project we can invoke the tasks task. With this task all tasks are printed to the console with their description. To get more information about a specific task we can use the Gradle help task with the command-line option --task followed by the task name. Gradle prints out the path, type, description, group and optional options for the task.

Let’s run the help task for the wrapper task:

$ gradle help --task wrapper
:help
Detailed task information for wrapper

Path
     :wrapper

Type
     Wrapper (org.gradle.api.tasks.wrapper.Wrapper)

Options
     --gradle-distribution-url     The URL to download the gradle distribution from.

     --gradle-version     The version of the Gradle distribution required by the wrapper.

Description
     Generates Gradle wrapper files. [incubating]

Group
     Build Setup

BUILD SUCCESSFUL

Total time: 0.568 secs

Or use the help task to get more information about the help task:

$ gradle -q help --task help
Detailed task information for help

Path
     :help

Type
     Help (org.gradle.configuration.Help)

Options
     --task     The task to show help for.

Description
     Displays a help message.

Group
     help

Written with Gradle 2.7.

Original blog post written on September 21, 2015.

Using Continuous Build Feature

Gradle introduced the continuous build feature in version 2.5. The feature is still incubating, but we can already use it in our daily development. The continuous build feature means Gradle will not shut down after a task is finished, but keeps running and looks for changes to files to re-run tasks automatically. It applies perfectly for a scenario where we want to re-run the test task while we write our code. With the continuous build feature we start Gradle once with the test task and Gradle will automatically recompile source files and run tests if a source file changes.

To use the continuous build feature we must use the command line option --continuous or the shorter version -t. With this option Gradle will start up in continuous mode. To stop Gradle we must use the Ctrl+D key combination.

In the following output we see how to start Gradle in continuous mode and run the test tasks. The first time our test code fails, we change the source file and without restarting Gradle the file is compiled and the test task is run again. Notice that Gradle will honour the task dependencies to see if files have changed:

$ gradle -t test
Continuous build is an incubating feature.
:compileJava UP-TO-DATE
:compileGroovy
:processResources UP-TO-DATE
:classes
:compileTestJava UP-TO-DATE
:compileTestGroovy
:processTestResources UP-TO-DATE
:testClasses
:test

com.mrhaki.SampleSpec > get message FAILED
    org.spockframework.runtime.SpockComparisonFailure at SampleSpec.groovy:7

1 test completed, 1 failed
:test FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':test'.
> There were failing tests. See the report at: file:///Users/mrhaki/.../build/reports/tests/index\
.html

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get mor\
e log output.

BUILD FAILED

Total time: 4.464 secs

Waiting for changes to input files of tasks... (ctrl-d to exit)
Change detected, executing build...

:compileJava UP-TO-DATE
:compileGroovy UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:compileTestJava UP-TO-DATE
:compileTestGroovy
:processTestResources UP-TO-DATE
:testClasses
:test

BUILD SUCCESSFUL

Total time: 1.792 secs

Waiting for changes to input files of tasks... (ctrl-d to exit)

Written with Gradle 2.6.

Original blog post written on August 24, 2015.

Check Task Dependencies With a Dry Run

We can run a Gradle build without any of the task actions being executed. This is a so-called dry run of our build. We can use the dry run of a build to see if the task dependencies we have defined or are defined in a plugin are defined properly. Because all tasks and task dependencies are resolved if we use the dry run mode we can see in the output which tasks are executed.

We define a simple build file with three tasks and some task dependencies:

def printTaskNameAction = {
    println "Running ${it.name}"
}

task first << printTaskNameAction

task second(dependsOn: first) << printTaskNameAction

task third(dependsOn: [first, second]) << printTaskNameAction

To run a Gradle build as a dry run we can use the command line option -m or --dry-run. So let’s execute the task third with the dry run command line option:

$ gradle -m third
:first SKIPPED
:second SKIPPED
:third SKIPPED

BUILD SUCCESSFUL

Total time: 2.242 secs
$

And we see in the output none of the tasks are really executed, because SKIPPED is shown, but we do see the task names of the tasks that are resolved.

Written with Gradle 2.2.

Original blog post written on November 17, 2014.

Skip Building Project Dependencies

If we use Gradle in a multi-module project we can define project dependencies between modules. Gradle uses the information from the project dependencies to determine which tasks need to be run. For example if module B depends on module A and we want to build module B, Gradle will also build module A for us, because module B depends on it. But if we know for sure that module A is up to date and has not changed, we can also instruct Gradle to skip building module A, when we build module B.

Let’s start with the following module structure, where each module depends on the module above it. So module services depends on common and module web depends on services:

.
├── common
├── services
└── web

When we want to build the service module we go to the services directory and execute the build task and we get the following output:

$ gradle build
:common:compileJava
:common:processResources
:common:classes
:common:jar
:services:compileJava
:services:processResources
:services:classes
:services:jar
:services:assemble
:services:compileTestJava
:services:processTestResources
:services:testClasses
:services:test
:services:check
:services:build

BUILD SUCCESSFUL

Total time: 8.013 secs

We see in the output that first the common module is build, because the services module depends on it. But if we work on this project and we know for sure the common module is up to date and has not changed since the last build (for example we didn’t checkout new sources from version control or changed sources in the common module ourselves), then we can skip building the common module. We use the command line option -a or --no-rebuild to tell Gradle to skip project dependencies.

When we run the build task from the services directory using the command line option -a we get the following output:

$ gradle -a build
:services:compileJava
:services:processResources
:services:classes
:services:jar
:services:assemble
:services:compileTestJava
:services:processTestResources
:services:testClasses
:services:test
:services:check
:services:build

BUILD SUCCESSFUL

Total time: 5.626 secs

This time only the services module is build, which also speeds up the build proces. Still this should only be used if we know ourselves the project dependencies are up to date.

Written with Gradle 2.2.1.

Original blog post written on December 05, 2014.

Continue Build Even with Failed Tasks

If we run a Gradle build and one of the tasks fails, the whole build stops immediately. So we have fast feedback of our build status. If we don’t want to this and want Gradle to execute all tasks, even though some might have failed, we use the command line option --continue. When we use the --continue command line option Gradle will execute every task where the dependent tasks are not failing. This is also useful in a multi-module project where we might want to build all projects even though some may have failing tests, so we get a complete overview of failed tests for all modules.

In the following Gradle build file we have two tasks. The task failTask throws a TaskExecutionException to purposely fail the task. The successTask will not fail:

task failTask << { task ->
    println "Running ${task.name}"

    throw new TaskExecutionException(
            task,
            new Exception('Fail task on purpose'))
}

task successTask << {
    println "Running ${it.name}"
}

Let’s run both tasks from the command line and see the output:

$ gradle failTask successTask
:failTask
Running failTask
:failTask FAILED

FAILURE: Build failed with an exception.

* Where:
Build file '/Users/mrhaki/samples/gradle/continue/build.gradle' line: 4

* What went wrong:
Execution failed for task ':failTask'.
> Fail task on purpose

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get mor\
e log output.

BUILD FAILED

Total time: 4.148 secs
$

We see the build has failed and only the task failTask is executed. Now we run the same two tasks, but we use the command line option --continue:

$ gradle --continue failTask successTask
:failTask
Running failTask
:failTask FAILED
:successTask
Running successTask

FAILURE: Build failed with an exception.

* Where:
Build file '/Users/mrhaki/samples/gradle/continue/build.gradle' line: 4

* What went wrong:
Execution failed for task ':failTask'.
> Fail task on purpose

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get mor\
e log output.

BUILD FAILED

Total time: 6.918 secs
$

This time the successTask is executed even though the failTask has failed again. Gradle will keep track of all tasks that have failed and displays a summary with all the tasks that have failed.

Written with Gradle 2.2.1

Original blog post written on December 09, 2014.

Profiling Information

If we want to know more about how much time is spent in tasks we can use the --profile command-line option. Gradle will generate a report file in the build/reports/profile directory. This report file is a HTML file with information about how much time is spent by the different tasks and processes of Gradle. The following report file is for a Gradle project with the Groovy plugin and we invoked $ gradle --profile build:

Written with Gradle 0.9-rc-3.

Original blog post written on November 18, 2010.

Use gradlew for Easy Gradle Execution

Gradle is a great build language. To use gradle for our project we can download the Gradle distribution and install it on our machine. But maybe we want to share our project with other people that don’t have Gradle installed. Or suppose we use a continuous integration server without native Gradle support, but with support to run command-line commands. Then we can use the Gradle wrapper. The wrapper is responsible for downloading the Gradle distribution and making it available for our project.

To enable the Gradle wrapper for our project we first add a new task to our build.gradle:

// File: build.gradle
task createWrapper(type: Wrapper) {
    gradleVersion = '0.9-preview-1'
}

Next we run $ gradle createWrapper and when the process is finished we take a look at the project directory and see the following new files:

gradle-wrapper.jar
gradle-wrapper.properties
gradlew
gradlew.bat

Now we only have to distribute this files with our project. Now anyone can run $ gradlew to execute the tasks defined in the Gradle build file. If Gradle hasn’t been installed yet, it will be downloaded and installed for the user and the task is executed!

More information is available in the Gradle User Guide.

Code written with Gradle 0.9-preview-1.

Original blog post written on May 29, 2010.

Change Gradle Wrapper Script Name

With the Gradle Wrapper task we can specify the name of the generated script files. By default the names are gradlew and gradlew.bat. The Wrapper task has the property scriptFile. We can set a different value for this property to let Gradle generate the script files with a different name.

In the following example we use the value mvnw (they will be surprised the build is so fast… ;-)) as the value:

task gradleWrapper(type: Wrapper) {
    scriptFile = 'mvnw'
}

Let’s run the gradleWrapper task:

$ gradle gradleWrapper
:gradleWrapper

BUILD SUCCESSFUL

Total time: 8.597 secs
$ ls mvnw*
mvnw     mvnw.bat
$

Written with Gradle 3.1.

Original blog post written on September 19, 2016.

Specify Wrapper Version and Distribution Type From Command Line

Gradle has the built-in task wrapper to create a Gradle wrapper. The Gradle wrapper can be part of our project so other people can build our project with Gradle, without the need for them to install Gradle. Also if we specify the Gradle wrapper we can make sure the correct Gradle version is used. To specify the version we must use the option --gradle-version. This version can be different than the Gradle version we use to create the Gradle wrapper. Since Gradle 3.1 we can also specify the distribution type of the Gradle wrapper. We choose between a binary distribution or the all distribution, which contains documentation and source code. Especially IDEs like to have the all distribution type, so they can provide better help in their editors.

With the following wrapper command we create a wrapper for Gradle 3.1 and the all distribution type. For a binary distribution we either use the value bin or we don’t specify the option, so Gradle falls back to the default value bin.

$ gradle wrapper --gradle-version 3.1 --distribution-type all
:wrapper

BUILD SUCCESSFUL

Total time: 1.012 secs

$

We can check the file gradle/wrapper/gradle-wrapper.properties and look for the key distributionUrl. We see the value points to the correct Gradle version and distribution type:

$ more gradle/wrapper/gradle-wrapper.properties
#Mon Sep 19 15:26:27 CEST 2016
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-3.1-all.zip
$

Written with Gradle 3.1

Original blog post written on September 19, 2016.

Check The Gradle Daemon Status

Since Gradle 3 the Gradle daemon is automatically used to run Gradle. This means Gradle will startup up faster after a first run. Gradle tries to re-use a daemon if it is available. We can check the status of the Gradle daemon processes with the new command-line option --status. We get the status results for the Gradle daemons with the same Gradle version that is used to view the status. So when we use Gradle 3.0 with the --status option we only see the 3.0 Gradle daemons.

The following example shows the sample output of running gradle with the --status option:

$ gradle --status
No Gradle daemons are running.
   PID STATUS   INFO
 28914 IDLE     3.0
 26854 STOPPED  (after being idle for 22 minutes and to reclaim system memory)

Only Daemons for the current Gradle version are displayed. See https://docs.gradle.org/3.0/usergu\
ide/gradle_daemon.html#sec:status

Written with Gradle 3.0.

Original blog post written on September 14, 2016.

Configuration

Changing the Project Name

By default the name of our Gradle project is the name of the directory the project is created in. We can check this by running the following command:

$ gradle properties | grep name
name: project

The first place we think of to change the project name is build.gradle, because that is also the file we can change for example the version property of our project:

name = 'newProjectName'

When we run gradle we get an error:

* What went wrong:
A problem occurred evaluating root project 'project'.
Cause: Cannot set the value of read-only property 'name' on root project 'project'.

So what can we do? We must create a file settings.gradle and set the property rootProject.name:

rootProject.name = 'newProjectName'

We can check the name of our project has changed by running the following command again:

$ gradle properties | grep name
name: newProjectName

Orginally written with Gradle 0.8 and re-written with Gradle 2.2.1.

Original blog post written on November 11, 2009.

Changing Name of Default Build File

Gradle uses the name build.gradle as the default name for a build file. If we write our build code in a file build.gradle then we don’t have to specify the build filename when we run tasks. We can create build files with a different name other than build.gradle. For example we can define our build logic in a file sample.gradle. To run the tasks from this build file we can use the command line option -b or --build-file followed by the file name. But we can also change the project settings and set a new default build file name for our project. With the changed project settings we do not have to use the command line options -b or --build-file.

Suppose we have the following build file with the name sample.gradle:

// File: sample.gradle
task sample(description: 'Sample task') << {
    println 'Sample task'
}

defaultTasks 'sample'

To run the sample task from the command line we can use the command line options -b or --build-file:

$ gradle -b sample.gradle
:sample
Sample task

BUILD SUCCESSFUL

Total time: 3.168 secs
$ gradle --build-file sample.gradle
:sample
Sample task

BUILD SUCCESSFUL

Total time: 2.148 secs
$

To change the default build file name for our project we create a file settings.gradle in our project. Inside the settings.gradle file we can change the property buildFileName for rootProject:

// File: settings.gradle
// Change default build file name for this project.
rootProject.buildFileName = 'sample.gradle'

Now we execute the tasks from sample.gradle without the options -b or --build-file:

$ gradle
:sample
Sample task

BUILD SUCCESSFUL

Total time: 3.312 secs
$

Code written with Gradle 2.1.

Original blog post written on October 21, 2014.

Display Gradle Information in the Build

In a Gradle build script we can access properties from Gradle and the project itself. To access the Gradle object we use the gradle property. Properties of the Project instance can be accessed directly in our script. We can use this information to display an information message each time we run the build script. Let’s make it look like something we also see when we run Grails:

// File: build.gradle
println """\
Welcome to Gradle $gradle.gradleVersion - http://www.gradle.org
Gradle home is set to: $gradle.gradleHomeDir
Gradle user directory is set to: $gradle.gradleUserHomeDir

Base directory: $projectDir
Running script ${relativePath(buildFile)}
"""

If we run the gradle we get to see the information:

$ gradle tasks
Welcome to Gradle 2.2.1 - http://www.gradle.org
Gradle home is set to: /Users/mrhaki/.gvm/gradle/2.2.1
Gradle user directory is set to: /Users/mrhaki/.gradle

Base directory is: /Users/mrhaki/Dropbox/gradle-goodness-notebook/manuscript/samples
Running script build.gradle

:tasks

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

BUILD SUCCESSFUL

Total time: 1.484 secs

Orginally written with Gradle 0.8 and re-written with Gradle 2.2.1.

Original blog post written on November 16, 2009.

Setting Global Properties For All Gradle Builds

To define project properties outside the Gradle build file, we can define them in a file gradle.properties in our project directory. If we want to define property values that are used by all Gradle builds we can create a file gradle.properties in the GRADLE_USER_HOME directory. By default this is in the USER_HOME/.gradle directory.

In the following example gradle.properties file we assign values to some properties that can be used in all Gradle builds we run on our computer. We store the file in USER_HOME/.gradle.

# Bintray username and password.
bintrayUser=mrhaki
bintrayPasword={secret}

# Enable Gradle daemon for all builds.
org.grade.daemon=true

Written with Gradle 2.7.

Original blog post written on October 02, 2015.

Using Properties for Multiple Environments or Profiles

A Gradle build script is a Groovy script, and this means we can use all classes available to Groovy in our Gradle build scripts. In this post we use the ConfigSlurper to read in properties for our project. The ConfigSlurper supports environments where we can define values for properties per environment. Suppose we have a dev, test and prod environment than we can define different values for the same property per environment. We can also override a default value in the environments section.

In our Gradle build script we look for a property env. We can pass a value for the env property to the build with the -P or --project-prop argument when we run Gradle. If the property is available we use the value to read in the properties for that environment. If the env property is not available we assume the default dev environment. Finally we assign the Config object (with all properties) returned by the ConfigSlurper to the project property config. In the Gradle build script we can access the properties with dotted notation: config.propName.

// File: build.gradle
loadConfiguration()

task printProps << {
    println "serverName:  $config.serverName"
    println "mail.server: $config.mail.server"
    println "mail.port:   $config.mail.port"
}

def loadConfiguration() {
    def environment = hasProperty('env') ? env : 'dev'
    setProperty 'environment', environment
    println "Environment is set to $environment"

    def configFile = file('config.groovy')
    def config = new ConfigSlurper(environment).parse(configFile.toURL())
    setProperty 'config', config
}
// File: config.groovy
mail {
    server = 'localhost'
    port = 25
}

environments {
    dev {
        serverName = 'http://localhost:9090'
    }

    test {
        serverName = 'http://testserver'
        mail {
            server = 'mail.testserver'
        }
    }

    prod {
        serverName = 'http://www.mrhaki.com'
        mail {
            port = 552
            server = 'mail.host.com'
        }
    }
}

We run Gradle with different values for the env property to see the values of the properties:

$ gradle -q printProps
Environment is set to dev
serverName:  http://localhost:9090
mail.server: localhost
mail.port:   25

$ gradle -q -Penv=dev printProps
Environment is set to dev
serverName:  http://localhost:9090
mail.server: localhost
mail.port:   25

$ gradle -q -Penv=test printProps
Environment is set to test
serverName:  http://testserver
mail.server: mail.testserver
mail.port:   25

$ gradle -q -Penv=prod printProps
Environment is set to prod
serverName:  http://www.mrhaki.com
mail.server: mail.host.com
mail.port:   552

Written with Gradle 0.8.

Original blog post written on November 17, 2009.

Define System Properties in gradle.properties File

To define system properties for our Gradle build we can use the command line option --system-prop or -D. But we can also add the values for system properties in the gradle.properties file of our project. This file contains project properties we want to externalized, but if we prefix the property name with systemProp. the property is turned into a system property. For a multi-module build only system properties defined in the gradle.properties file in the root of the project structure are used, others are ignored.

In the following build script we have the task showSystemProperty. Inside the task we assert the value of the system property sample and the project property sample:

// Simple task to show
// some properties.
task showSystemProperty << {

    // System property 'sample' is set
    // in gradle.properties file.
    assert System.properties.sample == 'Gradle is gr8'

    // Regular project property set
    // in gradle.properties file.
    assert project.sample == 'Gradle is great'

}

We can run the following command line command to make sure the assertions are true: $ gradle --system-prop "sample=Gradle is gr8" --project-prop "sample=Gradle is great" showSystemProperty.

Or we could create the following gradle.properties file in our project directory:

systemProp.sample = Gradle is gr8
sample = Gradle is great

Now we can run $ gradle showSystemProperty and the assertions are true.

Written with Gradle 2.3.

Original blog post written on March 04, 2015.

Changing the Gradle User Home Directory

We can change the Gradle user home directory in several ways. Gradle uses the directory .gradle in our home directory as the default Gradle user home directory. Here we can find for example the directory caches with downloaded dependencies. To change the Gradle user home directory we can set the environment variable GRADLE_USER_HOME and point it to another directory. The Gradle build script will look for this environment variable and then use the specified directory as the Gradle home directory.

$ export GRADLE_USER_HOME=/Users/mrhaki/dev/gradle
$ gradle -d
07:36:40.020 [main] INFO  org.gradle.launcher.Main - Starting Builder
07:36:40.120 [main] DEBUG org.gradle.launcher.Main - Gradle user home: /Users/mrhaki/dev/gradle
...

But this is not the only way to change the Gradle user home directory. We can also pass the Java system property gradle.user.home to Gradle and specify a new directory. In the following sample we use the environment variable GRADLE_OPTS to pass the Java system property, but we could also use JAVA_OPTS.

$ export GRADLE_OPTS=-Dgradle.user.home=/Users/mrhaki/dev/gradle
$ gradle -d
07:36:40.020 [main] INFO  org.gradle.launcher.Main - Starting Builder
07:36:40.120 [main] DEBUG org.gradle.launcher.Main - Gradle user home: /Users/mrhaki/dev/gradle
...

Or we can use the command-line options -g and --gradle-user-home when we run Gradle and use a different directory as a Gradle user home directory.

$ gradle -g /Users/mrhaki/dev/gradle -d
07:36:40.020 [main] INFO  org.gradle.launcher.Main - Starting Builder
07:36:40.120 [main] DEBUG org.gradle.launcher.Main - Gradle user home: /Users/mrhaki/dev/gradle
...
$ gradle --gradle-user-home=/Users/mrhaki/dev/gradle -d
07:36:40.020 [main] INFO  org.gradle.launcher.Main - Starting Builder
07:36:40.120 [main] DEBUG org.gradle.launcher.Main - Gradle user home: /Users/mrhaki/dev/gradle
...

Written with Gradle 0.9-rc-2.

Original blog post written on September 08, 2010.

Different Ways to Set Project Properties

We can add properties to our Gradle build script in several different ways.

  • First of all we can define the properties in the script itself using an ext block.
  • Or we can use the -P command-line argument to pass a property to the build script.
  • We can define a gradle.properties file and set the property in this file. We can place the file in our project directory or in the <USER_HOME>/.gradle directory. The properties defined in the property file in our home directory take precedence over the properties defined in the file in our project directory. As a bonus we can also define system properties in a gradle.properties file, we only have to prefix the property name with systemProp..
  • We can use an environment variable of which the name starts with ORG_GRADLE_PROJECT_ followed by the property name.
  • Or we use the Java system property that starts with org.gradle.project. followed by the property name.

The following sample Gradle build file uses all these techniques to get the value of properties:

description = '''\

To run this build script we must first
set an environment variable
ORG_GRADLE_PROJECT_property5=environment property

Run as:
gradle -Pproperty4="argument property" -Dorg.gradle.project.property6="system property"
'''

ext {
    property1 = 'Project property'
}

task assertProps(description: 'Print different properties') << {
    assert 'Project property' == property1
    assert 'gradle.properties property' == property2
    assert 'argument property' == property4
    assert 'environment property' == property5
    assert 'system property' == property6
    assert 'gradle.properties system property' == System.properties['property3']
}

defaultTasks 'assertProps'

We use the following gradle.properties file:

property2 = gradle.properties property
systemProp.property3 = gradle.properties system property

Original written with Gradle 0.9-rc-1 and re-written with Gradle 2.2.1.

Original blog post written on September 21, 2010.

Get Property Value With findProperty

Gradle 2.13 added a new method to get a property value: findProperty. This method will return a property value if the property exists or null if the property cannot be found. Gradle also has the property method to return a property value, but this method will throw an exception if the property is missing. With the new findProperty method and the Groovy elvis operator (?:) we can try to get a property value and if not found return a default value.

In the following example we have a task that tries to print the value of the properties sampleOld and sampleNew. We use the findProperty for sampleNew and the property method for sampleOld:

// File: build.gradle
task resolveProperties << {
    println "sampleOld -> ${project.hasProperty('sampleOld') ? project.property('sampleOld') : 'd\
efault value for sampleOld'}"
    println "sampleNew -> ${project.findProperty('sampleNew') ?: 'default value for sampleNew'}"
}

First run the task and not set the project properties sampleOld and sampleNew:

$ gradle -q resolveProperties
sampleOld -> default value for sampleOld
sampleNew -> default value for sampleNew
$

Next we use the -P command line option to set a value for the properties:

$ gradle -q -PsampleOld="Value sampleOld" -PsampleNew="Value sampleNew" resolveProperties
sampleOld -> Value sampleOld
sampleNew -> Value sampleNew
$

Written with Gradle 2.13.

Original blog post written on May 11, 2016.

Lazy Project Property Evaluation

Sometime we need to define a project property in our Gradle build file for which the value must be evaluated later than the assignment. We can do this in different ways in Gradle. For example for a String type property we can rely on Groovy’s support for lazy String evaluation. If the property is of a different type we can use Closure to define the value. The Closure code is not executed during the configuration phase directly, but we can write code to invoke the Closure at the right moment and get the value. Finally we can use the afterEvaluate method of the Project class to define a lazy property. Let’s look at the different options we have with some code samples.

First we look at a lazy String property. We illustrate this with an example of a multi-project build with the following layout:

.
├── api
├── app
├── build.gradle
├── lib
└── settings.gradle

In our settings.gradle file we define the projects we want to be part of the root project:

include 'api'
include 'lib'
include 'app'

Let’s create a build file with a lazy property buildVersion:

subproject {

    version = '1.0.0'

    // Use lazy String evaluation using
    // the ${->...} syntax. If we don't
    // make it lazy the value of project.version
    // is always 1.0.0, because that is the only
    // known value at this point and the changed value
    // in the app project is not noticed.
    ext.buildVersion = "${project.name}-${-> project.version}"

    task displayBuildVersion {
        description = 'Display value of buildVersion property'
        doFirst {
            println buildVersion
        }
    }

}

project('app') {
    // In the project app we define
    // a different value for the version
    // property. Remember this property
    // is used in the definition of the
    // buildVersion property.
    version = '1.0.3'
}

If we run the displayBuildVersion task we get the following output:

$ gradle -q displayBuildVersion

api-1.0.0
app-1.0.3
lib-1.0.0
$

If we have a property of another type than String we can use another mechanism to support lazy properties. We use a Closure to define the value and then invoke the Closure to calculate the value at a later time. In the next build script we add a support method lazyProperty to check if the property is defined with a Closure. If so we execute the Closure to calculate the value. Any other type is return as-is.

subprojects {

    ext {
        // Project property with Integer value.
        // Value is overridden in the 'app' project.
        port = 80

        // Value is lazy and depends on port
        // property. Port property can be overridden
        // later in the configuration phase of Gradle.
        // Using a Closure we can make the value
        // calculation at a later point in time.
        calculatedPort = { port + 10 }
    }

    task displayCalculatedPort {
        description = 'Display value of calculatedPort property'
        doFirst {
            print "Calculated port for ${project.name} "
            println lazyProperty(project, 'calculatedPort')
            // Alternative without support method:
            // println project.property('calculatedPort')()
            // Or even shorter:
            // println calculatedPort()
        }
    }

}

project('app') {
    // Override port property.
    ext.port = 8000
}

/**
 * Check if property value is a Closure. If so the value
 * is calculated by running the Closure and returned.
 * Otherwise the value is returned without calculation.
 *
 * @param project Project containing the property to calculate.
 * @param propertyName Name of the property to calculate value for.
 */
def lazyProperty(final Project project, final String propertyName) {
    // Get property.
    def propertyValue = project.property(propertyName)

    // Check for type of property to see if we can
    // run it as a Closure.
    if (propertyValue instanceof Closure) {
        // Invoke Closure to calculate the value.
        propertyValue()
    } else {
        // Return value as-is.
        propertyValue
    }
}

Let’s run the displayBuildVersion task and look at the output:

$ gradle -q displayCalculatedPort

Calculated port for api 90
Calculated port for app 8010
Calculated port for lib 90
$

Finally we use the afterEvaluate method:

subprojects {

    // Project property with Integer value.
    // Value is overriden in the 'app' project.
    ext.port = 80

    afterEvaluate {
        // Create calculatedPort property with
        // value at the latest moment.
        ext.calculatedPort = port + 10
    }

    task displayCalculatedPort {
        description = 'Display value of calculatedPort property'
        doFirst {
            print "Calculated port for ${project.name} "
            println calculatedPort
        }
    }

}

project('app') {
    // Override port property.
    ext.port = 8000
}

When we run the displayBuildVersion task and look at the output we see expected results:

$ gradle -q displayCalculatedPort

Calculated port for api 90
Calculated port for app 8010
Calculated port for lib 90
$

In our simple examples we used the lazy project property only in one task. We could have simply evaluated the value in the task definition as it is the only place where it is used. But if a property is used by other tasks and code in our build script we can rely on the mentioned solutions in this blog post.

Written with Gradle 3.0.

Original blog post written on September 14, 2016.

Using Objects for Version

One of the great things of Gradle is that the build scripts are code. We can use all the features of the Groovy language, we can refactor our build scripts to make them more maintainable, we can use variables and properties to define values and much more, just like our application code. In this post we see how we can create a class to define a version in our build script.

To set the version of a Gradle project we only have to assign a value to the version property. Normally we use a String value, but we can also assign an object. Gradle will use the toString() method of the object to get the String value for a version.

In the following build script we define a new class Version in our build script. We create an instance of the class and assign it to the version property. With the task printVersion we can see the value of the version property:

version = new Version(major: 2, minor: 1, revision: 14)

task printVersion {
    doFirst {
        println "Project version is $version"
    }
}

defaultTasks 'printVersion'

class Version {
    int major, minor, revision

    String toString() {
        "$major.$minor.$revision"
    }
}

When we execute the task from the command-line we see the following output:

$ gradle -q printVersion
Project version is 2.1.14

We can even extend the class definition and include a boolean property release. If the value is true the version stays the same, but if it is false the String value -SNAPHSOT is appended to the version. In the following example build file we check if a release task is part of the Gradle build execution and if so the release property of the Version object is set to true:

version = new Version(major: 2, minor: 1, revision: 14)

gradle.taskGraph.whenReady { taskGraph ->
    // Set version as release version
    // when release task is to be executed.
    version.release = taskGraph.hasTask(release)
}

task printVersion {
    doFirst {
        println "Project version is $version"
    }
}

task release {
    doFirst {
        println "Releasing application $version"
    }
}

defaultTasks 'printVersion'

class Version {
    int major, minor, revision
    boolean release

    String toString() {
        "$major.$minor.$revision${release ? '' : '-SNAPSHOT'}"
    }
}

When we invoke the release and printVersion tasks together or just the printVersion task we get the following output:

$ gradle -q printVersion release
Project version is 2.1.14
Releasing application 2.1.14
$ gradle -q
Project version is 2.1.14-SNAPSHOT

Written with Gradle 1.2.

Original blog post written on September 18, 2012.

Build Script Using Java Syntax

A Gradle build script is actually a Groovy script. The Gradle API uses Groovy a lot so we can have a nice DSL to define our builds. But we can also use Java code in a Groovy script. The compiler that compiles the build script understands Java code as well as the Groovy code. Sometimes I hear from people new to Gradle that they have difficulty understanding the DSL. I thought it would be a fun exercise to write a very simple Gradle build script using Java syntax.

Most notable is that we invoke the getProject method to get a reference to org.grade.api.Project. In the Gradle DSL we could use the simpler Groovy property reference project or just leave it out, because all method invocations in the build script are delegated to Project.

// Apply Java plugin.
getProject().getPluginManager().apply("java");

// Set repositories.
final RepositoryHandler repositories = getProject().getRepositories();
repositories.jcenter();

// Set dependencies.
final DependencyHandler dependencies = getProject().getDependencies();
dependencies.add("compile", "org.slf4j:slf4j-api:1.7.14");
dependencies.add("testCompile", "junit:junit:4.12");

// Add a new task.
final TaskContainer tasks = getProject().getTasks();
final Task helloWorldTask = tasks.create("helloWorld");
helloWorldTask.doFirst(new Action() {
    void execute(Object task) {
        System.out.println("Running " + task.getName());
    }
});


/* Equivalent to following Gradle DSL:

apply plugin: 'java'

repositories {
    jcenter()
}

dependencies {
    compile 'org.slf4j:slf4j-api:1.7.14'
    testCompile 'junit:junit:4.12'
}

task helloWorld << { task ->
    println "Running $task.name"
}
*/

Written with Gradle 2.11.

Original blog post written on February 19, 2016.

Tasks

Setting Default Tasks

To run a task with Gradle we must specify the task name as an argument. If we don’t specify a task name we get an error that Gradle cannot determine a task to execute. We can define the tasks that need to be executed when we don’t specify a task name explicitly. In a Gradle build script we invoke the method defaultTasks and pass the name or names of the tasks as arguments to the method. These tasks are executed when we run Gradle without any task name. When we run $ gradle tasks we can see which tasks are the default tasks.

// File: build.gradle
apply plugin: 'java'

defaultTasks 'clean', 'build'  // Run tasks clean and build if no task is specified.
$ gradle tasks | grep "Default tasks"
Default tasks: clean, build

Orginally written with Gradle 0.7, re-written with Gradle 2.2.1.

Original blog post written on November 17, 2009.

Using Optional Ant Task in Gradle

Gradle uses Groovy’s AntBuilder for Ant integration. But if we want to use an optional Ant task we must do something extra, because the optional tasks and their dependencies are not in the Gradle classpath. Luckily we only have to define our dependencies for the optional task in the build.gradle file and we can define and use the optional Ant task.

In the following sample we are using the scp Ant optional task. We define a configuration and assign the dependencies to this configuration. Then we can define the task and set the classpath property to the classpath of the configuration. We use asPath to convert the configuration classpath for the Ant task. In the sample we also see how we can ask for user input when the script is run. The passphrase for the ssh keyfile is a secret and we don’t want to keep it in a file somewhere, so we ask the user for it. The Java method System.console() return a reference to the console and with readPassword() we can get the value for the passphrase.

// File: build.gradle

// We define a new configuration with the name 'sshAntTask'.
// This configuration is used to define our dependencies.
configurations {
    sshAntTask
}

// Define the Maven central repository to look for the dependencies.
repositories {
    mavenCentral()
}

// Assign dependencies to the sshAntTask configuration.
dependencies {
    sshAntTask 'org.apache.ant:ant-jsch:1.7.1', 'jsch:jsch:0.1.29'
}

// Sample task which uses the scp Ant optional task.
task update {
    description = 'Update files on remote server.'

    // Get passphrase from user input.
    def console = System.console()
    def passphrase = console.readPassword('%s: ', 'Please enter the passphrase for the keyfile')

    // Redefine scp Ant task, with the classpath property set to our newly defined
    // sshAntTask configuration classpath.
    ant.taskdef(name: 'scp', classname: 'org.apache.tools.ant.taskdefs.optional.ssh.Scp',
            classpath: configurations.sshAntTask.asPath)

    // Invoke the scp Ant task. (Use gradle -i update to see the output of the Ant task.)
    ant.scp(todir: 'mrhaki@servername:/home/mrhaki',
            keyfile: '${user.home}/.ssh/id_rsa',
            passphrase: passphrase as String, // Use phassphrase entered by the user.
            verbose: 'true') {
        fileset(dir: 'work') {
            include(name: '**/**')
        }
    }
}

Written with Gradle 0.8.

Original blog post written on December 26, 2009.

Get User Input Values From Console

Gradle scripts are Groovy scripts. So we can use all the functionality of Groovy in our Gradle scripts. And because we can use Java classes as well, we can simply get user input values from the console using java.io.Console. With the Console class we can for example ask for a value to be used in our script. We can even ask for a password without the password being echoed on the console.

In the following sample build script we have a ask task that uses Console to let the user enter a username and password value. In the print task we use the values that are entered to print them to the console.

def username
def password

task ask << {
    def console = System.console()
    if (console) {
        username = console.readLine('> Please enter your username: ')
        password = console.readPassword('> Please enter your password: ')
    } else {
        logger.error "Cannot get console."
    }
}

task print << {
    println "Hello $username! Your password is ${password.size()} characters."
}

We can run the script (suppose we use help as password):

$ gradle -q ask print
> Please enter your username: mrhaki
> Please enter your password:
Hello mrhaki! Your password is 4 characters.

Written with Gradle 0.9-rc-1.

Original blog post written on September 23, 2010.

Group Similar Tasks

In Gradle we can assign a task to a group. Gradle uses the group for example in the output of $ gradle -t to output all the tasks of the same group together. We only have to set the group property with a value and our task belongs to a group.

In the following sample we add the tasks hello and bye to the group Greeting:

def GREETING_GROUP = 'Greeting'

task hello << {
   println 'Hello Gradle!'
}
hello.group = GREETING_GROUP
hello.description = 'Say hello.'

task bye {
    description= 'Say goodbye.'
    group = GREETING_GROUP
}
bye << {
    println 'Goodbye.'
}

If we run $ gradle tasks we get the following output:

$ gradle tasks
:tasks
...

Greeting tasks
--------------
bye - Say goodbye.
hello - Say hello.

...
$

Written with Gradle 0.9-rc-1 and re-written with Gradle 2.2.1.

Original blog post written on October 13, 2010.

Adding Tasks to a Predefined Group

In Gradle we can group related tasks using the group property of a task. We provide the name of our group and if we look at the output of the tasks task we can see our tasks grouped in section with the given name. In the next sample we create a new task publish and assign it the group name Publishing.

task publish(type: Copy) {
    from "sources"
    into "output"
}

configure(publish) {
    group = 'Publishing'
    description = 'Publish source code to output directory'
}

If we execute tasks we get the following output:

$ gradle tasks
:tasks

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

Help tasks
----------
dependencies - Displays the dependencies of root project 'taskGroup'.
help - Displays a help message
projects - Displays the sub-projects of root project 'taskGroup'.
properties - Displays the properties of root project 'taskGroup'.
tasks - Displays the tasks runnable from root project 'taskGroup' (some of the displayed tasks ma\
y belong to subprojects).

Publishing tasks
----------------
publish - Publish source code to output directory

To see all tasks and more detail, run with --all.

BUILD SUCCESSFUL

Total time: 2.327 secs

Suppose we apply the Java plugin to our project. We get a lot of new tasks, which are already in groups with names like Build and Documentation. If we want to add our own custom tasks to one of those groups we only have to use the correct name for the group property of our task. In the following build file we apply the Java plugin and use the Build group name as a group name for our task. The name is defined as a constant of the BasePlugin.

apply plugin: 'java'

task publish(type: Copy) {
    from 'sources'
    into 'output'
}

configure(publish) {
    group = BasePlugin.BUILD_GROUP // Or use 'build'
    description = 'Publish source code to output directory'
}

When we run tasks again we can see our task is in the Build section together with the tasks added by the Java plugin:

$ gradle tasks
:tasks

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

Build tasks
-----------
assemble - Assembles all Jar, War, Zip, and Tar archives.
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 the main classes.
clean - Deletes the build directory.
jar - Assembles a jar archive containing the main classes.
publish - Publish source code to output directory
testClasses - Assembles the test classes.

Documentation tasks
-------------------
javadoc - Generates Javadoc API documentation for the main source code.

Help tasks
----------
dependencies - Displays the dependencies of root project 'taskGroup'.
help - Displays a help message
projects - Displays the sub-projects of root project 'taskGroup'.
properties - Displays the properties of root project 'taskGroup'.
tasks - Displays the tasks runnable from root project 'taskGroup' (some of the displayed tasks ma\
y belong to subprojects).

Verification tasks
------------------
check - Runs all checks.
test - Runs the unit tests.

Rules
-----
Pattern: build<configurationname>: Assembles the artifacts of a configuration.
Pattern: upload<configurationname>: Assembles and uploads the artifacts belonging to a configurat\
ion.
Pattern: clean<taskname>: Cleans the output files of a task.

To see all tasks and more detail, run with --all.

BUILD SUCCESSFUL

Total time: 2.896 secs

Written with Gradle 1.0.

Original blog post written on June 13, 2012.

Getting Information About Buildscript Dependencies

If our build script needs extra dependencies, for example when we add a plugin, then we use a buildscript configuration block and define our dependencies in there. These dependencies are added to a configuration with the name classpath. To see the dependencies and transitive dependencies for the classpath configuration we use the task buildEnvironment. This task is available since Gradle 2.10

Suppose we have the following build file where define the Asciidoctor plugin using the new plugins configuration block. We also add a dependency for PDF generation in the buildscript block:

buildscript {
    dependencies {
        classpath 'org.asciidoctor:asciidoctorj-pdf:1.5.0-alpha.8'
    }
}

plugins {
    id "org.asciidoctor.convert" version "1.5.3"
}

apply plugin: 'org.asciidoctor.convert'

When we run the buildEnvironment tasks we get an overview of all dependencies:

$ gradle buildEnvironment
:buildEnvironment

------------------------------------------------------------
Root project
------------------------------------------------------------

classpath
+--- org.asciidoctor:asciidoctorj-pdf:1.5.0-alpha.8
|    \--- org.asciidoctor:asciidoctorj:1.5.2
|         +--- org.jruby:jruby-complete:1.7.16.1
|         +--- org.slf4j:slf4j-api:1.7.7
|         \--- com.beust:jcommander:1.35
\--- org.asciidoctor:asciidoctor-gradle-plugin:1.5.3

BUILD SUCCESSFUL

Total time: 7.161 secs

Written with Gradle 2.10.

Original blog post written on January 26, 2016.

Add Incremental Build Support to Tasks

Gradle has a very powerful incremental build feature. This means Gradle will not execute a task unless it is necessary. We can help Gradle and configure our task so it is ready for an incremental build.

Suppose we have a task that generates a file. The file only needs to be generated if a certain property value has changed since the last task execution. Or the file needs be generated again if a source file is newer than the generated file. These conditions can be configured by us, so Gradle can use this to determine if a task is up to date or not. If the task is up to date Gradle doesn’t execute the actions.

A Gradle task has an inputs and outputs property. We can assign a file(s), dir or properties as inputs to be checked. For outputs we can assign a file, dir or custom code in a closure to determine the output of the task. Gradle uses these values to determine if a task needs to be executed.

In the following sample build script we have a task generateVersionFile which create a file version.text in the project build directory. The contents of the file is the value of the version property. The file only needs to be generated if the value of version has changed since the last time the file was generated.

version = '1.0'
outputFile = file("$buildDir/version.txt")

task generateVersionFile << {
    if (!outputFile.isFile()) {
        outputFile.parentFile.mkdirs()
        outputFile.createNewFile()
    }
    outputFile.write "Version: $version"
}

generateVersionFile.inputs.property "version", version
generateVersionFile.outputs.files outputFile

task showContents << {
    println outputFile.text
}
showContents.dependsOn generateVersionFile

Let’s run our script for the first time:

$ gradle showContents
:generateVersionFile
:showContents
Version: 1.0

BUILD SUCCESSFUL

Now we run it again and notice how Gradle tells us the task is UP-TO-DATE:

$ gradle showContents
:generateVersionFile UP-TO-DATE
:showContents
Version: 1.0

BUILD SUCCESSFUL

Let’s change the build script and set the version to 1.1 and run Gradle:

$ gradle showContents
:generateVersionFile
:showContents
Version: 1.1

BUILD SUCCESSFUL

In a follow-up post we see how can apply this logic to a custom task class via annotations.

Written with Gradle 0.9-rc-1.

Original blog post written on October 13, 2010.

Add Incremental Build Support to Custom Tasks with Annotations

In a previous post we learned how we can use the inputs and outputs properties to set properties or files that need to be checked to see if a task is up to date. In this post we learn how a custom task class can use annotations to set input properties, file or files and output files or dir.

For input we can use @Input, @InputFile, @InputFiles or @InputDirectory annotations. Gradle uses the properties with annotations for checking if a task is up to date. Output file or directory can be marked with @OutputFile and @OutputDirectory.

task generateVersionFile(type: Generate) {
    version = '2.0'
    outputFile = file("$project.buildDir/version.txt")
}

task showContents << {
    println generateVersionFile.outputFile.text
}
showContents.dependsOn generateVersionFile

class Generate extends DefaultTask {
    @Input
    String version

    @OutputFile
    File outputFile

    @TaskAction
    void generate() {
        def file = getOutputFile()
        if (!file.isFile()) {
            file.parentFile.mkdirs()
            file.createNewFile()
        }
        file.write "Version: ${getVersion()}"
    }
}

We can run our task and get the following output:

$ gradle showContents
:generateVersionFile
:showContents
Version: 2.0

BUILD SUCCESSFUL

And if we run it again we see the task is now up to date:

$ gradle showContents
:generateVersionFile UP-TO-DATE
:showContents
Version: 2.0

BUILD SUCCESSFUL

We can change the version numer in our build script to 2.1 and see the output:

$ gradle showContents
:generateVersionFile
:showContents
Version: 2.1

BUILD SUCCESSFUL

Written with Gradle 0.9-rc-1.

Original blog post written on October 14, 2010.

Set Task Values with Project Convention

In a previous post we wrote a custom task to generate a file with version information. When we created the task in our build file we had to provide values for the task properties version and outputFile. Now we want these values to have default values and we want to be able to set values with project properties instead of only task properties.

First we write a plugin where we create a new Plain Old Groovy Object (POGO) which will store the project properties in a convention object. Next we assign the values from the convention object properties to the task properties with a closure. This means the value of the properties are lazy set: only when the task gets executed the task property values are calculated.

Let’s take a look at the source files for the plugin, POGO and task before we see what our new build script looks like.

// File: buildSrc/src/main/groovy/com/mrhaki/gradle/generate/GeneratePlugin.groovy
package com.mrhaki.gradle.generate

import org.gradle.api.Project
import org.gradle.api.Plugin

class GeneratePlugin implements Plugin<Project> {
    void apply(Project project) {
        def convention = new GeneratePluginConvention(project)
        project.convention.plugins.generate = convention

        project.tasks.withType(Generate.class).allTasks { Generate task ->
            task.conventionMapping.version = { convention.outputVersion ?: project.version }
            task.conventionMapping.outputFile = { convention.outputFile }
        }
    }
}
// File: buildSrc/src/main/groovy/com/mrhaki/gradle/generate/GeneratePluginConvention.groovy
package com.mrhaki.gradle.generate

import org.gradle.api.Project

class GeneratePluginConvention {
    final Project project
    String outputFilename
    String outputVersion

    public GeneratePluginConvention(Project project) {
        this.project = project
        this.outputFilename = 'version.txt'
    }

    File getOutputFile() {
        project.file("$project.buildDir/$outputFilename")
    }
}
// File: buildSrc/src/main/groovy/com/mrhaki/gradle/generate/Generate.groovy
package com.mrhaki.gradle.generate

import org.gradle.api.internal.ConventionTask
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.TaskAction

class Generate extends ConventionTask {
    @Input
    String version

    @OutputFile
    File outputFile

    @TaskAction
    void generate() {
        def file = getOutputFile()
        if (!file.isFile()) {
            file.parentFile.mkdirs()
            file.createNewFile()
        }
        file.write "Version: ${getVersion()}"
    }
}

Our build script has changed to:

// File: build.gradle
import com.mrhaki.gradle.generate.*

apply plugin: GeneratePlugin

version = '3.0'
// Or use explicit values:
// outputVersion = 'OUTPUT'
// outputFilename = 'another-version.txt'

task generateVersionFile(type: Generate)

task showContents << {
    println generateVersionFile.outputFile.text
}
showContents.dependsOn generateVersionFile

The version property value of the Generate task is now set by either a project property outputVersion or the project version. And the outputFile property is assigned from the default version.txt or the value of the property property outputFilename.

Written with Gradle 0.9-rc-1.

Original blog post written on October 14, 2010.

Methods Generated For Setting Task Properties

If we create our own tasks in Gradle we usually extend from the DefaultTask or a subclass of DefaultTask. The tasks that are included with Gradle also extend from this class. Gradle will create a proxy class for the actual class implementation and adds (among other things) also a property setter method. The method has the name of the property and has a single argument of the same type as the property. It is different from the setProperty and getProperty methods already added by Groovy. For example if we have a task with a property with the name message of type String then Gradle will add the method message(String) to the proxy class.

In the following example task we have one property user:

// File: buildSrc/src/main/groovy/com/mrhaki/gradle/Hello.groovy
package com.mrhaki.gradle

import org.gradle.api.DefaultTask
import org.gradle.api.tasks.TaskAction

class Hello extends DefaultTask {
    String user

    @TaskAction
    void sayHello() {
        println "Hello $user, how are you doing?"
    }
}

With the following test we can check that the method user(String) is actually created and we can use it to set a value for the user property:

// File: src/test/groovy/com/mrhaki/gradle/HelloTaskSpec.groovy
package com.mrhaki.gradle

import org.gradle.api.Project
import org.gradle.testfixtures.ProjectBuilder
import spock.lang.Specification
import spock.lang.Subject

class HelloTaskSpec extends Specification {

    @Subject
    private Hello task

    private Project project = ProjectBuilder.builder().build()

    def "user() method is generated to set user property"() {
        when:
        task = project.task('helloSample', type: Hello) {
            // Gradle generates proxy class to allow
            // 'setter(value)' methods for properties.
            // In this case we invoke user(String),
            // which maps to setUser(String).
            user 'sample'
        }

        then:
        task.user == 'sample'
    }

    def "Groovy syntax property assignment to set user property"() {
        when:
        task = project.task('helloSample', type: Hello) {
            // Groovy syntax for setUser(String).
            user = 'sample user'
        }

        then:
        task.user == 'sample user'
    }
}

The following build file has two tasks of type Hello where we use the user(String) method and a property assignment:

import com.mrhaki.gradle.Hello

task helloMrhaki(type: Hello) {
    user = 'mrhaki'
}

task helloHubert(type: Hello) {
    user 'Hubert'
}

The idea of this blog post is from watching a great presentation by Vitaliy Zasadnyy about Gradle plugins during a Mashup Gradle/Android Meetup.

Written with Gradle 2.11.

Original blog post written on February 29, 2016.

Lazy Task Properties

When we create our own custom tasks we might need to support lazy evaluation of task properties. A Gradle build has three phases: initialisation, configuration and execution. Task properties can be set during the configuration phase, for example when a task is configured in a build file. But our task can also evaluate the value for a task property at execution time. To support evaluation during execution time we must write a lazy getter method for the property. Also we must define the task property with def or type Object. Because of the Object type we can use different types to set a value. For example a Groovy Closure or Callable interface implementation can be used to execute later than during the configuration phase of our Gradle build. Inside the getter method we invoke the Closure or Callable to get the real value of the task property.

Let’s see this with a example task. We create a simple class with two task properties: user and outputFile. The user property must return a String object and the outputFile property a File object. For the outputFile property we write a getOutputFile method. We delegate to the Project.file method, which already accepts different argument types for lazy evaluation. We also write an implementation for getUser method where we run a Closure or Callable object if that is used to set the property value.

// File: buildSrc/src/main/groovy/com/mrhaki/gradle/Hello.groovy
package com.mrhaki.gradle

import org.gradle.api.DefaultTask
import org.gradle.api.tasks.TaskAction
import java.util.concurrent.Callable

class Hello extends DefaultTask {

    // Setter method will accept Object type.
    def outputFile

    File getOutputFile() {
        // The Project.file method already
        // accepts several argument types:
        // - CharSequence
        // - File
        // - URI or URL
        // - Closure
        // - Callable
        project.file outputFile
    }

    // Setter method will accept Object type.
    def user

    String getUser() {
        // If the value is set with a Closure
        // or Callable then we calculate
        // the value. Closure implements Callable.
        if (user instanceof Callable) {
            user.call()
        // For other types we return the
        // result of the toString method.
        } else {
            user.toString()
        }
    }

    @TaskAction
    void sayHello() {
        getOutputFile().text = "Hello ${getUser()}"
    }
}

We already learned in a previous post that setting a property value can also be done using a setter method that is created by Gradle. In the following specification we use different methods and types to assign values to the user and outputFile properties:

package com.mrhaki.gradle

import org.gradle.api.Project
import org.gradle.testfixtures.ProjectBuilder
import spock.lang.Specification
import spock.lang.Subject

import java.util.concurrent.Callable

class HelloTaskSpec extends Specification {

    @Subject
    private Hello task

    private Project project = ProjectBuilder.builder().build()

    def setup() {
        task = project.task('helloSample', type: Hello)
    }

    def "set user property during configuration phase"() {
        when:
        project.ext.username = 'sample'

        and:
        // Property value known at configuration time.
        task.user project.property('username')

        then:
        task.user == 'sample'
    }

    def "user property not set when value not known during configuration phase"() {
        when:
        // Property value not known at configuration time.
        // Exception will be thrown because username property
        // is not set yet.
        task.user project.property('username')

        and:
        project.ext.username = 'sample'

        then:
        thrown(MissingPropertyException)
    }

    def "set user property with lazy evaluation GString during configuration phase"() {
        when:
        // Property value not known at configuration time.
        // Using GString lazy evaluation.
        task.user "${ -> project.property('username')}"

        and:
        project.ext.username = 'lazyGString'

        then:
        task.user == 'lazyGString'
    }

    def "set user property during execution phase using Closure"() {
        when:
        // Property value known at execution time, but not
        // during configuration phase.
        task.user { project.property('username') }

        and:
        // Set value for user property assignment.
        project.ext.username = 'closureValue'

        then:
        task.user == 'closureValue'
    }

    def "set user property during execution phase using Callable"() {
        when:
        // Property value known at execution time, but not
        // during configuration phase.
        task.user = new Callable<String>() {
            @Override
            String call() throws Exception {
                project.property('username')
            }
        }

        and:
        // Set value for user property assignment.
        project.ext.username = 'callableValue'

        then:
        task.user == 'callableValue'
    }

    def "delegate getOutputFile to project.file() and support all types for project.file()"() {
        given:
        task.user 'mrhaki'

        task.outputFile { project.property('helloOutput') }

        and:
        project.ext.helloOutput = 'hello.txt'

        when:
        task.sayHello()

        then:
        task.outputFile.text == 'Hello mrhaki'
    }

}

Written with Gradle 2.11.

Original blog post written on February 29, 2016.

Adding Custom Extension To Tasks

We can add extensions to our project in Gradle to extend the build script with extra capabilities. Actually we can add extensions to any object in Gradle that implements the ExtensionAware interface. The Task interface for example extends the ExtensionAware interface so we can add custom extensions to Gradle tasks as well. Inside a task configuration we can then use that extension.

In the following build script we use a custom extension for JavaCompile tasks to configure the compiler -Xlint arguments. The extension is added via the plugin com.mrhaki.gradle.JavaCompilerLintPlugin. First we take a look ate the extension class. This is a POGO for configuring the compiler arguments with -Xlint options:

// File: buildSrc/src/main/groovy/com/mrhaki/gradle/JavaCompilerLintExtension.groovy
package com.mrhaki.gradle

/**
 * Extension class for use with JavaCompile tasks
 * to set -Xlint options.
 */
class JavaCompilerLintExtension {

    Boolean cast
    Boolean classfile
    Boolean deprecation
    Boolean depAnn
    Boolean divzero
    Boolean empty
    Boolean fallthrough
    Boolean finallyblocks
    Boolean options
    Boolean overrides
    Boolean path
    Boolean processing
    Boolean rawtypes
    Boolean serial
    Boolean staticref
    Boolean tryblocks
    Boolean unchecked
    Boolean varargs

    void enableAll() {
        cast = true
        classfile = true
        deprecation = true
        depAnn = true
        divzero = true
        empty = true
        fallthrough = true
        finallyblocks = true
        options = true
        overrides = true
        path = true
        processing = true
        rawtypes = true
        serial = true
        staticref = true
        tryblocks = true
        unchecked = true
        varargs = true
    }

    /**
     * Create list of compiler -Xlint arguments.
     *
     * @return List of -Xlint compiler arguments.
     */
    List<String> asCompilerArgs() {
        final List<String> optionArgs = []
        optionArgs << optionArg('cast', cast)
        optionArgs << optionArg('classfile', classfile)
        optionArgs << optionArg('deprecation', deprecation)
        optionArgs << optionArg('dep-ann', depAnn)
        optionArgs << optionArg('divzero', divzero)
        optionArgs << optionArg('empty', empty)
        optionArgs << optionArg('fallthrough', fallthrough)
        optionArgs << optionArg('finally', finallyblocks)
        optionArgs << optionArg('options', options)
        optionArgs << optionArg('overrides', overrides)
        optionArgs << optionArg('path', path)
        optionArgs << optionArg('processing', processing)
        optionArgs << optionArg('rawtypes', rawtypes)
        optionArgs << optionArg('serial', serial)
        optionArgs << optionArg('static', staticref)
        optionArgs << optionArg('try', tryblocks)
        optionArgs << optionArg('unchecked', unchecked)
        optionArgs << optionArg('varargs', varargs)

        // filter null values.
        final List<String> compilerArgs = optionArgs.findAll()
        return compilerArgs
    }

    /**
     * Create -Xlint compile option if option is set.
     *
     * @param name Name of the -Xlint compile option.
     * @param enable Set option argument as -Xlint:option if true, otherwise as -Xlint:-option if\
 false.
     * @return Null if enable is null, otherwise a valid -Xlint compiler option.
     */
    private String optionArg(final String name, final Boolean enable) {
        if (enable != null) {
            final String option = enable ? name : "-$name"
            return "-Xlint:$option"
        }
        return null
    }
}

Next we have a plugin class that registers the extension with the name lint on all JavaCompile tasks in our project:

// File: buildSrc/src/main/groovy/com/mrhaki/gradle/JavaCompilerLintPlugin.groovy
package com.mrhaki.gradle

import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.tasks.compile.JavaCompile

/**
 * Plugin for applying a custom extension to
 * JavaCompile tasks for configuring the
 * -Xlint options.
 */
class JavaCompilerLintPlugin implements Plugin<Project> {

    void apply(final Project project) {

        // For all JavaCompile tasks we add
        // a custom extension with the name lint
        // for configuring -Xlint options.
        project.tasks.withType(JavaCompile) { task ->

            // Let Gradle create a new extension.
            // Users can configure a compile task
            // from the Java plugin like:
            // compileJava {
            //   lint {
            //     cast = true
            //   }
            // }
            JavaCompilerLintExtension taskExtension =
                    task.extensions.create('lint', JavaCompilerLintExtension)

            // Use options set via the lint extension
            // and assign them to the options.compilerArgs
            // property.
            // We do this in doFirst because the options are not
            // set yet at the Gradle configuration phase.
            task.doFirst {
                options.compilerArgs = taskExtension.asCompilerArgs()
            }
        }
    }

}

We have everything ready, so let’s use the plugin in our Java project:

// File: build.gradle
apply plugin: 'java'

apply plugin: com.mrhaki.gradle.JavaCompilerLintPlugin

repositories {
    jcenter()
}

dependencies {
    testCompile 'junit:junit:4.11'
}

compileJava {
    // Here we use the custom
    // task extension. The closure
    // delegates to JavaCompilerLintExtension.
    lint {
        enableAll()
        empty = false
        depAnn = false
    }
}

compileTestJava {
    // All JavaCompile task have the
    // lint extension.
    lint {
        cast = true
    }
}

Written with Gradle 2.11.

Original blog post written on March 03, 2016.

Use Command Line Options With Custom Tasks

Suppose we have a custom task with some properties that can be configured. Normally we would add the configuration in the build script. But we can also use command line options to configure a task. So when we run the task from the command line we can provide a configuration value for the task on the command line. To see which command line options are available for a task we can use the Gradle built-in task help followed by the option --task and the task name. To indicate a property as command line option we use a @Option annotation. We can specify the name of the command line option, a short description and also the order that is used to display the options with the help task.

Let’s create a sample custom task and use the @Option annotation. In the following build file we create a custom task GenerateVersionFile. This task generates a file with a default name of version.txt in the build/ directory. The file contains the project version value. We make the property that defines the output filename as a command line option. This way the name can be defined when we run Gradle (and still of course using the default configuration in a build file).

// Import Option annotation
import org.gradle.api.internal.tasks.options.Option

version = 'demo'

// Create a task of the custom task type GenerateVersionFile.
task generateVersionFile(type: GenerateVersionFile)

/**
 * Custom task to generate a version value in a file.
 */
class GenerateVersionFile extends DefaultTask {

    String version

    // Specify outputFile property as
    // command line option.
    // Use as --outputFile filename.
    @Option(option = "outputFile",
            description = "File to store the project version in",
            order = 1)
    Object outputFile

    GenerateVersionFile() {
        // Set default value for outputFile as version.txt.
        outputFile = 'version.txt'

        // Description for the task.
        description = 'Generate a file with the project version'
    }

    @TaskAction
    void generate() {
        // Create directory for the output file if
        // it doesn't exist.
        final File versionFileDestination = getOutputFile()
        project.mkdir(versionFileDestination.parentFile)

        // Save version in file.
        versionFileDestination.text = getVersion()
    }

    @Input
    String getVersion() {
        return project.version
    }

    @OutputFile
    File getOutputFile() {
        return new File(project.buildDir, outputFile)
    }

}

If we run the help task for the generateVersionFile task we can see that our command line option is shown in the list of available options:

$ gradle help --task generationVersionFile
:help
Detailed task information for generateVersionFile

Path
     :generateVersionFile

Type
     GenerateVersionFile (GenerateVersionFile)

Options
     --outputFile     File where the project version is stored

Description
     Generate a file with the project version

Group
     -

BUILD SUCCESSFUL

Total time: 2.933 secs
$

Now we invoke the ` generateVersionFile ` task with a value for the command line option:

$ gradle generateVersionFile --outputFile version.saved
:generateVersionFile

BUILD SUCCESSFUL

Total time: 0.826 secs
$ more build/version.saved
demo
$

Written with Gradle 3.1.

Original blog post written on September 20, 2016.

Automatic Clean Tasks

Gradle adds the task rule clean<Taskname> to our projects when we apply the base plugin. This task is able to remove any output files or directories we have defined for our task. For example we can assign an output file or directory to our task with the outputs property. Or we can use the @OutputFile and @OutputDirectories annotations for custom task classes. The clean<Taskname> rule can delete the output files or directories for the task with the name <Taskname> for us. We don’t have to write the clean task ourselves we only have define the base plugin in our project. And Gradle will take care of the rest!.

apply plugin: 'base'

outputDir = file("$buildDir/generated-src")
outputFile = file("$buildDir/output.txt")

task generate << {
    outputDir.mkdirs()
    outputFile.write 'Generated by Gradle.'
}
generate.outputs.files outputFile
generate.outputs.dir outputDir

task showBuildDir << {
    def files = buildDir.listFiles()
    files.each {
        print   it.directory ? 'Dir:  ' : 'File: '
        println it.name
    }
    println "${files.size()} files in $buildDir.name"
}

We can first run the generate task and see the output file and directory.

$ gradle -q generate showBuildDir
Dir:  generated-src
File: output.txt
2 files in build

Next we can run the task cleanGenerate, which is added to the project by Gradle, and see the output files are gone.

$ gradle -q cleanGenerate showBuildDir
0 files in build

Written with Gradle 0.9-rc-1.

Original blog post written on October 15, 2010.

Parse Files with SimpleTemplateEngine in Copy Task

With the copy task of Gradle we can copy files that are parsed by Groovy’s SimpleTemplateEngine. This means we can expand properties in the source file and add Groovy code that is going to be executed. We must use the expand() method in the copy task where we can pass properties to be used in the source file.

version = 'DEMO'
group = 'com.mrhaki'

task copy(type: Copy) {
    from 'src/templates'
    into "$buildDir"
    include 'projectinfo.html.template'
    rename { file -> 'projectinfo.html' }
    expand(project: project, title: 'ProjectInfo', generated: new Date())
}

We define the following source file in src/templates/projectinfo.html.template:

<html>
    <head>
        <title>${title}</title>
    </head>
    <body>
        <h1>${project.name}</h1>

        <ul>
        <% project.properties.findAll { k,v -> v instanceof String }.each { key, value -> %>
            <li>$key = $value</li>
        <% } %>
        </ul>

        <hr />
        <p>Generated on ${generated.format('dd-MM-yyyy')}</p>
    </body>
</html>

When we run the copy task we get the following output:

Written with Gradle 0.9-rc-1.

Original blog post written on October 22, 2010.

Copy Files with Filtering

Gradle’s copy task is very powerful and includes filtering capabilities. This means we can change the contents of the files that are copied before they reach their new destination. We use the filter() method to define a filter. The good news is we can reuse the Ant filtering classes from the org.apache.tools.ant.filters package. We define the filtering class and can pass parameters for the filter. Or we can pass a closure which is passed each line as an argument. Within the closure we must return the filtered line.

import org.apache.tools.ant.filters.*

task('filterCopy', type: Copy) {
    from 'src/templates'
    into buildDir
    include '**/*.txt'
    filter { line -> line.contains('Gradle') ? line : '' }
    filter(ReplaceTokens, tokens: [author: 'mrhaki', gradleVersion: gradle.gradleVersion])
    filter(ConcatFilter, prepend: file('src/include/header.txt'))
}

Now let’s create a sample text file that will get filtered in src/templates/HelloGradle.txt:

This is just a simple text file. This line will not make it.
We show filtering capabilities of Gradle copy.
This file is written by @author@ with Gradle version @gradleVersion@.

And we create the file src/include/header.txt:

Include this header file.
-------------------------

After we run $ gradle filterCopy we get the following contents for the file build/HelloGradle.txt:

Include this header file.
-------------------------
We show filtering capabilities of Gradle copy in combination with the Ant filtering types.
This file is written by mrhaki with Gradle version 0.9-rc-1.

Written with Gradle 0.9-rc-1.

Original blog post written on October 24, 2010.

Renaming Files while Copying

With the Gradle copy task we can define renaming rules for the files that are copied. We use the rename() method of the copy task to define the naming rules. We can use a closure where the filename is the argument of the closure. The name we return from the closure is the new name of copied file. Or we can define a regular expression and set the replacement value for the corresponding regular expression. We can use groups in the regular expression and use them in the replacement value like $<group>.

task copyFiles(type: Copy) {
    from 'src/files'
    into "$buildDir/files"
    rename '(.*)-(.*).html', '$2/$1.html'
    rename ~/(.*).template.(.*)/, '$1.$2'
    rename { filename ->
        filename.replace 'java', 'groovy'
    }
}

Let’s create some source files, so the renaming rules can be applied to them.

src/files/index-en.html:

<html>
    <body>
        <h1>Hello Gradle</h1>
    </body>
</html>

src/files/index-nl_NL.html:

<html>
    <body>
        <h1>Hallo Gradle</h1>
    </body>
</html>

src/files/sample.template.txt:

Sample file.

src/files/Sample.java:

public class Sample {
    private String gradle = "Gradle";
}

We run $ gradle copyFiles and we get the following files in build/files:

nl_NL
 |
 +-- index.html
en
 |
 +-- index.html
Sample.groovy
sample.txt

Written with Gradle 0.9-rc-1.

Original blog post written on October 25, 2010.

Using CopySpec with Tasks

To define a Copy task we specify the files we want to copy and to which directory. This definition is a CopySpec instance. It contains the rules that defines what we want to copy. The archive tasks Jar, Zip and Tar also use a CopySpec instance.

When we create a task of type Copy we get a task object that implements the CopySpec interface. We can use all the methods from this interface to extend our recipe for copying tasks. In the following build file we first define the task website. We use CopySpec methods to configure the task. Then we define a task deploy of type Sync that also implements the CopySpec interface.

// Create a new task of type Copy.
// The website task is of type Copy
// and this means it implements the
// CopySpec interface.
task website(type: Copy) {
    into "${buildDir}/website"
    from 'src/webroot'

    into 'resources', {
        from 'src/assets'
    }
}

// We can use all CopySpec methods
// to add new specifications to
// the existing specifications.
website.into('resources') {
    from 'src/javascript'
}

// The copySpec method creates
// a CopySpec instance
// from the closure.
// The copySpec method is part of the
// Project object.
CopySpec manualSpec = copySpec {
    from('src/manual') {
        include '**/*.html'
    }
}
// And the with method accepts
// the CopySpec we created.
website.with(manualSpec)

// Print each file path
// that is copied.
website.eachFile {
    println it.path
}

// New task of type Sync.
// The Sync task is also implementing
// the CopySpec interface.
// (Just like archive tasks: Zip, Tar, Jar)
task deploy(type: Sync) {
    destinationDir = file("${buildDir}/production")
    from website
}

// Use rename method from CopySpec.
deploy.rename { file ->
    if (file == 'index.html') {
        'main.html'
    } else {
        file
    }
}

// And finally the exclude method.
deploy.exclude 'man.html'

When we run the deploy task and look at the files in the build directory we see how our copy specifications are executed:

$ gradle deploy
:website
index.html
resources/app.js
resources/site.css
man.html
:deploy

BUILD SUCCESSFUL

Total time: 3.643 secs
$ tree build/
build
├── production
│   ├── main.html
│   └── resources
│       ├── app.js
│       └── site.css
└── website
    ├── index.html
    ├── man.html
    └── resources
        ├── app.js
        └── site.css

4 directories, 7 files

Written with Gradle 2.2.

Original blog post written on November 24, 2014.

Handle Copying Duplicate Files

In Gradle we can configure how duplicate files should be handled by the Copy task. Actually we can configure how duplicate files are handled by any task that implements the CopySpec interface. For example archive tasks also implements this interface. We must use the setDuplicatesStrategy method to configure how Gradle behaves. The parameter is a value of the enumeration DuplicatesStrategy. We can use the values from the enum class or use String values, which are automatically converted to enum DuplicatesStrategy values.

We can choose the following strategies:

  • include: default strategy where the last duplicate file ‘wins’.
  • exclude: only the first found duplicate file is copied and ‘wins’.
  • warn: shows a warning on the console, but the last duplicate file ‘wins’ like with the include strategy.
  • fail: the build fails where duplicate files are found.

The following build file create four task of type Copy, each with a different duplicate strategy. In the directories src/manual and src/website we have a file COPY.txt. The content is simply a text line respectively COPY from src/manual and COPY from src/website:

// For each duplicate strategy we create a copy task.
['warn', 'include', 'exclude', 'fail'].each { strategy ->
    task "copyDuplicatesStrategy${strategy.capitalize()}"(type: Copy) {
        from 'src/manual'
        from 'src/webroot'

        into "$buildDir/copy"

        // Only the value for this property differs for
        // each created task.
        duplicatesStrategy = strategy

        // Print the used duplicates strategy when
        // the task starts.
        doFirst {
            println "Copying with duplicates strategy '${strategy}'."
        }

        // Print the contents of the copied file COPY.txt.
        doLast {
            println "Contents of COPY.txt:"
            println file("$buildDir/copy/COPY.txt").text
        }
    }
}

We can now invoke the four tasks and see how Gradle reacts:

$ gradle copyDuplicatesStrategyWarn
:copyDuplicatesStrategyWarn
Copying with duplicates strategy 'warn'.
Encountered duplicate path "COPY.txt" during copy operation configured with DuplicatesStrategy.WA\
RN
Contents of COPY.txt:
COPY from src/webroot


BUILD SUCCESSFUL

Total time: 3.728 secs
$ gradle copyDuplicatesStrategyInclude
:copyDuplicatesStrategyInclude
Copying with duplicates strategy 'include'.
Contents of COPY.txt:
COPY from src/webroot


BUILD SUCCESSFUL

Total time: 2.744 secs
$ gradle copyDuplicatesStrategyExclude
:copyDuplicatesStrategyExclude
Copying with duplicates strategy 'exclude'.
Contents of COPY.txt:
COPY from src/manual


BUILD SUCCESSFUL

Total time: 2.784 secs
$ gradle copyDuplicatesStrategyFail
:copyDuplicatesStrategyFail
Copying with duplicates strategy 'fail'.
:copyDuplicatesStrategyFail FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':copyDuplicatesStrategyFail'.
> Encountered duplicate path "COPY.txt" during copy operation configured with DuplicatesStrategy.\
FAIL

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get mor\
e log output.

BUILD FAILED

Total time: 2.786 secs

Written with Gradle 2.3.

Original blog post written on April 22, 2015.

Working with Live Task Collection

Gradle support the definition of so called live collections. These collections are mostly created based on criteria like with a filter() or matching() method. The collection content can change if the content of the source collection changes. For example the org.gradle.api.DomainObjectCollection interface has a matching method that returns a live collection. The list of tasks in a project implements this interface so we can have a live collection of tasks that match a certain criteria.

The nice thing about the live collection of tasks is that we can add tasks to the project after we have defined the task collection and they will be included in the collection. Normally if we would access the list of tasks in a project and use for example a findAll method than the returned list of tasks will not change. So if we add a new task to the project it will not be added to the list of task that apply to the condition of the findAll method.

Now if we use the matching() method on a list of tasks the result will be a live list of tasks. Even if we add tasks to the project after the definition of the list of tasks, they will be added to the collection.

Let’s see the following Gradle build file that defines the tasks allCompile and allCopy. Each task has dependencies on other tasks. We use the dependsOn() method of a task to set those dependencies. The dependsOn() method accepts a collection of other tasks. For the allCompile task we don’t use a live collection and for the allCopy task we use a live collection.

task allCompile << {
    println 'All is compiled.'
}
allCompile.dependsOn project.tasks.findAll {
    it.name.startsWith('compile')
}

task allCopy << {
    println 'Everything is copied.'
}
// Set dependencies with live collection of tasks.
allCopy.dependsOn project.tasks.matching {
    it.name.startsWith('copy')
}

// Add dependency tasks.
5.times {
    // Dependencies for compileAll task.
    task "compile${it}" << { println 'Compile the code.' }

    // Dependencies for copyAll task.
    task "copy${it}" << { println 'Copy something.' }
}

If we run the build script we get the following output:

$ gradle allCompile allCopy
:allCompile
All is compiled.
:copy0
Copy something.
:copy1
Copy something.
:copy2
Copy something.
:copy3
Copy something.
:copy4
Copy something.
:allCopy
Everything is copied.

BUILD SUCCESSFUL

Total time: 2.232 secs

We notice the allCompile tasks doesn’t have any dependencies, because those task dependencies didn’t exist when we used the dependsOn() method. The allCopy task has all task dependencies even though we created them later in the build script.

Bonus: we can use the findAll method to look for task dependencies, but we have to let Gradle evaluate this condition in a closure. So we can change our build script and use a closure with the dependsOn() method. Gradle will invoke the closure at execution time and not a configuration time. The dependencies tasks are then available and assigned as dependencies to the allCompile task:

task allCompile << {
    println 'All is compiled.'
}
// Use closure for resolving task dependencies.
allCompile.dependsOn {
    project.tasks.findAll {
        it.name.startsWith('compile')
    }
}

task allCopy << {
    println 'Everything is copied.'
}
// Set dependencies with live collection of tasks.
allCopy.dependsOn project.tasks.matching {
    it.name.startsWith('copy')
}

// Add dependency tasks.
5.times {
    // Dependencies for compileAll task.
    task "compile${it}" << { println 'Compile the code.' }

    // Dependencies for copyAll task.
    task "copy${it}" << { println 'Copy something.' }
}

If we invoke both tasks with Gradle we get the following output:

:compile0
Compile the code.
:compile1
Compile the code.
:compile2
Compile the code.
:compile3
Compile the code.
:compile4
Compile the code.
:allCompile
All is compiled.
:copy0
Copy something.
:copy1
Copy something.
:copy2
Copy something.
:copy3
Copy something.
:copy4
Copy something.
:allCopy
Everything is copied.

BUILD SUCCESSFUL

Total time: 2.258 secs

This blog post is based on Gradle version 1.0-rc-3.

Original blog post written on June 11, 2012.

Unpacking an Archive

To create an archive with Gradle is easy. We have several tasks like Zip, Tar, Jar, War and Ear to create a new archive. But there is no UnZip or UnTar to unpack an archive in Gradle. To unpack an archive we must use the Copy task and copy the contents of the archive to a specified destination directory. In Gradle we can use the zipTree() method to access the contents of an archive. So in our copy definition the source is the contents of the archive we access with the zipTree() method.

In the following build file we see a simple task to unzip a ZIP file with the name dist.zip in the directory src/dists. We unpack the contents to the directory build/unpacked/dist:

task unzip(type: Copy) {
    def zipFile = file('src/dists/dist.zip')
    def outputDir = file("${buildDir}/unpacked/dist")

    from zipTree(zipFile)
    into outputDir
}

The good thing is that tasks of type Copy automatically support Gradle’s incremental build support. This means that if the task has been executed once and the dist.zip file and output in the directory build/unpacked/dist has not change the task is up-to-date and isn’t executed.

We get the following output if we run the task twice:

$ gradle unzip
:unzip

BUILD SUCCESSFUL

Total time: 2.867 secs
unzip mrhaki$ gradle unzip
:unzip UP-TO-DATE

BUILD SUCCESSFUL

Total time: 2.606 secs
$

Written with Gradle 1.0.

Original blog post written on June 20, 2012.

Create Checksums for Archives

If we want to create a checksum for a file in Gradle we can use the ANT checksum task. We assign the file name to the file property of the task. Then the checksum task will generate a file with the checksum in the same directory.

Suppose we have a Java project and want to generate a checksum for the JAR file that is generated with the jar task. We can add an extra action to the jar task with the doLast() method. We pass a closure to this method and in the closure we define the action to create a checksum. The file name of the JAR file is accessible via the archivePath property:

apply plugin: 'java'

archivesBaseName = 'checksum-sample'

jar.doLast { task ->
    ant.checksum file: task.archivePath
}

When we invoke the jar task we get an extra file in the build/libs directory with the name checksum-sample.jar.MD5. This file contains the checksum for the file checksum-sample.jar.

We can make sure in our Gradle build file that all archive tasks (like Zip, Jar, War) generate a checksum file for the archive file of that task. In the following build file we use the withType() method in the afterEvaluate() method to add the checksum task to all tasks of type Zip. This means also task types that extend the Zip task, like Jar, War and Ear, will get the extra action. We also create extra archives in our project to show we don’t have to configure them explicitly with the checksum task, but that Gradle will add the action automatically.

apply plugin: 'java'

archivesBaseName = 'checksum-sample'

task sourceJar(type: Jar) {
    from sourceSets.main.allSource
    classifier = 'sources'
}

task javadocJar(type: Jar, dependsOn: 'javadoc') {
    from javadoc.destinationDir
    classifier = 'javadoc'
}

artifacts {
    archives sourceJar, javadocJar
}

tasks.withType(Zip) { task ->
    task.doLast {
        ant.checksum file: it.archivePath
    }
}

When we invoke the assemble task and look at the file that are generated we get the following command-line output:

$ gradle assemble
:compileJava
:processResources UP-TO-DATE
:classes
:jar
:javadoc
:javadocJar
:sourceJar
:assemble

BUILD SUCCESSFUL

Total time: 4.161 secs
$ ls -1 build/libs
checksum-sample-javadoc.jar
checksum-sample-javadoc.jar.MD5
checksum-sample-sources.jar
checksum-sample-sources.jar.MD5
checksum-sample.jar
checksum-sample.jar.MD5

Written with Gradle 1.0.

Original blog post written on June 27, 2012.

Specify Spock As Test Framework At Initialization

Since Gradle 2.11 we can specify the test framework to use when we initialise a project with the init task. There is a new option for this task: --test-framework. By default JUnit dependencies are added, but if we specify the value spock the Spock libraries are included in the dependencies section of our build.gradle file.

Let’s run the init task to create a Java project with Spock as test framework:

$ gradle init --type java-library --test-framework spock

Let’s see the contents of the build.gradle file that is generated:

/*
 * This build file was auto generated by running the Gradle 'init' task
 * by 'mrhaki' at '2/12/16 8:54 AM' with Gradle 2.11
 *
 * This generated file contains a sample Java project to get you started.
 * For more details take a look at the Java Quickstart chapter in the Gradle
 * user guide available at https://docs.gradle.org/2.11/userguide/tutorial_java_projects.html
 */

// Apply the java plugin to add support for Java
apply plugin: 'java'

// Apply the groovy plugin to also add support for Groovy (needed for Spock)
apply plugin: 'groovy'

// In this section you declare where to find the dependencies of your project
repositories {
    // Use 'jcenter' for resolving your dependencies.
    // You can declare any Maven/Ivy/file repository here.
    jcenter()
}

// In this section you declare the dependencies for your production and test code
dependencies {
    // The production code uses the SLF4J logging API at compile time
    compile 'org.slf4j:slf4j-api:1.7.14'

    // We use the latest groovy 2.x version for Spock testing
    compile 'org.codehaus.groovy:groovy-all:2.4.5'

    // Use the awesome Spock testing and specification framework even with Java
    testCompile 'org.spockframework:spock-core:1.0-groovy-2.4'
    testCompile 'junit:junit:4.12'
}

Notice in the dependencies section that the Groovy and Spock dependencies are added.

Written with Gradle 2.11.

Original blog post written on February 12, 2016.

Inter-Project Artifact Dependencies

When we define dependencies in Gradle we usually define them to compile source files. For example we create a Java project and need the Spring framework, then we define a dependencies configuration block with a dependency to the Spring libraries for the compile configuration. We can also use the dependencies configuration block to define dependencies on artifacts used in other tasks than the compiling source files. For example we might have a multi-module project where we want to aggregate artifacts from several projects into one place with the Copy task.

In the following example we have a multi-module project where projects projectA and projectB have a dist task to create a Tar file. In the project docker we want to copy the Tar files from those projects into the build directory. To achieve this we first apply the Gradle base plugin, because then we can use the artifacts configuration block. Inside the artifacts configuration we define that our dist task that creates the Tar file is assigned to the dockerDist configuration as artifact. This sets up our projects that create the artifact. The docker project uses these artifacts in the task prepare. The prepare task copies the artifacts of projectA and projectB to the build directory of the docker project.

configure(
    subprojects
        .findAll {
            it.name.startsWith('project') }) {

    // We need to base plugin for artifacts support.
    apply plugin: 'base'

    // Archive task where the archive file
    // is the artifact for our project we want
    // to use in aonther project.
    task dist(type: Tar) {
        compression 'gzip'
        from 'src/data'
    }

    configurations {
        // New configuration to associate
        // dist task output with.
        dockerDist
    }

    artifacts {
        // Declare output of dist task
        // as artifact for the dockerDist
        // configuration.
        dockerDist dist
    }
}

// Project with dependencies on the artifacts
// of projectA and projectB.
project('docker') {

    configurations {
        // Configuration needed to use
        // in the dependencies.
        dockerSource
    }

    dependencies {
        // Define that we depend on the artifacts
        // of projectA and projectB assigned to the
        // dockerDist configuration.
        dockerSource project(path: ':projectA', configuration: 'dockerDist')
        dockerSource project(path: ':projectB', configuration: 'dockerDist')
    }

    task prepare(type: Copy) {
        into "$buildDir/docker"

        // Use the artifacts from the other projects.
        // Gradle knows that tasks that created the artifacts
        // and will invoke them for us.
        from configurations.dockerSource
    }

}

Now let’s run the :docker:prepare task from the command line:

$ gradle :docker:prepare

:projectA:dist
:projectB:dist
:docker:prepare

BUILD SUCCESSFUL

Total time: 0.796 secs
$ tree docker/build/
docker/build
└── docker
    ├── projectA.tgz
    └── projectB.tgz

1 directory, 2 files
$

Notice we didn’t define a task dependency on the dist tasks, but Gradle knows by using the dependencies configuration which tasks need to be executed to fulfill all dependencies for the prepare task.

Written with Gradle 2.11.

Original blog post written on February 29, 2016.

Java and Groovy

Set Java Version Compatibility

We can use the properties sourceCompatibility and targetCompatibility provided by the Java plugin to define the Java version compatibility for compiling sources. The value of these properties is a JavaVersion enum constant, a String value or a Number. If the value is a String or Number we can even leave out the 1. portion for Java 1.5 and 1.6. So we can just use 5 or ‘6’ for example.

We can even use our own custom classes as long as we override the toString() method and return a String value that is valid as a Java version.

apply plugin: 'java'

sourceCompatibility = 1.6 // or '1.6', '6', 6,
                          // JavaVersion.VERSION_1_6,
                          // new Compatibility('Java 6')

class Compatibility {
    String version

    Compatibility(String versionValue) {
        def matcher = (versionValue =~ /Java (\d)/)
        version = matcher[0][1]
    }

    String toString() { version }
}

Written with Gradle 0.9-rc-2.

Original blog post written on November 09, 2010.

Set Java Compiler Encoding

If we want to set an explicit encoding for the Java compiler in Gradle we can use the options.encoding property. For example we could add the following line to our Gradle build file to change the encoding for the compileJava task:

apply plugin: 'java'

compileJava.options.encoding = 'UTF-8'

To set the encoding property on all compile tasks in our project we can use the withType() method on the TaskContainer to find all tasks of type Compile. Then we can set the encoding in the configuration closure:

apply plugin: 'java'

tasks.withType(Compile) {
    options.encoding = 'UTF-8'
}

Written with Gradle 1.0.

Original blog post written on June 18, 2012.

Using Gradle for a Mixed Java and Groovy Project

Gradle is a build system to build software projects. Gradle supports convention over configuration, build-in takss and dependency support. We write a build script in Groovy (!) to define our Gradle build. This means we can use all available Groovy (and Java) stuff we want, like control structures, classes and methods. In this post we see how we can use Gradle to build a very simple mixed Java and Groovy project.

To get started we must first have installed Gradle on our computers. We can read the manual to see how to do that. To check Gradle is installed correctly and we can run build script we type $ gradle -v at our shell prompt and we must get a result with the versions of Java, Groovy, operating system, Ivy and more.

It is time to create our Groovy/Java project. We create a new directory mixed-project:

$ mkdir mixed-project
$ cd mixed-project
$ touch build.gradle

Gradle uses plugins to define taks and conventions for certain type of projects. The plugins are distributed with Gradle and not (yet) downloaded from the internet. One of the plugins is the Groovy plugin. This plugin is extended from the Java plugin, so if we use the Groovy plugin we also have all functionality of the Java plugin. And that is exactly what we need for our project. The plugin provides a set of tasks like compile, build, assemble, clean and a directory structure convention. The plugin assumes we save our source files in src/main/java and src/main/groovy for example. The structure is similar to Maven conventions. As a matter of fact Gradle also has a Maven plugin that add Maven tasks like build, install to our build. For now we just need the Groovy plugin, so we open the file build.gradle in a text editor and add the following line:

apply plugin: 'groovy'

To see the lists of tasks we can now execute by just including this one line we return to our shell and type $ gradle tasks and we get the following list of tasks:

: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 classes 'main'.
clean - Deletes the build directory.
jar - Assembles a jar archive containing the main classes.
testClasses - Assembles classes 'test'.

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

Documentation tasks
-------------------
groovydoc - Generates Groovydoc API documentation for the main source code.
javadoc - Generates Javadoc API documentation for the main source code.

Help tasks
----------
components - Displays the components produced by root project 'mixed-project'. [incubating]
dependencies - Displays all dependencies declared in root project 'mixed-project'.
dependencyInsight - Displays the insight into a specific dependency in root project 'mixed-projec\
t'.
help - Displays a help message.
projects - Displays the sub-projects of root project 'mixed-project'.
properties - Displays the properties of root project 'mixed-project'.
tasks - Displays the tasks runnable from root project 'mixed-project'.

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 configurat\
ion.

To see all tasks and more detail, run with --all.

BUILD SUCCESSFUL

We don’t have any code yet in our project so we don’t have any task to run right now, but it is good to know all these tasks can be executed once we have our code. Okay, we have to create our directory structure according to the conventions to make it all work without to much configuration. We can do this all by hand but we can also use a trick described in the Gradle cookbook. We add a new task to our build.gradle file to create all necessary directories for us.

task initProject(description: 'Initialize project directory structure.') << {
    // Default package to be created in each src dir.
    def defaultPackage = 'com/mrhaki/blog'

    ['java', 'groovy', 'resources'].each {
        // convention.sourceSets contains the directory structure
        // for our Groovy project. So we use this struture
        // and make a directory for each node.
        sourceSets*."${it}".srcDirs*.each { dir ->
            def newDir = new File(dir, defaultPackage)
            logger.info "Creating directory $newDir"  // gradle -i shows this message.
            newDir.mkdirs()  // Create dir.
        }
    }
}

At the command prompt we type $ gradle initProject and the complete source directory struture is now created. Let’s add some Java and Groovy source files to our project. We keep it very simple, because this post is about Gradle and not so much about Java and Groovy. We create a Java interface in src/main/java/com/mrhaki/blog/GreetingService.java:

package com.mrhaki.blog;

public interface GreetingService {
    String greet(final String name);
}

We provide a Java implementation for this interface in src/main/java/com/mrhaki/blog/JavaGreetingImpl.java:

package com.mrhaki.blog;

public class JavaGreetingImpl implements GreetingService {
    public String greet(final String name) {
        return "Hello " + (name != null ? name : "stranger") + ". Greeting from Java.";
    }
}

And a Groovy implementation in src/main/groovy/com/mrhaki/blog/GroovyGreetingImpl.groovy:

package com.mrhaki.blog

class GroovyGreetingImpl implements GreetingService {
    String greet(String name) {
        "Hello ${name ?: 'stranger'}. Greeting from Groovy"
    }
}

We have learned Gradle uses Groovy to define and execute the build script. But this bundled Groovy is not available for our project. We can choose which version of Groovy we want and don’t have to rely on the version that is shipped with Gradle. We must define a dependency to the Groovy library version we want to use in our project in build.gradle. So we must add the following lines to the build.gradle file:

repositories {
    // Define Maven central repository to look for dependencies.
    mavenCentral()
}

dependencies {
    // group:name:version is a nice shortcut notation for dependencies.
    compile 'org.codehaus.groovy:groovy:2.3.9'
}

In our shell we type $ gradle compileJava compileGroovy to compile the source files we just created. If we didn’t make any typos we should see the message BUILD SUCCESSFUL at the command prompt. Let’s add some test classes to our project to test our simple implementations. We create src/test/java/com/mrhaki/blog/JavaGreetingTest.java:

package com.mrhaki.blog;

import static org.junit.Assert.*;
import org.junit.Test;

public class JavaGreetingTest {
    final GreetingService service = new JavaGreetingImpl();

    @Test public void testGreet() {
        assertEquals("Hello mrhaki. Greeting from Java.", service.greet("mrhaki"));
    }

    @Test public void testGreetNull() {
        assertEquals("Hello stranger. Greeting from Java.", service.greet(null));
    }
}

And we create a Groovy test class in src/test/groovy/com/mrhaki/blog/GroovyGreetingTest.groovy:

package com.mrhaki.blog

import static org.junit.Assert.*
import org.junit.Test

public class JavaGreetingTest {
    final GreetingService service = new JavaGreetingImpl()

    @Test public void testGreet() {
        assertEquals("Hello mrhaki. Greeting from Java.", service.greet("mrhaki"))
    }

    @Test public void testGreetNull() {
        assertEquals("Hello stranger. Greeting from Java.", service.greet(null))
    }
}

We add a dependency to build.gradle for JUnit:

dependencies {
    groovy 'org.codehaus.groovy:groovy:2.3.9'  // group:name:version is a nice shortcut notation \
for dependencies.
    testCompile 'junit:junit:4.11'
}

We return to the command prompt and type $ gradle test. Gradle compiles the code and runs the JUnit tests. The results of the test are stored in build/reports/tests. We can see the results in a web browser if we open index.html:

Let’s leave the coding part for now. It it time to package our code. We can use $ gradle build to create a JAR file with the compiled classes from the src/main directories. But first we make a change to build.gradle to include a version number. If we run $ gradle -r we get an overview of all properties for our project. Among them is the version property. We can set a value for the version property in the build.gradle file. We also set the basename for the archive:

// The script is all Groovy, so we make use of all methods and features.
version = "1.0-${new Date().format('yyyyMMdd')"
archivesBaseName = 'greeting'

We return to the command prompt and type $ gradle build. Gradle runs and if all is successful we see in build/libs the file greeting-1.0-20150105.jar. Here is the complete build.gradle with all changes we made:

apply plugin: 'groovy'

version = "1.0-${new Date().format('yyyyMMdd')}" // The script is all Groovy, so we make use of a\
ll methods and features.
archivesBaseName = 'greeting'

repositories {
    mavenCentral()  // Define Maven central repository to look for dependencies.
}

dependencies {
    compile 'org.codehaus.groovy:groovy:2.3.9'  // group:name:version is a nice shortcut notation\
 for dependencies.
    testCompile 'junit:junit:4.11'
}

task initProject(description: 'Initialize project directory structure.') << {
    // Default package to be created in each src dir.
    def defaultPackage = 'com/mrhaki/blog'

    ['java', 'groovy', 'resources'].each {
        // convention.sourceSets contains the directory structure
        // for our Groovy project. So we use this struture
        // and make a directory for each node.
        sourceSets*."${it}".srcDirs.each { dirs ->
            dirs.each { dir ->
                def newDir = new File(dir, defaultPackage)
                logger.info "Creating directory $newDir"  // gradle -i shows this message.
                newDir.mkdirs()  // Create dir.
            }
        }
    }
}

We learned how we can start a new project from scratch and with little coding get a compiled and tested archive wth our code. In future blog posts we learn more about Gradle, for example the multi-project support.

Original written with Gradle 0.8 and re-written with Gradle 2.2.1.

Original blog post written on November 07, 2009.

A Groovy Multi-project with Gradle

Gradle is a flexible build system that uses Groovy for build scripts. In this post we create a very simple application demonstrating a multi-project build. We create a Groovy web application with very simple domain, dataaccess, services and web projects. The sample is not to demonstrate Groovy but to show the multi-project build support in Gradle.

We start by creating a new application directory app and create two files settings.gradle and build.gradle:

$ mkdir app
$ cd app
$ touch settings.gradle
$ touch build.gradle

We open the file settings.gradle in a text editor. With the include method we define the subprojects for the application:

include 'domain', 'dataaccess', 'services', 'web'

Next we open build.gradle in the text editor. This build file is our main build file for the application. We can define all settings for the subprojects in this file:

subprojects {
    apply plugin: 'groovy'
    version = '1.0.0-SNAPSHOT'
    group = 'com.mrhaki.blog'

    // Make sure transitive project dependencies are resolved.
    configurations.compile.transitive = true

    repositories {
        mavenCentral()
    }

    dependencies {
        coimpile 'org.codehaus.groovy:groovy:2.3.9'
    }

    task initProject(description: 'Initialize project') << { task ->
        task.project.sourceSets*.groovy.srcDirs*.each {
            println "Create $it"
            it.mkdirs()
        }
    }
}

project(':dataaccess') {
    dependencies {
        compile project(':domain')
    }
}

project(':services') {
    dependencies {
        compile project(':dataaccess')
    }
}

project(':web') {
    // jetty plugin extends war plugin,
    // so we get all war plugin functionality as well.
    apply plugin: 'jetty'

    dependencies {
        compile project(':services')
        // Because configurations.compile.transitive = true
        // we only have to specify services project,
        // although we also reference dataaccess and domain projects.
    }

    // Add extra code to initProject task.
    initProject << { task ->
        def webInfDir = new File(task.project.webAppDir, '/WEB-INF')
        println "Create $webInfDir"
        webInfDir.mkdirs()
    }
}

The subprojects method accepts a closure and here we define common settings for all subprojects. The project method allows us to fine tune the definiton of a subproject. For each project we define project dependencies between the different projects for the compile configuration. This is a very powerful feature of Gradle, we define the project dependency and Gradle will make sure the dependent project is first build before the project that needs it. This even works if we invoke a build command from a subproject. For example if we run gradle build from the web project, all dependent projects are build first.

We also create a new task initProject for all subprojects. This task creates the Groovy source directories. In the web project we add an extra statement to the task to create the src/main/webapp/WEB-INF directory. This shows we can change a task definition in a specific subproject.

Okay it is time to let Gradle create our directories: $ gradle initProject. After the script is finished we have a new directory structure:

It is time to add some files to the different projects. As promised we keep it very, very simple. We define a domain class Language, a class in dataaccess to get a list of Language objects, a services class to filter out the Groovy language and a web component to get the name property for the Language object and a Groovlet to show it in the web browser. Finally we add a web.xml so we can execute the Groovlet.

// File: app/domain/src/main/groovy/com/mrhaki/blog/domain/Language.groovy
package com.mrhaki.blog.domain

class Language {
    String name
}
    // File: app/dataaccess/src/main/groovy/com/mrhaki/blog/data/LanguageDao.groovy
package com.mrhaki.blog.data

import com.mrhaki.blog.domain.Language

class LanguageDao {
    List findAll() {
        [new Language(name: 'Java'), new Language(name: 'Groovy'), new Language(name: 'Scala')]
    }
}
// File: app/services/src/main/groovy/com/mrhaki/blog/service/LanguageService.groovy
package com.mrhaki.blog.service

import com.mrhaki.blog.domain.Language
import com.mrhaki.blog.data.LanguageDao

class LanguageService {
    def dao = new LanguageDao()

    Language findGroovy() {
       dao.findAll().find { it.name == 'Groovy' }
    }
}
// File: app/web/src/main/groovy/com/mrhaki/blog/web/LanguageHelper.groovy
package com.mrhaki.blog.web

import com.mrhaki.blog.service.LanguageService

class LanguageHelper {
    def service = new LanguageService()

    String getGroovyValue() {
        service.findGroovy()?.name ?: 'Groovy language not found'
    }
}
// File: app/web/src/main/webapp/language.groovy
import com.mrhaki.blog.web.LanguageHelper

def helper = new LanguageHelper()

html.html {
    head {
        title "Simple page"
    }
    body {
        h1 "Simple page"
        p "My favorite language is '$helper.groovyValue'."
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<!-- File: app/web/src/main/webapp/WEB-INF/web.xml -->
<web-app>
    <servlet>
        <servlet-name>Groovy</servlet-name>
        <servlet-class>groovy.servlet.GroovyServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>Groovy</servlet-name>
        <url-pattern>*.groovy</url-pattern>
    </servlet-mapping>
</web-app>

We have created all the files and it is time to see the result. Thanks to the Jetty plugin we only have to invoke the jettyRun tasks and all files (and dependent projects) are compiled and processed:

$ cd web
$ gradle jettyRun
:domain:compileJava
:domain:compileGroovy
:domain:processResources
:domain:classes
:domain:jar
:domain:uploadDefaultInternal
:dataaccess:compileJava
:dataaccess:compileGroovy
:dataaccess:processResources
:dataaccess:classes
:dataaccess:jar
:dataaccess:uploadDefaultInternal
:services:compileJava
:services:compileGroovy
:services:processResources
:services:classes
:services:jar
:services:uploadDefaultInternal
:web:compileJava
:web:compileGroovy
:web:processResources
:web:classes
:web:jettyRun

We open a web browser and go to http://localhost:8080/web/language.groovy and get a simple web page with the results of all our labour:

This concludes this blog about the multi-project support of Gradle. What we need to remember is Gradle is great in resolving dependencies between projects. If one project dependents on another we don’t have to worry about first compiling the dependent project, Gradle does this for us. We can define tasks for each project, but still fine tune a task for a specific project. Also we have a certain freedom about the project structure, as long as we define the needed projects in the settings.gradle all will be fine. Also we only need one build.gradle (but can be more per project if we want) to configure all projects.

Original blog post written with Gradle 0.8 and re-written with Gradle 2.2.1.

Original blog post written on November 12, 2009.

Shortcut Notation for Dependencies

In a Gradle build script we define dependencies with the dependencies method. We can use a map notation or the short string notation. The string notation is the following format: group:name:version.

dependencies {
    compile group: 'commons-lang', name: 'commons-lang', version: '2.4'  // Map notation.
    compile(
        [group: 'commons-lang', name: 'commons-lang', version: '2.4'],
        [group: 'commons-io', name: 'commons-io', version: '1.4']
    )  // Multiple map notation.
    compile 'commons-lang:commons-lang:2.4', 'commons-io:commons-io:1.4'  // String notation.
}

Written with Gradle 0.8.

Original blog post written on November 17, 2009.

Display Dependencies for a Gradle Build

We can see the dependencies defined in our project by using the dependencies tasks. We get an overview of the dependencies for each configuration in our Gradle build.

// File: build.gradle
apply plugin: 'java'

repositories {
    mavenCentral()
}

dependencies {
    compile 'org.springframework:spring-webmvc:2.5.6'
}
$ gradle dependencies
:dependencies

------------------------------------------------------------
Root project
------------------------------------------------------------

archives - Configuration for archive artifacts.
No dependencies

compile - Compile classpath for source set 'main'.
\--- org.springframework:spring-webmvc:2.5.6
     +--- commons-logging:commons-logging:1.1.1
     +--- org.springframework:spring-beans:2.5.6
     |    +--- commons-logging:commons-logging:1.1.1
     |    \--- org.springframework:spring-core:2.5.6
     |         \--- commons-logging:commons-logging:1.1.1
     +--- org.springframework:spring-context:2.5.6
     |    +--- aopalliance:aopalliance:1.0
     |    +--- commons-logging:commons-logging:1.1.1
     |    +--- org.springframework:spring-beans:2.5.6 (*)
     |    \--- org.springframework:spring-core:2.5.6 (*)
     +--- org.springframework:spring-context-support:2.5.6
     |    +--- aopalliance:aopalliance:1.0
     |    +--- commons-logging:commons-logging:1.1.1
     |    +--- org.springframework:spring-beans:2.5.6 (*)
     |    +--- org.springframework:spring-context:2.5.6 (*)
     |    \--- org.springframework:spring-core:2.5.6 (*)
     +--- org.springframework:spring-core:2.5.6 (*)
     \--- org.springframework:spring-web:2.5.6
          +--- commons-logging:commons-logging:1.1.1
          +--- org.springframework:spring-beans:2.5.6 (*)
          +--- org.springframework:spring-context:2.5.6 (*)
          \--- org.springframework:spring-core:2.5.6 (*)

default - Configuration for default artifacts.
\--- org.springframework:spring-webmvc:2.5.6
     +--- commons-logging:commons-logging:1.1.1
     +--- org.springframework:spring-beans:2.5.6
     |    +--- commons-logging:commons-logging:1.1.1
     |    \--- org.springframework:spring-core:2.5.6
     |         \--- commons-logging:commons-logging:1.1.1
     +--- org.springframework:spring-context:2.5.6
     |    +--- aopalliance:aopalliance:1.0
     |    +--- commons-logging:commons-logging:1.1.1
     |    +--- org.springframework:spring-beans:2.5.6 (*)
     |    \--- org.springframework:spring-core:2.5.6 (*)
     +--- org.springframework:spring-context-support:2.5.6
     |    +--- aopalliance:aopalliance:1.0
     |    +--- commons-logging:commons-logging:1.1.1
     |    +--- org.springframework:spring-beans:2.5.6 (*)
     |    +--- org.springframework:spring-context:2.5.6 (*)
     |    \--- org.springframework:spring-core:2.5.6 (*)
     +--- org.springframework:spring-core:2.5.6 (*)
     \--- org.springframework:spring-web:2.5.6
          +--- commons-logging:commons-logging:1.1.1
          +--- org.springframework:spring-beans:2.5.6 (*)
          +--- org.springframework:spring-context:2.5.6 (*)
          \--- org.springframework:spring-core:2.5.6 (*)

runtime - Runtime classpath for source set 'main'.
\--- org.springframework:spring-webmvc:2.5.6
     +--- commons-logging:commons-logging:1.1.1
     +--- org.springframework:spring-beans:2.5.6
     |    +--- commons-logging:commons-logging:1.1.1
     |    \--- org.springframework:spring-core:2.5.6
     |         \--- commons-logging:commons-logging:1.1.1
     +--- org.springframework:spring-context:2.5.6
     |    +--- aopalliance:aopalliance:1.0
     |    +--- commons-logging:commons-logging:1.1.1
     |    +--- org.springframework:spring-beans:2.5.6 (*)
     |    \--- org.springframework:spring-core:2.5.6 (*)
     +--- org.springframework:spring-context-support:2.5.6
     |    +--- aopalliance:aopalliance:1.0
     |    +--- commons-logging:commons-logging:1.1.1
     |    +--- org.springframework:spring-beans:2.5.6 (*)
     |    +--- org.springframework:spring-context:2.5.6 (*)
     |    \--- org.springframework:spring-core:2.5.6 (*)
     +--- org.springframework:spring-core:2.5.6 (*)
     \--- org.springframework:spring-web:2.5.6
          +--- commons-logging:commons-logging:1.1.1
          +--- org.springframework:spring-beans:2.5.6 (*)
          +--- org.springframework:spring-context:2.5.6 (*)
          \--- org.springframework:spring-core:2.5.6 (*)

testCompile - Compile classpath for source set 'test'.
\--- org.springframework:spring-webmvc:2.5.6
     +--- commons-logging:commons-logging:1.1.1
     +--- org.springframework:spring-beans:2.5.6
     |    +--- commons-logging:commons-logging:1.1.1
     |    \--- org.springframework:spring-core:2.5.6
     |         \--- commons-logging:commons-logging:1.1.1
     +--- org.springframework:spring-context:2.5.6
     |    +--- aopalliance:aopalliance:1.0
     |    +--- commons-logging:commons-logging:1.1.1
     |    +--- org.springframework:spring-beans:2.5.6 (*)
     |    \--- org.springframework:spring-core:2.5.6 (*)
     +--- org.springframework:spring-context-support:2.5.6
     |    +--- aopalliance:aopalliance:1.0
     |    +--- commons-logging:commons-logging:1.1.1
     |    +--- org.springframework:spring-beans:2.5.6 (*)
     |    +--- org.springframework:spring-context:2.5.6 (*)
     |    \--- org.springframework:spring-core:2.5.6 (*)
     +--- org.springframework:spring-core:2.5.6 (*)
     \--- org.springframework:spring-web:2.5.6
          +--- commons-logging:commons-logging:1.1.1
          +--- org.springframework:spring-beans:2.5.6 (*)
          +--- org.springframework:spring-context:2.5.6 (*)
          \--- org.springframework:spring-core:2.5.6 (*)

testRuntime - Runtime classpath for source set 'test'.
\--- org.springframework:spring-webmvc:2.5.6
     +--- commons-logging:commons-logging:1.1.1
     +--- org.springframework:spring-beans:2.5.6
     |    +--- commons-logging:commons-logging:1.1.1
     |    \--- org.springframework:spring-core:2.5.6
     |         \--- commons-logging:commons-logging:1.1.1
     +--- org.springframework:spring-context:2.5.6
     |    +--- aopalliance:aopalliance:1.0
     |    +--- commons-logging:commons-logging:1.1.1
     |    +--- org.springframework:spring-beans:2.5.6 (*)
     |    \--- org.springframework:spring-core:2.5.6 (*)
     +--- org.springframework:spring-context-support:2.5.6
     |    +--- aopalliance:aopalliance:1.0
     |    +--- commons-logging:commons-logging:1.1.1
     |    +--- org.springframework:spring-beans:2.5.6 (*)
     |    +--- org.springframework:spring-context:2.5.6 (*)
     |    \--- org.springframework:spring-core:2.5.6 (*)
     +--- org.springframework:spring-core:2.5.6 (*)
     \--- org.springframework:spring-web:2.5.6
          +--- commons-logging:commons-logging:1.1.1
          +--- org.springframework:spring-beans:2.5.6 (*)
          +--- org.springframework:spring-context:2.5.6 (*)
          \--- org.springframework:spring-core:2.5.6 (*)

(*) - dependencies omitted (listed previously)

BUILD SUCCESSFUL

Total time: 3.669 secs
$

Another way is to add the plugin project-report to our project. We now get the the task dependencyReport in our project. When we run $ gradle dependencyReport we get a text file build/reports/project/dependencies.txt with the same overview of the dependencies.

Written with Gradle 0.8 and re-written with Gradle 2.2.1.

Original blog post written on November 21, 2009.

Getting More Dependency Insight

In most of our projects we have dependencies on other code, like libraries or other projects. Gradle has a nice DSL to define dependencies. Dependencies are grouped in dependency configurations. These configuration can be created by ourselves or added via a plugin. Once we have defined our dependencies we get a nice overview of all dependencies in our project with the dependencies task. We can add the optional argument --configuration to only see dependencies for the given configuration. But we can even check for a specific dependency where it is used, any transitive dependencies and how the version is resolved.

In the following sample build we define a compile dependency on Spring Boot and SLF4J API. The SLF4J API is also a transitive dependency for the Spring Boot dependency, so we can see how the dependencyInsight tasks shows a version conflict.

apply plugin: 'java'

// Set Bintray JCenter as repository.
repositories.jcenter()

dependencies {
    // Set dependency for Spring Boot
    compile "org.springframework.boot:spring-boot-starter-web:1.1.5.RELEASE"

    // Set dependency for SLF4J with conflicting version.
    compile 'org.slf4j:slf4j-api:1.7.1'
}

Now let’s run the dependencyInsight task for the dependency SLF4J API in the compile configuration:

$ gradle -q dependencyInsight --configuration compile --dependency slf4j-api
org.slf4j:slf4j-api:1.7.7 (conflict resolution)
+--- org.slf4j:jcl-over-slf4j:1.7.7
|    \--- org.springframework.boot:spring-boot-starter-logging:1.1.5.RELEASE
|         \--- org.springframework.boot:spring-boot-starter:1.1.5.RELEASE
|              \--- org.springframework.boot:spring-boot-starter-web:1.1.5.RELEASE
|                   \--- compile
+--- org.slf4j:jul-to-slf4j:1.7.7
|    \--- org.springframework.boot:spring-boot-starter-logging:1.1.5.RELEASE (*)
\--- org.slf4j:log4j-over-slf4j:1.7.7
     \--- org.springframework.boot:spring-boot-starter-logging:1.1.5.RELEASE (*)

org.slf4j:slf4j-api:1.7.1 -> 1.7.7
\--- compile

org.slf4j:slf4j-api:1.7.6 -> 1.7.7
\--- ch.qos.logback:logback-classic:1.1.2
     \--- org.springframework.boot:spring-boot-starter-logging:1.1.5.RELEASE
          \--- org.springframework.boot:spring-boot-starter:1.1.5.RELEASE
               \--- org.springframework.boot:spring-boot-starter-web:1.1.5.RELEASE
                    \--- compile

(*) - dependencies omitted (listed previously)

In the output we can see slf4j-api is referenced three times, once as a transitive dependency for jcl-over-slf4j, jul-to-slf4j and log4j-over-slf4j, once as transitive dependency for logback-classic and once as a direct dependency for the compile configuration. We also see the version is bumped to 1.7.7 where necessary, because the transitive dependency of jcl-over-slf4j defines the newest version.

The value we use for the --dependency option is used to do partial matching in the group, name or version properties of the dependencies. For example to see an insight in all dependencies with logging we can invoke $ gradle dependencyInsight --dependency logging.

We can also get an HTML report page with an overview of all dependencies. To get dependency insight we must click on the desired dependency from the HTML page and we get a similar output as via the command-line. First we must add the project-report plugin to our project. Next we invoke the dependencyReport task. When the task is finished we can open build/reports/project/dependencies/index.html in our web browser. When we navigate to the compile configuration and click on the slf4j-api dependency we get the following output:

Written with Gradle 2.0.

Original blog post written on August 11, 2014.

Exclude Transitive Dependency from All Configurations

We can exclude transitive dependencies easily from specific configurations. To exclude them from all configurations we can use Groovy’s spread-dot operator and invoke the exclude() method on each configuration. We can only define the group, module or both as arguments for the exclude() method.

The following part of a build file shows how we can exclude a dependency from all configurations:

...
configurations {
    all*.exclude group: 'xml-apis', module: 'xmlParserAPIs'
}

// Equivalent to:
configurations {
    all.collect { configuration ->
        configuration.exclude group: 'xml-apis', module: 'xmlParserAPIs'
    }
}
...

Written with Gradle 1.2.

Original blog post written on October 23, 2012.

Adding Dependencies Only for Packaging to War

My colleague, Tom Wetjens, wrote a blog post Package-only dependencies in Maven. He showed a Maven solution when we want to include dependencies in the WAR file, which are not used in any other scopes. In this blog post we will see how we solve this in Gradle.

Suppose we use the SLF4J Logging API in our project. We use the API as a compile dependency, because our code uses this API. But in our test runtime we want to use the SLF4J Simple implementation of this API. And in our WAR file we want to include the Logback implementation of the API. The Logback dependency is only needed to be included in the WAR file and shouldn’t exist in any other dependency configuration.

We first add the War plugin to our project. The war task uses the runtime dependency configuration to determine which files are added to the WEB-INF/lib directory in our WAR file. We add a new dependency configuration warLib that extends the runtime configuration in our project.

apply plugin: 'war'

repositories.jcenter()

configurations {
    // Create new dependency configuration
    // for dependencies to be added in
    // WAR file.
    warLib.extendsFrom runtime
}

dependencies {
    // API dependency for Slf4j.
    compile 'org.slf4j:slf4j-api:1.7.7'

    testCompile 'junit:junit:4.11'

    // Slf4j implementation used for tests.
    testRuntime 'org.slf4j:slf4j-simple:1.7.7'

    // Slf4j implementation to be packaged
    // in WAR file.
    warLib 'ch.qos.logback:logback-classic:1.1.2'
}

war {
    // Add warLib dependency configuration
    classpath configurations.warLib

    // We remove all duplicate files
    // with this assignment.
    // geFiles() method return a unique
    // set of File objects, removing
    // any duplicates from configurations
    // added by classpath() method.
    classpath = classpath.files
}

We can now run the build task and we get a WAR file with the following contents:

$ gradle build
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:war
:assemble
:compileTestJava
:processTestResources UP-TO-DATE
:testClasses
:test
:check
:build

BUILD SUCCESSFUL

Total time: 6.18 secs
$ jar tvf build/libs/package-only-dep-example.war
     0 Fri Sep 19 05:59:54 CEST 2014 META-INF/
    25 Fri Sep 19 05:59:54 CEST 2014 META-INF/MANIFEST.MF
     0 Fri Sep 19 05:59:54 CEST 2014 WEB-INF/
     0 Fri Sep 19 05:59:54 CEST 2014 WEB-INF/lib/
 29257 Thu Sep 18 14:36:24 CEST 2014 WEB-INF/lib/slf4j-api-1.7.7.jar
270750 Thu Sep 18 14:36:24 CEST 2014 WEB-INF/lib/logback-classic-1.1.2.jar
427729 Thu Sep 18 14:36:26 CEST 2014 WEB-INF/lib/logback-core-1.1.2.jar
   115 Wed Sep 03 09:24:40 CEST 2014 WEB-INF/web.xml

Also when we run the dependencies task we can see how the implementations of the SLF4J API relate to the dependency configurations:

$ gradle dependencies
:dependencies

------------------------------------------------------------
Root project
------------------------------------------------------------

archives - Configuration for archive artifacts.
No dependencies

compile - Compile classpath for source set 'main'.
\--- org.slf4j:slf4j-api:1.7.7

default - Configuration for default artifacts.
\--- org.slf4j:slf4j-api:1.7.7

providedCompile - Additional compile classpath for libraries that should not be part of the WAR a\
rchive.
No dependencies

providedRuntime - Additional runtime classpath for libraries that should not be part of the WAR a\
rchive.
No dependencies

runtime - Runtime classpath for source set 'main'.
\--- org.slf4j:slf4j-api:1.7.7

testCompile - Compile classpath for source set 'test'.
+--- org.slf4j:slf4j-api:1.7.7
\--- junit:junit:4.11
     \--- org.hamcrest:hamcrest-core:1.3

testRuntime - Runtime classpath for source set 'test'.
+--- org.slf4j:slf4j-api:1.7.7
+--- junit:junit:4.11
|    \--- org.hamcrest:hamcrest-core:1.3
\--- org.slf4j:slf4j-simple:1.7.7
     \--- org.slf4j:slf4j-api:1.7.7

warLib
+--- org.slf4j:slf4j-api:1.7.7
\--- ch.qos.logback:logback-classic:1.1.2
     +--- ch.qos.logback:logback-core:1.1.2
     \--- org.slf4j:slf4j-api:1.7.6 -> 1.7.7

(*) - dependencies omitted (listed previously)

BUILD SUCCESSFUL

Total time: 6.274 secs

Code written with Gradle 2.1.

Original blog post written on September 19, 2014.

Run Java Application From Build Script

Gradle has a special task to run a Java class from the build script: org.gradle.api.tasks.JavaExec. We can for example create a new task of type JavaExec and use a closure to configure the task. We can set the main class, classpath, arguments, JVM arguments and more to run the application.

Gradle also has the javaexec() method available as part of a Gradle project. This means we can invoke javaexec() directly from the build script and use the same closure to configure the Java application that we want to invoke.

Suppose we have a simple Java application:

// File: src/main/java/com/mrhaki/java/Simple.java
package com.mrhaki.java;

public class Simple {

    public static void main(String[] args) {
        System.out.println(System.getProperty("simple.message") + args[0] + " from Simple.");
    }

}

And we have the following Gradle build file to run Simple:

// File: build.gradle
apply plugin: 'java'

task(runSimple, dependsOn: 'classes', type: JavaExec) {
    main = 'com.mrhaki.java.Simple'
    classpath = sourceSets.main.runtimeClasspath
    args 'mrhaki'
    systemProperty 'simple.message', 'Hello '
}

defaultTasks 'runSimple'

// javaexec() method also available for direct invocation
// javaexec {
//    main = 'com.mrhaki.java.Simple'
//    classpath = sourceSets.main.runtimeClasspath
//    args 'mrhaki'
//    systemProperty 'simple.message', 'Hello '
// }

We can execute our Gradle build script and get the following output:

$ gradle
:compileJava
:processResources
:classes
:runSimple
Hello mrhaki from Simple.

BUILD SUCCESSFUL

Total time: 4.525 secs

Written with Gradle 0.9-rc-1.

Original blog post written on September 24, 2010.

Running Java Applications from External Dependency

With Gradle we can execute Java applications using the JavaExec task or the javaexec() method. If we want to run Java code from an external dependency we must first pull in the dependency with the Java application code. The best way to do this is to create a new dependency configuration. When we configure a task with type JavaExec we can set the classpath to the external dependency. Notice we cannot use the buildscript{} script block to set the classpath. A JavaExec task will fork a new Java process so any classpath settings via buildscript{} are ignored.

In the following example build script we want to execute the Java class org.apache.cxf.tools.wsdlto.WSDLToJava from Apache CXF to generate Java classes from a given WSDL. We define a new dependency configuration with the name cxf and use it to assign the CXF dependencies to it. We use the classpath property of the JavaExec task to assign the configuration dependency.

// File: build.gradle

// Base plugin for task rule clean<task>
apply plugin: 'base'

repositories.mavenCentral()

// New configuration for CXF dependencies.
configurations { cxf }

ext {
    // CXF version.
    cxfVersion = '2.6.2'

    // Artifacts for CXF dependency.
    cxfArtifacts = [
        'cxf-tools-wsdlto-frontend-jaxws',
        'cxf-tools-wsdlto-databinding-jaxb',
        'cxf-tools-common',
        'cxf-tools-wsdlto-core'
    ]
}

dependencies {
    // Assign CXF dependencies to configuration.
    cxfArtifacts.each { artifact ->
        cxf "org.apache.cxf:$artifact:$cxfVersion"
    }
}

// Custom task to generate Java classes
// from WSDL.
task wsdl2java(type: JavaExec) {
    ext {
        wsdlFile = 'src/wsdl/service-contract.wsdl'
        outputDir = file("$buildDir/generated/cxf")
    }

    inputs.file file(wsdlFile)
    outputs.dir outputDir

    // Main Java class to invoke.
    main = 'org.apache.cxf.tools.wsdlto.WSDLToJava'

    // Set classpath to dependencies assigned
    // to the cxf configuration.
    classpath = configurations.cxf

    // Arguments to be passed to WSDLToJava.
    args '-d', outputDir
    args '-client'
    args '-verbose'
    args '-validate'
    args wsdlFile
}

Code written with Gradle 1.2.

Original blog post written on October 22, 2012.

Pass Java System Properties To Java Tasks

Gradle is of course a great build tool for Java related projects. If we have tasks in our projects that need to execute a Java application we can use the JavaExec task. When we need to pass Java system properties to the Java application we can set the systemProperties property of the JavaExec task. We can assign a value to the systemProperties property or use the method systemProperties that will add the properties to the existing properties already assigned. Now if we want to define the system properties from the command-line when we run Gradle we must pass along the properties to the task. Therefore we must reconfigure a JavaExec task and assign System.properties to the systemProperties property.

In the following build script we reconfigure all JavaExec tasks in the project. We use the systemProperties method and use the value System.properties. This means any system properties from the command-line are passed on to the JavaExec task.

apply plugin: 'groovy'
apply plugin: 'application'

mainClassName = 'com.mrhaki.sample.Application'

repositories.jcenter()

dependencies {
    compile 'org.codehaus.groovy:groovy-all:2.4.4'
}

// The run task added by the application plugin
// is also of type JavaExec.
tasks.withType(JavaExec) {
    // Assign all Java system properties from
    // the command line to the JavaExec task.
    systemProperties System.properties
}

We write a simple Groovy application that uses a Java system property app.greeting to print a message to the console:

// File: src/main/groovy/com/mrhaki/sample/Application.groovy
package com.mrhaki.sample

println "Hello ${System.properties['app.greeting']}"

Now when we execute the run task (of type JavaExec) and define the Java system property app.greeting in our command it is used by the application:

$ gradle -Dapp.greeting=Gradle! -q run
Hello Gradle!

Written with Gradle 2.7.

Original blog post written on September 21, 2015.

Running Groovy Scripts as Application

In a previous post we learned how to run a Java application in a Gradle project. The Java source file with a main method is part of the project and we use the JavaExec task to run the Java code. We can use the same JavaExec task to run a Groovy script file.

A Groovy script file doesn’t have an explicit main method, but it is added when we compile the script file. The name of the script file is also the name of the generated class, so we use that name for the main property of the JavaExec task. Let’s first create simple Groovy script file to display the current date. We can pass an extra argument with the date format we wan’t to use.

// File: src/main/groovy/com/mrhaki/CurrentDate.groovy
package com.mrhaki

// If an argument is passed we assume it is the
// date format we want to use.
// Default format is dd-MM-yyyy.
final String dateFormat = args ? args[0] : 'dd-MM-yyyy'

// Output formatted current date and time.
println "Current date and time: ${new Date().format(dateFormat)}"

Our Gradle build file contains the task runScript of type JavaExec. We rely on the Groovy libraries included with Gradle, because we use localGroovy() as a compile dependency. Of course we can change this to refer to another Groovy version if we want to using the group, name and version notation together with a valid repository.

// File: build.gradle
apply plugin: 'groovy'

dependencies {
    compile localGroovy()
}

task runScript(type: JavaExec) {
    description 'Run Groovy script'

    // Set main property to name of Groovy script class.
    main = 'com.mrhaki.CurrentDate'

    // Set classpath for running the Groovy script.
    classpath = sourceSets.main.runtimeClasspath

    if (project.hasProperty('custom')) {
        // Pass command-line argument to script.
        args project.getProperty('custom')
    }
}

defaultTasks 'runScript'

We can run the script with or without the project property custom and we see the changes in the output:

$ gradle -q
Current date and time: 29-09-2014
$ gradle -q -Pcustom=yyyyMMdd
Current date and time: 20140929
$ gradle -q -Pcustom=yyyy
Current date and time: 2014

Code written with Gradle 2.1.

Original blog post written on September 29, 2014.

Alter Start Scripts from Application Plugin

For Java or Groovy projects we can use the application plugin in Gradle to run and package our application. The plugin adds for example the startScripts task which creates OS specific scripts to run the project as a JVM application. This task is then used again by the installDist that installs the application, and distZip and distTar tasks that create a distributable archive of the application. The startScripts tasks has the properties unixScript and windowsScript that are the actual OS specific script files to run the application. We can use these properties to change the contents of the files.

In the following sample we add the directory configuration to the CLASSPATH definition:

...
startScripts {

    // Support closures to add an additional element to
    // CLASSPATH definition in the start script files.
    def configureClasspathVar = { findClasspath, pathSeparator, line ->

        // Looking for the line that starts with either CLASSPATH=
        // or set CLASSPATH=, defined by the findClasspath closure argument.
        line = line.replaceAll(~/^${findClasspath}=.*$/) { original ->

            // Get original line and append it
            // with the configuration directory.
            // Use specified path separator, which is different
            // for Windows or Unix systems.
            original += "${pathSeparator}configuration"
        }

    }

    def configureUnixClasspath = configureClasspathVar.curry('CLASSPATH', ':')
    def configureWindowsClasspath = configureClasspathVar.curry('set CLASSPATH', ';')

    // The default script content is generated and
    // with the doLast method we can still alter
    // the contents before the complete task ends.
    doLast {

        // Alter the start script for Unix systems.
        unixScript.text =
            unixScript
                .readLines()
                .collect(configureUnixClasspath)
                .join('\n')

        // Alter the start script for Windows systems.
        windowsScript.text =
            windowsScript
                .readLines()
                .collect(configureWindowsClasspath)
                .join('\r\n')

    }

}
...

This post was inspired by the Gradle build file I saw at the Gaiden project.

Written with Gradle 2.3.

Original blog post written on April 19, 2015.

Running Groovy Scripts Like From Groovy Command Line

In a previous post we have seen how to execute a Groovy script in our source directories. But what if we want to use the Groovy command line to execute a Groovy script? Suppose we want to evaluate a small Groovy script expressed by a String value, that we normally would invoke like $ groovy -e "println 'Hello Groovy!'". Or we want to use the command line option -l to start Groovy in listening mode with a script to handle requests. We can achieve this by creating a task with type JavaExec or by using the Gradle javaexec method. We must set the Java main class to groovy.ui.Main which is the class that is used for running the Groovy command line.

In the following sample build file we create a new task runGroovyScript of type JavaExec. We also create a new dependency configuration groovyScript so we can use a separate class path for running our Groovy scripts.

// File: build.gradle

repositories {
    jcenter()
}

// Add new configuration for
// dependencies needed to run
// Groovy command line scripts.
configurations {
    groovyScript
}

dependencies {
    // Set Groovy dependency so
    // groovy.ui.GroovyMain can be found.
    groovyScript localGroovy()
    // Or be specific for a version:
    //groovyScript "org.codehaus.groovy:groovy-all:2.4.5"
}

// New task to run Groovy command line
// with arguments.
task runGroovyScript(type: JavaExec) {

    // Set class path used for running
    // Groovy command line.
    classpath = configurations.groovyScript

    // Main class that runs the Groovy
    // command line.
    main = 'groovy.ui.GroovyMain'

    // Pass command line arguments.
    args '-e', "println 'Hello Gradle!'"

}

We can run the task runGroovyScript and we see the output of our small Groovy script println 'Hello Gradle!':

$ gradle runGroovyScript
:runGroovyScript
Hello Gradle!

BUILD SUCCESSFUL

Total time: 1.265 secs
$

Let’s write another task where we use the simple HTTP server from the Groovy examples to start a HTTP server with Gradle. This can be useful if we have a project with static HTML files and want to serve them via a web server:

// File: build.gradle

repositories {
    jcenter()
}

configurations {
    groovyScript
}

dependencies {
    groovyScript localGroovy()
}

task runHttpServer(type: JavaExec) {
    classpath = configurations.groovyScript
    main = 'groovy.ui.GroovyMain'

    // Start Groovy in listening mode on
    // port 8001.
    args '-l', '8001'

    // Run simple HTTP server.
    args '-e', '''\
// init variable is true before
// the first client request, so
// the following code is executed once.
if (init) {
    headers = [:]
    binaryTypes = ["gif","jpg","png"]
    mimeTypes = [
        "css" : "text/css",
        "gif" : "image/gif",
        "htm" : "text/html",
        "html": "text/html",
        "jpg" : "image/jpeg",
        "png" : "image/png"
    ]
    baseDir = System.properties['baseDir'] ?: '.'
}

// parse the request
if (line.toLowerCase().startsWith("get")) {
    content = line.tokenize()[1]
} else {
    def h = line.tokenize(":")
    headers[h[0]] = h[1]
}

// all done, now process request
if (line.size() == 0) {
    processRequest()
    return "success"
}

def processRequest() {
    if (content.indexOf("..") < 0) { //simplistic security
        // simple file browser rooted from current dir
        def file = new File(new File(baseDir), content)
        if (file.isDirectory()) {
            printDirectoryListing(file)
        } else {
            extension = content.substring(content.lastIndexOf(".") + 1)
            printHeaders(mimeTypes.get(extension,"text/plain"))

            if (binaryTypes.contains(extension)) {
                socket.outputStream.write(file.readBytes())
            } else {
                println(file.text)
            }
        }
    }
}

def printDirectoryListing(dir) {
    printHeaders("text/html")
    println "<html><head></head><body>"
    for (file in dir.list().toList().sort()) {
        // special case for root document
        if ("/" == content) {
            content = ""
        }
        println "<li><a href='${content}/${file}'>${file}</a></li>"
    }
    println "</body></html>"
}

def printHeaders(mimeType) {
    println "HTTP/1.0 200 OK"
    println "Content-Type: ${mimeType}"
    println ""
}
    '''

    // Script is configurable via Java
    // system properties. Here we set
    // the property baseDir as the base
    // directory for serving static files.
    systemProperty 'baseDir', 'src/main/resources'
}

We can run the task runHttpServer from the command line and open the page http://localhost:8001/index.html in our web browser. If there is a file index.html in the directory src/main/resources it is shown in the browser.

$ gradle runGroovyScript
:runHttpServer
groovy is listening on port 8001
> Building 0% > :runHttpServer

Written with Gradle 2.11.

Original blog post written on February 10, 2016.

Running a Single Test

We can run test code with Gradle using the test task that is added by the Java plugin. By default all tests found in the project are executed. If we want to run a single test we can use the Java system property test.single with the name of the test. Actually the pattern for the system property is _taskName_.single. The taskName is the name of the task of type Test in our project. We will see how we can use this in our builds.

First we create a simple build.gradle file to run our tests:

// File: build.gradle
apply plugin: 'java'

repositories {
    mavenCentral()
}

dependencies {
    testCompile 'junit:junit:[4,)'
}

test {
    testLogging {
        // Show that tests are run in the command-line output
        events 'started', 'passed'
    }
}

Next we create two test classes with each a single test method, just to demonstrate we can invoke them as single test later on.

// File: src/test/java/com/mrhaki/gradle/SampleTest.java
package com.mrhaki.gradle;

import static org.junit.Assert.*;
import org.junit.*;

public class SampleTest {

    @Test public void sample() {
        assertEquals("Gradle is gr8", "Gradle is gr8");
    }

}
// File: src/test/java/com/mrhaki/gradle/AnotherSampleTest.java
package com.mrhaki.gradle;

import static org.junit.Assert.*;
import org.junit.*;

public class AnotherSampleTest {

    @Test public void anotherSample() {
        assertEquals("Gradle is great", "Gradle is great");
    }
}

To only run the SampleTest we must invoke the test task from the command-line with the Java system property -Dtest.single=Sample:

$ gradle -Dtest.single=Sample test
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:compileTestJava
:processTestResources UP-TO-DATE
:testClasses
:test

com.mrhaki.gradle.SampleTest > sample STARTED

com.mrhaki.gradle.SampleTest > sample PASSED

BUILD SUCCESSFUL

Total time: 11.404 secs

Notice only one test is executed now. Gradle will get the value Sample and uses it in the following pattern **/<Java system property value=Sample>*.class to find the test class. So we don’t have to type the full package and class name of our single test class. To only invoke the AnotherSampleTest test class we run the test task with a different value for the Java systme property:

$ gradle -Dtest.single=AnotherSample test
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:compileTestJava
:processTestResources UP-TO-DATE
:testClasses UP-TO-DATE
:test

com.mrhaki.gradle.AnotherSampleTest > anotherSample STARTED

com.mrhaki.gradle.AnotherSampleTest > anotherSample PASSED

BUILD SUCCESSFUL

Total time: 5.62 secs

We can also use a pattern for the Java system property to run multiple tests that apply to the pattern. For example we can use *Sample to run both SampleTest and AnotherSampleTest:

$ gradle -Dtest.single=*Sample test
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:compileTestJava
:processTestResources UP-TO-DATE
:testClasses UP-TO-DATE
:test

com.mrhaki.gradle.AnotherSampleTest > anotherSample STARTED

com.mrhaki.gradle.AnotherSampleTest > anotherSample PASSED

com.mrhaki.gradle.SampleTest > sample STARTED

com.mrhaki.gradle.SampleTest > sample PASSED

BUILD SUCCESSFUL

Total time: 5.605 secs

To show the Java system property also works for other tasks of type Test we add a new task to our build.gradle file. We name the task sampleTest and include our tests. We also apply the same testLogging now to all tasks with type Test so we can see the output.

// File: build.gradle
apply plugin: 'java'

repositories {
    mavenCentral()
}

dependencies {
    testCompile 'junit:junit:[4,)'
}

task sampleTest(type: Test, dependsOn: testClasses) {
    include '**/*Sample*'
}

tasks.withType(Test) {
    testLogging {
        events 'started', 'passed'
    }
}

Next we want to run only the SampleTest class, but now we use the Java system property -DsampleTest.single=S*:

$ gradle -DsampleTest.single=S* sampleTest
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:compileTestJava UP-TO-DATE
:processTestResources UP-TO-DATE
:testClasses UP-TO-DATE
:sampleTest

com.mrhaki.gradle.SampleTest > sample STARTED

com.mrhaki.gradle.SampleTest > sample PASSED

BUILD SUCCESSFUL

Total time: 10.677 secs

Code written with Gradle 1.6

Original blog post written on May 13, 2013.

Running Tests in Parallel

Once we apply the Java plugin we can run our tests with the test task. Normally each test is run sequentially, but we can also run tests in parallel. This speeds up the test task considerably especially with a lot of tests. We set the property maxParallelForks to a number greater than 1 to enable parallel tests.

We can also set the additional property forkEvery on the test task. With this property we define how many tests should run in a parallel test fork.

In the following build script we first create a lot of tests with the createTests task and then we can run them with the test task. We can pass the properties maxParallelForks and forkEvery to play around and see what happens. Of course we can also use hard coded values in our build script for the test task properties.

apply plugin: 'java'

repositories {
    mavenCentral()
}

dependencies {
    testCompile 'junit:junit:4.8.2'
}

test {
    if (project.hasProperty('maxParallelForks'))
        maxParallelForks = project.maxParallelForks as int
    if (project.hasProperty('forkEvery'))
        forkEvery = project.forkEvery as int
}

packages = 10
tests = 30

task createTests << {
    (0..packages).each { packageCounter ->
        def packageName = "gradletest${packageCounter}"
        (0..tests).each { classCounter ->
            def testClassName = "Gradle${classCounter}Test"
            copy {
                from 'src/templates'
                into 'src/test/java'
                expand([packageName: packageName, testClassName: testClassName])
                rename '(.*).java', packageName + '/' + testClassName + '.java'
                include 'SampleTest.java'
            }
        }
    }
}
// File: src/templates/SampleTest.java
package ${packageName};

import org.junit.Test;
import static org.junit.Assert.*;

public class ${testClassName} {

    @Test
    public void testString() throws Exception {
        Thread.sleep(200);
        assertEquals("mrhaki", "mr" + "haki");
    }
}

So first we create the tests: $ gradle createTests. And then we can experiment with different options:

$ gradle clean test
...
BUILD SUCCESSFUL

Total time: 1 mins 33.942 secs

$ gradle clean test -PmaxParallelForks=10
...
BUILD SUCCESSFUL

Total time: 36.68 secs

$ gradle clean test -PmaxParallelForks=4 -PforkEvery=25
...
BUILD SUCCESSFUL

Total time: 56.066 secs

Written with Gradle 0.9-rc-2.

Original blog post written on November 10, 2010.

Running All Tests From One Package

If we have a Gradle task of type Test we can use a filter on the command line when we invoke the task. We define a filter using the --tests option. If for example we want to run all tests from a single package we must define the package name as value for the --tests option. It is good to define the filter between quotes, so it is interpreted as is, without any shell interference.

If we configure the test task to output the test class name we can see that which tests are executed. In the following snippet we reconfigure the test task:

...
test {
    beforeTest { descriptor ->
       logger.lifecycle("Running test: $descriptor.className")
    }
}
...

Suppose we have a project and we execute all tests:

$ gradle test
...
:test
Running test: mrhaki.gradle.model.CourseSpec
Running test: mrhaki.gradle.model.TeacherSpec
Running test: mrhaki.gradle.service.CourseServiceSpec
...
$

To only run the tests in the mrhaki.gradle.model package we use the following command:

$ gradle test --tests "mrhaki.gradle.model.*"
...
:test
Running test: mrhaki.gradle.model.CourseSpec
Running test: mrhaki.gradle.model.TeacherSpec
...
$

We can even filter on a single test to be executed:

$ gradle test --tests "mrhaki.gradle.model.CourseSpec"
...
:test
Running test: mrhaki.gradle.model.CourseSpec
...
$

If we only want to run a single method of a test we can specify the method name as a filter:

$ gradle test --tests "mrhaki.gradle.model.CourseSpec.create new Course as immutable"
...
:test
Running test: mrhaki.gradle.model.CourseSpec
...
$

Written with Gradle 2.13.

Original blog post written on June 14, 2016.

Show Standard Out or Error Output from Tests

We use the Test task in Gradle to run tests. If we use the System.out.println or System.err.println methods in our test we don’t see the output when we execute the tests. We can customize the test task to show any output send to standard out or error in the Gradle output.

First we show our test class written with Spock, but it could also be a JUnit or TestNG test:

// File: src/test/groovy/com/mrhaki/gradle/SampleSpec.groovy
package com.mrhaki.gradle

import spock.lang.*

class SampleSpec extends Specification {

    def "check that Gradle is Gr8"() {
        when:
        def value = 'Gradle is great!'

        then:
        // Include a println statement, so
        // we have output to show.
        println "Value = [$value]"
        value == 'Gradle is great!'
    }

}

Now we write a simple Gradle build file which can execute our test:

// File: build.gradle
apply plugin: 'groovy' // Adds test task

repositories.jcenter()

dependencies {
    compile 'org.codehaus.groovy:groovy-all:2.3.7'
    testCompile 'org.spockframework:spock-core:0.7-groovy-2.0'
}

Let’s run the test task from the command line and look at the output:

$ gradle test
:compileJava UP-TO-DATE
:compileGroovy UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:compileTestJava
:compileTestGroovy
:processTestResources UP-TO-DATE
:testClasses
:test

BUILD SUCCESSFUL

Total time: 7.022 secs
$

Well at least our test is successful, but we don’t see the output of our println method invocation in the test. We customize the test task and add the testLogging method with a configuration closure. In the closure we set the property showStandardStreams to the value true. Alternatively we can set the events property or use the events method with the values standard_out and standard_err to achieve the same result. In the next build file we use the showStandardStreams property:

// File: build.gradle
apply plugin: 'groovy' // Adds test task

repositories.jcenter()

dependencies {
    compile 'org.codehaus.groovy:groovy-all:2.3.7'
    testCompile 'org.spockframework:spock-core:0.7-groovy-2.0'
}

test {
    testLogging {
        // Make sure output from
        // standard out or error is shown
        // in Gradle output.
        showStandardStreams = true

        // Or we use events method:
        // events 'standard_out', 'standard_error'

        // Or set property events:
        // events = ['standard_out', 'standard_error']

        // Instead of string values we can
        // use enum values:
        // events org.gradle.api.tasks.testing.logging.TestLogEvent.STANDARD_OUT,
        //        org.gradle.api.tasks.testing.logging.TestLogEvent.STANDARD_ERROR,
    }
}

We re-run the test task from the command line and look at the output to see the result from the println method:

$ gradle test
:compileJava UP-TO-DATE
:compileGroovy UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:compileTestJava
:compileTestGroovy
:processTestResources UP-TO-DATE
:testClasses
:test

com.mrhaki.gradle.SampleSpec > check that Gradle is Gr8 STANDARD_OUT
    Value = [Gradle is great!]

BUILD SUCCESSFUL

Total time: 8.716 secs
$

Written with Gradle 2.1.

Original blog post written on October 15, 2014.

Show More Information About Failed Tests

Running tests in Gradle is easy. Normally if one of the tests fails the build fails as well. But we don’t see immediately in the command-line output why a test fails. We must first open the generated HTML test report. But there are other ways as well.

First we create the following sample Gradle build file:

// File: build.gradle
apply plugin: 'java'

repositories {
    mavenCentral()
}

dependencies {
    testCompile 'junit:junit:[4,)'
}

And we use the following sample JUnit test class. Notice this test will always fail, which is what we want in this case.

// File: src/test/java/com/mrhaki/gradle/SampleTest.java
package com.mrhaki.gradle;

import org.junit.*;

public class SampleTest {

    @Test public void sample() {
        Assert.assertEquals("Gradle is gr8", "Gradle is great");
    }

}

To run our test we execute the test task. If we run the task we see in the output on which line the test fails, but we don’t see the assertion why it went wrong:

$ gradle test
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:compileTestJava
:processTestResources UP-TO-DATE
:testClasses
:test

com.mrhaki.gradle.SampleTest > sample FAILED
    org.junit.ComparisonFailure at SampleTest.java:8

1 test completed, 1 failed
:test FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':test'.
> There were failing tests. See the report at: file:///Users/mrhaki/Projects/mrhaki.com/blog/post\
s/samples/gradle/testlogging/build/reports/tests/index.html

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get mor\
e log output.

BUILD FAILED

Total time: 4.904 secs

We can run the test task again, but now set the Gradle logging level to info with the command-line option -i or --info. Now we get the assertion about what went wrong in the output:

$ gradle test -i
Starting Build
Settings evaluated using empty settings script.
Projects loaded. Root project using build file
...
Successfully started process 'Gradle Worker 1'
Gradle Worker 1 executing tests.
Gradle Worker 1 finished executing tests.

com.mrhaki.gradle.SampleTest > sample FAILED
    org.junit.ComparisonFailure: expected:<gradle is gr[8]> but was:<gradle is gr[eat]>
        at org.junit.Assert.assertEquals(Assert.java:115)
        at org.junit.Assert.assertEquals(Assert.java:144)
        at com.mrhaki.gradle.SampleTest.sample(SampleTest.java:8)
Process 'Gradle Worker 1' finished with exit value 0 (state: SUCCEEDED)

1 test completed, 1 failed
Finished generating test XML results (0.025 secs)
Generating HTML test report...
Finished generating test html results (0.027 secs)
:test FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':test'.
> There were failing tests. See the report at: file:///Users/mrhaki/Projects/mrhaki.com/blog/post\
s/samples/gradle/testlogging/build/reports/tests/index.html

* Try:
Run with --stacktrace option to get the stack trace. Run with --debug option to get more log outp\
ut.

BUILD FAILED

Total time: 5.117 secs

But this still generates a lot of noise. It is better to customize the test logging by configuring the test task. We can configure the logging on different levels. To get the information about the failure we want we only have to change the exceptionFormat property and set the value to full. Our Gradle build file now looks like this:

// File: build.gradle
apply plugin: 'java'

repositories {
    mavenCentral()
}

dependencies {
    testCompile 'junit:junit:[4,)'
}

test {
    testLogging {
        exceptionFormat = 'full'
    }
}

We can re-run the test task and use the normal logging level, but this time we also get the reason why our test fails, without the extra noise:

$ gradle test
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:compileTestJava UP-TO-DATE
:processTestResources UP-TO-DATE
:testClasses UP-TO-DATE
:test

com.mrhaki.gradle.SampleTest > sample FAILED
    org.junit.ComparisonFailure: expected:<gradle is gr[8]> but was:<gradle is gr[eat]>
        at org.junit.Assert.assertEquals(Assert.java:115)
        at org.junit.Assert.assertEquals(Assert.java:144)
        at com.mrhaki.gradle.SampleTest.sample(SampleTest.java:8)

1 test completed, 1 failed
:test FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':test'.
> There were failing tests. See the report at: file:///Users/mrhaki/Projects/mrhaki.com/blog/post\
s/samples/gradle/testlogging/build/reports/tests/index.html

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get mor\
e log output.

BUILD FAILED

Total time: 5.906 secs

Sample written with Gradle 1.6

Original blog post written on May 10, 2013.

Create JAR Artifact with Test Code for Java Project

Today, during my Gradle session, someone asked how to create a JAR file with the compiled test classes and test resources. I couldn’t get the task syntax right at that moment, so when I was at home I had to find out how we can create that JAR file. And it turned out to be very simple:

apply plugin: 'java'

task testJar(type: Jar) {
    classifier = 'tests'
    from sourceSets.test.classes
}

The magic is in the from method where we use sourceSets.test.classes. Because we use sourceSets.test.classes Gradle knows the task testClasses needs to be executed first before the JAR file can be created. And of course the assemble task will pick up this new task of type Jar automatically.

When we run the build we get the following output:

$ gradle assemble
:compileJava
:processResources
:classes
:jar
:compileTestJava
:processTestResources
:testClasses
:testJar
:assemble

Written with Gradle 0.9-rc-2.

Original blog post written on November 03, 2010.

Add Filtering to ProcessResources Tasks

When we apply the Java plugin (or any dependent plugin like the Groovy plugin) we get new tasks in our project to copy resources from the source directory to the classes directory. So if we have a file app.properties in the directory src/main/resources we can run the task $ gradle processResources and the file is copied to build/classes/main/app.properties. But what if we want to apply for example some filtering while the file is copied? Or if we want to rename the file? How we can configure the processResources task?

The task itself is just an implementation of the Copy task. This means we can use all the configuration options from the Copy task. And that includes filtering and renaming the files. So we need to find all tasks in our project that copy resources and then add for example filtering to the configuration. The following build script shows how we can do this:

import org.apache.tools.ant.filters.*

apply plugin: 'java'

version = '1.0-DEVELOPMENT'

afterEvaluate {
    configure(allProcessResourcesTasks()) {
        filter(ReplaceTokens,
               tokens: [version: project.version,
                        gradleVersion: project.gradle.gradleVersion])
    }
}

def allProcessResourcesTasks() {
    sourceSets.all.processResourcesTaskName.collect {
        tasks[it]
    }
}

Let’s create the following two files in our project directory:

# src/main/resources/app.properties
appversion=@version@
# src/test/resources/test.properties
gradleVersion=@gradleVersion@

We can now execute the build and look at the contents of the copied property files:

$ gradle build
:compileJava
:processResources
:classes
:jar
:assemble
:compileTestJava
:processTestResources
:testClasses
:test
:check
:build

BUILD SUCCESSFUL

Total time: 4.905 secs

$ cat build/classes/main/app.properties
appversion=1.0-DEVELOPMENT
$ cat build/classes/test/test.properties
gradleVersion=0.9-rc-2

Written with Gradle 0.9-rc-2.

Original blog post written on November 05, 2010.

Use Groovy Ruleset File with Code Quality Plugin

The codenarc plugin supports CodeNarc for Groovy projects. The default configuration file is XML based with the name codenarc.xml and must be placed in the directory config/codenarc. But CodeNarc also supports a Groovy DSL for writing configuration files. Suppose we have a configuration file with the name rules.groovy and we put it in the directory config/codenarc. In our build.gradle file we reference this file with the property configFile inside a codenarc configuration block. The codenarc plugin will pass this value on to CodeNarc and the rules defined in our Groovy ruleset file are used.

// File: config/codenarc/rules.groovy

ruleset {
    description 'Rules Sample Groovy Gradle Project'

    ruleset('rulesets/basic.xml')
    ruleset('rulesets/braces.xml')
    ruleset('rulesets/exceptions.xml')
    ruleset('rulesets/imports.xml')
    ruleset('rulesets/logging.xml') {
        'Println' priority: 1
        'PrintStackTrace' priority: 1
    }
    ruleset('rulesets/naming.xml')
    ruleset('rulesets/unnecessary.xml')
    ruleset('rulesets/unused.xml')
}
// File: build.gradle
['groovy', 'codenarc'].each {
    apply plugin: it
}

repositories {
    mavenCentral()
}

dependencies {
    compile group: 'org.codehaus.groovy', name: 'groovy', version: '2.3.9'
}

codenarc {
    configFile = file('config/codenarc/rules.groovy')
}

Written with Gradle 0.9.1 and re-written with Gradle 2.2.1.

Original blog post written on January 10, 2011.

Don’t Let CodeNarc Violations Fail the Build

With the codenarc plugin for Groovy project we use CodeNarc to check our code. By default we are not allowed to have any violations in our code, because if there is a violation the Gradle build will stop with a failure. If we don’t want to our build to fail, because of code violations, we can set the property ignoreFailures to true for the CodeNarc task.

The code-quality plugin adds two CodeNarc tasks to our project: codenarcMain and codenarcTest. We can simply set the property ignoreFailures for these tasks:

apply plugin: 'codenarc'

[codenarcMain, codenarcTest]*.ignoreFailures = true

We can also search for all tasks of type CodeNarc and set the ignoreFailures property. This is useful if we added new tasks of type CodeNarc to our project and want to change the property of all these tasks:

apply plugin: 'codenarc'

tasks.withType(CodeNarc).allTasks { codeNarcTask ->
    codeNarcTask.ignoreFailures = true
}

Written with Gradle 0.9.1 and re-written with Gradle 2.2.1.

Original blog post written on January 17, 2011.

IDE

Customize IDEA Project File Generation

With the Gradle IDEA plugin we can generate JetBrains IntelliJ IDEA project files. The plugin uses defaults from our project to generate the files. If we also apply the Java plugin to our project then the Java settings for the project files are generated. We can customize the file generation in several ways. The most low level method is using the withXml hook. With this hook we have access to the XML before the file is written to disk. Here we can add or change XML elements and attribute values.

We use a closure as argument for the withXml hook and Gradle adds a XmlProvider object as argument. The easiest way to manipulate the XML is getting a groovy.util.Node from the XmlProvider. We also can get a DOM Element or StringBuilder to work with.

In the following example build file we change the contents of the IDEA project file (with extension .ipr). We change the output directory of the JavaDoc tool. We use Groovy syntax to find the JavadocGenerationManager which is automatically added, because we have the Java plugin in our build file. We also change the Encoding component or create it when it doesn’t exist:

apply plugin: 'java'
apply plugin: 'idea'

idea {
    project {
        // Here we customize the .ipr file generation.
        ipr {
            // XML hook to customize the XML before
            // it is written to disk
            withXml { xmlProvider ->
                // Get root node.
                def project = xmlProvider.asNode()

                customizeJavaDoc project
                customizeEncoding project
            }
        }
    }
}

/* Customize JavadocGenerationManger component */
def customizeJavaDoc(project) {
    def javaDocGenerationManager =
        findComponent(project, 'JavadocGenerationManager')

    changeOption(
        javaDocGenerationManager,
        'OUTPUT_DIRECTORY',
        '$PROJECT_DIR$/out/javadoc')
}

/* Search component with given name */
def findComponent(project, name) {
    project.component.find { it.@name == name }
}

/* Set value for option node with given name */
def changeOption(node, name, value) {
    node.option.find { it.@name == name }.@value = value
}

/* Customize Encoding component */
def customizeEncoding(project) {
    def encoding = findComponent(project, 'Encoding')

    if (encoding) {
        // Change existing node.
        encoding.@useUTFGuessing = true
        encoding.@native2AsciiForPropertiesFiles = true
        encoding.@defaultCharsetForPropertiesFiles = 'UTF-8'
    } else {
        // Create new node with default values.
        project.appendNode 'Encoding',
                           [useUTFGuessing: true,
                            native2AsciiForPropertiesFiles: true,
                            defaultCharsetForPropertiesFiles: 'UTF-8']
    }
}

To add a XML structure we can use Groovy’s NodeBuilder. With the NodeBuilder we use builder syntax to define the structure. The result is a Node object that we can use to insert in the XML that is used to generate the IDEA project file. In the following example build.gradle file we create a new inspection profile with a customized Spelling inspection:

apply plugin: 'java'
apply plugin: 'idea'

idea {
    project {
        // Here we customize the .ipr file generation.
        ipr {
            // XML hook to customize the XML before
            // it is written to disk
            withXml { xmlProvider ->
                // Get root node.
                def project = xmlProvider.asNode()
                customizeSpellingInspection(project)
            }
        }
    }
}

/* Change or add spelling inspection */
def customizeSpellingInspection(project) {
    def inspections = findComponent(project, 'InspectionProjectProfileManager')

    if (inspections) {
        // Update existing profiles to disable spell checking
        // for processCode and processLiterals.
        def spellChecking = inspections.profiles.profile.option.inspection_tool.find {
            it.@class == 'SpellCheckingInspection'
        }
        if (spellChecking) {
            ['processCode', 'processLiterals'].each { optionName ->
                spellChecking.option.find { it.@name == optionName }.@value = 'false'
            }
        }
    } else {
        // Create new InspectionProjectProfileManager component.
        inspections = project.appendNode('component', [name: 'InspectionProjectProfileManager'])

        // Use NodeBuilder to build profiles XML structure.
        def builder = new NodeBuilder()
        def profiles = builder.profiles {
            profile(version: '1.0', is_locked: false) {
                option(name: 'myName', value: 'Default Inspections')
                option(name: 'myLocal', value: false)
                inspection_tool(class: 'SpellCheckingInspection', enabled: true,
                                level: 'TYPO', enabled_by_default: true) {
                    option(name: 'processCode', value: false)
                    option(name: 'processLiterals', value: false)
                    option(name: 'processComments', value: true)
                }
            }
        }
        // Add result from NodeBuilder
        inspections.append profiles

        // Extra nodes added with appendNode() method.
        inspections.appendNode 'option', [name: 'PROJECT_PROFILE', value: 'Default Inspections']
        inspections.appendNode 'option', [name: 'USE_PROJECT_PROFILE', value: true]
        inspections.appendNode 'version', [value: "1.0"]
    }
}

/* Search component with given name */
def findComponent(project, name) {
    project.component.find { it.@name == name }
}

Written with Gradle 1.2.

Original blog post written on September 21, 2012.

Download Javadoc Files For Dependencies In IDE

Gradle has an idea and eclipse plugin that we can use to configure IntelliJ IDEA and Eclipse project files. When we apply these plugins to our project we get extra tasks to generate and change project files. Inside our Gradle build file we get new configuration blocks to specify properties or invoke methods that will change the configuration files. One of the nice things to add is to let the IDE download Javadoc files for dependencies in our Java/Groovy projects. By default the sources for a dependency are already downloaded and added to the project, but Javadoc files are not downloaded.

In the example build file we use the idea and eclipse plugins. We also add an idea and eclipse configuration block. The place where we need to set the property downloadJavadoc is a bit different, but the end result will be the same.

// File: build.gradle
apply {
    plugin 'java'
    plugin 'idea'
    plugin 'eclipse'
}

idea {
    module {
        downloadJavadoc = true
    }
}

eclipse {
    classpath {
        downloadJavadoc = true
    }
}

repositories {
    jcenter()
}

dependencies {
    compile 'org.springframework:spring-context:4.2.1.RELEASE'
}

For example to create the correct files for IntelliJ IDEA we run the task idea. For an existing build file, we can select the Refresh all Gradle projects icon from the Gradle view and IDEA will download missing Javadoc files for the dependencies in our project. In the project file we see for example the location of the Javadoc JAR files:

...
<library name="Gradle: org.springframework:spring-beans:4.2.1.RELEASE">
  <CLASSES>
    <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.springframework/spring-be\
ans/4.2.1.RELEASE/6d39786b9f7b2a79897a0a83495f30002d9f8de3/spring-beans-4.2.1.RELEASE.jar!/" />
  </CLASSES>
  <JAVADOC>
    <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.springframework/spring-be\
ans/4.2.1.RELEASE/d7789802c418cd04462f0e2f00cf812912bea33d/spring-beans-4.2.1.RELEASE-javadoc.jar\
!/" />
  </JAVADOC>
  <SOURCES>
    <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.springframework/spring-be\
ans/4.2.1.RELEASE/5d017016084ccea18af667cf65e2927800967452/spring-beans-4.2.1.RELEASE-sources.jar\
!/" />
  </SOURCES>
</library>
...

When we run the eclipse task all Eclipse project files are generated. If we look in the generated .classpath file we see for example that the location for the Javadoc files is added:

...
<classpathentry sourcepath="/Users/mrhaki/.gradle/caches/modules-2/files-2.1/org.springframework/\
spring-context/4.2.1.RELEASE/676729ef55bb886663ed5454eb61d119ef712f17/spring-context-4.2.1.RELEAS\
E-sources.jar" kind="lib" path="/Users/mrhaki/.gradle/caches/modules-2/files-2.1/org.springframew\
ork/spring-context/4.2.1.RELEASE/5bf6dec08e77755c2ec913fde1b8e29083e70a76/spring-context-4.2.1.RE\
LEASE.jar">
    <attributes>
        <attribute name="javadoc_location" value="jar:file:/Users/mrhaki/.gradle/caches/modules-2\
/files-2.1/org.springframework/spring-context/4.2.1.RELEASE/8d9edd93ea9d918b0895eb6fd4d1c69b301de\
449/spring-context-4.2.1.RELEASE-javadoc.jar!/"/>
    </attributes>
</classpathentry>

...

Written with Gradle 2.7.

Original blog post written on September 30, 2015.

Quickly Open Test Report in IntelliJ IDEA

When we execute a Gradle test task in IntelliJ IDEA the IDE test runner is used. This means we can get a nice overview of all tests that have run. If a test is successful we get a green light, otherwise it is red in case of an error or orange in case of a failure. Gradle also generates a nice HTML report when we run the test task. It is placed in the directory build/reports/tests/. IntelliJ IDEA has a button which opens this report when we click on it. The following screenshot shows the button with the tooltip that opens a Gradle test report:

Written with IntelliJ IDEA 14 and Gradle 2.6.

Original blog post written on August 27, 2015.

Add Spring Facet To IntelliJ IDEA Module

To create IntelliJ IDEA project files with Gradle we first need to apply the idea plugin. We can then further customise the created files. In this blog post we will add a Spring facet to the generated module file. By adding the Spring facet IntelliJ IDEA can automatically search for Spring configuration files. We can then use the Spring view to see which beans are configured in our project.

In the following example build file we use the withXml hook. This method is invoked just before the XML file is generated. We get an argument of type XmlProvider. From the XmlProvider we can access the XML as org.w3c.dom.Element, StringBuilder or groovy.util.Node. We use Node to alter the XML. We check if a FacetManager component is available. We need this to add a facet of type Spring.

// File: build.gradle
apply plugin: 'groovy'
apply plugin: 'idea'

idea {
    module {
        iml {
            withXml {
                // Get root of module as groovy.util.Node.
                def moduleRoot = it.asNode()

                // Find if component with name 'FacetManager'
                // is already set.
                def facetManager = moduleRoot.component.find { component -> component.'@name' == \
'FacetManager'}
                if (!facetManager) {
                    // Create new component with name 'FacetManager'
                    facetManager = moduleRoot.appendNode('component', [name: 'FacetManager'])
                }

                // Find Spring facet it might already be there.
                def springFacet = facetManager.facet.find { facet -> facet.'@type' == 'Spring' &&\
 facet.'@name' == 'Spring' }
                if (!springFacet) {
                    // If not set create new facet node with name 'Spring'
                    // and type 'Spring' and apply a default configuration.
                    springFacet = facetManager.appendNode('facet', [type: 'Spring', name: 'Spring\
'])
                    springFacet.appendNode('configuration')
                }
            }
        }
    }
}

Written with Gradle 2.12 and IntelliJ IDEA 15.

Original blog post written on March 16, 2016.

Configure IntelliJ IDEA To Use Gradle As Testrunner

When we run tests in IntelliJ IDEA the code is compiled by IntelliJ IDEA and the JUnit test runner is used. We get a nice graphical overview of the tasks that are executed and their results. If we use Gradle as the build tool for our project we can tell IntelliJ IDEA to always use Gradle for running tests.

We must open the Preferences or Settings dialog window. There we can search for Gradle and then look for Build, Execution, Deployment | Build Tools | Gradle | Runner:

We can select different values for the Run tests using option. The default value is Platform Test Runner. To use Gradle we select Gradle Test Runner. Finally we can choose for Let me choose per test. Let’s select the option Gradle Test Runner and click on the OK button to close the dialog window.

Next we run our test code from the IDE:

The test is started using the Gradle tasks cleanTest and test with the option --tests with the name of our test class. In the Run window we can see how the test is executed and the graphical representation of the tests:

If we choose the option Let me choose per test then we must first select the runner if we run a test for the first time:

Our choice is remembered for next runs of the same test. We must delete the run configuration from Run | Edit configurations… if we want to choose the runner again.

Written with IntelliJ IDEA 15 and Gradle 2.12.

Original blog post written on March 16, 2016.

Enable Compiler Annotation Processing For IntelliJ IDEA

Suppose we have a project where we use Lombok annotations. To use it we must change the compiler configuration for our project and enable annotation processing. We can find this in the Preferences or Settings window under Build, Execution, Deployment | Compiler | Annotation Processors. Here we have a checkbox Enable annotation processing that we must check to use the annotations from IntelliJ IDEA. We can automate this setting with some configuration in our Gradle build file.

In the next example build file we alter the generated IntelliJ IDEA project file using the withXml hook. We can access the generated XML as a groovy.util.Node and change the XML.

// File: build.gradle
apply plugin: 'groovy'
apply plugin: 'idea'

repositories {
    jcenter()
}

dependencies {
    compile('org.projectlombok:lombok:1.16.6')
}

idea {
    project {
        ipr {
            withXml { provider ->
                // Get XML as groovy.util.Node to work with.
                def projectXml = provider.asNode()

                // Find compiler configuration component.
                def compilerConfiguration = projectXml.component.find { component ->
                    component.'@name' == 'CompilerConfiguration'
                }

                // Replace current annotationProcessing
                // that is part of the compiler configuration.
                def currentAnnotationProcessing = compilerConfiguration.annotationProcessing
                currentAnnotationProcessing.replaceNode {
                    annotationProcessing {
                        profile(name: 'Default', default: true, enabled: true) {
                            processorPath(useClasspath: true)
                        }
                    }
                }
            }
        }
    }
}

Written with Gradle 2.12 and IntelliJ IDEA 15.

Original blog post written on March 16, 2016.

Set VCS For IntelliJ IDEA In Build File

When we use the IDEA plugin in Gradle we can generate IntelliJ IDEA project files. We can customise the generated files in different ways. One of them is using a simple DSL to configure certain parts of the project file. With the DSL it is easy to set the version control system (VCS) used in our project.

In the next example build file we customise the generated IDEA project file and set Git as the version control system. The property is still incubating, but we can use it to have a proper configuration.

// File: build.gradle
apply plugin: 'groovy'
apply plugin: 'idea'

idea {
    project {
        // Set the version control system
        // to Git for this project.
        // All values IntelliJ IDEA supports
        // can be used. E.g. Subversion, Mercurial.
        vcs = 'Git'
    }
}

Written with Gradle 2.12 and IntelliJ IDEA 15.

Original blog post written on March 16, 2016.

Source Sets As IntelliJ IDEA Modules

IntelliJ IDEA 2016.1 introduced better support for Gradle source sets. Each source set in our project becomes a module in the IntelliJ IDEA project. And each module has it’s own dependencies, also between source sets. For example if we simply apply the java plugin in our project we already get two source sets: main and test. For compiling the test sources there is a dependency to the main source set. IntelliJ now knows how to handle this.

Let’s create a sample Gradle build file with an extra custom source set and see what we get in IntelliJ IDEA. In the following example build file we add the source set api. This source set contains interfaces without implementations. The implementations for the interfaces are in the default main source set. Finally we have some tests in the test source set that depend on the classes generated by the api and main source sets.

apply plugin: 'groovy'
apply plugin: 'idea'

sourceSets {
    // Define new source set
    // with the name api. This
    // source set contains interfaces,
    // implementation classes are in
    // the main source set.
    api
}

repositories {
    jcenter()
}

dependencies {

    // The default source set main
    // has a compile dependency on the
    // output of the api source set.
    // Gradle will invoke the apiClasses
    // task automatically if we compile
    // the sources in the main source set.
    compile sourceSets.api.output

    testCompile 'org.spockframework:spock-core:1.0-groovy-2.4'
}

When we invoke the idea task to generate the IntelliJ IDEA project files or we import the build.gradle file into IDEA we can see in the Gradle tool window a new element: Source Sets. When we open the node we see all the source sets in our project with their dependencies:

When we right click on the project and select Open Module Settings we get a new dialog window. All source sets are separate modules grouped into a single module. In our example the module group is idea-sourcesets and it has three modules. If we click on the idea-sourcesets_test and select the Dependencies tab we see a list of dependencies for the source set:

Written with Gradle 2.12 and IntelliJ IDEA 2016.1.

Original blog post written on March 18, 2016.

Miscellaneous

Parse Output from Exec Invocation

We can run executable commands from Gradle build scripts with the exec() method. We pass a closure with the name of the executable and arguments to the method. The executable is than executed with the given arguments and we get a ExecResult object back. If we want to grap the output from the executable and use it for example to do some parsing we can assign a custom outputstream to the executable. The output of the executable is than written to our outputstream and when the process is finished we can manipulate the content of the outputstream.

In the following sample script we invoke svn info to get Subversion information. We parse the output and get the last revision number from the output.

// File: build.gradle
task svninfo << {
    new ByteArrayOutputStream().withStream { os ->
        def result = exec {
            executable = 'svn'
            args = ['info']
            standardOutput = os
        }
        def outputAsString = os.toString()
        def matchLastChangedRev = outputAsString =~ /Last Changed Rev: (\d+)/
        println "Latest Changed Revision #: ${matchLastChangedRev[0][1]}"
    }
}

// Example output for svn info:
// Path: .
// URL: http://svn.host/svn/project
// Repository Root: http://svn.host/svn/
// Repository UUID: 9de3ae54-a9c2-4644-a1a1-838cb992bc8e
// Revision: 33
// Node Kind: directory
// Schedule: normal
// Last Changed Author: mrhaki
// Last Changed Rev: 33
// Last Changed Date: 2010-09-03 14:25:41 +0200 (Fri, 03 Sep 2010)

Written with Gradle 0.9-rc-1.

Original blog post written on October 07, 2010.

Define A Short Plugin Id For Custom Plugins

We can extend Gradle easily with the Gradle plugin system. We can write plugins and apply them on our build scripts with the apply() method. We can use the complete classname of the plugin as an argument, but also a short plugin id. For example if we want to apply the Java plugin we can write apply plugin: 'java'. ‘java’ is the short plugin id for the class org.gradle.api.plugins.JavaPlugin.

We can define a short plugin id for our own custom plugins as well, so it is much easier to apply them in our scripts. In the classpath of our plugin we must add the directory META-INF/gradle-plugins. In this directory we create a properties file where the name is equal to the short plugin id we want to use. Inside the property file we must define a single property implementation-class that points to our plugin class.

Let’s see how we can do this in the following sample. We first create a very simple Gradle plugin which only adds a task demo to the project. The task prints out the message MrhakiPlugin says hi!.

// File: buildSrc/src/main/groovy/com/mrhaki/gradle/MrHakiPlugin.groovy
package com.mrhaki.gradle

import org.gradle.api.*

class MrHakiPlugin implements Plugin<Project> {
    def void apply(Project project) {
        project.task('demo') << {
            println "MrhakiPlugin says hi!"
        }
    }
}

Next we create the properties file mrhaki.properties, because we want to use mrhaki as the short plugin id.

# File: buildSrc/src/main/resources/META-INF/gradle-plugins/mrhaki.properties
implementation-class=com.mrhaki.gradle.MrHakiPlugin

And that is all we need to do. Let’s create a build.gradle script and execute our plugin:

// File: build.gradle
apply plugin: 'mrhaki'
$ gradle -q demo
MrhakiPlugin says hi!

Written with Gradle 0.9-rc-1.

Original blog post written on September 24, 2010.

Base Plugin Usage

Gradle has plugins to provide functionality in a modularized way. One of the basic plugins is the base plugin.

When we apply the base plugin to our project we get a couple of tasks we can use:

assemble task that builds all archives of the project. clean task that deletes the build directory.

We get two configurations:

archives configuration for archives. default extends archives configuration.

For each configuration we get a build and upload task:

buildArchives and buildDefault builds all artifacts for the configuration. uploadArchives and uploadDefault upload artifacts for the configuration.

And we get a task rule clean<taskname> that is capable of cleaning the ouput files of any task we create in our project.

Also the following properties are added to the project:

distDirName directory to store archives created by the project. libsDirName directory to store JAR files created by the project. archivesBaseName base name for archives.

In the following sample we define a project with the base plugin and use the tasks, configurations and properties that are added by the plugin:

apply plugin: 'base'

version = '1.0'

archivesBaseName = 'sample'
distsDirName = 'dist'
libsDirName = 'projectlibs'

task simpleJar(type: Jar)

task simpleZip(type: Zip)

artifacts {
    archives simpleJar
}

repositories {
    flatDir name: 'localRepo', dirs: "$buildDir/repository"
}

uploadArchives {
    repositories {
        add project.repositories.localRepo
    }
}

Let’s run gradle with a couple of the tasks:

$ gradle clean assemble buildDefault uploadArchives cleanSimpleJar
:clean
:simpleJar
:simpleZip
:assemble
:buildDefault
:uploadArchives
:cleanSimpleJar

BUILD SUCCESSFUL

Total time: 3.416 secs

If we look in the build directory we see what is created:

build
  |
  +- dist
  |    |
  |    +- sample-1.0.zip
  |
  +- projectlibs
  |
  +- repository
       |
       +- sample-1.0.jar

Written with Gradle 0.9-rc-1.

Original blog post written on October 26, 2010.

Apply External Script With Plugin Configured Through Buildscript

Suppose we use the Gradle apply from: statement to import another Gradle build file into our build file. The external build file uses a Gradle plugin that needs a buildscript block to define the classpath configuration with the classes needed for the plugin. We cannot use the plugin id inside our external build script to use it, but we must use the type of the plugin. Otherwise Gradle cannot resolve the plugin from the main build file.

Let’s create a simple Gradle build that we want to include in our main Gradle build file:

// File: gradle/extra.gradle
buildscript {
    repositories.jcenter()
    dependencies.classpath 'com.bmuschko:gradle-docker-plugin:2.6.1'
}

// We use the type of the plugin instead of the id.
// This is the class that defines the plugin. We can leave of
// .class, because Gradle uses Groovy.
apply plugin: com.bmuschko.gradle.docker.DockerRemoteApiPlugin

// The following statement doesn't work if this file
// is included via apply from: in another Gradle build file.
// apply plugin: 'com.bmuschko.docker-remote-api'

...

In the following Gradle build file we import this script:

// File: build.gradle
apply from: 'gradle/extra.gradle'
...

Written with Gradle 2.8.

Original blog post written on October 21, 2015.

Init Script for Adding Extra Plugins to Existing Projects

Gradle is very flexible. One of the ways to alter the build configuration is with initialization or init scripts. These are like other Gradle scripts but are executed before the build. We can use different ways to add the init script to a build. For example we can use the command-line option -I or --init-script, place the script in the init.d directory of our GRADLE_HOME directory or USER_HOME/.gradle directory or place a file init.gradle in our USER_HOME/.gradle directory.

We can also use the apply method to include such a script in our build file. We can reference a file location, but also a URL. Imagine we place an init script on our company intranet to be shared by all developers, then we can include the script with the apply(from:) method. In the following build file we use this syntax to include the script:

apply plugin: 'java'
apply from: 'http://intranet/source/quality.gradle'

version = '2.1.1'
group = 'com.mrhaki.gradle.sample

The following script is an init script where we add the Checkstyle plugin to projects with the Java plugin and the Codenarc plugin to projects with the Groovy plugin. Because the Groovy plugin extends the Java plugin the Checkstyle plugin is added to the Groovy project as well.

Notice we also add the downloadCheckstyleConfig task. With this task we download from the intranet the Checkstyle configuration that needs to be used by the Checkstyle tasks.

// File: quality.gradle
allprojects {
    afterEvaluate { project ->
        def groovyProject = project.plugins.hasPlugin('groovy')
        def javaProject = project.plugins.hasPlugin('java')

        if (javaProject) {
            // Add Checkstyle plugin.
            project.apply plugin: 'checkstyle'

            // Task to download common Checkstyle configuration
            // from company intranet.
            task downloadCheckstyleConfig(type: DownloadFileTask) {
                description = 'Download company Checkstyle configuration'

                url = 'http://intranet/source/company-style.xml'
                destinationFile = checkstyle.configFile
            }

            // For each Checkstyle task we make sure
            // the company Checkstyle configuration is
            // first downloaded.
            tasks.withType(Checkstyle) {
                it.dependsOn 'downloadCheckstyleConfig'
            }
        }

        if (groovyProject) {
            // Add Codenarc plugin.
            project.apply plugin: 'codenarc'
        }
    }
}

class DownloadFileTask extends DefaultTask {
    @Input
    String url

    @OutputFile
    File destinationFile

    @TaskAction
    def downloadFile() {
        destinationFile.bytes = new URL(url).bytes
    }
}

Code written with Gradle 1.2

Original blog post written on October 24, 2012.

Alter Start Scripts from Application Plugin

For Java or Groovy projects we can use the application plugin in Gradle to run and package our application. The plugin adds for example the startScripts task which creates OS specific scripts to run the project as a JVM application. This task is then used again by the installDist that installs the application, and distZip and distTar tasks that create a distributable archive of the application. The startScripts tasks has the properties unixScript and windowsScript that are the actual OS specific script files to run the application. We can use these properties to change the contents of the files.

In the following sample we add the directory configuration to the CLASSPATH definition:

...
startScripts {

    // Support closures to add an additional element to
    // CLASSPATH definition in the start script files.
    def configureClasspathVar = { findClasspath, pathSeparator, line ->

        // Looking for the line that starts with either CLASSPATH=
        // or set CLASSPATH=, defined by the findClasspath closure argument.
        line = line.replaceAll(~/^${findClasspath}=.*$/) { original ->

            // Get original line and append it
            // with the configuration directory.
            // Use specified path separator, which is different
            // for Windows or Unix systems.
            original += "${pathSeparator}configuration"
        }

    }

    def configureUnixClasspath = configureClasspathVar.curry('CLASSPATH', ':')
    def configureWindowsClasspath = configureClasspathVar.curry('set CLASSPATH', ';')

    // The default script content is generated and
    // with the doLast method we can still alter
    // the contents before the complete task ends.
    doLast {

        // Alter the start script for Unix systems.
        unixScript.text =
            unixScript
                .readLines()
                .collect(configureUnixClasspath)
                .join('\n')

        // Alter the start script for Windows systems.
        windowsScript.text =
            windowsScript
                .readLines()
                .collect(configureWindowsClasspath)
                .join('\r\n')

    }

}
...

This post was inspired by the Gradle build file I saw at the Gaiden project.

Written with Gradle 2.3.

Original blog post written on April 19, 2015.

Set a Project Description

We can use the description property of a Gradle project to describe the purpose of our project.

version = '1.0-bugfix'

description = """\
Simple Gradle project to show how we can
use the description property of a project.
------------------------------------------
Project version: ${version}
Gradle version: ${gradle.gradleVersion}
------------------------------------------
"""

When we invoke $ gradle projects we get to see our project description in the output.

$ gradle -q projects

Root project 'description' - Simple Gradle project to show how we can
use the description property of a project.
------------------------------------------
Project version: 1.0-bugfix
Gradle version: 0.9-rc-2
------------------------------------------

No sub-projects

To see a list of the tasks of a project, run gradle <project-path>:tasks
For example, try running gradle :tasks

Written with Gradle 0.9-rc-2.

Original blog post written on November 08, 2010.

Using and Working with Gradle Version

To get the current Gradle version we can use the gradleVersion property of the Gradle object. This returns a string value we can use for displaying the values. If we want to compare Gradle versions we can use the GradleVersion object. With this class we can get the current version, but we can also compare Gradle versions. This can be useful in our build scripts if we have functionality based on a Gradle version.

In the following build file we first have a task that uses the gradleVersion of Gradle. Then inside the task we use the static method current of the GradleVersion class. We get an GradleVersion instance and we display different properties from this instance. In the task compareGradleVersion we create a GradleVersion instance with the static version method. We compare multiple GradleVersion objects and have different functionality based on the Gradle version.

task printGradleVersion << {
    // Get current Gradle version as object.
    final GradleVersion gradleVersion = GradleVersion.current()

    // Print different properties.
    println "Your Gradle version is ${gradleVersion.version}"
    println "Base version: ${gradleVersion.baseVersion}"
    println "Build time  : ${gradleVersion.buildTime}"
    println "Build number: ${gradleVersion.buildNumber}"
    println "Commit id   : ${gradleVersion.revision}"
    println "Next major  : ${gradleVersion.nextMajor}"
    println "Snapshot?   : ${gradleVersion.snapshot}"
}

task compareGradleVersion << {
    // Current Gradle version.
    final GradleVersion gradleVersion = GradleVersion.current()

    // Gradle version 2.1 as GradleVersion object.
    final GradleVersion gradle2_1 = GradleVersion.version('2.1')

    // Compare versions.
    if (gradleVersion > gradle2_1) {
        println "Your Gradle version is newer than 2.1"
    } else if (gradleVersion == gradle2_1) {
        println "Your Gradle version is 2.1"
    } else {
        println "Your Gradle version is older than 2.1"
    }
}

When we run the tasks we get the following output:

$ gradle -q printGradleVersion
Gradle version is 2.2

Your Gradle version is 2.2
Base version: Gradle 2.2
Build time  : 2014-11-10 13:31:44 UTC
Build number: none
Commit id   : aab8521f1fd9a3484cac18123a72bcfdeb7006ec
Next major  : Gradle 3.0
Snapshot?   : false
$ gradle -q compareGradleVersion
Your Gradle version is newer than 2.1
$

Thanks to John Engelman who showed me this class on a pull request for the Gradle Grails plugin.

Written with Gradle 2.2.

Original blog post written on November 24, 2014.

Rename Ant Task Names When Importing Ant Build File

Migrating from Ant to Gradle is very easy with the importBuild method from AntBuilder. We only have to add this single line and reference our existing Ant build XML file and all Ant tasks can now be executed as Gradle tasks. We can automatically rename the Ant tasks if we want to avoid task name collisions with Gradle task names. We use a closure argument with the importBuild method and return the new task names. The existing Ant task name is the first argument of the closure.

Let’s first create a simple Ant build.xml file:

<project>

    <target name="showMessage"
        description="Show simple message">

        <echo message="Running Ant task 'showMessage'"/>

    </target>

    <target name="showAnotherMessage"
        depends="showMessage"
        description="Show another simple message">

        <echo message="Running Ant task 'showAnotherMessage'"/>

    </target>

</project>

The build file contains two targets: showMessage and showAnotherMessage with a task dependency. We have the next example Gradle build file to use these Ant tasks and prefix the original Ant task names with ant-:

// Import Ant build and
// prefix all task names with
// 'ant-'.
ant.importBuild('build.xml') { antTaskName ->
    "ant-${antTaskName}".toString()
}

// Set group property for all
// Ant tasks.
tasks.matching { task ->
    task.name.startsWith('ant-')
}*.group = 'Ant'

We can run the tasks task to see if the Ant tasks are imported and renamed:

$ gradle tasks --all
...
Ant tasks
---------
ant-showAnotherMessage - Show another simple message [ant-showMessage]
ant-showMessage - Show simple message
...
$

We can execute the ant-showAnotherMessage task and we get the following output:

$ gradle ant-showAnotherMessage
:ant-showMessage
[ant:echo] Running Ant task 'showMessage'
:ant-showAnotherMessage
[ant:echo] Running Ant task 'showAnotherMessage'

BUILD SUCCESSFUL

Total time: 3.953 secs
$

Written with Gradle 2.2.1

Original blog post written on December 24, 2014.

Use Git Commit Id in Build Script

The nice thing about Gradle is that we can use Java libraries in our build script. This way we can add extra functionality to our build script in an easy way. We must use the classpath dependency configuration for our build script to include the library. For example we can include the library Grgit, which provides an easy way to interact with Git from Java or Groovy code. This library is also the basis for the Gradle Git plugin.

In the next example build file we add the Grgit library to our build script classpath. Then we use the open method of the Grgit class. From the returned object we invoke the head to get the commit id identified as id. With the abbreviatedId property we get the shorter version of the Git commit id. The build file also includes the application plugin. We customize the applicationDistribution CopySpec from the plugin and expand the properties in a VERSION file. This way our distribution always includes a plain text file VERSION with the Git commit id of the code.

buildscript {

    repositories {
        jcenter()
    }

    dependencies {
        // Add dependency for build script,
        // so we can access Git from our
        // build script.
        classpath 'org.ajoberstar:grgit:1.1.0'
    }

}

apply plugin: 'java'
apply plugin: 'application'

ext {
    // Open the Git repository in the current directory.
    git = org.ajoberstar.grgit.Grgit.open(file('.'))

    // Get commit id of HEAD.
    revision = git.head().id
    // Alternative is using abbreviatedId of head() method.
    // revision = git.head().abbreviatedId
}

// Use abbreviatedId commit id in the version.
version = "2.0.1.${git.head().abbreviatedId}"

// application plugin extension properties.
mainClassName = 'sample.Hello'
applicationName = 'sample'

// Customize applicationDistribution
// CopySpec from application plugin extension.
applicationDistribution.with {
    from('src/dist') {
        include 'VERSION'
        expand(
            buildDate: new Date(),
            // Use revision with Git commit id:
            revision : revision,
            version  : project.version,
            appName  : applicationName)
    }
}

// Contents for src/dist/VERSION:
/*
Version: ${version}
Revision: ${revision}
Build-date: ${buildDate.format('dd-MM-yyyy HH:mm:ss')}
Application-name: ${appName}
*/

assemble.dependsOn installDist

When we run the build task for our project we get the following contents in our VERSION file:

Version: 2.0.1.e2ab261
Revision: e2ab2614011ff4be18c03e4dc1f86ab9ec565e6c
Build-date: 22-04-2015 13:53:31
Application-name: sample

Written with Gradle 2.3.

Original blog post written on April 22, 2015.

Getting Announcements from Gradle Build

We can use the Gradle announce plugin to send announcements from the build process. We can send data to Twitter (I don’t know if our followers are waiting for this, but if you want to you can), but also to notification applications on our local computers. For Mac OSX Growl is supported, for Linux notify-send and for Windows Snarl.

The plugin adds an announce object with the announce() method. The method accepts two arguments. The first argument is the message and the second argument is either twitter or local to indicate where to send the announcement.

apply plugin: 'announce'

task info {
    doLast {
        announce.announce "Running $it.name", 'local'
        println gradle.gradleVersion
    }
}

Here we see the announcement as Growl message:

We can also get an announcement object for only sending announcement to the local notification applications. We can use a send() method that accepts a title for the announcement as first argument and the message as second argument. To get the local announcement object we invoke announce.local:

apply plugin: 'announce'

task info {
    doLast {
        // Now we can specify a title and message
        announce.local.send "Gradle Info Task", 'Running'
        println gradle.gradleVersion
    }
}

To automatically send out notifications when a task is executed we can implement the TaskExecutionListener interface. From the implementation we can use the announce.local object. In the following example build file we create the class TaskAnnouncer and use the addTaskExecutionListener() method to add it to the TaskExecutionGraph available through gradle.taskGraph:

apply {
    plugin 'announce'
}

gradle.taskGraph.addTaskExecutionListener new TaskAnnouncer(localAnnouncer: announce.local)

task info {
    doLast {
        println gradle.gradleVersion
    }
}

class TaskAnnouncer implements TaskExecutionListener {
    Announcer localAnnouncer

    @Override
    void afterExecute(final Task task, final TaskState state) {
        String message
        if (state.failure) {
            message = "Failure: $state.failure.message"
        } else if (state.executed) {
            message = 'Done'
        } else if (state.skipped) {
            message = "Skipped: $state.skipMessage"
        }
        send task, message
    }

    @Override
    void beforeExecute(final Task task) {
        send task, 'Ready to run'
    }

    private void send(final Task task, final String message) {
        final String title = "Gradle build: $task.project.name:$task.name"
        localAnnouncer.send title, message
    }
}

Automatically announce build results

To get the build results after running a build we only have to apply the build-announcements plugin to our Gradle build. This plugin uses the local notification applications to send out a message with a summary of the build. If the build failed we get a message with the task name that failed. For a successful build we can see how many task were executed.

apply {
    plugin 'announce'
    plugin 'build-announcements'
}

task info {
    doLast {
        println gradle.gradleVersion
    }
}

The following screenshots show the result of a successful and non-successful build:

Apply for all Gradle builds

To add the plugins to all Gradle builds on our local computer we can create a so-called init script. Init scripts are executed before a project build script. We can place the init scripts at several locations. Let’s create a new init script announce.gradle in our $USER_HOME/.gradle/init.d directory. If we don’t have this directory yet, we can create it ourselves. All files in this directory are treated as init scripts by Gradle and are executed automatically. Here is the contents of the announce.gradle script:

rootProject {
    apply {
        plugin 'announce'
        plugin 'build-announcements'
    }
}

Written with Gradle 1.2.

Original blog post written on October 05, 2012.

Extending DSL

Gradle already has a powerful DSL, but Gradle wouldn’t be Gradle if we couldn’t extend the DSL ourselves. Maybe we have our own naming conventions in our company or we have a special problem domain we want to express in a Gradle build script. We can use the ExtensionContainer, available via project.extensions, to add new concepts to our build scripts. In the Standardizing your enterprise build environment webinar by Luke Daley some examples are shown on how to extend the DSL. Also in the samples folder of the Gradle distribution are examples on how to create a custom DSL.

Let’s first create a simple DSL extension. We first define a new class CommonDependencies with methods to define dependencies in a Java project. We want to use these methods with descriptive names in our build scripts. To add the class we use the create() method of the ExtensionContainer. The first argument is a name that needs to be unique within the build. The name can be used together with a configuration block in the script to invoke methods on the class we pass as the second argument. Finally we can pass constructor arguments for the class as last arguments of the create() method.

/**
 * Class for DSL extension. A default repository is added
 * to the project. The use<name>() methods add
 * dependencies to the project.
 */
class CommonDependencies {
    /** Reference to project, so we can set dependencies/repositories */
    final Project project

    CommonDependencies(final Project project) {
        this.project = project

        // Set mavenCentral() repository for project.
        project.repositories {
            mavenCentral()
        }
    }

    /**
     * Define Spock for testCompile dependency
     * @param version Version of Spock dependency with default 0.7-groovy-2.0
     */
    void useSpock(final String version = '0.7-groovy-2.0') {
        project.dependencies {
            testCompile "org.spockframework:spock-core:$version"
        }
    }

    /**
     * Define Spring for compile dependency
     * @param version Version of Spring dependency with default 3.2.3.RELEASE
     */
    void useSpring(final String version = '3.2.3.RELEASE') {
        project.dependencies {
            compile "org.springframework:spring-core:$version"
        }
    }

}

// Add DSL extension 'commonDependencies' with class CommonDependencies
// passing project as constructor argument.
project.extensions.create('commonDependencies', CommonDependencies, project)

apply plugin: 'java'

// Use new DSL extension. Notice we can use configuration closures just
// like we are used to with other Gradle DSL methods.
commonDependencies {
    useSpock()
    useSpring '3.1.4.RELEASE'
}

// We can still use the Java plugin dependencies configuration.
dependencies {
    compile 'joda-time:joda-time:2.1'
}

We can invoke the dependencies task from the command-line and we see all dependencies are resolved correctly:

$ gradle dependencies
...
compile - Compile classpath for source set 'main'.
+--- org.springframework:spring-core:3.1.4.RELEASE
|    +--- org.springframework:spring-asm:3.1.4.RELEASE
|    \--- commons-logging:commons-logging:1.1.1
\--- joda-time:joda-time:2.1
...
testCompile - Compile classpath for source set 'test'.
+--- org.springframework:spring-core:3.1.4.RELEASE
|    +--- org.springframework:spring-asm:3.1.4.RELEASE
|    \--- commons-logging:commons-logging:1.1.1
+--- joda-time:joda-time:2.1
\--- org.spockframework:spock-core:0.7-groovy-2.0
     +--- junit:junit-dep:4.10
     |    \--- org.hamcrest:hamcrest-core:1.1 -> 1.3
     +--- org.codehaus.groovy:groovy-all:2.0.5
     \--- org.hamcrest:hamcrest-core:1.3
...

We can also use a plugin to extend the Gradle DSL. In the plugin code we use the same project.extensions.create() method so it is more transparent for the user. We only have to apply the plugin to a project and we can use the extra DSL methods in the build script. Let’s create a simple plugin that will extend the DSL with the concept of a book and chapters. The following build script shows what we can do after we have applied the plugin:

apply plugin: 'book'

book {
    title 'Groovy Goodness Notebook'
    chapter project(':chapter1')
    chapter project(':chapter2')
}

To achieve this we first create the following directory structure with files:

+ sample
  + buildSrc
     + src/main/groovy/com/mrhaki/gradle
       + Book.groovy
       + BookPlugin.groovy
     + src/main/resources/META-INF/gradle-plugins
       + book.properties
  + book
    + build.gradle
  + chapter1/src/html
    + index.html
  + chapter2/src/html
    + index.html
  + settings.gradle

The Book class will be added as DSL extension. The class has a method to set the title property and a method to add chapters which are Gradle project objects.

// File: buildSrc/src/main/groovy/com/mrhaki/gradle/Book.groovy
package com.mrhaki.gradle

import org.gradle.api.*

class Book {
    String title
    List<Project> chapters = []

    void title(final String title) {
        this.title = title
    }

    void chapter(final Project chapter) {
        chapters << chapter
    }
}

Next we create the BookPlugin class. The plugin will add the Book class as DSL extension. But we also create a task aggregate that will visit each chapter that is defined and then copies the content from the scr/html folder in the chapter project to the aggregate folder in the build folder. Finally we add a dist task that will simply archive the contents of the aggregated files.

// File: buildSrc/src/main/groovy/com/mrhaki/gradle/BookPlugin.groovy
package com.mrhaki.gradle

import org.gradle.api.*
import org.gradle.api.tasks.*
import org.gradle.api.tasks.bundling.Zip

class BookPlugin implements Plugin<Project> {
    void apply(Project project) {
        project.configure(project) {
            apply plugin: 'base'

            def book = project.extensions.create 'book', Book

            afterEvaluate {
                // Create task in afterEvaluate, so chapter projects
                // are resolved, otherwise chapters is empty.
                tasks.create(name: 'aggregate') {

                    // Skip task if no chapters are defined.
                    onlyIf { !book.chapters.empty }

                    doFirst {
                        // Copy content in src/html of 'book' directory.
                        copy {
                            from file('src/html')
                            into file("${buildDir}/aggregate")
                        }

                        // Copy content in src/html of chapter directories.
                        book.chapters.each { chapterProject ->
                            copy {
                                from chapterProject.file('src/html')
                                into file("${buildDir}/aggregate/${chapterProject.name}")
                            }
                        }
                    }
                }
            }

            tasks.create(name: 'dist', dependsOn: 'aggregate', type: Zip) {
                from file("${buildDir}/aggregate")
            }
        }
    }
}

We create the file book.properties to tell Gradle about our new plugin:

# File: buildSrc/src/main/resources/META-INF/gradle-plugins/book.properties
implementation-class=com.mrhaki.gradle.BookPlugin

Our plugin is finished, so we can add a book project and some chapter projects. In the settings.gradle file we define an inclusion for these directories:

// File: settings.gradle
include 'chapter1'
include 'chapter2'
include 'book'

In the chapter directories we can add some sample content in the src/html directories. And in the book folder we create the following build.gradle file:

// File: book/build.gradle
apply plugin: 'book'

book {
    title 'Groovy Goodness Notebook'
    chapter project(':chapter1')
    chapter project(':chapter2')
}

Now from the book folder we can run the aggregate and dist tasks. The end result is that all files from the chapter src/html folder are in the build/aggregate folder. And in the build/distributions folder we have the file book.zip containing the files.

Code written with Gradle 1.6.

Original blog post written on May 29, 2013.

Create Objects Using DSL With Domain Object Containers

Gradle offers the NamedDomainObjectContainer class to create a collection of objects defined using a clean DSL. The only requirement for the objects we want to create is that they have a constructor that takes a String argument and a name property to identify the object. The value for the name property must be unique within the collection of objects. We create a new instance of a NamedDomainObjectContainer with the container method of the Gradle Project class. We can add the NamedDomainObjectContainer instance to the extensions property of our project, so we can use a DSL to create instances of objects that need to be in the NamedDomainObjectContainer object in our project.

The following code shows a simple build script in which we want to create a collection of Product objects. The creation of the NamedDomainObjectContainer object is done in a plugin so we only have to apply the plugin to use the DSL to create Product objects:

apply plugin: ProductsPlugin

// DSL to define new objects of type Product.
products {
    // Create Product with name pencil.
    pencil {
        price = 0.05
    }
    // Create Product with name crayon.
    crayon {
        price = 0.18
    }
}


class ProductsPlugin implements Plugin<Project> {
    void apply(final Project project) {
        // Create NamedDomainObjectContainer instance for
        // a collection of Product objects
        NamedDomainObjectContainer<Product> productContainer =
            project.container(Product)

        // Add the container instance to our project
        // with the name products.
        project.extensions.add('products', productContainer)

        // Simple task to show the Product objects
        // that are created by Gradle using
        // the DSL syntax in our build file.
        project.task('reportProducts') << {
            def products = project.extensions.getByName('products')

            products.all {
                // A Product instance is the delegate
                // for this closure.
                println "$name costs $price"
            }
        }
    }
}

class Product {
    // We need a name property
    // so the object can be created
    // by Gradle using a DSL.
    String name

    BigDecimal price

    Product(final String name) {
        this.name = name
    }
}

We can run the reportProducts task to see the name and price properties of the Product instances:

$ gradle -q reportProducts
crayon costs 0.18
pencil costs 0.05
$

Written with Gradle 2.11.

Original blog post written on February 19, 2016.

Using Nested Domain Object Containers

In a previous post we learned how to use the NamedDomainObjectContainer class. We could create new objects using a nice DSL in Gradle. But what if we want to use DSL syntax to create objects within objects? We can use the same mechanism to achieve this by nesting NamedDomainObjectContainer objects.

We want to support the following DSL to create a collection of Server objects, where each server can have multiple Node objects:

// File: build.gradle
apply plugin: com.mrhaki.gradle.DeploymentPlugin

deployments {
    aws {
        url = 'http://aws.address'

        nodes {
            node1 {
                port = 9000
            }
            node2 {
                port = 80
            }
        }
    }

    cf {
        url = 'http://cf.address'

        nodes {
            test {
                port = 10001
            }
            acceptanceTest {
                port = 10002
            }
        }
    }
}

This would create two Server objects with then names aws and cf. Each server has Node objects with names like node1, node2, test and acceptanceTest. Let’s look at the Server class where we have added a nodes property of type NamedDomainObjectContainer as the nested object container. Also notice the nodes method so we can use the DSL syntax to create Node objects.

// File: buildSrc/src/main/groovy/com/mrhaki/gradle/Server.groovy
package com.mrhaki.gradle

import org.gradle.api.NamedDomainObjectContainer

class Server {

    /**
     * An instance is created in the plugin class, because
     * there we have access to the container() method
     * of the Project object.
     */
    NamedDomainObjectContainer<Node> nodes

    String url

    String name

    /**
     * We need this constructor so Gradle can create an instance
     * from the DSL.
     */
    Server(String name) {
        this.name = name
    }

    /**
     * Inside the DSL this method is invoked. We use
     * the configure method of the NamedDomainObjectContainer to
     * automatically create Node instances.
     * Notice this is a method not a property assignment.
     * <pre>
     * server1 {
     *     url = 'http://server1'
     *     nodes { // This is the nodes() method we define here.
     *         port = 9000
     *     }
     * }
     * </pre>
     */
    def nodes(final Closure configureClosure) {
        nodes.configure(configureClosure)
    }

}

And the Node class:

// File: buildSrc/src/main/groovy/com/mrhaki/gradle/Node.groovy
package com.mrhaki.gradle

class Node {

    String name

    Integer port

    /**
      * We need this constructor so Gradle can create an instance
      * from the DSL.
      */
    Node(String name) {
        this.name = name
    }
}

To make the DSL work we use a custom plugin so we can add the DSL for creating the objects to our project:

// File: buildSrc/src/main/groovy/com/mrhaki/gradle/DeploymentPlugin.groovy
package com.mrhaki.gradle

import org.gradle.api.Project
import org.gradle.api.Plugin
import org.gradle.api.NamedDomainObjectContainer

class DeploymentPlugin implements Plugin<Project> {

    public static final String EXTENSION_NAME = 'deployments'

    private static final String DEPLOY_TASK_PATTERN = 'deployOn%sTo%s'

    private static final String REPORTING_TASK_NAME = 'reportDeployments'

    private static final String TASK_GROUP_NAME = 'Deployment'

    void apply(final Project project) {
        setupExtension(project)
        createDeploymentTasks(project)
        createReportTask(project)
    }

    /**
     * Create extension on the project for handling the deployments
     * definition DSL with servers and nodes. This allows the following DSL
     * in our build script:
     * <pre>
     * deployments {
     *     server1 {
     *         url = 'http://server'
     *         nodes {
     *             node1 {
     *                 port = 9000
     *             }
     *         }
     *     }
     * }
     * </pre>
     */
    private void setupExtension(final Project project) {

        // Create NamedDomainObjectContainer for Server objects.
        // We must use the container() method of the Project class
        // to create an instance. New Server instances are
        // automatically created, because we have String argument
        // constructor that will get the name we use in the DSL.
        final NamedDomainObjectContainer<Server> servers =
            project.container(Server)

        servers.all {
            // Here we have access to the project object, so we
            // can use the container() method to create a
            // NamedDomainObjectContainer for Node objects.
            nodes = project.container(Node)
        }

        // Use deployments as name in the build script to define
        // servers and nodes.
        project.extensions.add(EXTENSION_NAME, servers)
    }

    /**
     * Create a new deployment task for each node.
     */
    private void createDeploymentTasks(final Project project) {
        def servers = project.extensions.getByName(EXTENSION_NAME)

        servers.all {
            // Actual Server instance is the delegate
            // of this closure. We assign it to a variable
            // so we can use it again inside the
            // closure for nodes.all() method.
            def serverInfo = delegate

            nodes.all {
                // Assign this closure's delegate to
                // variable so we can use it in the task
                // configuration closure.
                def nodeInfo = delegate

                // Make node and server names pretty
                // for use in task name.
                def taskName =
                    String.format(
                        DEPLOY_TASK_PATTERN,
                        name.capitalize(),
                        serverInfo.name.capitalize())

                // Create new task for this node.
                project.task(taskName, type: DeploymentTask) {
                    description = "Deploy to '${nodeInfo.name}' on '${serverInfo.name}'"
                    group = TASK_GROUP_NAME

                    server = serverInfo
                    node = nodeInfo
                }
            }
        }
    }

    /**
     * Add reporting task to project.
     */
    private void createReportTask(final Project project) {
        project.task(REPORTING_TASK_NAME, type: DeploymentReportTask) {
            description = 'Show configuration of servers and nodes'
            group = TASK_GROUP_NAME
        }
    }
}

We also have two custom tasks that use the Server and Node instances that are created by the DSL in our build file. The DeploymentTask is configured from the plugin where the server and node properties are set:

// File: buildSrc/src/main/groovy/com/mrhaki/gradle/DeploymentTask.groovy
package com.mrhaki.gradle

import org.gradle.api.tasks.TaskAction
import org.gradle.api.DefaultTask

class DeploymentTask extends DefaultTask {

    Server server

    Node node

    /**
     * Simple implementation to show we can
     * access the Server and Node instances created
     * from the DSL.
     */
    @TaskAction
    void deploy() {
        println "Deploying to ${server.url}:${node.port}"
    }

}

The DeploymentReportTask references the project extensions to get a hold of the Server and Node objects:

// File: buildSrc/src/main/groovy/com/mrhaki/gradle/DeploymentReportTask.groovy
package com.mrhaki.gradle

import org.gradle.api.tasks.TaskAction
import org.gradle.api.DefaultTask

class DeploymentReportTask extends DefaultTask {

    /**
     * Simple task to show we can access the
     * Server and Node instances also via the
     * project extension.
     */
    @TaskAction
    void report() {
        def servers = project.extensions.getByName(DeploymentPlugin.EXTENSION_NAME)

        servers.all {
            println "Server '${name}' with url '${url}':"

            nodes.all {
                println "\tNode '${name}' using port ${port}"
            }
        }
    }

}

Let’s run the tasks task first to see which tasks are added by the plugin. Next we invoke some tasks:

$ gradle -q tasks
...
Deployment tasks
----------------
deployOnAcceptanceTestToCf - Deploy to 'acceptanceTest' on 'cf'
deployOnNode1ToAws - Deploy to 'node1' on 'aws'
deployOnNode2ToAws - Deploy to 'node2' on 'aws'
deployOnTestToCf - Deploy to 'test' on 'cf'
reportDeployments - Show configuration of servers and nodes
...
$ gradle -q deployOnNode2ToAws

Deploying to http://aws.address:80
$ gradle -q reportDeployments

Server 'aws' with url 'http://aws.address':
 Node 'node1' using port 9000
 Node 'node2' using port 80
Server 'cf' with url 'http://cf.address':
 Node 'acceptanceTest' using port 10002
 Node 'test' using port 10001
$

Written with Gradle 2.11.

Original blog post written on February 19, 2016.

Distribute Custom Gradle in Our Company

The Gradle wrapper allows us to let developers use Gradle without the need for every developer to install Gradle. We can add the output of the Gradle wrapper task to version control. Developers only need to checkout the source for a project and invoke the gradlew or gradlew.bat scripts. The scripts will look for a Gradle distribution and download it to the local computer of a developer. We can customize the Gradle wrapper and provide a different source for the Gradle distribution. For example we can add the Gradle distribution ZIP file on our company intranet. We then use the distributionUrl property of the Wrapper task to reference the intranet location where we place the Gradle distribution ZIP file.

In the following sample file we use the distributionUrl property to reference our company intranet:

task createWrapper(type: Wrapper) {
    distributionUrl = 'http://intranet/tools/gradle-1.2-bin.zip'
}

When we execute the createWrapper task the wrapper script files and supporting files are created in the project directory. It is fine to remove the createWrapper task from the build file. If we want to change the location of the Gradle distribution ZIP file or if the version changes we change the file gradle/wrapper/gradle-wrapper.properties. This file has a key distributionUrl. We can change the value of this key, save the file, add it to version control. If other developers checkout the project the new Gradle distribution location is used and they will use the Gradle version we want them to use.

In a previous post we saw an initialization or init script that could be applied for all projects that are developed in our company. To distribute this script we can customize the Gradle distribution, put it on our company intranet and use the Gradle wrapper properties to reference this customized Gradle distribution. It turns out it is very easy to create a customized Gradle distribution with custom init scripts, plugin code, classpath extensions or anything else that can be part of a Gradle distribution. The code in this post is based on the samples from Luke Daley’s presentation at SpringOne 2GX 2012. These samples can be found at Github.

In the following build file we customize a default Gradle distribution. First the Gradle distribution ZIP file for a specific Gradle version is downloaded. We create a new custom Gradle distribution ZIP file with a custom version number based on the downloaded ZIP file. We add a init script from the src/scripts/init.d directory. We can add also other files like JAR files to the custom Gradle distribution ZIP file. We have applied the base plugin and therefore can use configurations. We also reconfigure the uploadCompanyGradle task so we can use SCP to copy the resulting Gradle ZIP file to our company intranet webserver.

// Base plugin adds configuration
// support and build<Configuration>
// and upload<Configuration> tasks.
apply plugin: 'base'

// Version is the version of the
// company Gradle. This can be independent
// of the Gradle version.
version = '1.0.2'

ext {
    // Gradle version to use as template for
    // the custom Gradle distribution.
    gradleVersion = '1.2'
}

// Extra configuration to store
// company Gradle ZIP artifact and
// use for uploading.
configurations {
    companyGradle
}


task downloadGradle(type: DownloadGradle) {
    description 'Download Gradle version from Gradle distributions website'

    gradleVersion project.gradleVersion
    destinationDir file("$buildDir/gradle-downloads")
}

task companyGradleZip(type: Zip, dependsOn: downloadGradle) {
    description 'Add extra files to company Gradle distribution'

    // Name for company Gradle ZIP distribution.
    baseName = 'company-gradle'
    classifier = 'bin'

    from zipTree(downloadGradle.destinationFile)
    into("${downloadGradle.distributionNameBase}") {
        into('init.d') {
            from "src/scripts/init.d"
        }
        // We can do extra stuff here
        // to add to the company Gradle
        // distribution ZIP file.
    }
}

artifacts {
    companyGradle companyGradleZip
}

// Configure upload task for company
// Gradle distribution ZIP.
uploadCompanyGradle {
    repositories {
        // Add SSH resolver to copy the resulting
        // company Gradle distribution to the
        // company webserver.
        // user and userPassword are defined in external
        // gradle.properties file.
        add(new org.apache.ivy.plugins.resolver.SshResolver()) {
            name = 'company-intranet'
            user = intranetUsername
            userPassword = intranetPassword
            host = 'intranet'

            addArtifactPattern "/Library/WebServer/Documents/tools/[artifact]-[revision]-[classif\
ier].[ext]"
        }
    }
    uploadDescriptor = false

    // We can also use Maven repositories
    // (or other custom repositories) to upload.
}


class DownloadGradle extends DefaultTask {
    @Input
    String gradleVersion

    @Input
    File destinationDir

    @Input
    String gradleDownloadBase = "http://services.gradle.org/distributions"

    @TaskAction
    doDownloadGradle() {
        destinationFile.bytes = new URL(downloadUrl).bytes
    }

    String getDownloadUrl() {
        "$gradleDownloadBase/$downloadFileName"
    }

    String getDistributionNameBase() {
        "gradle-$gradleVersion"
    }

    String getDownloadFileName() {
        "$distributionNameBase-bin.zip"
    }

    @OutputFile
    File getDestinationFile() {
        new File(destinationDir, downloadFileName)
    }
}

We create a very simple init script in the directory src/scripts/init.d. This file is added to the init.d directory of our company Gradle distribution. Files in this directory are automatically executed.

// File: src/scripts/init.d/sample.gradle
println "You are running the build with the Company Gradle"

In a project where we want to use the company Gradle distribution we either set the distributionUrl property of the Wrapper task or change the property distributionUrl in the file gradle/wrapper/gradle-wrapper.properties.

# File: gradle/wrapper/gradle-wrapper.properties
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=http://intranet/tools/company-gradle-1.0.2-bin.zip

When we use the gradlew script for the first time our company Gradle distribution ZIP file is downloaded and extracted. Notice the line “You are running the build with the Company Gradle” in the output from the sample.gradle init script.

$ ./gradlew tasks
Downloading http://intranet/tools/company-gradle-1.0.2-bin.zip
.................................................................................
.................................................................................
.................................................................................
.................................................................................
.................................................................................
.................................................................................
.................................................................................
.................................................................................
.................................................................................
.................................................................................
.................................................................................
.................................................................................
.................................................................................
.................................................................................
.................................................................................
.................................................................................
.................................................................................
.................................................................................
.................................................................................
....................
Unzipping /Users/mrhaki/.gradle/wrapper/dists/company-gradle-1.0.2-bin/6scnmmqpv8l6j6f30lud9v9o6o\
/company-gradle-1.0.2-bin.zip to /Users/mrhaki/.gradle/wrapper/dists/company-gradle-1.0.2-bin/6sc\
nmmqpv8l6j6f30lud9v9o6o
Set executable permissions for: /Users/mrhaki/.gradle/wrapper/dists/company-gradle-1.0.2-bin/6scn\
mmqpv8l6j6f30lud9v9o6o/gradle-1.2/bin/gradle
You are running the build with the Company Gradle
:tasks

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

Help tasks
----------
dependencies - Displays the dependencies of root project 'simple-project'.
help - Displays a help message
projects - Displays the sub-projects of root project 'simple-project'.
properties - Displays the properties of root project 'simple-project'.
tasks - Displays the tasks runnable from root project 'simple-project' (some of the displayed tas\
ks may belong to subprojects).

Other tasks
-----------
wrapper

To see all tasks and more detail, run with --all.

BUILD SUCCESSFUL

Total time: 3.643 secs

We have seen how easy it is to distribute Gradle in our company. Even customizing the distribution ZIP file is easy to do and will ensure all developers share the same (customized) Gradle version and use the same configuration.

Code written with Gradle 1.2.

The code is available at Github.

Original blog post written on October 26, 2012.