Jan 26, 2012

Building with Gradle

Recently I was on a project that needed to chose a build system. We looked at the usual suspects, ant and maven and after having less then wonderful experiences with both I looked for an alternative. That’s where Gradle came in. In this post I’m going to go through getting started on a simple project. In subsequent posts I plan on increasing the complexity and offer solutions to common issues found on a project.

Why choose gradle

I’ll assume since you’re here that you have an interest in gradle and may have already scoured the internet for comparisons of it with other popular build tools (Ant and Maven).  Below are some of the reasons why I chose Gradle for my project and not the others.

1. Gradle is built on top of a full fledged programming language (groovy) and has direct access to all of it’s goodies.  No reliance on plugins for custom scripting (Maven, Ant plugin), no overhead of creating custom Java classes or plugins, and finally no angle bracket soup (ant scripting).

2. Gradle out of the box has support for dependency management via Ivy and is compatible with all Maven repositories including enterprise ones (Artifactory, Nexus, etc.).

3. Gradle supports multi-module projects without having to reinvent the wheel (ant).

4. Finally, Gradle is extremely flexible allowing for just about any build configuration you could imagine unlike other build tools (I’m looking at you Maven).

First thing first, install gradle.  There is good online documentation on how this is done here http://gradle.org/installation.

Project Layout

So lets get started with our first project.  For this post lets keep it simple with a single module.  We’ll set it up though with the forethought that we may be adding more modules in the future.

I’ll use a flat layout since this is most compatible with the Eclipse IDE. The project layout will look thusly:

  • trunk
    • example-core
    • example-parent

Gradle also allows for a hierarchical layout but I’ve found that Eclipse does not do well with projects that include sub-projects.

The modules are intended to be used in the following manner:

example-core
Contains domain classes, services, and any data access objects.  Really the essence of the whole system.

example-parent
Contains common configuration information for the entire projects (all modules).  This is where our main build.gradle script will be found.

Most of our build configuration will be done  in example-parent with details specific to modules found in the module directories.  The module build files usually contain information on dependencies and artifacts.  They’ll also have custom tasks that are specific to that module. But before we start our build file we need to setup one other piece of information, the settings.gradle file.

 

settings.gradle

The settings.gradle file is responsible for letting gradle know what modules are part of the project and where they are.  By default Gradle assumes that settings.gradle will be found in the directory in which the build is executed but if it’s not then gradle will look in the parent directory. If still not found in the parent it will continue going up the directory hierarchy until it’s found.  There is one exception though, if there happens to be a directory named ‘master’ then it will look in there also.  This facilitates flat layouts.

Here’s our settings.gradle file found in the trunk directory:

Now with that there we are ready to create our first build script in example-parent.

 

build.gradle

All default gradle build scripts should be named build.gradle, it’s the name that the gradle command will look for by default when run. Let’s look at our simple build script, I’ll go through the sections afterwards.

Now let’s walk through it.  Remember that this script is really a groovy script with a very detailed and structured DSL, you can do anything in this DSL that you could do in groovy.  An example is the SLF4J_VERSION variable set at the top.  This is a regular old groovy variable and is used later in constructing the dependency strings (using GString replacement).

The group and version propeties set at the top are part of the Project object which is the groovy object that the DSL is delegating to by default.  By setting these properties you tell gradle what group and version this project is and can be used for dependency management.  If this project is pushed into a maven repository it will use these properties as part of the pom.xml.

Next is the allprojects block which is part of the DSL. This block will apply all configurations found in the block to each module in the project. For this example that would be example-parent and example-core. You are free to do any configuration you care to here as if this was a single module project.

Inside the allprojects block is the repositories block.  This block is used to configure what repositories will be used to locate dependencies. Here I have used a convenient method to include the maven central repository.  Gradle supports all kinds of different repositories from maven and ivy to even a  file system. It is extremely flexible. In a previous project I was on we stored all of our dependenies in source control inside a subdirectory found in our project and Gradle used that as it’s repository.

The subprojects block is used to configure all projects except for the root project. Generally the root project does not produce any artifacts therefore won’t have a need for many configuration parts. Here we set up a couple of configurations.

Configurations are used to store sets of dependencies and files.  In this example I’ve created two common configurations for common test dependencies and common compile dependencies later on we’ll use these in our other modules.

The dependencies block is where all dependencies for this project are declared.  A dependency needs to be added to a configuration, here I’ve added JUnit to the commonTest configuration and slf4j with log4j to the common configuration. You’ll notice that the dependencies are declared using a string.  Gradle supports a few different ways of doing this along with methods for excluding transitive dependencies but I didn’t do that here. The pattern I’m following is ‘group:name:version’.

example-core build.gradle

The example-core project is going to be a simple java project with one main file that prints out “Hello World” (I know, I know). We’ll set this up so it can be run from the command line using gradle. We’ll also make it so we can create an archive for deployment, therefore everyone can enjoy our genius easily.

Here’s the build.gradle file found in example-core:

Besides the code for the HelloWorld class this is all you need to set it up.  If you look at the top of the script you may notice the ‘apply plugin:’ stanza.  This imports a plugin into this script and applies its conventions to the build.  In this case it also imports the java plugin as part of it.  You can apply as many plugins as you like and you can also use plugins that are imported from any URL (file, http, etc.), the application plugin is built into gradle by default.

