August 8, 2015

Gradle is currently the king of the build systems (at least in the Java world). Although the framework is not the number one by popularity, it is the one most devs want to learn [there is a survey that showed this]. Recently, I have had the chance to get to get to know it through a relatively autonomous project I have been doing. Through that praxis it has been easy to see the power and ease of use (if not necessarily simplicity) of the framework.

### Setup

You don’t need to have Gradle installed to run Gradle scripts. Thats clever isnt it? in fact, it is even recommended to install Gradle just for running the scripts. You should run your scripts via the Gradle wrapper (gradlew.bat) instead. By this method, the specified version of Gradle in your build script will be downloaded and cached in your project directory, so then when another team member pulls the project from version control, their build will be performed in exactly the same way!

As for editing the build script - this is when you might need to do some installation. The SpringTool Suite provides a nice IDE for Gradle, and that does need to be installed.

### Basic Structure

Gradle essentially works to define a series of tasks. When you say you want to run a task, Gradle looks for any tasks that must be run before your tasks (its dependencies). So it builds up a graph of tasks to be run. Each task also has some parameters - for instance, a compile folder looks for a particular folder containing source files and it must write the output class files to a particular folder.

The really handy thing though, is that Gradle also allows you to use any number of predefined plugins. These give you a whole task structure, pre-formed, for a particular type of project. In my project, I used the Java plugin as well as the war plugin. With these plugins, Gradle knows that if I am to “assemble” my project, then I must compile some Java files and also I must package the built files together with my web app’s resources (web.xml, css, jsp files etc) and put them all into a web application .war file.

### Convention over Configuration

Gradle pushes towards convention over configuration. Because there are quite a number of reasonable, equivalent Java project structures, it doesn’t know where I am actually keeping my resources. Instead, it makes a convention, and provides a reasonable default setting for each of the tasks. So we have two choices - change all the parameters to match the existing project structure, or change the structure itself so that the default Gradle settings pick up all the corresponding project files.

Hopefully, it is obvious that the latter is much better. There is no risk of defining parameters in the wrong place, no worrying about the formatting of the paths, and no (possible) need to define the same parameters multiple times. The idea is: for a particular project type, the structure can be standardized in a logical way that will meet the developers’ needs. So we don’t need to design a structure ourselves every time.

On the other hand, there are a couple of instances where it seems they do want us to decide every time. For example, in the repository settings. The convention is to put something like:

 repository { mavenCentral }

Which, after we declare all our dependencies, is the service we will use to resolve the repositories. Whats nice - and is in contrast to Maven itself - is that I may also use a flat directory to resolve my simple declared dependencies in case I’m offline or I want to use jars from my own pre-existing private project.

 repository { mavenCentral flatDir("/lib") }

So we see that configuration is only really exposed where it is important to make the user aware of whats going on, or in cases where there can be no clear convention. So, we have to say that we have a Java project by “apply plugin java”, but we don’t have to define loads of standard Java machinery - like where to find the compiler, or where the sources are, or what the tasks should be to make a jar. There are sensible conventions we can use for this second category. N.B. the Gradle eclipse plugin is excellent for cleaning the workspace after restructuring a project

### Using a proper programming language for a build script

Gradle is based on Groovy, which is a proper programming language rather than a declarative XML or JSON etc. It can: use inheritance for tasks, do arbitrary looping and all sorts of logic, and in brief, you can use it to write pretty much any type of task that you’d like to be performed. Of course, you are preferred to write as little code as possible. Why write your own file copy task when there is an existing one that can be extended? Then you just need to plug in your own parameters. Similarly, for more complex and specialist tasks, it would be better to write a plugin rather than keep tasks in a specific project’s build script (as always, fix problems as far upstream as you can). I was able to make use of one such plug-in made for use with Tomcat - when we can reuse, everyone gains.

Clearly, this has only just scratched the surface. Hopefully, when I return to my Java projects, I will get some time to write a part two which will go much more into details on the internal structure of the language, the patterns that are used, and how Groovy’s closures are important to the design of Gradle’s DSL. In the meantime, my initial impressions suggest that the combination of a dynamic programming language with Gradle’s DSL centred around the task structure is much more powerful and productive than previous build tools.