Sunday, September 11, 2011

Introduction to Gradle Part 1

Many build tools have been created for building and packaging software. Make, Ant, Maven, etc., all seem to have their own frameworks of completing tasks that are driven by the structure of each build tool.   The industry while successfully using existing tools constantly requires more features in a build tool to adapt to newer technologies that are emerging (OSGi, Grails, Scala, Jetty, numerous advances in IDEs, etc.).

This article focuses on using Gradle, because of the wide usage of Java and J2EE as an enterprise platform along with Groovy as a Java meta-language.   Gradle is also poised to become the next widely adopted build tool in the world of software build tools and configuration management because Gradle bridges the feature gap between Ant and Maven.

Gradle is a build tool based on the popular language Groovy.  Groovy is an extension of Java and has many built-in features and frameworks for completing complex tasks.  Gradle has many features which will be described briefly.

Gradle fills in the build tool feature gaps that the two most commonly used build tools, Maven and Ant.  Either tool may have one or less of the following features, but neither tool has all of the following features:
  • Both Ant and Maven build scripts are based in xml.  A build tool that allows the flexibility of a true language allows the developer to include libraries into the build file.  The semantics of a language based build tool also allow flexible development and lower learning curves if the Domain Specific Language (DSL) used in the build tool are already known.
  • Software configuration managers are often the keepers of all build knowledge.  A build tool that does not require special knowledge either due to complexity of a project or intimate knowledge about a build tool configuration keeps both developers and software configuration managers involved in the process at all times.
  • The choice of being able to create builds by convention ala Maven or in a more free manner such as Ant is an either/or choice of build tools. 
  • Good dependency management.  While Ant has dependency management in Ivy and Maven has its own dependency management.  A tool that is simple and excels in dependency management is needed.
  • Partial builds.  In Maven when building a multi-project module it is difficult to build only specific projects without building all projects.
  • A potpourri of plugins and other 3rd party modules that are easy to integrate into a build.  Also, a build tool should allow users to easily build their own customizations with minimum hassle.

The history of software build tools has always had challenges.  Builds in Make for instance used tabs in the semantics of a build file, Ant without ant-contrib or other 3rd party libraries was never meant to run a for loop, Maven pom files are difficult to manage for the average developer trying to include a custom task not in the standard lifecycle.  Each build tool had its own idiosyncrasies.

Gradle Features

Gradle provides the following features:
  • Robust dependency management.  Gradle uses Ivy to resolve dependencies on third party libraries or even inter-project dependencies.  Circular dependencies will also cause build failures.  Gradle also builds on Ivy with its own distinct features not found in the standard Ivy distribution.
  • Gradle allows you to build by convention, IF you choose to build by convention.  The alternative of building by non-convention or a unique non-standard convention is just as easy to select.
  • Ant tasks can be called directly from Gradle.  If a project already is written in ANT, it is possible to convert all, some, or none of the build scripts to Gradle.
  • Gradle being a meta-language of Java allows many of the same object types to be introduced into the build scripts.  The richness of Java/Groovy  objects introduce a higher level of flexibility when writing build scripts.
  • Gradle executes builds by phases.  Maven has a similar architecture, but Gradle makes configuring special hooks by execution phase simple.
  • Support for existing Maven and Ivy repositories exists through client modules.
  • Multiproject builds are made simple in Gradle with features such as configuration injection across projects, partial builds, and robust transitive dependency management.
  • Since Gradle is based on Groovy, it is possible to include third party libraries and customize build scripts based on the Groovy and Java languages.
  • Numerous plugins for Jetty, wars, Scala, Maven, code quality (metrics), OSGi, Eclipse, and Reporting.
  • A Gradle wrapper, that will setup Gradle-free machines with a fresh new copy of Gradle from your repository.

A Simple Build Script

Let us start by creating a simple Gradle build script for Java code.  The installation and setup of Gradle can be found here. Project A is a simple java project that follows Java naming conventions and uses a Maven central repository and a local maven repository located in the local file system at /tmp/repos/.

Build Project A (simple Java jar build)


(Project A/build.gradle)


// We can add comments to the build script using typical Java/Groovy notation
//We need to specify the java plugin which by default looks for src/main/java


usePlugin 'java'
usePlugin 'maven'

version = 1.0

configure(install.repositories.mavenInstaller) {
    pom.version = '1.0'
    pom.artifactId = 'project-A'
}


repositories {
      mavenCentral() // Setup the Maven repository
      mavenRepo(urls : "file://localhost/tmp/repos/")
}

// call uploadArchives to deploy to repository
uploadArchives {
    repositories.mavenDeployer {
        repository(url: "file://localhost/tmp/repos/")
        pom.groupId = 'projectA'
        pom.version = '1.0'
        pom.artifactId = 'projectA'
    }
}

install.dependsOn ':uploadArchives'

dependencies { // Add external dependencies here

      compile group: 'commons-cli', name: 'commons-cli', version: '1.0'
      compile group: 'commons-logging', name: 'commons-logging', version: '1.0.4'
      compile group: 'commons-net', name: 'commons-net', version: '1.4.1'
      compile group: 'junit', name: 'junit', version: '4.7'
      compile group: 'org.codehaus.groovy', name: 'groovy-all', version: '1.6.2'
      compile group: 'quartz', name: 'quartz', version: '1.5.1'
      compile group: 'xstream', name: 'xstream', version: '1.2.1'
      compile group: 'commons-net', name: 'commons-net', version: '2.0'
}


