ant Notes


Links

Collection of great links to ant tutorials, docs, types, etc.


Setting up ant

If you're confused about environment variables or other issues on Linux or Windows, read these steps carefully as I tell you enough across all steps to perform what's needed on both platforms although you might be learning in a subsequent step how to do something on your platform in an earlier one. (Have I confused you more?)

  1. Go to the ant Binary Distributions and download the package that corresponds to your OS.
  2.  
  3. If on Windows, take care to extract the zip close to the root because of long path names sometimes too long for Windows.
  4.  
  5. Unzip or tar -xf to an appropriate installation point; I like to use my private filesystem: /home/russ/tools/apache-ant-1.8.1 or its Windows equivalent.
  6.  
  7. Create ANT_HOME to point to where you installed. On Windows use Start -> Control Panel -> System -> Advanced system settings -> Environment Variables -> User variables. Add this new variable as something like C:\Users\russ\bin\apache-ant-1.8.1.
  8.  
  9. Ensure your JAVA_HOME point to the right place to find the JRE, for example, on Windows,
    	C:\Users\russ\dev\workspaces\workspace\Fun>dir %JAVA_HOME%\bin\java.exe
    must work.
  10.  
  11. Ensure also that ant's bin subdirectory be on your search path:
    	~/dev/workspaces/workspace/Fun > set $PATH="$PATH:$ANT_HOME/bin"
    On Windows, you'll be executing ant.bat on that path.
  12.  
  13. Do a trial run with ant somewhere:
    	C:\Users\russ\dev\workspaces\workspace\Fun>ant -version
    	Apache Ant version 1.8.1 compiled on April 30 2010

ant built-in properties

These are hard to find for some reason.

 
basedir the absolute path of the project's basedir (as set with the basedir attribute of ).
ant.file the absolute path of the buildfile.
ant.version the version of ant
ant.project.name the name of the project that is currently executing; it is set in the name attribute of .
ant.java.version the JVM version ant detected; currently it can hold the values "1.2", "1.3", "1.4" and "1.5".
ant.home home directory of ant
java.version JRE version
java.vendor JRE vendor
java.vendor.url Java vendor URL
java.home Java installation directory
java.vm.specification.version JVM specification version
java.vm.specification.vendor JVM specification vendor
java.vm.specification.name JVM specification name
java.vm.version JVM implementation version
java.vm.vendor JVM implementation vendor
java.vm.name JVM implementation name
java.specification.version JRE specification version
java.specification.vendor JRE specification vendor
java.specification.name JRE specification name
java.class.version Java class format version number
java.class.path Java class path
java.ext.dirs Path of extension directory(ies)
os.name Operating system name
os.arch Operating system architecture
os.version Operating system version
file.separator File separator ("/" on UNIX)
path.separator Path separator (":" on UNIX)
line.separator Line separator ("\n" on UNIX)
user.name User's account name
user.home User's home directory

System and personal variables

Here's how to get at your own system's environment from ant:

	<property environment="env" />

	<echo> Hostname: ${env.COMPUTERNAME} </echo>
	<echo> Path: ${env.Path} </echo>

Help me! Some of my variables won't expand!

If you see something very unpleasant like:

C:\Users\russ\1. dev\workspaces\android-workspace\ScoreModel>ant show-vars
Buildfile: C:\Users\russ\dev\workspaces\workspace\Fun\build.xml

show-vars:
     [echo]  build.dir=.
     [echo]  classes.dir=${build.dir}/classes
     [echo]  test.classes.dir=./test
     [echo]  jar.dir=${build.dir}/jar
     [echo]  src.dir=./src
     [echo]  lib.dir=./lib

BUILD SUCCESSFUL
Total time: 0 seconds

...and you're tempted to scream, "Why does ${build.dir} expand correctly some of the time, as used to define src.dir, and not others, for example, classes.dir?", stop and remember that you defined build.dir too late, or that you're working with two files, for example, build.xml which includes build-commons.xml (the one defining build.dir) later than classes.dir was defined by the first. Don't panic, just open your eyes!

