ANT vs Maven vs Gradle

Raman Bhadauria
8 min readMar 25, 2020

--

Before getting into ANT, Maven or Gradle, we must first understand few things related to them.

Dependency: In general, a dependency refers to when something requires some another thing to get executed itself. Simply put, ‘A’ has a dependency on ‘B’ if ‘A’ requires ‘B’ for its successful execution. In software world, a dependency is anything that your application requires for its successful execution. It is basically any external support library required by the application. e.g. zuul, hystrix, lombok, jdbc, etc.

Initially, we used to manage dependencies by:

  • downloading the jar file of the required library manually from the internet and adding it to our project.
  • writing a script that will automatically download the library from an external source over the network.

Problems faced before these tools:

  • Adding the dependencies by manually downloading them from the internet is a very tiring task.
  • Our scripts might fail if the URL of the external source changes over the internet.
  • It is very difficult to identify and manage the transitive dependencies in our application.

Dependency management tool: It resolves and manages the dependencies required by the application.

Build tool: It automates the creation of executable applications from the source code. Building incorporates compiling, linking and packaging the code into a usable or executable form. Build automation involves:

  1. Downloading dependencies
  2. Compiling source code into binary code
  3. Packaging that binary code
  4. Running tests
  5. Deployment to production systems

Dependency management and build tools in java:

  • ANT + Ivy (2000/2004)
  • Maven (2004)
  • Gradle (2012)

Apache ANT

Apache ANT (Another Neat Tool) is an open source project by Apache, released in the year 2000. It is a java library used for automating the build processes for java applications. Also, it can be used for building non-java applications. It follows the principle of “Configuration over convention”. In Ant, different phases of build process are called “Targets”. ANT build files are written in XML and by convention, they are called “build.xml”. It contains three elements: project, target and task. Each build file contains one project. Every single thing in the build.xml is under the project element. Project contains at least one target (default). Target contains a set of tasks inside it and defines a specific state of the build process. A task is a piece of code which can be executed. Each node can have attributes associated with them:

Attributes of Project:

  • Name: The name of the project.
  • Basedir: The root folder for the project and it is optional.
  • Default: The default target for the build. The project can have one or more number of targets. It is used to specify the default target of the project.

Attributes of Target:

  • Name: The name of the target.
  • Description: A description about the target.
  • Depends: List of all targets, separated by comma that this target depends on.
  • If: The target is executed if the attribute is true.
  • Unless: The target is executed if the attribute is not set.

Build.xml example:

<?xml version=”1.0"?>
<project>
<target name=”hello”>
<echo>Hello, World</echo>
</target>
</project>

Add dependency in Ant + Ivy:

<dependency org="org.projectlombok" name="lombok" rev="1.18.10"/>

The main benefit of Ant is its flexibility. Ant doesn’t impose any coding conventions or project structure on the developer. Consequently, this means that Ant requires developers to write all the commands by themselves, which sometimes leads to large build files and are hard to maintain. Initially, Ant had no built-in support for dependency management. Later it adopted Apache Ivy, developed as a sub-project of Apache Ant project, for the purpose of dependency management.

Apache Maven

It is a dependency management and a build automation tool, released in 2004. It continues using XML but overcomes the drawbacks by following the principle of “Convention over configuration”. It relies on conventions and provides predefined commands (goals). Its configuration file, containing build and dependency management instructions, is by convention called “pom.xml” and is present in the root folder of the project.

Maven workflow

Maven engine takes pom.xml and the project as inputs. It reads the pom.xml file and downloads the dependencies mentioned in it as jar files into the local repository. Then, it executes the life cycles, build phases and plugins. In the end, a packaged and tested artifact is generated.

pom.xml example:

<?xml version="1.0" encoding="UTF-8"?> 
<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
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.5.RELEASE</version>
<relativePath/>
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>

Some important tags in the pom.xml file:

  • groupId: It represents the organisation to which the project belongs to.
  • artifactId: It is name of the project.
  • version: It represents the version of the project.
  • packaging: It represents the final form of the build of the project. e.g. jar, war.

Add dependency in maven:

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
</dependency>

Maven repository:

A repository is a directory where all the packaged jar files exist along with their own pom files. These pom files contains the external dependencies of that project. In this way maven downloads dependency of dependencies of your project recursively until all the required dependencies are present in your local repository. There are three types of repositories in maven:

  1. Local repository: It is a repository present on the developer’s machine itself.
  2. Organisation level/remote repository: It is a repository present within the organization.
  3. Central repository: It is a repository over the internet, created and maintained by the maven community.

