Antlibs & Namespaces

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

The reference material consulted while preparing these pages is listed in the section bibliography.

This page is written against Apache Ant 1.10.18. Every example was run on JDK 26 on 2026-10-16 with the Ant 1.10.18 distribution unpacked in a scratch directory and the ant launch script on the PATH. The optional libraries were downloaded from Maven Central and their SHA-1 checked: AntUnit 1.5.0, Apache Ivy 2.6.0, the Props antlib 1.0.0, the Compress antlib 1.5 and Ant-Contrib 1.0b3. Paths in console output were shortened to /work/..., $ANT_HOME and /home/user.

A build file can only use a task whose name Ant knows. Before Ant 1.6 (2003) a library of tasks could ship a properties file with all its names, but the build file writer had no way to prefix those names or to override one that clashed with another library. Ant 1.6 added two things that solve this and that this page is about: XML namespaces for build files, so that a definition lives in a namespace and cannot clash with another one, and the antlib, a descriptor file that lists the definitions of a library, which can be found automatically from the name of a namespace. The page starts with how Ant’s own tasks are registered, explains namespaces and the descriptor, packages the tidepool <checkstations> task of Writing Custom Tasks as an antlib, and ends with the places where a library can be installed and a tour of the optional libraries.

Core, optional and third-party: where a name comes from

Three kinds of definitions coexist in a build, and they differ in how Ant finds them, not in how they look in the XML:

Kind Examples How Ant finds it

Core

copy, javac, jar, echo, condition

Registered by lists inside ant.jar; always available.

Optional

junitlauncher, sshexec, jdepend, xz

Registered by the same lists, but implemented in an ant-*.jar in ANT_HOME/lib, and some of them need a third-party library that you add. The list is on Installation.

Antlib

AntUnit, Compress, CycloneDX, Props (Apache), Ivy, Ant-Contrib and your own

Not registered by Ant. Declared by the build with <taskdef> or <typedef>, or found through an antlib: namespace URI.

The registration of core and optional definitions is plain text inside ant.jar, which can be listed and read:

$ unzip -l $ANT_HOME/lib/ant.jar | grep -E 'antlib.xml|defaults.properties'
org/apache/tools/ant/antlib.xml
org/apache/tools/ant/listener/defaults.properties
org/apache/tools/ant/taskdefs/defaults.properties
org/apache/tools/ant/types/conditions/antlib.xml
org/apache/tools/ant/types/defaults.properties
org/apache/tools/ant/types/resources/comparators/antlib.xml
org/apache/tools/ant/types/resources/selectors/antlib.xml
$ unzip -p $ANT_HOME/lib/ant.jar org/apache/tools/ant/taskdefs/defaults.properties | grep -E '^(copy|javac|junitlauncher|sshexec)='
copy=org.apache.tools.ant.taskdefs.Copy
javac=org.apache.tools.ant.taskdefs.Javac
junitlauncher=org.apache.tools.ant.taskdefs.optional.junitlauncher.confined.JUnitLauncherTask
sshexec=org.apache.tools.ant.taskdefs.optional.ssh.SSHExec

The task lists are defaults.properties files, one name=class line per task, and the ones for conditions, selectors and comparators are antlib.xml files with componentdef entries marked onerror="ignore":

$ unzip -p $ANT_HOME/lib/ant.jar org/apache/tools/ant/antlib.xml | grep -B1 -A1 -m3 'componentdef name'
  <!-- conditions -->
  <componentdef name="and" onerror="ignore"
       classname="org.apache.tools.ant.taskdefs.condition.And"/>
  <componentdef name="canCreateSymlink" onerror="ignore"
       classname="org.apache.tools.ant.taskdefs.condition.CanCreateSymbolicLink"/>
  <componentdef name="contains" onerror="ignore"
       classname="org.apache.tools.ant.taskdefs.condition.Contains"/>

Ant parses that antlib.xml when it starts a build, as a run with -v shows:

$ ant -v -f where.xml | grep antlib.xml
parsing buildfile jar:file:$ANT_HOME/lib/ant.jar!/org/apache/tools/ant/antlib.xml with URI = jar:file:$ANT_HOME/lib/ant.jar!/org/apache/tools/ant/antlib.xml from a zip file