(Obviously, I'm embarrassing myself by even noting a solution to this problem, but then I've a lot to be humble about: I'm not always the brightest Crayola™ in the box.)


How to manage buildnumber

Stackoverflow answers this question.


Some good ant targets I want to remember

This stuff was generated by Eclipse pursuant to an Export command.

Setting up the classpath(s).

      <path id="junit4.libraryclasspath">
          <pathelement location="${ECLIPSE_HOME}/plugins/org.junit_4.8.1.v4_8_1_v20100427-1100/junit.jar" />
          <pathelement location="${ECLIPSE_HOME}/plugins/org.hamcrest.core_1.1.0.v20090501071000.jar" />
      </path>
      ...
      <path id="myapplication.classpath">
          <pathelement location="build/classes" />
          <path refid="Apache Tomcat v6.0 [Apache Tomcat v6.0].libraryclasspath" />
          <path refid="junit4.libraryclasspath" />
          <pathelement location="lib/apache/log4j-1.2.16.jar" />
          <pathelement location="lib/apache/servlet-api.jar" />
          <pathelement location="lib/hibernate/antlr-2.7.6.jar" />
          <pathelement location="lib/hibernate/c3p0-0.9.1.jar" />
          <pathelement location="lib/hibernate/hibernate-jpa-2.0-api-1.0.1.Final.jar" />
          <pathelement location="lib/hibernate/hibernate3.jar" />
          <pathelement location="lib/hibernate/javassist-3.12.0.GA.jar" />
          <pathelement location="lib/jboss/webplatform-servicelocator-1.0.jar" />
          <pathelement location="lib/jersey/asm-3.1.jar" />
          <pathelement location="lib/jersey/jersey-client-1.6.jar" />
          <pathelement location="lib/jersey/jersey-core-1.6.jar" />
          <pathelement location="lib/jersey/jersey-json-1.6.jar" />
          <pathelement location="lib/jersey/jersey-server-1.6.jar" />
          <pathelement location="lib/jersey/oauth-server-1.10.jar" />
          <pathelement location="lib/jersey/oauth-signature-1.10.jar" />
          <pathelement location="lib/mysql/mysql-connector-java-5.1.16-bin.jar" />
          <pathelement location="lib/mongo/mongo-2.6.3.jar" />
          <pathelement location="lib/mongo/morphia-0.99.jar" />
          <pathelement location="lib/apache/commons-collections-3.1.jar" />
          <pathelement location="lib/apache/commons-configuration-1.7.jar" />
          <pathelement location="lib/apache/commons-lang-2.6.jar" />
          <pathelement location="lib/gson/gson-1.7.1.jar" />
          <pathelement location="lib/hibernate/jta-1.1.jar" />
          <pathelement location="lib/postgres/postgresql-9.1-901.jdbc4.jar" />
          <pathelement location="lib/spring/org.springframework.transaction-3.0.6.RELEASE.jar" />
          <pathelement location="lib/hibernate/dom4j-1.6.1.jar" />
          <pathelement location="lib/slf4j/slf4j-api-1.6.1.jar" />
          <pathelement location="lib/slf4j/slf4j-nop-1.6.1.jar" />
      </path>

Building both the application and the JUnit tests.

      <target name="build-project" depends="init">
          <echo message="${ant.project.name}: ${ant.file}" />

          <javac debug="true"
	                  debuglevel="${debuglevel}"
	                  destdir="build/classes"
	                  source="${source}"
	                  target="${target}"
	                  includeantruntime="false">
              <src path="src" />
              <classpath refid="myapplication.classpath" />
          </javac>

          <javac debug="true"
	                  debuglevel="${debuglevel}"
	                  destdir="build/classes"
	                  source="${source}"
	                  target="${target}"
	                  includeantruntime="false">
              <src path="test" />
              <classpath refid="myapplication.classpath" />
          </javac>

      </target>

Building the JUnit report. This turns up as a subdirectory with index.html you can browse. Builds one actual test, named AccountManagerTest.

      <property name="junit.output.dir" value="junit" />
      ...
      <target name="AccountManagerTest">
          <mkdir dir="${junit.output.dir}"/>
          <junit fork="yes" printsummary="withOutAndErr">
              <formatter type="xml"/>
              <test name="com.acme.web.user.manager.AccountManagerTest" todir="${junit.output.dir}"/>
          <classpath refid="rest-server.classpath"/>
          </junit>
      </target>
      ...
      <target name="junitreport">
          <junitreport todir="${junit.output.dir}">
              <fileset dir="${junit.output.dir}">
                  <include name="TEST-*.xml" />
              </fileset>
              <report format="frames" todir="${junit.output.dir}" />
          </junitreport>
      </target>


ant-contrib

There is a library, ant-contrib, that adds functionality that seems as if it should have been there in ant, in particular, conditional expressions.

Sometimes, in order for Eclipse (usually not when invoked from the command line) not to complain about pulling in this collection of tasks and tools, a random ant property must be defined before signalling it to ant. If you add it, then get mysterious errors, just define something as the first statement in build.xml after the header:

<?xml version="1.0" ?>
<!DOCTYPE project>
<project name="rest-server" default="deploy">

  <property name="eclipse_bug" value="make Eclipse not complain about pulling in ant-contrib" />
  ...

Later, before it's used, define the task resource. The paths here demonstrate that I'm consuming it from a project I've set up in Eclipse. I've placed the ant-contrib JAR under subdirectory lib off the project's root.

  <!-- Consume ant-contrib, which gives us if, then and else, from its library -->
  <taskdef resource="net/sf/antcontrib/antcontrib.properties">
    <classpath>
      <pathelement location="${basedir}/lib/ant-contrib/ant-contrib-1.0b3.jar" />
    </classpath>
  </taskdef>

Later, I use conditional expressions, one of the many benefits offered by ant-contrib. Search for an <else> element too (not used here).

  <if>
    <!-- Calculated properties for PostgreSQL... -->
    <equals arg1="${database}" arg2="postgres" />
    <then>
      <property name="jdbc_driver" value="${postgres_jdbc_driver}" />
      <property name="driver_classname" value="${postgres_driver_classname}" />
    </then>
  </if>

See the full example of ant-contrib use in Using Apache ant in Eclipse.


Advice on ant and Eclipse

Having trouble getting your build to work differently between local, development integration and production environments? I gave this advice once.

- Include (not using the ant include directive) a property file that is either missing or different in the build environment depending what you want to happen. For example, put this before other decisions are made:

    <property file="somepath/build.properties" />

and use it to define things like whether it's a LOCAL, DEV or PROD build. Then the rest of build.xml can know how to perform the build.

You see, somepath/build.properties, where this might be /opt/acme/apps/mywebapp/conf/build.properties, will exist on every build host, but it will be different because belonging to the owner of the host. On your Jenkins build server, it will have whatever's needed to do the production build (hypothetical example only).

- Note that, in ant, any time you define a property, the first definition holds from then on. (Re-)defining it later in the build has no effect. Use this to your advantage by making default definitions that hold when somepath/build.properties is missing (because ant doesn't complain that an included file is missing) or different but influences the decisions made in defining properties later.

- If you have really big problems splitting hairs, you can resort to using if-then-else from ant-contrib (Google for "ant if then else" and see above). This requires adding an ant library JAR. It takes some experimentation to figure out how to use it, but it can be useful to a) the beginning ant coder and b) the ant build from hell that must have this sort of power to solve its problems.

