ant

The Misunderstood Maven

Graeme Rocher answered my previous post. He strongly believes that maven will be replaced by Gant, Raven or Buildr.

I decide to learn about all them to see their features. Maybe I could find something better than Maven, something that justify expecting the replacement. But as soon as I started to evaluate the solutions I verified that some of then cannot be compared to maven. Gant is much more close to Ant to Maven. A lot of people think of maven as a replacement for ant, for them they are equivalent tools with different approaches. They are not equivalent tools, actually maven is a different category of tool. So I think is a good idea to explain what this is "different category".

Ant is a rewrite of Make in java. The main advantage of ant over make was the new build file format (in xml instead of the really bad space sensitive makefile format) and the fact that is cross-platform (so better for using in a cross-platform language). So ant operate in the same level of abstraction of make, a little higher because it abstracts the operating system but the developer still creating targets (even the vocabulary is the same in both tools). The main advance of the make/ant model is an different computational model (this is better explained by Martin Fowler in http://martinfowler.com/articles/rake.html#DependencyBasedProgramming, in my personal point of view it is a specialization of lazy evaluation in the pure functional languages sense, but it came from a different context and solving a different domain problem).

So in make/ant model you define targets and dependencies between this targets to make your build.

For example:

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

  <property name="dist" location="dist"/>

  <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>

I took this example from ant manual - http://ant.apache.org/manual/using.html#example.

As you can see in this simple build file is defining three properties and four targets. These four targets are really commons (init, compile, dist and clean).

Let's look close to the compile target:

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

Who, that uses ant for more than 6 months, have not written (or copy-and-paste) this target at least 5 times?
If we analyze we can see that it has one dependency (init), one task (javac) and two parameters for the task (srcdir and destdir). Why should I put this in my way if all I need is to specify the srcdir and the destdir?

When maven started it was just a layer on top of ant to avoid copying the targets around, but looking forward for opportunities to improve they make the right question: "why not put the files always in the same place?". It was born the standard layout directory of maven. Instead of having to think in the directory layout for each project use the same and take advantage of standardization ("Where is the java files?" /src/main/java "and the test cases?" /src/test/java "how do I put my resources files?" /src/main/resources "no, but it is just for my test environment" ok, /src/test/resources). This directory layout was created by maven, before the commonest one was /src for java sources and /bin to put the generate ("where should I put the groovy files?" "Choose a directory name and put in your build file" (or copy the maven layout /src/main/groovy)).

So maven is not comparable with ant/make it works on top of this layer. You still need the "scripts" but now the scripts are encapsulated in plugins that exposes goals (the tasks) and properties (that allow configuration when the default is not enough). This plugins are managed by the dependency system themselves so you don't need to install then, you just need to get then from the repository, the external ones (you can find virtually all open source java libraries/frameworks available there) or your internal (any medium size should have one, it's just a http server with a specific repository layout). Maven works in the project management level, you just set a few properties and your project is running. You don't need to write/copy-and-paste a war target the generate a war, just declare that that your packaging property is "war" and the package target will generate a war instead of a jar (yes, it is just that you don't need to read the manual).

So with this understanding of the different layers levels of maven and ant/make, let's see the tools cited by Graeme.

Gant:
Quoting there site "Gant is a build tool for scripting Ant tasks using Groovy instead of XML to specify the build logic." So they are change the xml syntax of ant to a groovy one, it seems a a good idea to me. But you still have to copy the scripts around. It's not there problem, they are in the ant/make level and anyway they are doing a step in the right direction. (The weakest point in ant IMHO is to use xml to define the targets, for me xml is for data, I never liked programming in xml (like jelly).

