Collection of great links to ant tutorials, docs, types, etc.
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?)
Start -> Control Panel -> System -> Advanced system settings -> Environment Variables -> User variables
C:\Users\russ\dev\workspaces\workspace\Fun>dir %JAVA_HOME%\bin\java.exe
~/dev/workspaces/workspace/Fun > set $PATH="$PATH:$ANT_HOME/bin"
C:\Users\russ\dev\workspaces\workspace\Fun>ant -version Apache Ant version 1.8.1 compiled on April 30 2010
These are hard to find for some reason.
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>
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.)
Stackoverflow answers this question.
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>
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.
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.
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
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>
See discussion here.