- Use built-in ant task to spill out the property definitions here and there in your script and squint through them to make sure they have the values you intend.

- Use ant -v to see much more about what ant is doing.

- I suggest using the latest possible version of ant. This will tend to avoid problems of older version behavior which can be bewildering sometimes, especially if the version of ant you're running from the command line is different from the one in Eclipse. Don't use anything older than ant 1.8.x. Typically, however, unless you're doing something odd, this point isn't that important. (It just can be is all I'm saying.)

- Google "eclipse ant version" to get help with how to ensure you're running the latest ant there.

- It doesn't matter whether you run ant inside of Eclipse or outside, it should behave the same. Choose which seems easier to you. On Linux, this would probably be a bash shell in my opinion.

- Google for help in ant using "ant <topic>". There is a LOT of help out there.


Installing ant 1.8 on Ubuntu

Here's how to get a proper ant running since Ubuntu doesn't see ant 1.8 as "stable." (It's only what? 2 years old already?) This makes use of the Eclipse development team's personal package archive (PPA).

    master ~/uas/accountmgr $ sudo apt-get install ant
    [sudo] password for russ:
    Reading package lists... Done
    Building dependency tree
    Reading state information... Done
    The following packages were automatically installed and are no longer required:
      linux-headers-3.2.0-29 linux-headers-3.2.0-29-generic
    Use 'apt-get autoremove' to remove them.
    The following extra packages will be installed:
      ant-optional ca-certificates-java default-jre-headless icedtea-6-jre-cacao icedtea-6-jre-jamvm
    ...
    Adding debian:thawte_Primary_Root_CA_-_G3.pem
    Adding debian:GlobalSign_Root_CA.pem
    Adding debian:Chambers_of_Commerce_Root_-_2008.pem
    done.
    Setting up openjdk-6-jre-lib (6b24-1.11.5-0ubuntu1~12.04.1) ...
    master ~/uas/accountmgr $ ant -version
    Unable to locate tools.jar. Expected to find it in /usr/lib/jvm/java-6-openjdk-amd64/lib/tools.jar
    Apache Ant(TM) version 1.7

    master ~/uas/accountmgr $ sudo add-apt-repository ppa:eclipse-team/debian-package
    You are about to add the following PPA to your system:
     PPA for the Eclipse package being developed by Debian.

    To install the OpenPGP key run
    sudo apt-key adv --recv-keys --keyserver keyserver.ubuntu.com 5126890CDCC7AFE0
     More info: https://launchpad.net/~eclipse-team/+archive/debian-package
    Press [ENTER] to continue or ctrl-c to cancel adding it

    gpg: keyring `/tmp/tmpHh1FYc/secring.gpg' created
    gpg: keyring `/tmp/tmpHh1FYc/pubring.gpg' created
    gpg: requesting key DCC7AFE0 from hkp server keyserver.ubuntu.com
    gpg: /tmp/tmpHh1FYc/trustdb.gpg: trustdb created
    gpg: key DCC7AFE0: public key "Launchpad PPA for Eclipse Team" imported
    gpg: Total number processed: 1
    gpg:               imported: 1  (RSA: 1)
    OK
    master ~/uas/accountmgr $ sudo apt-get update
    ...
    master ~/uas/accountmgr $ ant -version
    Apache Ant(TM) version 1.8.2 compiled on December 3 2011

To ~/.profile, I added:

    export JAVA_HOME=/home/russ/dev/jdk1.6.0_38

How to make a tarball in ant...

Shows how to make a tarball while including specific files and excluding others.

    <!-- p r o j e c t - t a r b a l l ===================================== -->
    <!-- This creates a tarball of the project; it does not leave an Eclipse
         project, but instead, something that can be used to create an Eclipse
         project (sources) using the Eclipse create-project wizard.
         -->
    <target name="project-tarball" depends="clean" description="Create a project tarball.">
        <echo message="Make a tarball of this project for distribution..." />
        <mkdir dir="${dist.dir}" />
        <tar destfile="${dist.dir}/${ant.project.name}.tar" basedir="${basedir}" excludes="dist/**,
                       extras/private/**,
                       .settings/**,
                       .classpath,
                       .project*" />
        <gzip destfile="${dist.dir}/${ant.project.name}.tar.gz" src="${dist.dir}/${ant.project.name}.tar" />
        <delete file="${dist.dir}/${ant.project.name}.tar" />
    </target>

Trouble in Eclipse with ant...

See discussion here.