1.7. Comparing Maven with Ant
The authors of this book have no interest in creating a feud between
Apache Ant and Apache Maven, but we are also cognizant of the fact
that most organizations have to make a decision between the two
standard solutions: Apache Ant and Apache 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 which 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 Ant with
specific instructions for compiling and packaging your output. Look at
the following example of a simple build.xml
file:
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"/>
<!-- Ouput into ${build} into a 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 which includes the javac
task that 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. While there are some recent
developments that 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 A Simple Ant build.xml File is shown in
A Sample Maven pom.xml.
A Sample Maven pom.xml.
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>org.sonatype.mavenbook</groupId>
<artifactId>my-project</artifactId>
<version>1.0-SNAPSHOT</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 containing
nothing more than some source code and producing a simple JAR. It is a
project which closely follows Maven conventions and doesn’t require
any dependencies or customization. If we wanted 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
which 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/classes
”,
“Create a JAR from the …”, etc. Where Ant had to be explicit about
the process, there was something “built-in” to Maven that just knew
where the source code was and how it should be processed.
The differences between Ant and Maven in this example are:
-
Apache Ant
-
-
Ant doesn’t have formal conventions like a common project
directory structure or default behavior. 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. It knows where your source code is because
you followed the convention. Maven’s Compiler plugin put the
bytecode in
target/classes
, and it produces 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 was invoked when you executed
mvn
install
. This command told Maven to execute a series of
sequential lifecycle phases until it reached the install
lifecycle phase. As a side effect of this journey through the
lifecycle, Maven executed a number of default plugin goals which
did things like compile and create a JAR.
Maven has built-in intelligence about common project tasks in the form
of Maven plugins. If you wanted to write and execute unit tests, all
you would 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 wanted to deploy a
web application and not a JAR, all you would need to do is change your
project type to war
and put your docroot in
${basedir}/src/main/webapp
. Sure, you can do all of this with
Ant, but you will be writing the instructions from scratch. In Ant,
you would first have to figure out where the JUnit JAR file should
be. Then you would have to create a classpath that includes the JUnit
JAR file. 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 like 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 which 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 by just
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.