The consequence of "registered by name, implemented in another JAR" is that an optional task whose JAR is missing is known but not loadable, and Ant says so. The experiment runs <junitlauncher/> in the full distribution and in a copy of the lib directory without ant-junitlauncher.jar (every other JAR was linked from the distribution):

$ ant -f launcher.xml
Buildfile: /work/tidepool-antlib/launcher.xml

try:

BUILD SUCCESSFUL
Total time: 0 seconds
$ ANT_HOME=fakeant ant -f launcher.xml
Buildfile: /work/tidepool-antlib/launcher.xml

try:

BUILD FAILED
/work/tidepool-antlib/launcher.xml:4: Problem: failed to create task or type junitlauncher
Cause: the class org.apache.tools.ant.taskdefs.optional.junitlauncher.confined.JUnitLauncherTask was not found.
        This looks like one of Ant's optional components.
Action: Check that the appropriate optional JAR exists in
        -fakeant/lib
        -/home/user/.ant/lib
        -a directory added on the command line with the -lib argument

Do not panic, this is a common problem.
The commonest cause is a missing JAR.

This is not a bug; it is a configuration problem


Total time: 0 seconds

The message names the class, says that it is one of Ant’s optional components and lists the three places where a JAR is looked up (ANT_HOME/lib, the user’s ~/.ant/lib and directories added with -lib). Those places come back below.

XML namespaces in a build file

Ant 1.6 turned namespace support on in its XML parser. The effect is that a colon in an element name now means a prefix, and an attribute called xmlns or xmlns:prefix is a namespace declaration rather than a parameter of the task. Build files from before 1.6 that used colons in custom task names, or tasks that had attributes called xmlns..., broke at that point.

A definition is placed in a namespace with the uri attribute of <taskdef> and <typedef>; the build file then maps a prefix to the same URI with xmlns:prefix, at the root or on any element. The URI is just an identifier, it is never fetched. The rules from the manual page XML Namespace Support:

  • The default namespace of Ant itself is antlib:org.apache.tools.ant. Elements with no namespace belong to it, and a definition without uri goes there: this is why a <taskdef> without uri defines a bare element.

  • An element in a namespace can be written with a prefix (<my:task xmlns:my="...">) or by making the namespace the default for a subtree (<task xmlns="...">).

  • Elements nested in a namespaced element are expected in the same namespace; since Ant 1.6.2 they may also be in Ant’s default namespace.

  • Attributes are used to configure an element only if they have no namespace (the default namespace does not apply to attributes) or are in the namespace of their element. Attributes of another namespace are ignored, so build files can carry metadata; an element of a namespace that was never defined is an error.

  • A definition can only be used where its type is accepted, so mixing namespaces works for typed nesting, such as a custom condition inside Ant’s <condition>.

The tidepool antlib below is used under the prefix td in one build file and under the prefix tide and the URI urn:tidepool in another. Neither the prefix nor the URI is a property of the library; the build file chooses both when it defines.

The antlib descriptor

An antlib file is an XML file with the root element antlib. Its children are the definition tasks that Ant bundles with this role, namely taskdef, typedef, macrodef, presetdef and scriptdef, and any other task that extends org.apache.tools.ant.taskdefs.AntlibDefinition, such as componentdef. That is how a single descriptor can define Java tasks, a macro written in XML and defaults for an existing task. The descriptor of the tidepool library:

tidepool-ant/src/main/resources/io/tidepool/ant/antlib.xml
<?xml version="1.0" encoding="UTF-8"?>
<antlib xmlns:current="ant:current">
  <taskdef name="checkstations" classname="io.tidepool.ant.CheckStationsTask"/>
  <taskdef name="foreachstation" classname="io.tidepool.ant.ForEachStationTask"/>

  <presetdef name="check-strict">
    <current:checkstations minconstituents="2" failonerror="true"/>
  </presetdef>

  <macrodef name="check-all">
    <attribute name="dir" default="data"/>
    <attribute name="includes" default="stations.csv"/>
    <sequential>
      <current:check-strict>
        <fileset dir="@{dir}" includes="@{includes}"/>
      </current:check-strict>
    </sequential>
  </macrodef>
