user-icon Rene Kugel
17. December 2014
timer-icon 9 min

A build tool named Gradle – Part 3

This is the last part of my blog post series about the build tool Gradle. In the other two parts (Part 1, Part 2), I showed you the basics of the topic build and how to use some common Gradle elements. In this part I want to compare Gradle with the known build tool Apache Ant. Therefore I will show some examples and their implemantion with Gradle, respectively Ant. Let’s go.

When it comes to the usage of something new, people always ask one question: “Why should I use this?” – Well, this is a good question. But in my opinion, there’s also a good answer at least in this case “Less code by more functionality”. When we look at the most used build tools Ant and Maven, we will always find the same statements “Ant is very specific – you have to declare almost everything” and “Maven is very rigid – if you don’t abide to the conventions, it is difficult to continue”.  Gradle tried to combine the benefits of these two tool without the drawbacks. And in my eyes they succeeded. But let’s look at some examples. Before you read on, check that you understood the Gradle basics. Therefore I recommend you to read my previous post of this serie or browse through the Gradle User Guide.

Common Operations

To discern the general differences between Ant and Gradle we will create a minimal working example and have a look at the corresponding implementation. Let’s say we have a source folder including the packages test and main, additionally we have a resource folder including some scripts and property files. Our source code has some dependencies on an external library named commons-cli-1.2. Now we want to execute some common build operations:

  1. Clean up the stuff from old builds
  2. Download the external library
  3. Check our code with Checkstyle
  4. Compile the source code
  5. Run the functional tests
  6. Pack everything together in a JAR file

In Ant the source code of these steps could look something like this:


The same functionallity in Gradle is given with the following code:


At first glance you would say “… but there are less code lines in Ant than in Gradle.” – Yes, you’re right! But please consinder that there are no dependencies are declared an no repository is stated. Therefore you need two more files in Ant: ivy.xml (about 10 more line) and ivysettings.xml (about 20 more lines). Gradle comes with Ivy native dependency management. Thus you can declare your dependencies and your repository in the Gradle DSL, directly in your build files.

Furthermore I think that the Gradle DSL ist much more easier to read. You don’t have an XML overhead which bloats your build script. One drawback that I see in Gradle is the usage of properties and methods. In Ant there are only XML elements which are easy to set. In Gradle you have to differentiate between properties and methods. While writing my code I often asked myself “Is this a method or do I have to set an property?”. So I had to look up in the documentation for the correct usage. You can recognize methods by an name followed from round brackets with a parameter. Properties you can recognize by the assignment with the equal sign.  If you have a look a the code block above (task packageJar – line 68 – 76) you can see, that the files that are packed into the jar are declared with a method: from(bin/main), while the destination directory is set by a property: destinationDir = file(“build”).  This can be confusing. Do you agree with me?

 The Ant Wrapper

A really nice feature within Gradle is the Ant wrapper, espacilly for the migration from Ant to Gradle. It practically allows you to use every Ant operation inside your Gradle build script. I’ll show you how that works. Let’s start with a simple echo operation. In Ant you would write something like this.


By using the Ant wrapper in Gradle, your code would look like this.


Nested elements are passed in a clousre. Let’s have a look.
Ant:


Gradle (with Ant wrapper):


At least you can even integrate external Ant task with the wrapper. I think that’s a pretty nice feature.

Usage of Controll Stuctures

A known issue in Ant ist the usage of logical controll structures. I am talking about if-statements, loops etc. Sure, in Ant there are possibilties to integrate such elements like using ant-contrib or target dependencies. But through the fact that Gradle is Groovy based, we can use some native operations. In the following example we want to check the operating system and copy the corresponding files.

Ant:


Gradle:


I think the difference is pretty obvious. Gradle has the classic if-statement, while Ant works with ‘if’ and ‘unless’ target dependencies. What would you prefer?

Manipulation of the execution order of Gradle tasks

While I was working with Gradle 1.5 (and lower), I had to struggle with the problem of the task execution order. This problem was solved with version 1.6. I want to show you the problem and how it was solved, so you can understand the functionality of Gradle a little better.