Whenever a dependency is specified in the pom.xml file of a project, maven searches for it in the local repository. If it is not found there, it looks for it in the remote/org wide repository. If it is not present even there, then it looks for it in the central repository.

Maven prescribes strict project structure, while Ant provides flexibility there as well. It’s strict conventions comes with a price of being a lot less flexible than Ant — goal customization is very hard.

Drawbacks of maven:

  1. Dependency management doesn’t handle conflicts well between different versions of the same library.
  2. Customization of goals is hard.

Gradle

It is a open-source dependency management and build automation tool, released in 2012. It combines the good parts of Apache Ant and Apache Maven and builds on top of them and uses a domain specific language (based on Groovy) instead of XML. It adopted flexibility from Ant and its life-cycle from Maven. It also follows the principle of “Convention over configuration”. It supports Maven and Ivy repositories for retrieving the dependencies. Its configuration file is by convention known as “build.gradle” and is present in the root folder of the project. Gradle gave its build steps name “tasks”, as opposed to Ant’s “targets” or Maven’s “phases”. Google adopted Gradle as the default build tool for Android OS.

Gradle does not use XML. Instead, it had its own Domain Specific Language based on Groovy (one of JVM languages). As a result, Gradle build scripts tend to be much shorter and clearer than those written for Ant or Maven. The amount of boilerplate code is much smaller with Gradle.

Gradle configurations

  • implementation: It is used to declare dependencies that we don’t want to expose to our consumers’ compile time.
  • api: It is used to declare dependencies that are part of our API, i.e. for dependencies that we explicitly want to expose to our consumers.
  • compileOnly: It allows us to declare dependencies that should only be available at compile time, but are not needed at runtime. An example use case for this configuration is an annotation processor like Lombok, which modifies the bytecode at compile time. After compilation it’s not needed anymore, so the dependency is not available at runtime.
  • runtimeOnly: It allows us to declare dependencies that are not needed at compile time, but will be available at runtime. An example is SLF4J where we include slf4j-api to the implementation configuration and an implementation of that API (like slf4j-log4j12 or logback-classic) to the runtimeOnly configuration.
  • testImplementation: Similar to implementation, but dependencies declared with testImplementation are only available during compilation and runtime of tests. We can use it for declaring dependencies to testing frameworks like JUnit or Mockito that we only need in tests and that should not be available in the production code.
  • testCompileOnly: Similar to compileOnly, but dependencies declared with testCompileOnly are only available during compilation of tests and not at runtime.
  • testRuntimeOnly: Similar to runtimeOnly, but dependencies declared with testRuntimeOnly are only available during runtime of tests and not at compile time.

Projects and tasks in Gradle

Every Gradle build consists of one or more projects. Each project consists of a set of tasks. Each task represents a single piece of work that a build performs e.g. generate JavaDoc, publish some archives to a repository, etc.

build.gradle example

plugins {
id 'org.springframework.boot' version '2.2.5.RELEASE'
id 'io.spring.dependency-management' version '1.0.9.RELEASE'
id 'java'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter'
}
}

Gradle repository:

The “aliases” in Gradle are used in case of adding Maven repositories to our project build. These aliases are as following:

  • mavenCentral(): This alias stands for the dependencies that are fetched from the central Maven 2 repository.
  • jcenter(): This alias stands for the dependencies that are fetched from the Bintray’s JCenter Maven repository.
  • mavenLocal(): This alias stands for the dependencies that are fetched from the local Maven repository.

Example: add the central Maven repository in our project, add the following code snippet to our ‘build.gradle’ file:

repositories {
mavenCentral()
}

Add dependency in Gradle:

compile group: 'org.hibernate', name: 'hibernate-core', version: '3.6.7.Final'

Advantages:

  1. It handles transitive dependencies pretty well. If a conflicting transitive dependency exists in the project then to resolve it, it selects the latest version of the dependency. For example, dependency ‘A’ internally requires dependency ‘C’ with version 2.0 and dependency ‘B’ internally requires dependency ‘C’ with version 3.0. Then Gradle will use the latest verison of dependency ‘C’.
  2. Gradle’s configuration files are smaller in size and more cleaner as it uses domain specific language, based on Groovy instead of XML.
  3. Gradle uses incremental build concept and avoids work by tracking input and output of tasks and only running what is necessary, and only processing files that changed when possible and hence, performs faster than maven.

--

--