Pages

Sunday, November 11, 2012

Gradle - Opinion, experience and examples

Opinion and Experience

I had quite a short experience with Gradle, but the impression it left is of an incredible tool.

I don't want to diss any other build tools out there like Ant or Maven, they're great,
and if you're a real savvy, you'll get the job done probably pretty fast.
I generally believe that one needs to use the right tool for the right task, but I'd dare say
that whatever you're doing with Maven or Ant you could probably do better with Gradle -
it is simpler, faster, shorter to write any build file, much more concise yet very powerful.

I have also my experience with Ant for years and with Maven, and I can tell you that I appreciate
Ant very much as a build tool. It does exactly what you expect it to do.
When you read a build.xml file, you'd hardly find something you don't understand.
It's just clear how things work, and what is expected at the end.
The problem is, you need to write so much "build code" in order to get your job done, especially
in a complex system, when you have includes of other extension.xml files you rely on and so on.
What about dependency resolution? - OK, so you could use Ivy, but that's just for dependency resolution.
It's not a "full" build tool in the sense of Maven that comes with creating your packages out of the box, deploying it to your tomcat and distributing it to your Repository.

Yes, Maven can be great but I must say that my 'like' metric towards it is not so stable.
It is too complicated to get simple stuff done... compiling GWT? - Get a plugin!
In Ant, it's really simple to compile a GWT project + you get the example from the GWT website.
In Gradle you can simply execute Ant Tasks, so no plugin extra work needed.
Want to include / exclude resources from your WAR file... why does it have to be complicated?
Dependency management and resolution is awesome, distributing your artifacts into a repository
is great, and fairly easy to do.

However, despite the good stuff of these build tools, I must say that from my personal experience  Gradle tops them.
Gradle like many other posts on the internet would say - brings all the good stuff from the worlds
of Ant and Maven while keeping the ugly stuff out.

What do I mean by that?

  1. It is the easiest build tool I've seen to get started with - simply download, extract and set the GRADLE_HOME environment variable on your path.
    Like Ant - no other configuration is needed! Sweet.
  2. Very well documented!
  3. You can execute any Ant task from the Gradle build files. Any.
  4. Dependency resolution - from Maven repositories, it leverages the existing repositories you may already have in your corporation without needing to change anything, while decreasing the verbosity of dependency declarations in the build file itself.
  5. Gradle is based on Groovy, so you can just program your build.
    And you can use all the libraries of Groovy. And it runs on the JVM, so you could theoretically use any Java library you like when running the build.
    Did I already say powerful?
  6. It is dynamic - You can tell Gradle in run-time for example, which sub-projects you'd like to include in your Project's build. I did that, using naming conventions for instance when I had a process that was auto-generating entire projects ready to be built in a pre-compilation phase. Incredible!
  7. No need to sit and rewrite ant targets to do jobs like clean, compile, jar, war etc... it is all there already!
  8. The integration with Eclipse is excellent. All you need is there.
  9. It just works!



Let me give you an example of how a simple Gradle build (build.gradle) file looks like:

apply plugin: 'java'
apply plugin: 'eclipse'
sourceCompatibility = 1.6
version = '1.0'
jar {
    manifest {
        attributes 'Implementation-Title': 'My project', 'Implementation-Version': version
    }
}
repositories {
    mavenCentral()
}
dependencies {
    compile group: 'commons-collections', name: 'commons-collections', version: '3.2'
    testCompile group: 'junit', name: 'junit', version: '4.+'
}