</antlib>

Three lines carry the ideas of the page:

  • The <taskdef> elements have a name and a classname but no classpath and no uri: the library is loaded from a class path that is already set up, and the namespace is whatever the user’s uri or xmlns says.

  • xmlns:current="ant:current" declares a prefix for the special namespace ant:current. While an antlib is being loaded, every definition it makes lands in the namespace chosen by the caller, which the antlib cannot know. ant:current names that namespace, whatever it is, so the macro check-all can use the task and the preset check-strict that the same file defines. The log of a run shows the prefix: the preset runs under the name current:check-strict (see below).

  • Names can be reused from the core: the manual’s example defines a presetdef named javac in an antlib, which users who put it in their own namespace get as local:javac, with defaults, without touching the core <javac>. The definitions of Macros & Presets work the same way inside an antlib.

A macro or preset that forgets current: fails as soon as the antlib is used from a namespace, because the unprefixed name is looked up in the default namespace. The experiment is a variant of the descriptor whose preset says <checkstations .../> and not <current:checkstations .../>:

<antlib>
  <taskdef name="checkstations" classname="io.tidepool.ant.CheckStationsTask"/>
  <presetdef name="check-strict">
    <checkstations minconstituents="2"/>
  </presetdef>
</antlib>
$ ant -lib build/broken -f use-broken.xml
Buildfile: /work/tidepool-antlib/use-broken.xml

check:

BUILD FAILED
/work/tidepool-antlib/use-broken.xml:5: The following error occurred while executing this line:
Unable to find typedef checkstations

Total time: 0 seconds

The message names the definition that is missing but not the file that contains the mistake.

Packaging the tidepool task as an antlib

The tasks keep their classes. The only new file is the descriptor, which goes in the package directory of the library under the fixed name antlib.xml. The JAR then has the classes and, next to them, the descriptor:

build.xml
<?xml version="1.0" encoding="UTF-8"?>
<project name="tidepool-antlib" default="jar" basedir=".">
  <property name="classes.dir" location="build/classes"/>
  <property name="jar.file" location="build/tidepool-ant.jar"/>

  <target name="compile">
    <mkdir dir="${classes.dir}"/>
    <javac srcdir="tidepool-ant/src/main/java" destdir="${classes.dir}"
           release="21" includeantruntime="false" encoding="UTF-8">
      <classpath>
        <pathelement location="${ant.core.lib}"/>
      </classpath>
    </javac>
  </target>

  <target name="jar" depends="compile">
    <jar destfile="${jar.file}">
      <fileset dir="${classes.dir}"/>
      <fileset dir="tidepool-ant/src/main/resources"/>
    </jar>
  </target>
</project>
$ ant jar
Buildfile: /work/tidepool-antlib/build.xml

compile:
    [mkdir] Created dir: /work/tidepool-antlib/build/classes
    [javac] Compiling 2 source files to /work/tidepool-antlib/build/classes

jar:
      [jar] Building jar: /work/tidepool-antlib/build/tidepool-ant.jar

BUILD SUCCESSFUL
Total time: 0 seconds
$ unzip -l build/tidepool-ant.jar
META-INF/
META-INF/MANIFEST.MF
io/
io/tidepool/
io/tidepool/ant/
io/tidepool/ant/CheckStationsTask$ReportMode.class
io/tidepool/ant/CheckStationsTask$Require.class
io/tidepool/ant/CheckStationsTask.class
io/tidepool/ant/ForEachStationTask.class
io/tidepool/ant/antlib.xml

The <javac> classpath uses ${ant.core.lib}, the built-in property that holds the absolute path of ant.jar. Two other properties that the launcher sets are ant.home and ant.library.dir, the directory from which Ant’s JARs were loaded (usually ANT_HOME/lib); the latter is not set when Ant runs inside an IDE.

where.xml
<?xml version="1.0" encoding="UTF-8"?>
<project name="where" default="show">
  <target name="show">
    <echo>ant.home=${ant.home}</echo>
    <echo>ant.core.lib=${ant.core.lib}</echo>
    <echo>ant.library.dir=${ant.library.dir}</echo>
    <echo>ant.version=${ant.version}</echo>
  </target>
