Ant: Installation
|
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 (released on 6 September 2026). Every example marked as run was executed on JDK 26
on 2026-10-16, on macOS, in a scratch directory; output that comes from a run is shown as printed, with the long scratch path
shortened to <scratch> and the home directory to <home>. Anything that could not be run here (Windows, package managers,
building Ant from source, a real proxy) is labelled "not run here". The commands use a POSIX shell; the Windows equivalents are
given from the manual’s own text.
Ant is installed by unpacking an archive. There is no installer, no daemon and no wrapper: the installation is a directory with a
launcher script, the core JAR and a set of optional JARs that are present but inert until the libraries they depend on are
also present. Most of the trouble people have with Ant installations is one of four things: the wrong Java, a stale CLASSPATH,
an optional task whose library is missing, or a proxy. This page walks through each, and shows the message you will see. The
page after this one, Running Ant, covers the command line. Which
release to install is the subject of
Versions & Release History. The running scenario
(tidepool) is described on the section landing page; for the other two tools
see Maven installation and
Gradle installation.
System requirements
Ant 1.10 needs a JDK of version 8 or later to run. A JRE is enough to start Ant, but then many tasks do not work, because they need
classes that only a JDK carries (javac, javadoc, jar signing tools). The manual lists Linux, Unix flavours such as Solaris and
HP-UX, macOS, Windows NT descendants, OS/2 Warp, Novell Netware 6 and OpenVMS as platforms where Ant has been used; the ones used
most for development are Linux, macOS, Windows and Unix. Windows 9x has not been supported since Ant 1.7. The Java floors of the
older lines (1.9: Java 5, 1.8: 1.4, 1.7: 1.3, 1.6: 1.2) and the JDK releases that removed features Ant had to follow are in the
history page.
The Java that runs Ant is not the Java that compiles your project: the javac task can pass release, source and target to
compile for an older platform, and the java task can fork a different JVM. That separation is why the Running Ant
page distinguishes JAVA_HOME (the JVM that runs Ant) from the toolchain a build chooses for itself. Maven has toolchains for
the same purpose and Gradle has toolchains with automatic provisioning; Ant has no equivalent, so a build that needs a specific JDK
passes it to the tasks explicitly.
Source: Ant manual: System Requirements
Getting Ant
The binary distribution
The latest stable version is on the Ant home page and the download page. The binary distribution comes as three archives with
the same content: .zip (the format recommended for Windows, usable anywhere), .tar.gz and .tar.bz2. Choose the one your
platform unpacks best. Next to each archive the download page lists the verification files: an .asc PGP signature, a .sha512
digest, and, in 1.10.18, a CycloneDX SBOM (.cdx.json, with its own signature and checksum) for the archive. The KEYS file with the release managers'
public keys is in the main distribution directory.
Older releases, with RELEASE-NOTES-<version>.html files and the ant-current-* convenience names, are in the archive at
https://archive.apache.org/dist/ant/. The manual advises against using ant-current-bin.zip for normal downloads, because the
mirror redirection of the download page is faster and spares the Apache servers.
Source: Apache Ant: Binary Distributions
Package managers and bundled copies
Several package managers carry Ant, and the version they carry lags behind the download page by a different amount each:
-
Homebrew has the
antformula (1.10.18 on 2026-10-16, read from the formula’s JSON onformulae.brew.sh), withopenjdkas a dependency. -
SDKMAN! has the
antcandidate (its API reported 1.10.17 as the default on 2026-10-16, one release behind). -
Linux distributions have an
antpackage; Chocolatey and Scoop have one on Windows. Their versions were not looked up here. -
The JPackage project distributes an RPM, which the manual describes: with it you need neither
ANT_HOMEnorJAVA_HOME, and ifANT_HOMEis set the JPackage layout is ignored since Ant 1.7.
None of these installers was run here ("not run here"); the manual does not document any of them except the JPackage RPM.
Two bundled-copy hazards are in the manual and worth stating. All the main IDEs (Eclipse, NetBeans, IntelliJ IDEA) ship with a
copy of Ant, the most recent one at the time of the IDE release; some add IDE-specific tasks that only work with IDE JARs on Ant’s
path, and the Ant team cannot take bug reports about them. Many Java applications, in particular application servers, also carry
their own Ant, and some of them add their own ant.bat or ant.sh to the PATH; if Ant starts behaving strangely after
installing such a product, run ant -diagnostics (below) to see which Ant you are actually running. A build that must be
reproducible should not depend on whichever Ant the machine has, which is the reason Maven and Gradle have wrappers; Ant has none, so the
usual ways to pin it are a download step in the CI job that checks the SHA-512, or a container image (see
CI & IDE Integration).
The source distribution
If you prefer the source, the download page has the source archives, and the Git repository has the leading edge; the manual notes
that the first response to many bug reports is to ask whether the latest version has the problem, and that building a pre-release
is not as risky as it sounds. Building Ant needs a greedy compiler such as OpenJDK or Oracle javac (it does not work with gcj or kjc) and
the auxiliary JARs of the tasks you want in lib/optional; the manual’s commands are build -Ddist.dir=<dir> dist on Windows
and sh build.sh -Ddist.dir=<dir> dist elsewhere, with install and install-lite targets that overwrite the current ANT_HOME.
Building from source was not run here; everything on this page uses the binary distribution.
Verifying the download
Ant publishes two checks for each archive, and the PGP signature is the one to trust. Take the .asc file and KEYS from the main
distribution directory, not from a mirror. The steps below were run on the Ant 1.10.18 .tar.gz, with a throwaway GNUPGHOME so
the check leaves your keyring alone. The .sha512 file holds the bare digest with no file name (see the
Supply-Chain Security page for the shasum -c form and for
the tampered-file counter-example); here the digest is compared directly.
EXPECTED=$(cat apache-ant-1.10.18-bin.tar.gz.sha512)
ACTUAL=$(shasum -a 512 apache-ant-1.10.18-bin.tar.gz | cut -d' ' -f1)
[ "$EXPECTED" = "$ACTUAL" ] && echo "SHA-512 matches"
mkdir gnupg && chmod 700 gnupg
export GNUPGHOME=$PWD/gnupg
tar -xzf apache-ant-1.10.18-bin.tar.gz -C dist # KEYS is also inside the archive
gpg --import dist/apache-ant-1.10.18/KEYS
gpg --verify apache-ant-1.10.18-bin.tar.gz.asc apache-ant-1.10.18-bin.tar.gz
SHA-512 matches
gpg: Signature made Thu Sep 3 18:26:19 2026 CEST
gpg: using EDDSA key 6A93161EB1990E8346E7BA2B23738DFD7C40DE43
gpg: issuer "bodewig@apache.org"
gpg: Good signature from "Stefan Bodewig <bodewig@apache.org>" [unknown]
gpg: WARNING: This key is not certified with a trusted signature!
gpg: There is no indication that the signature belongs to the owner.
Primary key fingerprint: BC26 C53A F531 F8B4 B3F5 930A AFBD 3AF8 EAFA 72DA
Subkey fingerprint: 6A93 161E B199 0E83 46E7 BA2B 2373 8DFD 7C40 DE43
"Good signature" with the "not certified" warning is the expected result of a fresh keyring: the signature is valid for a key that
appears in KEYS, and the warning says that you have not told GnuPG to trust that key. The question left is how you came to
trust KEYS; fetching it over HTTPS from downloads.apache.org and comparing the fingerprint with the Apache web of trust is the
stronger route. Because this verification used the KEYS file that came inside the archive itself, it proves the archive is
internally consistent, not that it is genuine. Do it with the file from the main distribution directory for a real check.
The SBOM that ships in the archive is covered in the supply-chain page (26 CycloneDX JSON files, one for each JAR in lib).
Unpacking and the directory layout
Choose a directory, unpack the archive there, and call that directory ANT_HOME. The distribution unpacked here is 44 MB on disk:
tar -xzf apache-ant-1.10.18-bin.tar.gz -C /opt
ls /opt/apache-ant-1.10.18 /opt/apache-ant-1.10.18/bin
CONTRIBUTORS
INSTALL
KEYS
LICENSE
NOTICE
README
WHATSNEW
bin
contributors.xml
etc
fetch.xml
get-m2.xml
lib
manual
patch.xml
ant
ant.bat
ant.cmd
antRun
antRun.bat
antRun.pl
antenv.cmd
complete-ant-cmd.pl
envset.cmd
lcp.bat
runant.pl
runant.py
runrc.cmd
(The listing is of the real 1.10.18 archive; /opt is the manual-style location, and the run extracted into a scratch directory.)
| Entry | What it is |
|---|---|
|
The launchers: |
|
|
|
XSL stylesheets for reports ( |
|
The HTML manual, offline. Every "since Ant 1.x" statement in it is the authoritative statement for the release you have. |
|
Build files that download the libraries the optional tasks need (see below). |
|
Release notes, text files, the PGP keys, and the build file the project uses to apply patches. |
Only bin and lib are required to run Ant; the rest can be left out of a minimal copy, for example in a container image. The
JAR files in lib look redundant until you remember that each optional task has a JAR of its own: ant-jsch.jar holds the scp and
sshexec tasks and needs the JSch library, which is not in the distribution, and the same pattern repeats for ant-commons-net.jar
(FTP), ant-junitlauncher.jar (JUnit Platform), ant-xz.jar and the others. The next sections show what that means.
Source: Ant manual: Installing Ant
Setup: JAVA_HOME, ANT_HOME and PATH
Three environment variables matter, and only one is required in practice.
-
PATHmust containANT_HOME/binso thatantcan be typed without its full path. -
ANT_HOMEis used by the launcher to find the libraries. On Unix and on Windows NT descendants the scripts can guess it; the manual recommends not relying on that. -
JAVA_HOMEselects the JDK. If it is unset the launcher looks for ajavaon thePATH(on macOS it asks/usr/libexec/java_home). A JDK is recommended, because some tasks need the Java tools.
The manual’s snippets for each shell are below (/usr/local/ant and the JDK 11 paths are its example values):
# Linux, Unix and macOS, bash or zsh
export ANT_HOME=/usr/local/ant
export JAVA_HOME=/usr/local/jdk-11.0.8+10
export PATH=${PATH}:${ANT_HOME}/bin
# Linux and Unix, csh or tcsh
setenv ANT_HOME /usr/local/ant
setenv JAVA_HOME /usr/local/jdk-11.0.8+10
set path=( $path $ANT_HOME/bin )
rem Windows and OS/2, for the current command-line session only
set ANT_HOME=c:\ant
set JAVA_HOME=c:\jdk11.0.8_10
set PATH=%PATH%;%ANT_HOME%\bin
These settings last only for the shell session you type them in; put them in a profile file (or, on Windows, in the system
environment dialog) to make them permanent. The Windows batch file expects ANT_HOME and JAVA_HOME to be set without quotes and
without a trailing \ or /, and CLASSPATH to be unset or empty; since Ant 1.10.16, ant.bat exits with a non-zero status if
ANT_HOME is not set. A symbolic link such as /usr/local/jdk pointing at the current JDK makes updates easier. Do not install
ant.jar into the lib/ext directory of a JDK: Ant is an application, and extension classes are subject to security
restrictions (the directory does not exist on current JDKs anyway). None of the Windows snippets was run here ("not run here"); they are
the manual’s.
The commands below were run: a clean copy of Ant 1.10.18 reached through a symbolic link on the PATH, with ANT_HOME unset (the
script resolved the link and guessed ANT_HOME), and then with JAVA_HOME pointing at two other JDKs. The build file is a
four-line probe that prints what Ant knows about itself:
<?xml version="1.0" encoding="UTF-8"?>
<project name="install-check" default="check">
<property environment="env."/>
<property name="env.CLASSPATH" value=""/>
<target name="check" description="Print what Ant sees of its installation">
<echo>ant.version = ${ant.version}</echo>
<echo>ant.java.version = ${ant.java.version}</echo>
<echo>ant.home = ${ant.home}</echo>
<echo>java.home = ${java.home}</echo>
</target>
<target name="no-classpath" description="Fail when CLASSPATH is set">
<fail message="Unset $CLASSPATH / %CLASSPATH% before running Ant!">
<condition>
<not>
<equals arg1="${env.CLASSPATH}" arg2=""/>
</not>
</condition>
</fail>
</target>
</project>
ln -s <scratch>/dist/apache-ant-1.10.18/bin/ant <scratch>/bin-on-path/ant
env -u ANT_HOME PATH="<scratch>/bin-on-path:$PATH" ant -version
env -u ANT_HOME PATH="<scratch>/bin-on-path:$PATH" ant -diagnostics | grep '^ant.home'
ant -p
ant check
JAVA_HOME=<home>/Library/Java/JavaVirtualMachines/jbr-21.0.10/Contents/Home ant check
JAVA_HOME=<home>/Library/Java/JavaVirtualMachines/jbr-17.0.14/Contents/Home ant check
Apache Ant(TM) version 1.10.18 compiled on September 3 2026
ant.home: <scratch>/dist/apache-ant-1.10.18
ant.home: <scratch>/dist/apache-ant-1.10.18
ant.home : <scratch>/dist/apache-ant-1.10.18
Main targets:
check Print what Ant sees of its installation
no-classpath Fail when CLASSPATH is set
Buildfile: <scratch>/demo/build.xml
check:
[echo] ant.version = Apache Ant(TM) version 1.10.18 compiled on September 3 2026
[echo] ant.java.version = 26
[echo] ant.home = <scratch>/dist/apache-ant-1.10.18
[echo] java.home = /Library/Java/JavaVirtualMachines/jdk-26.jdk/Contents/Home
[echo] ant.java.version = 21
[echo] java.home = <home>/Library/Java/JavaVirtualMachines/jbr-21.0.10/Contents/Home
[echo] ant.java.version = 17
[echo] java.home = <home>/Library/Java/JavaVirtualMachines/jbr-17.0.14/Contents/Home
The first line is the output of -version; the three ant.home lines are the three places where the diagnostics report prints
the home. The last block is the echo lines of the two JAVA_HOME runs, with the Buildfile and
BUILD SUCCESSFUL lines left out. The probe is the same in all runs, which is the point: one Ant directory, three JVMs, chosen
by JAVA_HOME alone.
When the Java cannot be found, the launcher script (not Ant) prints its own message and exits with status 1. Both JAVA_HOME
and JAVACMD (the full path of the java executable, which wins over JAVA_HOME) were tried with a wrong value:
JAVA_HOME=/nonexistent ant -version
JAVACMD=/nonexistent/java ant -version
We cannot run Java, please ensure you have Java installed.
We have tried to execute /nonexistent/bin/java but failed.
If you have installed Java in a unusual place you can set JAVA_HOME
to the directory containing the Java installation.
We cannot run Java, please ensure you have Java installed.
We have tried to execute /nonexistent/java but failed.
If you have installed Java in a unusual place you can set JAVA_HOME
to the directory containing the Java installation.
Source: Ant manual: Setup
Configuration files read by the shell launcher
The ant script is a shell script, and it sources up to three configuration files before it starts the JVM, unless --noconfig is
the first argument: /etc/ant.conf (only if ANT_HOME is not set), $HOME/.ant/ant.conf and $HOME/.antrc. They are shell
fragments, so they can set ANT_OPTS, ANT_ARGS, JAVA_HOME or JAVACMD for every run of one user. This is read from the script
itself; the manual only hints that some scripts read an extra script stored in the user’s home directory. A demonstration, with a .antrc in a scratch home directory that sets an Ant property through ANT_OPTS:
echo 'ANT_OPTS="-Xmx64m -Dgreeting=hello-from-antrc"' > <scratch>/rchome/.antrc
HOME=<scratch>/rchome ant -f rc.xml
HOME=<scratch>/rchome ant --noconfig -f rc.xml
[echo] greeting=hello-from-antrc
[echo] greeting=${greeting}
The two lines are the echo output of the two runs: the property was defined with the file, and an unset property is printed as
its own ${greeting} text without it (that is what Ant does with an undefined property, covered in
Properties). The Windows ant.bat has its own mechanism: it
calls antrc_pre.bat before and antrc_post.bat after the launch if they exist in %HOME%, %HOMEDRIVE%%HOMEPATH% or %USERPROFILE% (read from the
script, not run here). The --usejikes script option still exists and is a relic of the Jikes compiler of the
early 2000s. The manual’s statement that the scripts add tools.jar when JAVA_HOME is set is a relic too: JDK 9 removed
tools.jar, and the script only adds it if the file exists.
Source: Ant manual: Setup, Advanced
Checking the installation
A new shell and two commands are the check the manual proposes. With no build file in the directory, Ant reports it and exits with status 1; that failure proves Ant itself started:
mkdir empty && cd empty
ant -version
ant
echo "exit status $?"
Apache Ant(TM) version 1.10.18 compiled on September 3 2026
Buildfile: build.xml does not exist!
Build failed
exit status 1
If either fails, the manual’s checklist is the right one: ANT_HOME must resolve to the directory that contains
bin/ant (%ANT_HOME%\bin\ant.bat on Windows), JAVA_HOME is optional but must contain bin/java when set, and PATH must
contain ANT_HOME/bin. The manual’s own example output shows version 1.9.2 from 2013; the 1.10.18 text above replaces it.
Source: Ant manual: Check Installation
Where Ant looks for libraries
Ant needs libraries in two situations: its own optional tasks need third-party JARs, and your build needs JARs for tasks it
defines (taskdef, antlibs, a test framework). There are five places, in the order the manual lists them:
-
ANT_HOME/lib: available to every user and every build of this installation. -
~/.ant/lib(the.ant/libdirectory in the user’s home, since Ant 1.6): available to every build of that user. -
A
-libargument on the command line: a directory or JAR for one run. -
The
CLASSPATHenvironment variable: avoid. -
A
<classpath>on the task that needs the library. Since Ant 1.7.0 evenjunitcan loadjunit.jarthis way. This is the preferred option when a build can locate the library by a relative path, a property, an environment variable or an Ivy download, because the build file then carries its own requirements.
The -lib option and the -nouserlib and -noclasspath switches that turn two of these off are described in
Running Ant. The difference between the places is easy to see
with ant -diagnostics. A clean copy of Ant 1.10.18 has no JSch, so the tasks that depend on it are listed as missing. The same
Ant is then run with a home directory that has .ant/lib/jsch-0.1.55.jar (the home was moved with -Duser.home through
ANT_OPTS, so that the real home was not touched), then with -nouserlib, and then with -lib pointing at the same directory:
ant -diagnostics | sed -n '/Tasks availability/,/^A task/p'
ANT_OPTS="-Duser.home=<scratch>/fakehome" ant -diagnostics
ANT_OPTS="-Duser.home=<scratch>/fakehome" ant -nouserlib -diagnostics
ant -lib <scratch>/fakehome/.ant/lib -diagnostics
Tasks availability
-------------------------------------------
sshsession : Missing dependency com.jcraft.jsch.Logger
scp : Missing dependency com.jcraft.jsch.Logger
image : Missing dependency javax.media.jai.PlanarImage
jdepend : Missing dependency jdepend.textui.JDepend
xz : Missing dependency org.tukaani.xz.FilterOptions
sshexec : Missing dependency com.jcraft.jsch.Logger
ftp : Missing dependency org.apache.commons.net.ftp.FTPConnectionClosedException
A task being missing/unavailable should only matter if you are trying to use it
USER_HOME/.ant/lib jar listing
-------------------------------------------
user.home: <scratch>/demo/../fakehome
jsch-0.1.55.jar (282591 bytes)
Tasks availability
-------------------------------------------
image : Missing dependency javax.media.jai.PlanarImage
jdepend : Missing dependency jdepend.textui.JDepend
xz : Missing dependency org.tukaani.xz.FilterOptions
ftp : Missing dependency org.apache.commons.net.ftp.FTPConnectionClosedException
A task being missing/unavailable should only matter if you are trying to use it
The first block is the clean installation: seven tasks are missing. The second has the JAR in the user library directory, and
sshsession, scp and sshexec are now available (the USER_HOME/.ant/lib jar listing section is printed too). With
-nouserlib the output was identical to the first block, and with -lib it was identical to the second (the listing of the user
directory is the only difference): the library location changes what the one run can load, and that is the whole mechanism.
Source: Ant manual: Optional Tasks
Never set CLASSPATH
The manual spends a section on CLASSPATH, and its advice is blunt: do not ever set it. Ant does not need it; setting it makes the
JARs visible to every Java program on the machine and causes support calls. If you ignore that, never put quotes in it, and never end
it with a backslash, because that breaks the way Ant quotes the string. The usual symptom described in the manual is that Ant
fails to find org.apache.tools.ant.launch.Launcher, or reports a strange Java start-up error; ant -noclasspath (or unsetting the
variable) is the quick test. On the machine used for this page, with JDK 26 and the shell launcher, a CLASSPATH with a quote
inside, one that ended with a backslash, and one that named a directory that does not exist all started Ant 1.10.18 normally; that
symptom was not reproduced here (not run here for Windows, where the manual says the batch script is more fragile).
What can be done at the top of every build file is to refuse to run when the variable is set. The probe build file earlier has a
target no-classpath that does it, with the guard the manual suggests plus one line it omits: <property name="env.CLASSPATH"
value=""/>. Without that line, when CLASSPATH is unset, Ant leaves the text ${env.CLASSPATH} unexpanded, it is not equal to
the empty string, and the guard fails exactly when it should pass. The run below used that fixed version:
ant -q no-classpath
CLASSPATH=/some/lib.jar ant no-classpath
BUILD SUCCESSFUL
Total time: 0 seconds
Buildfile: <scratch>/demo/build.xml
no-classpath:
BUILD FAILED
<scratch>/demo/build.xml:14: Unset $CLASSPATH / %CLASSPATH% before running Ant!
Total time: 0 seconds
The environment property reads the real environment, so -noclasspath does not make the guard pass: the switch changes what Ant
loads, not what the environment says.
Optional tasks and their libraries
An optional task is one whose implementation needs an external library. The task classes ship in the distribution (the ant-*.jar
files in lib), the library does not. If the library is missing, Ant cannot create the task, and says so with a message that is
worth recognising. The build below uses scp against localhost with a key file that does not exist (nothing connects anywhere):
<project name="optional-demo" default="upload">
<target name="upload">
<scp file="data.txt" todir="user@localhost:/tmp" keyfile="${user.home}/.ssh/id_example" trust="true"/>
</target>
<target name="diag">
<diagnostics/>
</target>
</project>
ant upload
Buildfile: <scratch>/scp-demo/build.xml
upload:
BUILD FAILED
<scratch>/scp-demo/build.xml:4: Problem: failed to create task or type scp
Cause: Could not load a dependent class com/jcraft/jsch/Logger
It is not enough to have Ant's optional JARs
you need the JAR files that the optional tasks depend upon.
Ant's optional task dependencies are listed in the manual.
Action: Determine what extra JAR files are needed, and place them in one of:
-<scratch>/dist/apache-ant-1.10.18/lib
-<home>/.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 missing class, the three places to put the JAR, and tells you that this is a configuration problem. With JSch on the path (the Ant copy used for the next section), the same build file fails later and differently, which shows that the task now loads:
BUILD FAILED
<scratch>/scp-demo/build.xml:4: com.jcraft.jsch.JSchException: java.io.FileNotFoundException: <home>/.ssh/id_example (No such file or directory)
The diag target is the build-file form of ant -diagnostics: the diagnostics task prints the same report from inside a build,
which is what you want when asking how Ant behaves under an IDE.
Source: Apache Ant manual: Library Dependencies and SCP
The library table
The manual lists which library each feature needs. The table follows the manual’s list for 1.10.18 (the libraries that fetch.xml
can install are marked in the last column).
| JAR | Needed for | Where it comes from | fetch.xml target |
|---|---|---|---|
|
The |
|
|
|
The |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
GraalVM JavaScript ( |
|
the GraalVM project |
|
|
|
|
|
Groovy, Jython, JRuby, BeanShell, NetRexx, Rhino, Jacl or JTcl, Judo |
|
each project |
|
|
|
Apache Commons and Logging projects |
|
|
|
|
|
|
|
Apache Xalan and XML Commons |
|
|
|
various |
|
Source: Ant manual: Library Dependencies
The advice that goes with the table is to prefer the task’s own <classpath> or a dependency manager when a build can. A
committed lib directory, an Ivy retrieve (see Ivy Overview)
or the Maven Resolver Ant Tasks
(see Maven Resolver Ant Tasks) make a build
independent of how this one workstation was set up. Maven and Gradle have the opposite default: the plugin or the dependency
declaration brings its libraries, and there is nothing to install by hand.
fetch.xml: downloading the libraries
The distribution ships a build file that downloads most of those JARs from Maven Central. It has one required property, dest,
that chooses where the JARs go: system is ANT_HOME/lib (recommended by the manual), user is ~/.ant/lib and optional is
the lib/optional directory of an Ant source tree. The targets are listed by ant -f fetch.xml -p (the descriptions are the build
file’s own); the full list in 1.10.18 is: all (everything except Jython), antunit, ivy, logging, junit, junitlauncher,
xml, networking, regexp, antlr, bcel, jdepend, bsf, jruby, beanshell, jython, rhino, graal.js, script,
debugging, javamail, jakartamail, jspc, jai, netrexx, xz, ant-cyclonedx and get-m2.
A behaviour that is not in the manual and that matters: fetch.xml itself needs the Maven Resolver Ant Tasks. On its first run it
downloads maven-resolver-ant-tasks-1.4.0-uber.jar (the version in libraries.properties, validated against a checksum kept there) into the destination directory, and then uses it to resolve each library with its transitive dependencies into your
local Maven repository (~/.m2/repository) before copying the JARs. The run below was done on a fresh copy of the distribution
with -Duser.home moved by ANT_OPTS, so that nothing reached the real home directory, and downloaded from Maven Central:
ANT_OPTS="-Duser.home=<scratch>/fh3" ant -f fetch.xml -Ddest=system networking
Buildfile: <scratch>/dist3/apache-ant-1.10.18/fetch.xml
pick-dest:
[echo] Downloading to <scratch>/dist3/apache-ant-1.10.18/lib
probe-m2:
download-m2:
[echo] Downloading to <scratch>/dist3/apache-ant-1.10.18/lib
[get] Getting: https://repo1.maven.org/maven2/org/apache/maven/resolver/maven-resolver-ant-tasks/1.4.0/maven-resolver-ant-tasks-1.4.0-uber.jar
[get] To: <scratch>/dist3/apache-ant-1.10.18/lib/maven-resolver-ant-tasks-1.4.0-uber.jar
[get] ....................................................
[get] ....................................................
[get] ....................................................
[get] ....................................................
[get] ....................................................
[get] ....................................................
[get] .............................
dont-validate-m2-checksum:
validate-m2-checksum:
checksum-mismatch:
checksum-match:
get-m2:
macros:
init:
networking:
[resolver:resolve] Downloading https://repo1.maven.org/maven2/commons-net/commons-net/3.13.0/commons-net-3.13.0.pom
[resolver:resolve] Downloaded https://repo1.maven.org/maven2/commons-net/commons-net/3.13.0/commons-net-3.13.0.pom (26 KB at 203.1 KB/sec)
[resolver:resolve] Downloading https://repo1.maven.org/maven2/org/apache/commons/commons-parent/97/commons-parent-97.pom
[resolver:resolve] Downloaded https://repo1.maven.org/maven2/org/apache/commons/commons-parent/97/commons-parent-97.pom (78 KB at 1080.4 KB/sec)
[resolver:resolve] Resolving artifacts
[resolver:resolve] Downloading https://repo1.maven.org/maven2/commons-net/commons-net/3.13.0/commons-net-3.13.0.jar
[resolver:resolve] Downloaded https://repo1.maven.org/maven2/commons-net/commons-net/3.13.0/commons-net-3.13.0.jar (330 KB at 1996.4 KB/sec)
[copy] Copying 2 files to <scratch>/dist3/apache-ant-1.10.18/lib
[resolver:resolve] Downloading https://repo1.maven.org/maven2/com/jcraft/jsch/0.1.55/jsch-0.1.55.jar
[resolver:resolve] Downloaded https://repo1.maven.org/maven2/com/jcraft/jsch/0.1.55/jsch-0.1.55.jar (276 KB at 1999.8 KB/sec)
[copy] Copying 1 file to <scratch>/dist3/apache-ant-1.10.18/lib
BUILD SUCCESSFUL
Total time: 1 second
(Some of the Downloading and Downloaded pairs, for parent POMs, a JUnit BOM, the commons-io dependency of commons-net and the POM of
JSch, were removed from the real output to keep it short; every line shown is real and in order.) The result in lib was
three new JARs next to the distribution’s own, plus the uber JAR that did the work:
commons-io-2.21.0.jar
commons-net-3.13.0.jar
jsch-0.1.55.jar
maven-resolver-ant-tasks-1.4.0-uber.jar
Run before and after, ant -diagnostics changed from seven missing tasks to three (image, jdepend and xz remained): ftp,
scp, sshexec and sshsession were available. With junitlauncher as the target, the JUnit Platform 1.10.5 jars (junit-platform-launcher,
-engine, -commons) came in together with apiguardian-api and opentest4j.
Two traps showed up on the way. The first is that the build refuses more than one target, with a message that blames a bug in the Maven Ant tasks:
ant -f fetch.xml -Ddest=system networking junitlauncher
BUILD FAILED
<scratch>/dist2/apache-ant-1.10.18/fetch.xml:102: Cannot execute multiple targets due to the bug in Maven Ant tasks
Total time: 0 seconds
Run fetch.xml once per target (a shell loop does it) rather than listing targets together; the all target works because it is
a single target that depends on the others. The second trap is that the versions are those pinned in lib/libraries.properties, which
for 1.10.18 means JUnit Platform 1.10.5, CycloneDX Antlib 0.1 and Resolver Ant Tasks 1.4.0, all older than the releases
named on the history page. For a build that needs current libraries, resolve them in the build itself.
Proxy configuration
Every task and thread in Ant’s JVM shares one HTTP, FTP and SOCKS proxy configuration. A get, an XSL task that fetches a stylesheet
by URL, an Ivy resolver and any third-party task that uses java.net.URL are all affected, and a build that works at home can
hang behind a corporate firewall. The only way to fix it is to configure the JVM, and the manual gives four ways:
-
-autoproxyon the command line (since Ant 1.7): Ant sets thejava.net.useSystemProxiessystem property, so the JVM reads the operating system’s settings. The manual calls the results inconsistent (it has been reported to break an IBM JVM on AIX and to not always work on Linux). To make it the default, put it inANT_ARGS. -
Explicit JVM properties in
ANT_OPTS:-Dhttp.proxyHost,-Dhttp.proxyPortand the matching HTTPS, FTP and SOCKS properties. This is the best option for a machine that does not roam. -
The
setproxytask in the build file, which sets the same JVM properties. If it is used in a build file for a firewall, take the host and port from properties, so that other users can override the defaults. -
Passing the properties through
ANT_ARGSas JVM options (the manual says explicit JVM options are probably the best solution until JVM auto-detection works everywhere).
Setting a property on Ant’s own command line does not work, because -D defines an Ant property, not a JVM system property. That
is easy to demonstrate, with a tiny class that prints the JVM property and a build file that runs it inside Ant’s JVM (the proxy host
is a placeholder that does not exist):
public class Sys {
public static void main(String[] a) {
System.out.println("System property http.proxyHost = " + System.getProperty("http.proxyHost"));
}
}
<project name="proxy-jvm" default="show">
<property name="proxy.host" value="proxy.example.test"/>
<property name="proxy.port" value="8080"/>
<target name="compile">
<javac srcdir="." includes="Sys.java" destdir="." includeantruntime="false"/>
</target>
<target name="show" depends="compile">
<java classname="Sys" classpath="." fork="false"/>
</target>
<target name="show-setproxy" depends="compile">
<setproxy proxyhost="${proxy.host}" proxyport="${proxy.port}"/>
<java classname="Sys" classpath="." fork="false"/>
</target>
</project>
ant -Dhttp.proxyHost=proxy.example.test show
ANT_OPTS=-Dhttp.proxyHost=proxy.example.test ant show
ant show-setproxy
Buildfile: <scratch>/demo/pp2.xml
compile:
[javac] Compiling 1 source file to <scratch>/demo
show:
[java] System property http.proxyHost = null
BUILD SUCCESSFUL
Total time: 0 seconds
show:
[java] System property http.proxyHost = proxy.example.test
show-setproxy:
[java] System property http.proxyHost = proxy.example.test
The three blocks are the results of the three commands (the second and third show only the line of interest). The first prints
null: -D defined an Ant property named http.proxyHost (it appears in <echoproperties/>), and no JVM property. ANT_OPTS
and setproxy set the real JVM property. The diagnostics report has a "Java1.5+ proxy settings" section that also shows the
system properties: with ANT_OPTS set, http.proxyHost and http.proxyPort were listed there. -autoproxy was run too and
reported "Direct connection", which is the correct answer for a machine with no system proxy; the behaviour behind a real proxy
was not run here ("not run here").
The manual is candid about the limits: Ant cannot go beyond what the JVM does for authenticating proxies, and diagnosing them
remotely is hard, so users behind a secure proxy have to spend time with the JVM properties until they work. The Maven Resolver
Ant Tasks and Ivy have their own proxy settings in addition to the JVM’s (a <proxy> type in the first, the credentials and
settings elements in the second); the Maven Resolver Ant Tasks
page covers them. In Maven the equivalent is the <proxies> element of settings.xml, and in Gradle the systemProp.http.proxyHost
properties of gradle.properties.
Source: Ant manual: Proxy Configuration
Source: Ant manual: the setproxy task
ant -diagnostics
ant -diagnostics prints a report of Ant’s view of itself, and is the first thing the user mailing list will ask you for. It checks and
prints, in this order: where Ant runs from, the version of ant.jar and of the ant-*.jar files and whether they match, the JARs in
ANT_HOME/lib and in the user’s library directory, the optional tasks that are available and the ones that are not (with the missing
class), the XML parser and the XSLT processor, whether symbolic links can be created, the JVM system properties, the temporary
directory (an unwritable one, or one with a wrong timestamp, gives obscure failures in many tasks), the locale and time zone as Java
sees them (a wrong zone confuses dependency checks), and the proxy settings. An excerpt of the run on a clean 1.10.18:
------- Ant diagnostics report -------
Apache Ant(TM) version 1.10.18 compiled on September 3 2026
-------------------------------------------
Implementation Version
-------------------------------------------
core tasks : 1.10.18 in file:<scratch>/dist/apache-ant-1.10.18/lib/ant.jar
-------------------------------------------
ANT PROPERTIES
-------------------------------------------
ant.version: Apache Ant(TM) version 1.10.18 compiled on September 3 2026
ant.java.version: 26
Is this the Apache Harmony VM? no
Is this the Kaffe VM? no
Is this gij/gcj? no
ant.core.lib: <scratch>/dist/apache-ant-1.10.18/lib/ant.jar
ant.home: <scratch>/dist/apache-ant-1.10.18
-------------------------------------------
USER_HOME/.ant/lib jar listing
-------------------------------------------
user.home: <home>
No such directory.
-------------------------------------------
XML Parser information
-------------------------------------------
XML Parser : com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl
XML Parser Location: unknown
-------------------------------------------
XSLT Processor information
-------------------------------------------
XSLT Processor : com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl
XSLT Processor Location: unknown
-------------------------------------------
Symlinking information
-------------------------------------------
can create symbolic links: true
The report also lists the Is this ... VM? lines that belong to Java environments of the 2000s; they are always "no" today. Use
ant -diagnostics to answer "which Ant is this" (the ant.home line catches the bundled copies mentioned above), "which Java" and "does
this task have its library". From inside a build, or from an IDE, add the diagnostics task to a target. The temporary-directory
and time-zone checks are the ones that surprise people: a network drive as the temporary directory is a classic cause of tasks that
fail only on one machine.
Source: Ant manual: the diagnostics task
Several installations side by side
Ant needs no registration, so keeping several versions is a matter of directories. A practical layout is one unpacked directory per
version (/opt/apache-ant-1.10.17, /opt/apache-ant-1.10.18), a symbolic link /opt/ant to the one in use, and PATH containing
/opt/ant/bin. That works because the launcher script resolves the link to find ANT_HOME (the first run in this page’s setup
section shows it). To try a new release for one command, call its bin/ant by full path. The manual adds that IDEs can be pointed at
a different Ant, which removes the inconsistency between the command line and the IDE, and the packaged-JPackage case: to run an
Apache Ant from a machine that also has a JPackage Ant, use its full path and pass --noconfig to the script.
Upgrading within the 1.10 line is a matter of unpacking the new release and reading the "changes that could break older
environments" part of its release notes, which the history page summarises for each release since 1.10.0. Libraries added to
ANT_HOME/lib by fetch.xml live in the old directory and must be added again (or, better, moved to ~/.ant/lib or to the
build). The lib directory is therefore a poor place for libraries you care about; it is wiped by the next upgrade, whereas the
user directory is not.
Troubleshooting summary
| What you see | Cause | What to do |
|---|---|---|
|
Ant started and found no build file in the current directory. |
Change to the project directory, or pass |
|
|
Set |
|
An optional task’s library is missing. |
Put the JAR in |
Ant starts but picks the wrong libraries or the wrong version |
A |
|
Downloads hang or fail behind a firewall |
The JVM has no proxy settings. |
|
|
|
One target per run, or |
The messages in the first three rows were produced by the runs above. The rest of the cause and fix columns repeat the manual.
The install-time choices have effects later in the series. The optional libraries decide which test tasks work (see
Testing with JUnit & junitlauncher); the
library locations decide how build files find the antlibs that Antlibs & Namespaces
introduces; the proxy settings decide whether the dependency managers of
Ivy Settings & Resolvers can reach Maven Central. The
book Java Development with Ant (Manning, 2002) was written for Ant 1.5; its installation advice predates -lib, ~/.ant/lib, fetch.xml, JDK 9 and later, and the Resolver Ant Tasks.