How to include ant-contrib.jar dynamically in Ant
Solution 1:
The best way is to put the Ant-Contrib jarfile inside you project. For example, let's say the build.xml
is in the root of your project. Create a directory called ant.lib\ant-contrib
inside your project, then put the ant-contrib*.jar
in this folder. You can use this method for other optional Ant tasks that you might need (for example, Ivy, Findbugs, Cobrrtura, etc).
Then, in your build.xml
file, you can do this:
<taskdef resource="net/sf/antcontrib/antlib.xml">
<classpath>
<fileset dir="${basedir}/ant.lib/ant-contrib"/>
</classpath>
</taskdef>
I like doing it this way because the optional jars with the tasks are included with the project. If you check everything into your version control system, someone can checkout your code, and do the build without downloading Ant-Contrib and installing it themselves.
You can define an XML namespaces. This gives your Ant-Contrib tasks a prefix in order to avoid task name collisions in case you use other optional ant tasks that have the same task name. Plus, it alerts users that this is not a standard Ant task.
If you use an XML namespace, you need to put a XMLNS declaration in your <project>
heading. This will contain a URI that will connect your Ant Contrib tasks to your XML namespace. For example, the ac:
namespace is for all Ant Contrib tasks:
<project name="my.project" default="package" basedir="."
xmlns:ac="http://ant-contrib.sourceforge.net">
<taskdef resource="net/sf/antcontrib/antlib.xml"
uri="http://ant-contrib.sourceforge.net">
<classpath>
<fileset dir="${basedir}/ant.lib/ant-contrib"/>
</classpath>
</taskdef>
What this does is match the XML namespace (xmlns) of ac
with the URI http://ant-contrib.sourceforge.net
. The URI could be anything. For example:
<project name="my.project" default="package" basedir="."
xmlns:ac="hamburger:with-fries">
<taskdef resource="net/sf/antcontrib/antlib.xml"
uri="hamburger:with-fries">
<classpath>
<fileset dir="${basedir}/ant.lib/ant-contrib"/>
</classpath>
</taskdef>
The standard is to use something like antlib:net.sf.antcontrib
:
<project name="my.project" default="package" basedir="."
xmlns:ac="antlib:net.sf.antcontrib">
<taskdef resource="net/sf/antcontrib/antlib.xml"
uri="antlib:net.sf.antcontrib">
<classpath>
<fileset dir="${basedir}/ant.lib/ant-contrib"/>
</classpath>
</taskdef>
However, I like using the URL of the project. That way, if someone wants documentation on Ant-Contrib tasks, they know the URL where the Ant-Contrib project lives.
In all three cases above, I've defined the XML namespace with ac
. Thus, you have to prefix all Ant-Contrib task names with ac:
. You could use antcontrib
or whatever you like. With the ac:
namespace, your Ant-contrib tasks will look like this:
<ac:if>
<istrue value="${include.debug.code}"/>
<ac:then>
[...]
</ac:then>
<ac:else>
[...]
</ac:else>
<ac:if>
If you skip the whole namespace thing, you can simply use the Ant-Contrib tasks as documented:
<if>
<istrue value="${include.debug.code}"/>
<then>
[...]
</then>
<else>
[...]
</else>
Solution 2:
The best solution is to integrate the apache ivy dependency manager. Ivy can be used to manage all your build classpaths Maven-style!
Example
ivy.xml
This file describes your project's 3rd party dependencies. Ivy uses configurations to logically group files together. In your case note the special "build" configuration uses to configure ANT tasks needed by the build:
<ivy-module version="2.0">
<info organisation="com.myspotontheweb" module="demo"/>
<configurations>
<conf name="compile" description="Required to compile application"/>
<conf name="runtime" description="Additional run-time dependencies" extends="compile"/>
<conf name="test" description="Required for test only" extends="runtime"/>
<conf name="build" description="3rd party ANT tasks"/>
</configurations>
<dependencies>
<!-- compile dependencies -->
<dependency org="org.slf4j" name="slf4j-api" rev="1.6.4" conf="compile->default"/>
<!-- runtime dependencies -->
<dependency org="org.slf4j" name="slf4j-simple" rev="1.6.4" conf="runtime->default"/>
<!-- test dependencies -->
<dependency org="junit" name="junit" rev="4.10" conf="test->default"/>
<!-- Build dependencies -->
<dependency org="ant-contrib" name="ant-contrib" rev="1.0b3" conf="build->default"/>
</dependencies>
</ivy-module>
Note:
- Dependencies are retrieved by default from Maven Central that now hosts approx 90% of open source jars
build.xml
<project name="demo" default="build" xmlns:ivy="antlib:org.apache.ivy.ant" xmlns:antcontrib="antlib:net.sf.antcontrib">
<target name="bootstrap" description="Install ivy">
<mkdir dir="${user.home}/.ant/lib"/>
<get src="https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.apache.ivy&a=ivy&v=LATEST&e=jar"
dest="${user.home}/.ant/lib/ivy.jar"/>
</target>
<target name="init" description="Use ivy to resolve classpaths">
<ivy:resolve/>
<ivy:report todir='build/ivy-reports' graph='false' xml='false'/>
<ivy:cachepath pathid="compile.path" conf="compile"/>
<ivy:cachepath pathid="runtime.path" conf="runtime"/>
<ivy:cachepath pathid="test.path" conf="test"/>
<ivy:cachepath pathid="build.path" conf="build"/>
</target>
<target name="taskdefs" depends="init" description="Declare 3rd party ANT tasks">
<taskdef uri="antlib:net.sf.antcontrib" classpathref="build.path"/>
</target>
<target name="build" depends="taskdefs" description="Build logic using ant-contrib tasks">
<echo message="The first five letters of the alphabet are:"/>
<antcontrib:for list="a,b,c,d,e" param="letter">
<sequential>
<echo>Letter @{letter}</echo>
</sequential>
</antcontrib:for>
</target>
<target name="clean" description="Cleanup build files">
<delete dir="build"/>
</target>
<target name="clean-all" depends="clean" description="Additionally purge ivy cache">
<ivy:cleancache/>
</target>
</project>
Notes:
- There is a special "bootstrap" target used to kick-start a new installation. Unfortunately ivy is not distributed with core ANT
- The cachepath ivy task is used to create ANT paths containing the jars downloaded and cached by ivy.
- The ivy report task is very useful to understand the transitive dependencies of your 3rd party libraries.
- antcontrib is now distributed as an ANT lib explaining the weird namespacing stuff.