Initial situation

For a smooth execution of your build script dependencies between your tasks are mandatory. You’re able to declare these dependencies in Gradle with the ‘dependsOn: taskX’  operation. Have a look at the following example:


At a first glace this looks easy and very logical, but there was a problem if a task had more than one dependency.

Problem (with Gradle 1.5)

Let’s say you have a task A which depends on two other tasks – the task and C. But you want C to be executed before B. I.e. the execution shuold be C -> B -> A. Now you create you’re task dependency with the expression taskA(dependsOn: C, B). Unfortunately Gradle 1.5 doesn’t respect the execution order in the comma-separated list, but calls the depending task in an alphabetic order. The following chart shows this problem.

task-execution

This was a strong discussed problem. These links will show you the discussion between the community and the developers:
http://issues.gradle.org/browse/GRADLE-427
http://markmail.org/thread/wn6ifkng6k7os4qn

Solution (with Gradle 1.6)

With the update to version 1.6. there was a method called ‘mustRunAfter()’ added. With this method you can exactly control the order of your task execution. In my case I had several test tasks for my application: flowTest, integrationTest, cliTest, uiTest and unitTest.  Additionally I had a task that should run all these tests: runAllServerTests.  First there should ran the unitTest, because if these fail it makes no sense to run any other test. But the other tests should also be able to run without a dependency on unitTest. With Gradle 1.5 this was not possible because of the alphabetic execution order. With Gradle 1.6 (and the mustRunAfter() method)  I created the following construct which matches my requirements.

task-execution-order-solved

So, if I call i.e. the task integrationTest it has no dependency on unitTest and runs without any other task. But if I call the runAllServerTests, it will run the unitTest in first place and afterwards all the other tests.

Code Quantity

In the introduction to this post I said “Less code by more functionality”. Now I will tell you why you need less code. At least there are two important factors that lead to this statement.

1.Global configuration:
In Ant you often have to repeat yourself. If you have several tasks with the same classpath, you have to declare this path to every task. Gradle gives you the opportunity to configure such opperations on a global level. Here’s an example:


2.XML vs. Groovy
We all know that XML is very heavy. With starting- and ending tags you produce very much (redundant) code. Groovy in comparison is much more light weighted. Thus, in my opinion, Groovy is easier to read. Do you agree with me? When I started to migrate my project from Ant to Gradle I wasn’t sure that I will have less code in the end. But at least I came to the following result:

code-qu

Final Conclusion

Basically Gradle offers some efficient features thus you can create a performant and dynamic build process. With the native dependency managment you get a easier maintainability  of the external dependencies. The easy usage of control structures can help you to make your build more dynamically and because of the easy readable Gradle DSL you get a better overview of your processes. Plugins spare you a lot of work and are easy to integrate. In the end you can go a long way with a few lines of code. And if there is a operation not supported in Gradle you can still use the Ant Wrapper.

On the other side it took me very long to understand all the functions and coherence. I didn’t like the user guide. It often uses complex examples to show simple operations.  If you don’t manage your task dependencies in a good way, you will find yourself in a task chaos. Also there are many different notations and it is not obvious which notation is the best for a special case. To use Gradle in a efficient way you need to understand all the background processes and coherence. But if you’re able to see the bigger picture of Gradle, you know the best way to create build processes.

Should I switch from Ant to Gradle?

As so often the answer to this question is: It depends! True to the motto “Never change a running system” I would advise you against a cutover from Ant to Gradle, given that you have a good running Ant process which likely doesn’t have any major changes. This would be a big effort. But if you’re at the beginning of a new project it pays to have a closer look at Gradle. Also if you have a small and simple Ant process and you are interested to get familiar with another build tool, I would recommend you to try to ‘translate’ your process into Gradle.

Read the first part of this post: A build tool named Gradle – Part 1
Read the second part of this post: A build tool named Gradle – Part 2

-René

Comment article