24 Feb 05

Ant 1.6 Goody: <import>

If you’re using Ant but have yet to upgrade to Ant 1.6, then you’re missing out. Just to give you a glimpse of what you’re missing, over the next several weeks I’ll post quick examples of new features in Ant 1.6 that help reduce duplication in your build process so that you spend less time writing and maintaining build files.

To kick-start the tour, let’s begin with a new feature that promotes consistency across projects. The <import> task lets you conveniently mix in tasks from other build files. Say, for example, you have a build file called build-common.xml that includes common properties and tasks, such as a compile target.

  <project name="common">
    ...
    <target name="compile" depends="prepare">
      <javac srcdir="${src.dir}"
             destdir="${build.prod.dir}"
             classpathref="project.classpath"/>
    </target>
    ...
  </project>

Tomorrow you’re breaking ground on a new Java project, and it’s up to you to define the one-step build process. Obviously it will need to compile source files, so you need a compile target. Rather than falling prey to copy/paste reuse, create a project-specific build.xml file and import the existing build-common.xml file.

  <project name="dms" basedir=".">
    <import file="build-common.xml" />
  </project>

By importing build-common.xml, the project-specific build file has inherited all the common tasks and properties. For example, you’ve barely broken a sweat typing angle brackets and you’re already compiling code:

  $ ant compile
  Buildfile: build.xml

  prepare:
    [mkdir] Created dir: /Users/mike/work/dms/build/prod
  compile:
    [javac] Compiling 4 source file to /Users/mike/work/dms/build/prod

  BUILD SUCCESSFUL
  Total time: 3 seconds

Ah, but your project needs to do some work before and after the compilation step. It’s tempting to copy the compile target and modify it for project-specific needs, but that’s only because you haven’t tasted another spice of <import>: target overriding. Simply override the compile target in your build.xml file to add more behavior.

  <project name="dms" basedir=".">

    <!-- Override common properties here. -->

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

    <target name="compile"
            depends="pre-compile, common.compile, post-compile" />

    <target name="pre-compile">
      <echo>Compiling...</echo>
    </target>

    <target name="post-compile">
      <echo>Done.</echo>
    </target>

  </project>

Notice that the project-specific compile target uses a dependency order to ensure that the local pre-compile target is invoked before the common compile target (specified as common.compile) and the local post-compile target is called at the end.

Now you have a project-specific compile step:

  $ ant compile
  Buildfile: build.xml

  pre-compile:
     [echo] Compiling...
  prepare:
  common.compile:
  post-compile:
     [echo] Done.
  compile:

  BUILD SUCCESSFUL
  Total time: 3 seconds

Nothing was compiled because everything was up-to-date from the previous build, but notice that the common.compile target was indeed bracketed with your project-specific steps.

You’re done! So, now that your build process is in place, what’s next? Well, because setting up the build didn’t take long, you probably still remember that code you need to write. And now that you’re using the <import> task, hopefully you can steer clear of the build file until next week, when we peek at another new feature.