Java

Ant vs Maven vs Gradle

Choosing the Right Build Tool: Ant vs Maven vs Gradle

During software development, developers have to rebuild the same code over and over again. They often try to use bash scripts or other scripting languages to automate the task. However, there are build tools available that are more appropriate for build automation. The predominant build tools are:

Let’s investigate the tools to find out more.

Apache Ant with Ivy

Apache Ant is a Java-based command line tool that uses XML files to define build scripts. It’s predominantly used for Java builds but it can also be used for C/C++ development. Built-in tasks provide ways to compile, assemble, test and run software applications. Users can also create their own “antlibs” to enhance the functionality of Ant. Apache Ivy is a dependency management tool that integrates easily with Ant to provide a more robust ecosystem. The development of Ant started in 2000.

Pros

  • Better control over the overall build process
  • Flexible enough to work with any work process

Cons

  • XML based build files can grow large and unmaintainable
  • Lots of time and resources are necessary to maintain the build scripts
  • IDE integration is difficult to achieve

Ant with Ivy Example

You can install the latest Ant from here. You have to download the zip, expand and put the bin folder in your path. You can use the following command to see if Ant is installed properly:

$ ant -version
Apache Ant(TM) version 1.10.1 compiled on February 2 2017

Once you have Ant installed, you can download the latest Ivy jar and put it in the lib folder inside the Ant directory.

After you have Ant installed, create folders helloworld and helloworld/src. Inside the src folder, put helloworld.java file with the code:

/**************************

Prints Out "Hello World!"

***************************/

public class helloworld {

    public static void main(String[] args) {
        System.out.println("Hello World!");
    }

}

Now in the helloworld folder create a build.xml file with the following code:

<project xmlns:ivy="antlib:org.apache.ivy.ant" name="helloworld" default="jar">

<property name="src.dir" value="src"/>
<property name="build.dir" value="build"/>
<property name="classes.dir" value="${build.dir}/classes"/>
<property name="bin.dir" value="${build.dir}/bin"/>
<property name="lib.dir" value="lib" />
<path id="lib.path.id">
<fileset dir="${lib.dir}" />
</path>

<target name="resolve">
<ivy:retrieve />
</target>

<target name="clean">
<delete dir="${build.dir}"/>
</target>

<target name="compile" depends="resolve">
<mkdir dir="${classes.dir}"/>
<javac srcdir="${src.dir}" destdir="${classes.dir}" classpathref="lib.path.id"/>
</target>

<target name="jar" depends="compile">
<mkdir dir="${bin.dir}"/>
<jar destfile="${bin.dir}/${ant.project.name}.jar" basedir="${classes.dir}"/>
</target>

</project>

And in the same helloworld folder, create the ivy.xml file with the following code:

<ivy-module version="2.0">
<info organisation="org.apache" module="helloworld"/>
<dependencies>
<dependency org="junit" name="junit" rev="4.12"/>
</dependencies>
</ivy-module>

The directory structure should look like this:

helloworld
|-- build.xml
|-- ivy.xml
`-- src
       `-- helloworld.java

Now you can run the build with the command:

$ ant jar

A successful build should provide output like this:

$ ant jar
Buildfile: /Users/zak/_work/LearnBuildScripts/LearnANT/helloworld/build.xml

resolve:
[ivy:retrieve] :: Apache Ivy 2.4.0 - 20141213170938 :: http://ant.apache.org/ivy/ ::
[ivy:retrieve] :: loading settings :: url = jar:file:/Users/zak/BuildTools/ANT/apache
-ant-1.10.1/lib/ivy-2.4.0.jar!/org/apache/ivy/core/settings/ivysettings.xml
[ivy:retrieve] :: resolving dependencies :: org.apache#helloworld;working@Zakirs-
MacBook-Air.local
[ivy:retrieve]     confs: [default]
[ivy:retrieve]     found junit#junit;4.12 in public
[ivy:retrieve]     found org.hamcrest#hamcrest-core;1.3 in public
[ivy:retrieve] :: resolution report :: resolve 397ms :: artifacts dl 15ms
---------------------------------------------------------------------
|                  |            modules            ||   artifacts   |
|       conf       | number| search|dwnlded|evicted|| number|dwnlded|
---------------------------------------------------------------------
|      default     |   2   |   0   |   0   |   0   ||   4   |   0   |
---------------------------------------------------------------------
[ivy:retrieve] :: retrieving :: org.apache#helloworld
[ivy:retrieve]     confs: [default]
[ivy:retrieve]     0 artifacts copied, 4 already retrieved (0kB/39ms)