I think that what is so nice about this build file, is that you can understand it right away.
Lets go over the lines to try and understand this file:


  1. The first two lines declaring the "apply plugin...." means that you are telling Gradle to use plugins such as 'java' for example to it would know how to compile Java source to classes.
    Gradle is based on plugins, which facilitate all the needed 'know-how' and logic of performing
    various tasks. - You could write your own custom plugins as well.
  2. The 'eclipse' plugin for example generates the needed files for importing your Gradle project into Eclipse.
    You can read more on that on - The Eclipse Plugin.
  3. sourceCompatibility - tells Gradle what version of Java your source files are written in.
    You can also specify the 'targetCompatibility' attribute in order to compile your sources into a lower version of Java.
  4. version - Declares a variable called 'version' with the version number of your package
    (Be it a JAR or WAR file). The Jar files that are generated with the Gradle 'build' task, have a Maven convention. So the version number is appended to the name of the package at the end. (for example: "MyPackage-1.2.jar", in case version was given the value "1.2").
  5. jar { ... } - This is a Gradle Task is a "task" (can be thought of an Ant task) which is part of the 'java' plugin that assembles a Jar file from your compiled code.
    You can add to the manifest file your own attributes, such as a version number.
    Note that  'Implementation-Version': version is using the version number declared earlier in paragraph 3, but you could have used any other variable to place your value in there.
  6. repositories { mavenCentral() } - determines the repositories from which you want to perform dependency resolution.
    In this case - using the Maven Central repository.
  7. dependencies { ... } - determines the needed dependencies for your build.
    Like Maven's dependency scope, in Gradle there are also so called 'scopes' which you declare for your dependencies: compile, runtime, testCompile & testRuntime.In this case we declare for example the following:
    testCompile group: 'junit', name: 'junit', version: '4.+'Which makes sure to use the JUnit version 4 and above during compilation of test files.


That's it.
It's really short and simple!
Now you can just run from your command line : > gradle clean build
And you will get your project cleaned, compiled, packaged ("Jarred"), tested and with a JUnit test report, all under the 'build' directory of your project.
Of course, you can also distribute the artifacts (your Jars) to your repository, or just copy them
flat to an "Artifacts" directory of your project and so on.

But this was only to demonstrate how easy it is to create a build file, that does so much.

Examples

Running an Ant Task from Gradle build file:

You can run an Ant tasks from a Gradle build file simply by writing something like this:
ant.echo(message: "Building a project....")

This task in particular is not that useful... it's just a print, but it demonstrates how easy it is to execute Ant tasks!
Especially if you consider migrating from Ant, and you have custom Ant Tasks written
which you want to re-use - then you could easily do this.



Running an external Java program from an external Jar:

This could be handy if you want to run anything which is for example at a pre/post phase
of the build.
The snippet below shows how you would apply an XSLT on a XML file, using
the xalan library:
ant.java(jar: "lib/xalan.jar", fork: "true") {
arg(line: "-xml" + " abc.xml")
arg(line: "-xsl" + " myXslt.xsl")
}

(the "lib" directory is at the project's root directory as were the "abc.xml" and "myXslt.xsl" files).


Including Sub-Projects in your Project's build dynamically:

When your project is comprised from sub-projects, it is necessary to tell Gradle about
those sub-projects in a dedicated file called settings.gradle.
In that file, one needs to "include" the sub-projects that need to be built by Gradle.
So typically you could see something like this:
include 'server', 'client'
The great thing about Gradle is that in the settings file you can pretty much put just about anything
you put in a build file. The settings file is executed in the initialization phase of the build.
So if you want to dynamically include sub-projects into your build because you're generating them
from another process, or because you have many of them, or for any other reason, you can do
something like this:


new File(".").eachFile(groovy.io.FileType.DIRECTORIES) { f ->    if(f.name.startsWith("subProjectPrefix") {                
                            include f         
                 }
          }


This will just iterate over the root directory of your project, check if the current directory
it iterates on starts with the "subProjectPrefix" prefix, and includes it if it does.


This was really just the tip of what Gradle can offer you.
You can find many examples on the internet as well as on Gradle's installation directory.
(Look under %GRADLE_HOME%/samples - there are many build files examples).
So if you're using Ant, Ivy or Maven I recommend you to look into it, and give it a try,
I don't think you will regret it.


Gradle - http://www.gradle.org/

No comments:

Post a Comment