Wednesday, February 24, 2016

Gradle

The Gradle build system


Since the Gradle build system (http://www.gradle.org) gets more and more attention I decided to have a look at how it works. This article should give you a brief overview of some of the features so that you get the taste of Gradle. It is not meant to be a tutorial to get you started and it’s recommended that you know about another build system.

What is it?

Gradle is a build system that combines and extends the best features of Ant and Maven. It supports task based build processes like Ant and follows convention over configuration like Maven. On top of that, Gradle build scripts are written in the Groovy language allowing to define more complex behaviour in cases where it is necessary. But you don’t need to be a Groovy expert because Gradle comes with a domain specific language (DSL) to define the builds.
Dependencies can be managed using Ivy and Maven repositories or by yourself if that is required. Most of the behaviour is defined in Gradle plug-ins that ship with its distribution. They can be used out of the box and adapted to your needs.

A simple example

A simple example of a build.gradle script for a Java project looks like this:
1
apply plugin: 'java'
It expects the default layout from Maven which consists of src/main/java, src/main/resource, src/test/java and src/test/resource. And it expects the source to be compatible with the current JVM it is running on. To change these properties of the ‘java’ plug-in you can use Gradle’s DSL:
1
2
sourceSets.main.java.srcDirs = ['src/java', 'src/generated']
sourceCompatibility = 1.6

Dependency management

Repositories

Gradle can access Maven and Ivy repositories. Here are some samples how to configure the repositories:
1
2
3
4
5
6
7
8
9
10
11
12
13
repositories {
  mavenLocal()
  maven {
  }
  mavenCentral()
}
repositories {
  ivy {
  }
}
The look-ups will be done in the order given within the repositories section. So in the above Maven example it will first look in the local repository of the user, then on the company’s server and finally on Maven Central.

Dependencies

To depend on an internal module, i.e. a module from the same project, you just write:
1
2
3
dependencies {
  compile project(':shared')
}
You can also add dependencies to external modules from Maven or Ivy repositories if you have defined them as described above.
1
2
3
4
dependencies {
  runtime group: 'org.hibernate', name: 'hibernate', version: '3.0.5'
  testRuntime 'org.mockito:mockito-core:1.8.4'
}
The above sample contains the long and the short version of a module identifier. If you have a dependency on a local file then you can specify it with:
1
2
3
4
dependencies {
  compile files('libs/a.jar', 'libs/b.jar')
  testCompile fileTree(dir: 'testlibs', include: '*.jar')
}
More sophisticated features can be found in the documentation and include stuff like:
  • overwriting transitive dependencies
  • use Ivy specific dependency features
  • etc.

Tasks

Tasks are the central building blocks of the build process. They consist of two lists of actions. One list is to configure the task and the other to execute it. Here is a sample:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Just define an empty task
task myTaskA
// Add a configuration action
myTaskA {
  println "config A"
}
// Add an execution action
myTaskA << {
  println "action A"
}
// Another task depending on myTaskA with configuration and action
task myTaskB (dependsOn: myTaskA) {
  println "config B"
  doLast {
    println "action B"
  }
}
Now you can start it:
1
$ gradle myTaskB
The configuration steps will be executed first. And since myTaskB depends on myTaskA the output will be:
1
2
3
4
5
6
7
8
config A
config B
:myTaskA
action A
:myTaskB
action B
BUILD SUCCESSFUL
Tasks can also be of a predefined type. The follwoing example uses the JavaExec type to start some Java code:
1
2
3
4
5
6
7
8
9
10
apply plugin: 'java'
// dependencies, repositories etc.
task startMain(type: JavaExec, dependsOn: classes) {
  main = 'mypackage.Main'
  args = "-x -y -z".split().toList()
  classpath sourceSets.main.classesDir
  classpath configurations.compile
}
The command gradle startMain prepares the classes and then starts the program using the classes and also the external dependencies as classpath.

Maven sample comparison

Just to be clear: this is not an A is better than B comparison. The purpose is only to show you the two different versions of a Maven and a Gradle build script that achieve the same thing.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
   <modelVersion>4.0.0</modelVersion>
   <groupId>ch.bbv.mavendemo</groupId>
   <artifactId>simpleMavenDemo</artifactId>
   <version>0.0.1-SNAPSHOT</version>
   <properties>
       <java.source.version>1.7</java.source.version>
       <junit.version>4.8.2</junit.version>
   </properties>
   <dependencies>
       <dependency>
           <groupId>junit</groupId>
           <artifactId>junit</artifactId>
           <version>${junit.version}</version>
           <scope>test</scope>
       </dependency>
   </dependencies>
   <build>
       <plugins>
           <plugin>
               <artifactId>maven-compiler-plugin</artifactId>
               <version>2.3.1</version>
               <configuration>
                   <source>${java.source.version}</source>
                   <target>${java.source.version}</target>
               </configuration>
           </plugin>
       </plugins>
   </build>
</project>
In Gradle this would be:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
apply plugin: 'java'
apply plugin: 'maven'
group = 'ch.bbv.gradledemo'
version = '0.0.1-SNAPSHOT'
//the default artifact id is the project name, which is per default the directory name of the project
project.ext.junitVersion='4.8.2'
sourceCompatibility = 1.7
targetCompatibility = 1.7
repositories {
   mavenCentral()
}
dependencies {
   testCompile 'junit:junit:'+junitVersion
}
The ‘maven’ plug-in allows you to install the artifact in your local Maven repository. Gradle’s DSL makes the build script much easier to read. Of course one could use polyglot Maven but that didn’t take off yet.

IDE integration

The first thing I tried was the Gradle plug-in called ‘eclipse’ (it’s a bit unlucky that everything is now called plug-in). This plug-in can generate eclipse configuration files with settings, project and classpath. This works quite well but you have to update and refresh the project yourself after changing the build.gradle file.
The opposite way around there are also plug-ins for eclipse. The first one is Groovy support so you can edit Groovy code in eclipse. The second one is the Gradle plug-in itself which manages the dependencies of your project, allows you to start Gradle tasks and more.
There is support for other IDEs as well. The current state of tooling can be found here: http://www.gradle.org/tooling

Documentation

Gradle comes with a comprehensive set of documentation including a user guide, reference material and even a book titled “Building and Testing with Gradle”. You can read the book online if you register on Gradleware’s web site. It’s a good read and covers all the stuff you need to get started with Gradle.
The user guide documents all the parts of Gradle and will be continuously extended as Gradle grows. The reference material is generated Java/Groovy doc for Gradle’s API and DSL.

Conclusion

I see three scenarios where Gradle can be a good candidate for your build system.
  1. If you are currently using Ant and the build.xmls get too complicated to handle. With the task based build and the support for existing Ant tasks, Gradle support the migration from Ant.
  2. You have trouble to get Maven to do what you want and maybe already started to write your own Maven plug-ins. Gradle supports the convention over configuration paradigm similar to Maven but can also be scripted with Groovy to do more powerful stuff.
  3. You start a new project.
Gradle as a build system makes a very good impression to me. Hopefully there will be more plug-ins developed when more and more projects use it. I’ve seen that a Jacoco plug-in is under development for example. Gradle itself will continuously be extended and there is also a short- and long-term road-map available. It is licensed under the Apache License, Version 2.0 and is used by open source projects like Spring and Hibernate and also by commercial entities.
Even though it has a bit of a steep learning curve it is worth a closer look.

This entry was posted in ALM, Java and tagged , , . Bookmark the permalink.