compile:
[mkdir] Created dir: /Users/zak/_work/LearnBuildScripts/LearnANT/helloworld/build/
classes
[javac] /Users/zak/_work/LearnBuildScripts/LearnANT/helloworld/build.xml:22: warning:
'includeantruntime'was not set, defaulting to build.sysclasspath=last; set to false
for repeatable builds
[javac] Compiling 1 source file to /Users/zak/_work/LearnBuildScripts/LearnANT/
helloworld/build/classes

jar:
[mkdir] Created dir: /Users/zak/_work/LearnBuildScripts/LearnANT/helloworld/build/bin
[jar] Building jar: /Users/zak/_work/LearnBuildScripts/LearnANT/helloworld/build/bin/
helloworld.jar

BUILD SUCCESSFUL
Total time: 6 seconds

You can try out the jar file like this:

$ java -cp build/bin/helloworld.jar helloworld
Hello World!

We have defined the jar file to be put in the build/bin folder. The folders get created during the build. The ant jar command calls the jar target in the build.xml.

Maven

Maven was developed to resolve the problems faced with Ant-based scripting. It kept the XML files but took a different approach to organization. In Ant, developers have to create all the tasks. Maven decreases the task creation by implementing stronger standards for organizing code. As a result, it’s easier to get started on standard projects.

It also introduced dependency downloads which made the development easier. Before the introduction of Ivy in Ant, users had to manage dependencies locally. Maven adopted the dependency management philosophy first.

However, Mavens strict standards make it difficult to write custom build scripts. The tool is easy to work with as long as the project follow the strict standards.

Pros

  • Automatic dependency downloads
  • All dependencies are automatically recorded in source control as part of the Maven scripts
  • Standardizes and simplifies the build process
  • Easily integrates with IDEs and CI/CD systems

Cons

  • Not flexible in creating custom workflows
  • Steep learning curve and the process is difficult for novices to understand
  • Time-consuming to solve build problems and new library integrations
  • Not good with multiple versions of the same dependency

Maven Example

You can download the latest Maven from here. You can check the installation like this:

$ mvn --version
Apache Maven 3.5.2 (138edd61fd100ec658bfa2d307c43b76940a5d7d; 2017-10-18T00:58:13-07:00)
Maven home: /Users/zak/BuildTools/Maven/apache-maven-3.5.2
Java version: 1.8.0_74, vendor: Oracle Corporation
Java home: /Library/Java/JavaVirtualMachines/jdk1.8.0_74.jdk/Contents/Home/jre
Default locale: en_US, platform encoding: UTF-8
OS name: "mac os x", version: "10.11.6", arch: "x86_64", family: "mac"

Create a helloworld folder and generate a project with the following command:

$ mvn archetype:generate -DgroupId=com.companyname.helloworld -DartifactId=helloworld
-DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false

It should create the folder structure and generate the output that looks like this:

[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building Maven Stub Project (No POM) 1
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] >>> maven-archetype-plugin:3.0.0:generate (default-cli) > generate-sources
@ standalone-pom >>>
[INFO]
[INFO] <<< maven-archetype-plugin:3.0.0:generate (default-cli) < generate-sources
@ standalone-pom <<<
[INFO]
[INFO]
[INFO] --- maven-archetype-plugin:3.0.0:generate (default-cli) @ standalone-pom ---
[INFO] Generating project in Batch mode
[INFO] ----------------------------------------------------------------------------
[INFO] Using following parameters for creating project from Old (1.x) Archetype:
maven-archetype-quickstart:1.0
[INFO] ----------------------------------------------------------------------------
[INFO] Parameter: basedir, Value: /Users/zak/_work/LearnBuildScripts/LearnMaven
[INFO] Parameter: package, Value: com.companyname.helloworld
[INFO] Parameter: groupId, Value: com.companyname.helloworld
[INFO] Parameter: artifactId, Value: helloworld
[INFO] Parameter: packageName, Value: com.companyname.helloworld
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] project created from Old (1.x) Archetype in dir: /Users/zak/_work/
LearnBuildScripts/LearnMaven/helloworld
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 8.602 s
[INFO] Finished at: 2018-01-27T00:05:37-08:00
[INFO] Final Memory: 15M/152M
[INFO] ------------------------------------------------------------------------

The folder structure should look like this:

helloworld
|-- pom.xml
`-- src
    |-- main
    |   `-- java
    |       `-- com
    |           `-- companyname
    |               `-- helloworld
    |                   `-- App.java
    `-- test
        `-- java
            `-- com
                `-- companyname
                    `-- helloworld
                        `-- AppTest.java

The pom.xml contains the build configurations. Inside the pom.xml the code looks like this:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/
XMLSchema-instance"

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0
_0.xsd">

