Comparing Maven and Ant

Although the previous section should convince you that the authors of this book have no interest in creating a feud between Apache Ant and Apache Maven, we are cognizant of the fact that most organizations have to make a decision between Ant and Maven. In this section, we compare and contrast the tools.

Ant excels at build process; it is a build system modeled after make with targets and dependencies. Each target consists of a set of instructions that are coded in XML. There is a copy task and a javac task as well as a jar task. When you use Ant, you supply it with specific instructions for compiling and packaging your output. Look at the simple build.xml file shown in Example 1-1.

Example 1-1. A simple Ant build.xml file

<project name="my-project" default="dist" basedir=".">
    <description>
        simple example build file
    </description>
  <!-- set global properties for this build -->
  <property name="src" location="src/main/java"/>
  <property name="build" location="target/classes"/>
  <property name="dist"  location="target"/>

  <target name="init">
    <!-- Create the time stamp -->
    <tstamp/>
    <!-- Create the build directory structure used by compile -->
    <mkdir dir="${build}"/>
  </target>

  <target name="compile" depends="init"
        description="compile the source " >
    <!-- Compile the java code from ${src} into ${build} -->
    <javac srcdir="${src}" destdir="${build}"/>
  </target>

  <target name="dist" depends="compile"
        description="generate the distribution" >
    <!-- Create the distribution directory -->
    <mkdir dir="${dist}/lib"/>

    <!-- Put everything in ${build} into the MyProject-${DSTAMP}.jar file -->
    <jar jarfile="${dist}/lib/MyProject-${DSTAMP}.jar" basedir="${build}"/>
  </target>

  <target name="clean"
        description="clean up" >
    <!-- Delete the ${build} and ${dist} directory trees -->
    <delete dir="${build}"/>
    <delete dir="${dist}"/>
  </target>
</project>

In this simple Ant example, you can see how you have to tell Ant exactly what to do. There is a compile goal that includes the javac task, which compiles the source in the src/main/java directory to the target/classes directory. You have to tell Ant exactly where your source is, where you want the resulting bytecode to be stored, and how to package this all into a JAR file. Although some recent developments help make Ant less procedural, a developer’s experience with Ant is in coding a procedural language written in XML.

Contrast the previous Ant example with a Maven example. In Maven, to create a JAR file from some Java source, all you need to do is create a simple pom.xml, place your source code in ${basedir}/src/main/java, and then run mvn install from the command line. The example Maven pom.xml that achieves the same results as the simple Ant file listed in Example 1-1 is shown in Example 1-2.

Example 1-2. A simple Maven pom.xml

<project>
  <modelVersion>4.0.0</modelVersion>
  <groupId>org.sonatype.mavenbook</groupId>
  <artifactId>my-project</artifactId>
  <version>1.0</version>
</project>

That’s all you need in your pom.xml. Running mvn install from the command line will process resources, compile source, execute unit tests, create a JAR, and install the JAR in a local repository for reuse in other projects. Without modification, you can run mvn site and then find an index.html file in target/site that contains links to Javadoc and a few reports about your source code.

Admittedly, this is the simplest possible example project: a project that contains only source code and produces a JAR; a project that follows Maven conventions and doesn’t require any dependencies or customization. If we want to start customizing the behavior, our pom.xml is going to grow in size, and in the largest of projects, you can see collections of very complex Maven POMs that contain a great deal of plugin customization and dependency declarations. But even when your project’s POM files become more substantial, they hold an entirely different kind of information from the build file of a similarly sized project using Ant. Maven POMs contain declarations: “This is a JAR project,” and “The source code is in src/main/java.” Ant build files contain explicit instructions: “This is project,” “The source is in src/main/java,” “Run javac against this directory,” “Put the results in target/classses,” “Create a JAR from the ....”, etc. Where Ant has to be explicit about the process, there is something “built-in” to Maven that just knows where the source code is and how it should be processed.

The differences between Ant and Maven in this example are:

Apache Ant
  • Ant doesn’t have formal conventions such as a common project directory structure; you have to tell Ant exactly where to find the source and where to put the output. Informal conventions have emerged over time, but they haven’t been codified into the product.

  • Ant is procedural; you have to tell Ant exactly what to do and when to do it. You have to tell it to compile, then copy, then compress.

  • Ant doesn’t have a lifecycle; you have to define goals and goal dependencies. You have to attach a sequence of tasks to each goal manually.

Apache Maven
  • Maven has conventions: in the example, it already knew where your source code was because you followed the convention. It put the bytecode in target/classes, and it produced a JAR file in target.

  • Maven is declarative; all you had to do was create a pom.xml file and put your source in the default directory. Maven took care of the rest.

  • Maven has a lifecycle, which you invoked when you executed mvn install. This command told Maven to execute a series of sequence steps until it reached the lifecycle. As a side effect of this journey through the lifecycle, Maven executed a number of default plugin goals that did things such as compile and create a JAR.

Maven has built-in intelligence about common project tasks in the form of Maven plugins. If you want to write and execute unit tests, all you need to do is write the tests, place them in ${basedir}/src/test/java, add a test-scoped dependency on either TestNG or JUnit, and run mvn test. If you want to deploy a web application and not a JAR, all you need to do is change you project type to WAR and put your docroot in ${basedir}/src/main/webapp. Sure, you could do all of this with Ant, but you would be writing the instructions from scratch. In Ant, you would first have to figure out where the JUnit JAR file should be, and then you would have to create a classpath that includes the JUnit JAR file, and then you would tell Ant where it should look for test source code, write a goal that compiles the test source to bytecode, and execute the unit tests with JUnit.

Without supporting technologies such as antlibs and Ivy (and even with these supporting technologies), Ant has the feeling of a custom procedural build. An efficient set of Maven POMs in a project that adheres to Maven’s assumed conventions has surprisingly little XML compared to the Ant alternative. Another benefit of Maven is the reliance on widely shared Maven plugins. Everyone uses the Maven Surefire plugin for unit testing, and if someone adds support for a new unit testing framework, you can gain new capabilities in your own build just by incrementing the version of a particular Maven plugin in your project’s POM.

The decision to use Maven or Ant isn’t a binary one, and Ant still has a place in a complex build. If your current build contains some highly customized process, or if you’ve written some Ant scripts to complete a specific process in a specific way that cannot be adapted to the Maven standards, you can still use these scripts with Maven. Ant is made available as a core Maven plugin. Custom Maven plugins can be implemented in Ant, and Maven projects can be configured to execute Ant scripts within the Maven project lifecycle.

Get Maven: The Definitive Guide now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.