Differences between Ant and Maven [closed]

Could someone tell me the differences between Ant and Maven? I have never used either. I understand that they are used to automate the building of Java projects, but I do not know where to start from.


Solution 1:

In Maven: The Definitive Guide, I wrote about the differences between Maven and Ant in the introduction the section title is "The Differences Between Ant and Maven". Here's an answer that is a combination of the info in that introduction with some additional notes.

A Simple Comparison

I'm only showing you this to illustrate the idea that, at the most basic level, Maven has built-in conventions. Here's a simple Ant build 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 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.

<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 which only contains source code and which produces a JAR. A project which 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/classses", "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.

High-level Comparison

The differences between Ant and Maven in this example? Ant...

  • doesn't have formal conventions like 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.
  • is procedural, you have to tell Ant exactly what to do and when to do it. You had to tell it to compile, then copy, then compress.
  • doesn't have a lifecycle, you had to define goals and goal dependencies. You had to attach a sequence of tasks to each goal manually.

Where Maven...

  • has conventions, 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.
  • 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.
  • 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 which did things like compile and create a JAR.

What About Ivy?

Right, so someone like Steve Loughran is going to read that comparison and call foul. He's going to talk about how the answer completely ignores something called Ivy and the fact that Ant can reuse build logic in the more recent releases of Ant. This is true. If you have a bunch of smart people using Ant + antlibs + Ivy, you'll end up with a well designed build that works. Even though, I'm very much convinced that Maven makes sense, I'd happily use Ant + Ivy with a project team that had a very sharp build engineer. That being said, I do think you'll end up missing out on a number of valuable plugins such as the Jetty plugin and that you'll end up doing a whole bunch of work that you didn't need to do over time.

More Important than Maven vs. Ant

  1. Is that you use a Repository Manager to keep track of software artifacts. I'd suggest downloading Nexus. You can use Nexus to proxy remote repositories and to provide a place for your team to deploy internal artifacts.
  2. You have appropriate modularization of software components. One big monolithic component rarely scales over time. As your project develops, you'll want to have the concept of modules and sub-modules. Maven lends itself to this approach very well.
  3. You adopt some conventions for your build. Even if you use Ant, you should strive to adopt some form of convention that is consistent with other projects. When a project uses Maven, it means that anyone familiar with Maven can pick up the build and start running with it without having to fiddle with configuration just to figure out how to get the thing to compile.

Solution 2:

Maven is a Framework, Ant is a Toolbox

Maven is a pre-built road car, whereas Ant is a set of car parts. With Ant you have to build your own car, but at least if you need to do any off-road driving you can build the right type of car.

To put it another way, Maven is a framework whereas Ant is a toolbox. If you're content with working within the bounds of the framework then Maven will do just fine. The problem for me was that I kept bumping into the bounds of the framework and it wouldn't let me out.

XML Verbosity

tobrien is a guy who knows a lot about Maven and I think he provided a very good, honest comparison of the two products. He compared a simple Maven pom.xml with a simple Ant build file and he made mention of how Maven projects can become more complex. I think that its worth taking a look at a comparison of a couple of files that you are more likely to see in a simple real-world project. The files below represent a single module in a multi-module build.

First, the Maven file:

<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-4_0_0.xsd">

    <parent>
        <groupId>com.mycompany</groupId>
        <artifactId>app-parent</artifactId>
        <version>1.0</version>
    </parent>

    <modelVersion>4.0.0</modelVersion>
    <artifactId>persist</artifactId>
    <name>Persistence Layer</name>

    <dependencies>

        <dependency>
            <groupId>com.mycompany</groupId>
            <artifactId>common</artifactId>
            <scope>compile</scope>
            <version>${project.version}</version>
        </dependency>

        <dependency>
            <groupId>com.mycompany</groupId>
            <artifactId>domain</artifactId>
            <scope>provided</scope>
            <version>${project.version}</version>
        </dependency>

        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate</artifactId>
            <version>${hibernate.version}</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>commons-lang</groupId>
            <artifactId>commons-lang</artifactId>
            <version>${commons-lang.version}</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring</artifactId>
            <version>${spring.version}</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>org.dbunit</groupId>
            <artifactId>dbunit</artifactId>
            <version>2.2.3</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.testng</groupId>
            <artifactId>testng</artifactId>
            <version>${testng.version}</version>
            <scope>test</scope>
            <classifier>jdk15</classifier>
        </dependency>

        <dependency>
            <groupId>commons-dbcp</groupId>
            <artifactId>commons-dbcp</artifactId>
            <version>${commons-dbcp.version}</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>com.oracle</groupId>
            <artifactId>ojdbc</artifactId>
            <version>${oracle-jdbc.version}</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.easymock</groupId>
            <artifactId>easymock</artifactId>
            <version>${easymock.version}</version>
            <scope>test</scope>
        </dependency>

    </dependencies>

</project>

And the equivalent Ant file:

<project name="persist" >

    <import file="../build/common-build.xml" />


    <path id="compile.classpath.main">
        <pathelement location="${common.jar}" />
        <pathelement location="${domain.jar}" />
        <pathelement location="${hibernate.jar}" />
        <pathelement location="${commons-lang.jar}" />
        <pathelement location="${spring.jar}" />
    </path>


    <path id="compile.classpath.test">
        <pathelement location="${classes.dir.main}" />
        <pathelement location="${testng.jar}" />
        <pathelement location="${dbunit.jar}" />
        <pathelement location="${easymock.jar}" />
        <pathelement location="${commons-dbcp.jar}" />
        <pathelement location="${oracle-jdbc.jar}" />
        <path refid="compile.classpath.main" />
    </path>


    <path id="runtime.classpath.test">
        <pathelement location="${classes.dir.test}" />
        <path refid="compile.classpath.test" />
    </path>


</project>

tobrien used his example to show that Maven has built-in conventions but that doesn't necessarily mean that you end up writing less XML. I have found the opposite to be true. The pom.xml is 3 times longer than the build.xml and that is without straying from the conventions. In fact, my Maven example is shown without an extra 54 lines that were required to configure plugins. That pom.xml is for a simple project. The XML really starts to grow significantly when you start adding in extra requirements, which is not out of the ordinary for many projects.

But you have to tell Ant what to do

My Ant example above is not complete of course. We still have to define the targets used to clean, compile, test etc. These are defined in a common build file that is imported by all modules in the multi-module project. Which leads me to the point about how all this stuff has to be explicitly written in Ant whereas it is declarative in Maven.

Its true, it would save me time if I didn't have to explicitly write these Ant targets. But how much time? The common build file I use now is one that I wrote 5 years ago with only slight refinements since then. After my 2 year experiment with Maven, I pulled the old Ant build file out of the closet, dusted it off and put it back to work. For me, the cost of having to explicitly tell Ant what to do has added up to less than a week over a period of 5 years.

Complexity

The next major difference I'd like to mention is that of complexity and the real-world effect it has. Maven was built with the intention of reducing the workload of developers tasked with creating and managing build processes. In order to do this it has to be complex. Unfortunately that complexity tends to negate their intended goal.

When compared with Ant, the build guy on a Maven project will spend more time:

  • Reading documentation: There is much more documentation on Maven, because there is so much more you need to learn.
  • Educating team members: They find it easier to ask someone who knows rather than trying to find answers themselves.
  • Troubleshooting the build: Maven is less reliable than Ant, especially the non-core plugins. Also, Maven builds are not repeatable. If you depend on a SNAPSHOT version of a plugin, which is very likely, your build can break without you having changed anything.
  • Writing Maven plugins: Plugins are usually written with a specific task in mind, e.g. create a webstart bundle, which makes it more difficult to reuse them for other tasks or to combine them to achieve a goal. So you may have to write one of your own to workaround gaps in the existing plugin set.

In contrast:

  • Ant documentation is concise, comprehensive and all in one place.
  • Ant is simple. A new developer trying to learn Ant only needs to understand a few simple concepts (targets, tasks, dependencies, properties) in order to be able to figure out the rest of what they need to know.
  • Ant is reliable. There haven't been very many releases of Ant over the last few years because it already works.
  • Ant builds are repeatable because they are generally created without any external dependencies, such as online repositories, experimental third-party plugins etc.
  • Ant is comprehensive. Because it is a toolbox, you can combine the tools to perform almost any task you want. If you ever need to write your own custom task, it's very simple to do.

Familiarity

Another difference is that of familiarity. New developers always require time to get up to speed. Familiarity with existing products helps in that regard and Maven supporters rightly claim that this is a benefit of Maven. Of course, the flexibility of Ant means that you can create whatever conventions you like. So the convention I use is to put my source files in a directory name src/main/java. My compiled classes go into a directory named target/classes. Sounds familiar doesn't it.

I like the directory structure used by Maven. I think it makes sense. Also their build lifecycle. So I use the same conventions in my Ant builds. Not just because it makes sense but because it will be familiar to anyone who has used Maven before.

Solution 3:

Ant is mainly a build tool.

Maven is a project and dependencies management tool (which of course builds your project as well).

Ant+Ivy is a pretty good combination if you want to avoid Maven.

Solution 4:

Just to list some more differences:

  • Ant doesn't have formal conventions. You have to tell Ant exactly where to find the source, where to put the outputs, etc.
  • Ant is procedural. You have to tell Ant exactly what to do; tell it to compile, copy, then compress, etc.
  • Ant doesn't have a lifecycle.
  • Maven uses conventions. It knows where your source code is automatically, as long as you follow these conventions. You don't need to tell Maven where it is.
  • Maven is declarative; All you have to do is create a pom.xml file and put your source in the default directory. Maven will take care of the rest.
  • Maven has a lifecycle. You simply call mvn install and a series of sequence steps are executed.
  • Maven has intelligence about common project tasks. To run tests, simple execute mvn test, as long as the files are in the default location. In Ant, you would first have to JUnit JAR file is, then create a classpath that includes the JUnit JAR, then tell Ant where it should look for test source code, write a goal that compiles the test source and then finally execute the unit tests with JUnit.

Update:

This came from Maven: The Definitive Guide. Sorry, I totally forgot to cite it.

Solution 5:

Maven or Ant? is a very similar question to this one, which should help you answer your questions.

What is Maven? on the official site.

edit: For a new/greenfield project, I'd recommend using Maven: "convention over configuration" will save you a decent chunk of time in writing and setting up build and deployment scripts. When you use ant, the build script tends to grow over time in length and complexity. For existing projects, it can be hard to shoehorn their configuration/layout into the Maven system.