</project>
$ ant -q -f where.xml
     [echo] ant.home=$ANT_HOME
     [echo] ant.core.lib=$ANT_HOME/lib/ant.jar
     [echo] ant.library.dir=$ANT_HOME/lib
     [echo] ant.version=Apache Ant(TM) version 1.10.18 compiled on September 3 2026

BUILD SUCCESSFUL
Total time: 0 seconds

Using the antlib

There are three ways to bring the library to a build, from the least to the most explicit.

By namespace: antlib:java.package

A namespace URI of the form antlib:java.package is special. When Ant meets an element in such a namespace and nothing has been defined for it yet, it looks for a resource java/package/antlib.xml on its own class path and, if it finds one, loads it into that namespace. The class path here is Ant’s own, not the build’s: the JAR must be in ANT_HOME/lib, in ~/.ant/lib, or on -lib or CLASSPATH. The manual notes that the restriction to the default class path may be removed in a later version.

The build file needs only the namespace declaration:

use.xml
<?xml version="1.0" encoding="UTF-8"?>
<project name="use" default="check" basedir="."
         xmlns:td="antlib:io.tidepool.ant">

  <target name="check">
    <td:checkstations file="data/stations.csv" minconstituents="2" report="full"/>
    <td:check-all/>
    <td:foreachstation file="data/stations.csv">
      <echo>station ${station.id}</echo>
    </td:foreachstation>
  </target>
</project>

Without the JAR on Ant’s class path, the namespace is "an antlib declaration" with no definitions, and Ant tells where it looked (three paths, shown here after shortening):

$ ant -f use.xml
Buildfile: /work/tidepool-antlib/use.xml

check:

BUILD FAILED
/work/tidepool-antlib/use.xml:6: Problem: failed to create task or type antlib:io.tidepool.ant:checkstations
Cause: The name is undefined.
Action: Check the spelling.
Action: Check that any custom tasks/types have been declared.
Action: Check that any <presetdef>/<macrodef> declarations have taken place.
No types or tasks have been defined in this namespace yet

This appears to be an antlib declaration.
Action: Check that the implementing library exists in one of:
        -$ANT_HOME/lib
        -/home/user/.ant/lib
        -a directory added on the command line with the -lib argument


Total time: 0 seconds

With the library on -lib the same file runs, and uses all three kinds of definition in the library: a task, a macro that wraps a preset that wraps the task, and the task container. The log prefix current:check-strict is the ant:current namespace at work:

$ ant -lib build/tidepool-ant.jar -f use.xml
Buildfile: /work/tidepool-antlib/use.xml

check:
[td:checkstations] Stations seen: BOS,HNL,SF
[td:checkstations] 1 file(s), 3 station(s): OK
[current:check-strict] 1 file(s), 3 station(s): OK
     [echo] station SF
     [echo] station BOS
     [echo] station HNL

BUILD SUCCESSFUL
Total time: 0 seconds
flowchart TD E["element in the build file, for example td:checkstations"] --> N{"namespace URI of the element"} N -->|"none"| C["core and optional tasks and types: lists inside ant.jar"] N -->|"antlib:java.package"| A{"definitions for this URI already loaded?"} N -->|"any other URI"| D{"typedef or taskdef with that uri already ran?"} A -->|yes| U["use the definition"] A -->|no| L["look for java/package/antlib.xml on Ant's class path"] L -->|found| P["load it, put its definitions in this URI, use the definition"] L -->|not found| X["fail: failed to create task or type, no types or tasks have been defined in this namespace yet"] D -->|yes| U D -->|no| Y["fail: failed to create task or type, the name is undefined"]

By taskdef or typedef from inside the build file

When the JAR belongs to the project (checked into the repository, or built by the same build) and should not be installed in Ant, the build loads it explicitly with <taskdef> or <typedef>, with a resource, a uri and a classpath. The manual calls this "the best solution" for the case, because it needs no setup outside the project:

use-taskdef.xml
<?xml version="1.0" encoding="UTF-8"?>
<project name="use-taskdef" default="check" basedir="."
         xmlns:td="antlib:io.tidepool.ant">

  <taskdef uri="antlib:io.tidepool.ant" resource="io/tidepool/ant/antlib.xml"
           classpath="build/tidepool-ant.jar"/>

  <target name="check">
    <td:check-all/>
  </target>
</project>
$ ant -f use-taskdef.xml
Buildfile: /work/tidepool-antlib/use-taskdef.xml

check:
[current:check-strict] 1 file(s), 3 station(s): OK

BUILD SUCCESSFUL
Total time: 0 seconds

<typedef> and <taskdef> are interchangeable for a resource that points at an antlib, because the descriptor says which definitions are tasks. The uri need not be an antlib: URI; any name works, as long as the xmlns of the build file uses the same string. The next file picks urn:tidepool and the prefix tide, and checks every CSV in the data directory (the build fails, because bad.csv is in there; the second half of the output is the error, whose location is inside the JAR, where the failing preset was written):

use-urn.xml
<?xml version="1.0" encoding="UTF-8"?>
<project name="use-urn" default="check" basedir="."
         xmlns:tide="urn:tidepool">

  <typedef resource="io/tidepool/ant/antlib.xml" uri="urn:tidepool"
           classpath="build/tidepool-ant.jar"/>

  <target name="check">
    <tide:check-all includes="*.csv" dir="data"/>
  </target>
</project>
$ ant -f use-urn.xml
Buildfile: /work/tidepool-antlib/use-urn.xml

check:
[current:check-strict] bad.csv:3 latitude 137.8 is outside [-90.0, 90.0]
[current:check-strict] bad.csv:4 latitude is not a number: 'north'
[current:check-strict] bad.csv:4 has 1 constituent(s), at least 2 required
[current:check-strict] bad.csv:5 duplicate station id SF
[current:check-strict] bad.csv:5 has 0 constituent(s), at least 2 required
[current:check-strict] stations.csv:2 duplicate station id SF
[current:check-strict] stations.csv:3 duplicate station id BOS

BUILD FAILED
/work/tidepool-antlib/use-urn.xml:9: The following error occurred while executing this line:
jar:file:/work/tidepool-antlib/build/tidepool-ant.jar!/io/tidepool/ant/antlib.xml:14: 7 problem(s) found in station data

Total time: 0 seconds

Leaving out the uri defines the names in Ant’s default namespace, so that the build file needs no prefix at all. That is convenient and gives up the protection against clashes, which is the reason for namespaces in the first place:

use-global.xml
<?xml version="1.0" encoding="UTF-8"?>
<project name="use-global" default="check" basedir=".">

  <taskdef resource="io/tidepool/ant/antlib.xml" classpath="build/tidepool-ant.jar"/>

  <target name="check">
    <check-all/>
  </target>
</project>
$ ant -f use-global.xml
Buildfile: /work/tidepool-antlib/use-global.xml

check:
[current:check-strict] 1 file(s), 3 station(s): OK

BUILD SUCCESSFUL
Total time: 0 seconds

Two ways to get this wrong. The <taskdef> without a uri, used with a namespace prefix, defines the names, but in the wrong place (the message is the same one as with a missing JAR, which is why it is so confusing):

<project xmlns:td="antlib:io.tidepool.ant">
  <taskdef resource="io/tidepool/ant/antlib.xml" classpath="build/tidepool-ant.jar"/>
  ...
  <td:check-all/>
$ ant -f wrong-uri.xml
Buildfile: /work/tidepool-antlib/wrong-uri.xml

check:

BUILD FAILED
/work/tidepool-antlib/wrong-uri.xml:8: Problem: failed to create task or type antlib:io.tidepool.ant:check-all
Cause: The name is undefined.
Action: Check the spelling.
Action: Check that any custom tasks/types have been declared.
Action: Check that any <presetdef>/<macrodef> declarations have taken place.
No types or tasks have been defined in this namespace yet

This appears to be an antlib declaration.
Action: Check that the implementing library exists in one of:
        -$ANT_HOME/lib
        -/home/user/.ant/lib
        -a directory added on the command line with the -lib argument


Total time: 0 seconds