<modelVersion>4.0.0</modelVersion>
<groupId>com.companyname.helloworld</groupId>
<artifactId>helloworld</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>helloworld</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

You can generate the jar file using the following command:

$ mvn package

[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building helloworld 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ helloworld ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e.
build is platform dependent!
[INFO] skip non existing resourceDirectory /Users/zak/_work/LearnBuildScripts/LearnMaven/
helloworld/src/main/resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ helloworld ---
[INFO] Changes detected - recompiling the module!
[WARNING] File encoding has not been set, using platform encoding UTF-8, i.e. build is
platform dependent!
[INFO] Compiling 1 source file to /Users/zak/_work/LearnBuildScripts/LearnMaven/
helloworld/target/classes
[INFO]
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @
helloworld ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e.
build is platform dependent!
[INFO] skip non existing resourceDirectory /Users/zak/_work/LearnBuildScripts/LearnMaven/
helloworld/src/test/resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ helloworld ---
[INFO] Changes detected - recompiling the module!
[WARNING] File encoding has not been set, using platform encoding UTF-8, i.e. build is
platform dependent!
[INFO] Compiling 1 source file to /Users/zak/_work/LearnBuildScripts/LearnMaven
/helloworld/target/test-classes
[INFO]
[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ helloworld ---
[INFO] Surefire report directory: /Users/zak/_work/LearnBuildScripts/LearnMaven
/helloworld/target/
surefire-reports

-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running com.companyname.helloworld.AppTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.014 sec

Results :

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0

[INFO]
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ helloworld ---
[INFO] Building jar: /Users/zak/_work/LearnBuildScripts/LearnMaven/helloworld/target/
helloworld-1.0-SNAPSHOT.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 5.624 s
[INFO] Finished at: 2018-01-27T00:11:10-08:00
[INFO] Final Memory: 16M/114M
[INFO] ------------------------------------------------------------------------

You can run the jar file like this:

$ java -cp target/helloworld-1.0-SNAPSHOT.jar com.companyname.helloworld.App
Hello World!

The jar file is put in the target folder.

Gradle

Gradle combines the power of Ant and Maven. The first version of Gradle was released in 2012. It has seen fast adoption. Google is currently using it for Android OS.

Instead of XML, Gradle uses the Groovy language. As a result, build scripts in Gradle are easier to write and read. It was initially using Ivy for dependency management, but it is using its own dependency engine now.

Pros

  • Provides standardization while staying flexible
  • Easy to read and write build scripts
  • Better at handling multiple versions of dependencies
  • Able to handle multiple programming languages and technologies
  • Active community helping develop the tool
  • Gradle DSL (Domain-Specific Language) makes it simple configuration structure
  • Gradle provides performance improvements using incrementally, build cache and the Gradle Daemon

Cons

  • IDE integration not as good as Maven

Gradle Example

You can install Gradle from here. Once you set up Gradle in your path, you can check it by:

$ gradle --version

------------------------------------------------------------
Gradle 4.5
------------------------------------------------------------

Build time:   2018-01-24 17:04:52 UTC
Revision:     77d0ec90636f43669dc794ca17ef80dd65457bec

Groovy:       2.4.12
Ant:          Apache Ant(TM) version 1.9.9 compiled on February 2 2017
JVM:          1.8.0_74 (Oracle Corporation 25.74-b02)
OS:           Mac OS X 10.11.6 x86_64

Next, create the following directory structure:

helloworld
|-- build.gradle
`-- src
    |-- main
       `-- java
            `-- helloworld
                `-- helloworld.java

For the helloworld.java put the code from the Ant example. And for build.gradle put in the following code:

apply plugin: 'java'
 
version = '1.0'
 
repositories {
    mavenCentral()
}
 
dependencies {
    testCompile group: 'junit', name: 'junit', version: '4.12'
}

You can use “gradle tasks –all” command to look at all the commands available. Gradle automatically picks up the plugins you specify in the build.gradle file and shows you the extra tasks available due to the plugins.

You can get the build by running:

$ gradle jar

BUILD SUCCESSFUL in 1s
2 actionable tasks: 2 executed

You can run your jar like this:

$ java -cp build/libs/helloworld-1.0.jar helloworld
Hello World!

The jar file is put in the build/libs folder.

Conclusion

Among the build tools, Ant can be useful for smaller projects while Maven is better for making sure all developers follow the same rules. Gradle is the latest tool that provides the most flexibility.

References:

About the author

Zak H

Zak H. lives in Los Angeles. He enjoys the California sunshine and loves working in emerging technologies and writing about Linux and DevOps topics.