Raven:
Raven it's between both levels. It uses Rake and Gems. Rake is a re-implementation of make in ruby, like ant in java, but with a syntax better than xml (it uses ruby itself as the language for the scripts instead of the awful make syntax or xml syntax of ant). As in other make implementations, you still need to copy your scripts around. Gem is a package system used in ruby. It manages not just the dependencies but the install process of the software. Gems is similar to the package system of the linux distributions (if you want to go deep read about Portage or APT). You can search/install/upgrade/remove/query applications with just one command. This pattern works really well in system administrator. Gems brings this pattern for the development environment. Maven just get the library from your repository and make it available in the the classpath of your build environment (maven has the concept of scopes and each scope has a different classpath), Gems do that installing the library in your system, what is a more wide operation. The other downside is that the final artifact of gems is always a gem. Generating anything different of that is a side effect of a project specific script provided by you. Gem is user visible, the command is present in the user environment, maven is developer only, just the final artifact is delivered. The advantage is that for developers of desktop applications, gems is the installer (forget about InstallShield or Izpack). For server developer there is no installer at all, we delivery a war (or ear, or sar :) ) and this is deployed in the application server (or servlet container). Bring the package system pattern (in the portage/apt meaning) for build environment is a interesting idea because they are unifying a broader pattern (manager dependencies). But in the packaging system the main artifact are programs, libraries are there just as dependency for multiple programs (portage is able to manage side-by-side 2 versions of the same package but not all package are able to do that). In the developer environment there is no programs, libraries are the main artifact in the dependency system, put 2 versions of the same library is trivial (it's not installed, it is just available in the system file), I can use version 2.4 of some library in one project and version 2.5 in another. If they can remove the unnecessary complexity in the developer environment this can be interesting to see. Things a became more and more distributed, type "emerge mutt" (or "aptitude install mutt") and have the package and dependencies downloaded, installed and pre-configured (these package system are able to interact with the user) are amazing. Type "mvn package" in a clean machine and have all the libraries downloaded and placed in the right place is amazing. Have the same tool scaling up and down for both situation will be great. That is not yet the case for gems (developer environment is not their focus) or maven (manager installation is completely out of their scope) . But this is an are this can be worth researching. (If it has the simplicity of the minimal approach of the GoboLinux package system it will be a paradigm shift in the field).

- Buildr:
They use the maven "file layout, artifact specifications, local and remote repositories" (good, don't reinvent the wheel, if it works copy and improve it if it is possible). They simplify maven using ruby (what is cool IMHO), but they simplify the maven model in the wrong place. Quoting the main page of their site: "No overhead for building “plugins” or configuration. Just write new tasks or functions.". Ok, do I have to re-write again the compile task? do I have to re-write again the compile task? re-write again the compile task? re-write ... OK, you got the idea. That was a RBT (Really Bad Thing, ok, I made-up this name). So they copied maven but they missed the main point of maven. Using ruby is a good idea, but I need a way to reuse this scripts. Plugin (or give another name if you want) is just a script that can be reused. They think that maven plugin system is complex, simplify it. Actually this is a good point to simplify maven using ruby, a maven plugin has to implement org.apache.maven.plugin.AbstractMojo (http://maven.apache.org/guides/plugin/guide-java-plugin-development.html). Thanks to the dynamic typing I think that this can be simplified in ruby. Not all tasks are an "one-off task". So buildr can be a maven replacement if they provide a good way to reuse the scripts. But for now I don't want to get back to the times when all I have was ant and a big set of scripts.

So Gant is not intended to be a maven replacement (it is a ant improvement using groovy instead xml). Raven is a different sort of tool (ant/make level plus installer, no reuse of the scripts). Buildr is the only one that is intended to be a maven replacement, but they made a big mistake removing the plugin system. It will be interesting to look again if they fix this.

PS: Answering a comment thay "all ASF projects used to have Ant, they have Maven now... There is a reason ;)", Graeme says that "ASF use maven because maven is Apache and they have to eat their own dog food". Actually this is inaccurate, ant was created in the very first project of jakarta (jakarta was the java branch inside Apache Foundation, now there are quite a few top level java projects). Jakarta was create when sun licensed the source code to the JavaServer Web Development Kit (JSWDK) to Apache Foundation to create Tomcat in 1.999. Ant was created by James Davidson in the process of opening the source of the jswdk. It was necessary a cross platform build tool because they could not know anymore in what platform developers will use to build it. So practical problem, practical solution, re-implement make in java. So ASF didn't change because they "have to eat their own dog food", both was created in the ASF (maven was created inside turbine web framework that was a good alternative to struts but is dead now because of internal problems) and I don't think that none of then are dog food. They are different tools, each one with its own merits. Both projects helped to evolve the way we build systems with new ideas.

Syndicate content