And a resource that does not exist, in a <taskdef> that has no onerror. Despite the default of fail, a missing resource is only a warning, and the build goes on; onerror="failall" (Ant 1.6) makes it an error:

$ ant -f wrong-resource.xml
Buildfile: /work/tidepool-antlib/wrong-resource.xml
  [taskdef] Could not load definitions from resource io/tidepool/antlib.xml. It could not be found.

check:

BUILD SUCCESSFUL
Total time: 0 seconds
$ ant -f wrong-resource-failall.xml
Buildfile: /work/tidepool-antlib/wrong-resource-failall.xml

BUILD FAILED
/work/tidepool-antlib/wrong-resource-failall.xml:3: Could not load definitions from resource io/tidepool/antlib.xml. It could not be found.

Total time: 0 seconds

Use failall whenever a missing library should end the build. The other values are report, which also warns when a definition cannot be loaded, and ignore, which is what Ant’s own antlib.xml uses on every componentdef, so that a definition whose class is missing is silently skipped; the manual’s antlib example uses onerror="ignore" for the same reason.

Everything said on Writing Custom Tasks about class loaders applies: a <taskdef resource=...> with a classpath creates one AntClassLoader for the whole descriptor, so the tasks of one antlib share classes and cooperate with no loaderref. This is why the manual recommends an antlib for "tasks and types that are supposed to cooperate".

Installing the library for every build

The third way, the JAR on Ant’s own class path, is a matter of where it is. Ant 1.6 added -lib and the per-user directory; the order is the one the manual lists:

  1. jars given with -lib, in the order on the command line (the CLASSPATH environment variable is also passed to Ant as a -lib, unless -noclasspath is given);

  2. jars in ${user.home}/.ant/lib (unless -nouserlib is given);

  3. jars in ANT_HOME/lib.

Ant itself starts with a minimal class path; the rest is built by its launcher from these three sources. The directory can be exercised without changing the real home directory, by pointing the user.home system property of the launcher JVM to a scratch directory through ANT_OPTS:

$ mkdir -p fakehome/.ant/lib && cp build/tidepool-ant.jar fakehome/.ant/lib/
$ ANT_OPTS=-Duser.home=$PWD/fakehome ant -f use.xml
Buildfile: /work/tidepool-antlib/use.xml

check:
[td:checkstations] Stations seen: BOS,HNL,SF
[td:checkstations] 1 file(s), 3 station(s): OK
[current:check-strict] 1 file(s), 3 station(s): OK
     [echo] station SF
     [echo] station BOS
     [echo] station HNL

BUILD SUCCESSFUL
Total time: 0 seconds
$ ANT_OPTS=-Duser.home=$PWD/fakehome ant -nouserlib -f use.xml
Buildfile: /work/tidepool-antlib/use.xml

check:

BUILD FAILED
/work/tidepool-antlib/use.xml:6: Problem: failed to create task or type antlib:io.tidepool.ant:checkstations
Cause: The name is undefined.
Action: Check the spelling.
...

ANT_ARGS carries command-line arguments of Ant that apply to every run in that shell, including -lib; the manual’s examples are a logger, a listener and -find. Here it carries a -lib and -q:

$ ANT_ARGS="-lib build/tidepool-ant.jar -q" ant -f use.xml
     [echo] station SF
     [echo] station BOS
     [echo] station HNL

BUILD SUCCESSFUL
Total time: 0 seconds

Choose by who must have the library:

Location Use it for

-lib on the command line, ANT_ARGS or a wrapper script

One build or one CI job; the JAR can live in the repository and the command line is the documentation.

~/.ant/lib

Tools that belong to one developer (an IDE helper, a personal logger). The build must not depend on it, or it fails on every other machine.

ANT_HOME/lib

A shared installation (a build server image). Changing it means modifying the Ant installation, which the manual says should be lockable, and the reason to prefer the user directory for personal jars.

<taskdef> with classpath

A library that is part of the project. Nothing outside the repository is needed.

For reproducible builds the last row (or -lib with a version-controlled path) is the only one that does not depend on the machine.

The optional antlibs

The Apache Ant project publishes a small set of antlibs, all of which use the mechanism described above and are on Maven Central (org.apache.ant). The current ones are listed on the Ant Libraries page, with their compatibility with Ant:

Antlib Namespace URI (from the JAR) Purpose Run on this page

AntUnit 1.5.0

antlib:org.apache.ant.antunit

Tests for tasks and build files, with assertion tasks; needs Ant 1.10.13 or later.

Yes, below and on Writing Custom Tasks

Props 1.0.0

antlib:org.apache.ant.props

Extra property evaluators for the PropertyHelper: nested properties, string operations and others.

Yes, below

Compress 1.5

antlib:org.apache.ant.compress

Archive and compression formats that Ant’s core lacks: 7z, ar, arj, cpio, brotli and others (it needs Commons Compress). Its last release is from 2017.

Not run here; the namespace was read from the descriptor in the JAR

CycloneDX 0.2

antlib:org.apache.ant.cyclonedx

Creates CycloneDX SBOMs, from Ivy modules or from information given by hand.

Not run here; the namespace was read from the descriptor in the JAR. See Supply-Chain Security

Outside the Ant project, two libraries are in common use. Apache Ivy is a separate Apache project with its own antlib (antlib:org.apache.ivy.ant), covered on Ivy Overview. Ant-Contrib (version 1.0b3) is a third-party library with if, for, foreach, var and many other tasks. It is dormant and not hosted by Apache, although the Ant site lists it on its projects page and the manual pages of macrodef and script link to it, and most of what it was used for has a core replacement (import, macrodef, local and the if:/unless: attributes); it is shown here because old build files use it, and the antlib URI given in the manual page of antlibs is antlib:net.sf.antcontrib.

All three load the same way. This build file declares all three libraries with <taskdef>, which shows that a build can mix sources and namespaces, and uses one task from each (the Ivy call downloads commons-csv from Maven Central into the Ivy cache, which is why the report says that nothing was downloaded: the cache of the machine already had the artefacts):

optional.xml
<?xml version="1.0" encoding="UTF-8"?>
<project name="optional" default="all" basedir="."
         xmlns:au="antlib:org.apache.ant.antunit"
         xmlns:ivy="antlib:org.apache.ivy.ant"
         xmlns:ac="antlib:net.sf.antcontrib">

  <target name="all" depends="antunit, ivy, contrib"/>

  <target name="antunit">
    <typedef resource="org/apache/ant/antunit/antlib.xml" uri="antlib:org.apache.ant.antunit"
             classpath="lib/ant-antunit-1.5.0.jar"/>
    <au:assertTrue message="1 is not 1">
      <equals arg1="1" arg2="1"/>
    </au:assertTrue>
    <echo>AntUnit loaded from an explicit classpath</echo>
  </target>

  <target name="ivy">
    <taskdef resource="org/apache/ivy/ant/antlib.xml" uri="antlib:org.apache.ivy.ant"
             classpath="lib/ivy-2.6.0.jar"/>
    <ivy:cachepath organisation="org.apache.commons" module="commons-csv" revision="1.14.1"
                   inline="true" conf="default" pathid="csv.path"/>
    <pathconvert property="csv.jar" refid="csv.path" pathsep=", ">
      <mapper type="flatten"/>
    </pathconvert>
    <echo>Ivy resolved: ${csv.jar}</echo>
  </target>

  <target name="contrib">
    <taskdef resource="net/sf/antcontrib/antlib.xml" uri="antlib:net.sf.antcontrib"
             classpath="lib/ant-contrib-1.0b3.jar"/>
    <ac:for list="SF,BOS,HNL" param="id">
      <sequential>
        <echo>station @{id}</echo>
      </sequential>
    </ac:for>
  </target>
</project>
$ ant -f optional.xml
Buildfile: /work/tidepool-antlib/optional.xml

antunit:
     [echo] AntUnit loaded from an explicit classpath