sourceSets { // Defines which source files to include in the build

      main {
            java {
            }
            resources {
            }
      }
}




Run 'gradle assemble'
Output:


:compileJava
downloading (2 KB) http://repo1.maven.org/maven2/commons-cli/commons-cli/1.0/commons-cli-1.0.pom
..
downloading (2 KB) http://repo1.maven.org/maven2/commons-lang/commons-lang/1.0/commons-lang-1.0.pom
...
downloading (5 KB) http://repo1.maven.org/maven2/commons-logging/commons-logging/1.0.4/commons-logging-1.0.4.pom
.....
downloading (1 KB) http://repo1.maven.org/maven2/junit/junit/4.7/junit-4.7.pom
..
downloading (24 KB) http://repo1.maven.org/maven2/org/codehaus/groovy/groovy-all/1.6.2/groovy-all-1.6.2.pom
...................
downloading (9 KB) http://repo1.maven.org/maven2/org/apache/ant/ant/1.7.1/ant-1.7.1.pom
........
downloading (4 KB) http://repo1.maven.org/maven2/org/apache/ant/ant-parent/1.7.1/ant-parent-1.7.1.pom
.....
downloading (2 KB) http://repo1.maven.org/maven2/org/apache/ant/ant-launcher/1.7.1/ant-launcher-1.7.1.pom
...
downloading (6 KB) http://repo1.maven.org/maven2/jline/jline/0.9.94/jline-0.9.94.pom
.....
downloading (146 B) http://repo1.maven.org/maven2/quartz/quartz/1.5.1/quartz-1.5.1.pom
..
downloading (371 B) http://repo1.maven.org/maven2/xstream/xstream/1.2.1/xstream-1.2.1.pom
..
downloading (6 KB) http://repo1.maven.org/maven2/com/thoughtworks/xstream/xstream/1.2.1/xstream-1.2.1.pom
......
downloading (7 KB) http://repo1.maven.org/maven2/com/thoughtworks/xstream/xstream-parent/1.2.1/xstream-parent-1.2.1.pom
.......
downloading (638 B) http://repo1.maven.org/maven2/xpp3/xpp3_min/1.1.3.4.O/xpp3_min-1.1.3.4.O.pom
..
downloading (7 KB) http://repo1.maven.org/maven2/commons-net/commons-net/2.0/commons-net-2.0.pom
.......
downloading (24 KB) http://repo1.maven.org/maven2/org/apache/commons/commons-parent/11/commons-parent-11.pom
...................
downloading (4 KB) http://repo1.maven.org/maven2/org/apache/apache/4/apache-4.pom
.....
downloading (30 KB) http://repo1.maven.org/maven2/commons-cli/commons-cli/1.0/commons-cli-1.0.jar
.......................
downloading (38 KB) http://repo1.maven.org/maven2/commons-logging/commons-logging/1.0.4/commons-logging-1.0.4.jar
............................
downloading (232 KB) http://repo1.maven.org/maven2/junit/junit/4.7/junit-4.7.jar
..................................................................................
downloading (4.45 MB) http://repo1.maven.org/maven2/org/codehaus/groovy/groovy-all/1.6.2/groovy-all-1.6.2.jar
.........................................................................
downloading (353 KB) http://repo1.maven.org/maven2/quartz/quartz/1.5.1/quartz-1.5.1.jar
........................................................................
downloading (63 KB) http://repo1.maven.org/maven2/commons-lang/commons-lang/1.0/commons-lang-1.0.jar
..............................................
downloading (1.32 MB) http://repo1.maven.org/maven2/org/apache/ant/ant/1.7.1/ant-1.7.1.jar
...............................................................................
downloading (87 KB) http://repo1.maven.org/maven2/jline/jline/0.9.94/jline-0.9.94.jar
..............................................................
downloading (12 KB) http://repo1.maven.org/maven2/org/apache/ant/ant-launcher/1.7.1/ant-launcher-1.7.1.jar
..........
downloading (349 KB) http://repo1.maven.org/maven2/com/thoughtworks/xstream/xstream/1.2.1/xstream-1.2.1.jar
..................................................................................
downloading (24 KB) http://repo1.maven.org/maven2/xpp3/xpp3_min/1.1.3.4.O/xpp3_min-1.1.3.4.O.jar
...................
:processResources
:classes
:jar
:assemble

BUILD SUCCESSFUL


Total time: 1 mins 13.323 secs



The build will generate classes and a final jar file in the following directories:

ProjectA/.classpath
ProjectA/.classpath.old
ProjectA/.settings/org.eclipse.jdt.core.prefs
ProjectA/bin/com/pac/domain/Employee.class
ProjectA/.project
ProjectA/build.gradle
ProjectA/build/libs/ProjectA-1.0.jar
ProjectA/build/classes/main/com/pac/domain/Employee.class
ProjectA/build/ivy.xml
ProjectA/build/poms/pom-default.xml
ProjectA/src/main/java/com/pac/domain/Employee.java

One thing to note.  If all of the dependencies have not been added to the gradle an error will occur.  The list of dependencies includes transitive dependencies or libraries that are required by libraries listed in your dependency list.  One way to automate the compilation of transitive dependencies is to add the following line to the build script...


configurations.compile.transitive = true


Any library that is required, but is not explicitly defined in the build script will be managed automatically with this entry.  Also, it is possible to manage transitive dependencies at a more granular level (disabling for specific libraries).  One example of this is as follows:

            dependency("commons-cli:commons-cli:1.0") {
                transitive = false
            }


No comments: