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 3
rd 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
}