ivy:
[ivy:cachepath] :: Apache Ivy 2.6.0 - 20260712075753 :: https://ant.apache.org/ivy/ ::
[ivy:cachepath] :: loading settings :: url = jar:file:/work/tidepool-antlib/lib/ivy-2.6.0.jar!/org/apache/ivy/core/settings/ivysettings.xml
[ivy:cachepath] :: resolving dependencies :: org.apache.commons#commons-csv-caller;working
[ivy:cachepath] 	confs: [default]
[ivy:cachepath] 	found org.apache.commons#commons-csv;1.14.1 in public
[ivy:cachepath] 	found commons-io#commons-io;2.20.0 in public
[ivy:cachepath] 	found commons-codec#commons-codec;1.19.0 in public
[ivy:cachepath] :: resolution report :: resolve 67ms :: artifacts dl 2ms
	---------------------------------------------------------------------
	|                  |            modules            ||   artifacts   |
	|       conf       | number| search|dwnlded|evicted|| number|dwnlded|
	---------------------------------------------------------------------
	|      default     |   3   |   0   |   0   |   0   ||   3   |   0   |
	---------------------------------------------------------------------
     [echo] Ivy resolved: commons-csv-1.14.1.jar, commons-io-2.20.0.jar, commons-codec-1.19.0.jar

contrib:
     [echo] station SF
     [echo] station BOS
     [echo] station HNL

all:

BUILD SUCCESSFUL
Total time: 0 seconds

The Props antlib is an example of a library that has no tasks. Its definitions are PropertyHelper delegates, that are plugged in with the <propertyhelper> task, so the namespace is what makes it useful and a -lib is the usual way to load it:

props.xml
<?xml version="1.0" encoding="UTF-8"?>
<project name="props" default="show" basedir="."
         xmlns:props="antlib:org.apache.ant.props">

  <propertyhelper>
    <props:nested/>
  </propertyhelper>

  <property name="station" value="BOS"/>
  <property name="name.BOS" value="Boston"/>

  <target name="show">
    <echo>${name.${station}}</echo>
  </target>
</project>
$ ant -lib lib/ant-props-1.0.0.jar -f props.xml
Buildfile: /work/tidepool-antlib/props.xml

show:
     [echo] Boston

BUILD SUCCESSFUL
Total time: 0 seconds

Nested property references such as ${name.${station}} do not work in core Ant. Without the <props:nested/> evaluator, Ant refuses the <propertyhelper> as empty; with it, the inner reference is expanded first and the outer one then finds name.BOS.

Maven and Gradle

Neither tool has a namespace for tasks, because both have a different extension model:

Maven Gradle

How a library of tasks is added

a plug-in with a version in the POM or in pluginManagement, resolved from a repository

a plug-in in plugins {} or a class path dependency in the build script, resolved from a repository

Name clashes

a goal is always addressed as prefix:goal, so two plug-ins with the same goal name do not clash

task names live in the project; registering a second task with an existing name fails the build

Version of the library

part of the POM

part of the build script and the version catalog

The Ant way of getting a library, a JAR in a directory that the launcher scans or a -lib, has neither versions nor repositories; the Ivy and Maven Resolver Ant Tasks pages show the two ways to add them (Ivy Overview and Maven Resolver Ant Tasks). In the other direction, a Gradle build calls Ant tasks through its ant object, described on Ant Integration. The equivalence of concepts is in Comparing Ant, Maven & Gradle.

Where older material is outdated

  • Books and web pages from before Ant 1.6 declare every task of a library with its own <taskdef>, or with a properties resource, and no namespace. The mechanism described here (descriptors, the uri attribute and antlib: URIs) is what the Apache libraries use.

  • Ant-Contrib examples with <taskdef resource="net/sf/antcontrib/antcontrib.properties"/> define the names globally and rely on a JAR in ANT_HOME/lib or a classpath; the antlib form shown above is the one the manual recommends. Most of the library is superseded by import, macrodef, local and the if: and unless: attributes, covered on Targets & Extension Points.

  • The manual page for 1.10.18 still says that the requirement that an antlib: resource be on the default class path "may be removed in future versions"; it has not been removed, so a JAR that is found by namespace must be on Ant’s own class path.

  • Material that says to copy every JAR into ANT_HOME/lib predates -lib and ~/.ant/lib; changing the installation is no longer the first choice.

  • Pre-1.6 build files with colons in custom task names (<my:task>) break on current Ant, where the prefix has to be declared.

  • Antlibs that were .NET or Visual SourceSafe tools are retired; see the retired-antlibs page rather than old tutorials.

References