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:
- Apache Ant with Ivy
- Maven
- Gradle
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:
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:
Now in the helloworld folder create a build.xml file with the following code:
<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:
<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:
|– build.xml
|– ivy.xml
`– src
`– helloworld.java
Now you can run the build with the command:
A successful build should provide output like this:
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:
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:
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:
-DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
It should create the folder structure and generate the output that looks like this:
[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:
|— 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:
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:
[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:
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 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:
|– 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:
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:
BUILD SUCCESSFUL in 1s
2 actionable tasks: 2 executed
You can run your jar like this:
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:
- http://ant.apache.org/
- http://ant.apache.org/ivy/
- https://maven.apache.org/
- https://gradle.org/
- http://makble.com/gradle-junit-helloworld-example
- https://examples.javacodegeeks.com/core-java/gradle/gradle-hello-world-tutorial/
- https://gradle.org/maven-vs-gradle/
- https://maven.apache.org/guides/getting-started/maven-in-five-minutes.html
- https://stackoverflow.com/questions/20755437/java-build-tools-ant-vs-maven
- https://technologyconversations.com/2014/06/18/build-tools/
- https://www.quora.com/What-are-the-pros-and-cons-of-Maven-versus-Ant-as-building-tools-for-Java