I then set the version of this module. The group is inherited from the root project so we don’t need to set it here also, in fact the version was too but I overrode it.

For configurations I merely extended the compile and compileTest configurations to include the ones we set up in example-parent.  You may be asking yourself where compileTest and compile came from.  Well, those were brought in via the apply plugin statement. They are standard configurations used by the java plugin.

For this example I don’t require any additional dependencies so I left it empty for display purposes. In real use you would omit it all together.

Finally, I set the mainClassName property to the fully qualified name of the class that I want to be executed when this application is run. This property was added by the application plugin.

Performing a Build

Now we are ready to perform a build.  I’ve skipped the part were we actually write the code but you can get it at github (https://github.com/jhollandus/opiblog), look at the p1 tag for this post.

First, open a command shell (cmd, bash, etc.) and change to the example-core directory.  From there execute this command:

gradle build

You should see gradle startup and then echo out the different parts of the build lifecycle it’s going through.  In this case we don’t have any unit tests so that’ll go quickly but the java plugin brings in an entire build cycle that includes a test phase. The output should resemble this:

jholland-mac:example-core jholland$ gradle build
:example-core:compileJava
Download http://repo1.maven.org/maven2/org/slf4j/slf4j-api/1.6.4/slf4j-api-1.6.4.pom
Download http://repo1.maven.org/maven2/org/slf4j/slf4j-parent/1.6.4/slf4j-parent-1.6.4.pom
Download http://repo1.maven.org/maven2/org/slf4j/slf4j-log4j12/1.6.4/slf4j-log4j12-1.6.4.pom
Download http://repo1.maven.org/maven2/log4j/log4j/1.2.14/log4j-1.2.14.pom
Download http://repo1.maven.org/maven2/log4j/log4j/1.2.16/log4j-1.2.16.pom
Download http://repo1.maven.org/maven2/org/slf4j/slf4j-api/1.6.4/slf4j-api-1.6.4.jar
Download http://repo1.maven.org/maven2/org/slf4j/slf4j-log4j12/1.6.4/slf4j-log4j12-1.6.4.jar
Download http://repo1.maven.org/maven2/log4j/log4j/1.2.16/log4j-1.2.16.jar
:example-core:processResources
:example-core:classes
:example-core:jar
:example-core:assemble
:example-core:compileTestJava UP-TO-DATE
:example-core:processTestResources UP-TO-DATE
:example-core:testClasses UP-TO-DATE
:example-core:test
Download http://repo1.maven.org/maven2/junit/junit/4.8.2/junit-4.8.2.pom
Download http://repo1.maven.org/maven2/junit/junit/4.8.2/junit-4.8.2.jar
:example-core:check
:example-core:build

BUILD SUCCESSFUL

Total time: 5.031 secs
jholland-mac:example-core jholland$

As you can see Gradle downloaded the necessary dependencies and then compiled the code. If you build again you’ll find that it won’t download the dependencies again and it won’t recompile your code. Gradle fully supports incremental builds and will only process files that have changed.

Now if you run ‘gradle run’ it will execute our main class. You should see the following output, note that the logging is currently also going to stdout.

jholland-mac:example-core jholland$ gradle run
:example-core:compileJava UP-TO-DATE
:example-core:processResources UP-TO-DATE
:example-core:classes UP-TO-DATE
:example-core:run
21:54:47,089        DEBUG HelloWorld:10 - Executing...
Hello World

BUILD SUCCESSFUL

Total time: 2.736 secs
jholland-mac:example-core jholland$

Finally if you run ‘gradle distZip’ Gradle will produce a deployable zip file that you can use to execute the program. You can find the zip archive example-core-1.0-SNAPSHOT.zip in the build/distributions directory. The distZip and run tasks are a part of the application plugin.

And that about all for now.  In the next post I’ll create an additional module to show how multi module builds work. In the meantime check out the Gradle documentation online at http://gradle.org/documentation

About the Author

Object Partners profile.

One thought on “Building with Gradle

  1. Pablo says:

    Hi
    It is possible to deploy the parent inside a remote maven repository and download the gradle script when I try to build core and web to use the parent script. ?
    Thanks

    1. Loree says:

      Seem to be two different directories; in moc &#r8/6;/home1rod2igonicolas/Mùsica’ instead of ‘/home/rodrigonicolas/BACK Mùsica’.Remenber to install the libraries too (second command into the post above).

Leave a Reply to Pablo Cancel reply

Your email address will not be published.

Related Blog Posts
Natively Compiled Java on Google App Engine
Google App Engine is a platform-as-a-service product that is marketed as a way to get your applications into the cloud without necessarily knowing all of the infrastructure bits and pieces to do so. Google App […]
Building Better Data Visualization Experiences: Part 2 of 2
If you don't have a Ph.D. in data science, the raw data might be difficult to comprehend. This is where data visualization comes in.
Unleashing Feature Flags onto Kafka Consumers
Feature flags are a tool to strategically enable or disable functionality at runtime. They are often used to drive different user experiences but can also be useful in real-time data systems. In this post, we’ll […]
A security model for developers
Software security is more important than ever, but developing secure applications is more confusing than ever. TLS, mTLS, RBAC, SAML, OAUTH, OWASP, GDPR, SASL, RSA, JWT, cookie, attack vector, DDoS, firewall, VPN, security groups, exploit, […]