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.

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.

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 ant formula (1.10.18 on 2026-10-16, read from the formula’s JSON on formulae.brew.sh), with openjdk as a dependency.

  • SDKMAN! has the ant candidate (its API reported 1.10.17 as the default on 2026-10-16, one release behind).

  • Linux distributions have an ant package; 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_HOME nor JAVA_HOME, and if ANT_HOME is 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

bin

The launchers: ant (shell), ant.bat and ant.cmd (Windows), antRun and its variants (they start external programs for the exec task), runant.pl and runant.py (Perl and Python launchers), complete-ant-cmd.pl (shell completion helper).

lib

ant.jar (the core), ant-launcher.jar (the launcher that builds Ant’s classpath from the library locations below), the ant-*.jar files with the optional tasks, and a .pom, .cdx.json and .cdx.xml file next to each JAR. libraries.properties pins the versions that fetch.xml downloads.

etc

XSL stylesheets for reports (junit-frames.xsl, junit-frames-xalan1.xsl, junit-noframes.xsl, jdepend.xsl, jdepend-frames.xsl, coverage-frames.xsl, changelog.xsl, log.xsl, tagdiff.xsl, mmetrics-frames.xsl, maudit-frames.xsl), a checkstyle directory with more of them, and ant-bootstrap.jar.

manual

The HTML manual, offline. Every "since Ant 1.x" statement in it is the authoritative statement for the release you have.

fetch.xml, get-m2.xml

Build files that download the libraries the optional tasks need (see below). get-m2.xml fetches the Maven Resolver Ant Tasks that fetch.xml itself relies on.

WHATSNEW, README, INSTALL, KEYS, LICENSE, NOTICE, CONTRIBUTORS, contributors.xml, patch.xml

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.

Setup: JAVA_HOME, ANT_HOME and PATH

Three environment variables matter, and only one is required in practice.

  • PATH must contain ANT_HOME/bin so that ant can be typed without its full path.

  • ANT_HOME is 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_HOME selects the JDK. If it is unset the launcher looks for a java on the PATH (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.

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.

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.

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:

  1. ANT_HOME/lib: available to every user and every build of this installation.

  2. ~/.ant/lib (the .ant/lib directory in the user’s home, since Ant 1.6): available to every build of that user.

  3. A -lib argument on the command line: a directory or JAR for one run.

  4. The CLASSPATH environment variable: avoid.

  5. A <classpath> on the task that needs the library. Since Ant 1.7.0 even junit can load junit.jar this 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.

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.

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

junit.jar (4.x) and hamcrest

The junit task (may be on the task’s own classpath instead of Ant’s).

https://junit.org/

junit

junit-platform-launcher, junit-platform-engine, junit-platform-commons (JUnit 5 Platform jars)

The junitlauncher task; more libraries are needed for the chosen test engines.

https://junit.org/

junitlauncher

jsch 0.1.55 or later

scp, sshexec, sshsession.

http://www.jcraft.com/jsch/

networking

commons-net

ftp, rexec, telnet. The manual says the minimum to compile Ant is 1.4.0 and warns against 3.2 for performance; 1.10.18’s libraries.properties has 3.13.0.

https://commons.apache.org/net/

networking

jakarta.mail (plus activation on Java 11 and later when needed)

mail and the deprecated mimemail.

https://eclipse-ee4j.github.io/mail/

jakartamail

bcel

classfileset, the ClassConstants filter reader and, optionally, ejbjar.

https://commons.apache.org/bcel/

bcel

xz (XZ for Java 1.6 or later)

xz, unxz, xzresource, and xz compression in tar and untar.

https://www.tukaani.org/xz/java.html

xz

GraalVM JavaScript (js, js-scriptengine)

script and scriptdef with JavaScript on Java 15 and later, where Nashorn is gone. It is not a drop-in replacement and needs more GraalVM libraries.

the GraalVM project

graal.js

bsf 2.3.0 or later (2.4.0 for recent Rhino)

script through the Bean Scripting Framework; BSF 2.4.0 also needs Commons Logging.

https://commons.apache.org/bsf/

bsf

Groovy, Jython, JRuby, BeanShell, NetRexx, Rhino, Jacl or JTcl, Judo

script and scriptdef in those languages; the Groovy Ant tasks (groovy-ant).

each project

script, jython, jruby, beanshell, netrexx, rhino

commons-logging, log4j 1.x

CommonsLoggingListener and Log4jListener (deprecated).

Apache Commons and Logging projects

logging

jdepend

jdepend.

https://github.com/clarkware/jdepend

jdepend

xalan, serializer, xml-resolver (1.1 or later)

junitreport; the xmlcatalog type if external catalog files are wanted.

Apache Xalan and XML Commons

xml

antlr, jai, jai-imageio-core, jakarta-regexp, oro

antlr; image and imageio (TIFF, PCX, PNM, RAW); the regexp mapper types if you do not use java.util.regex.

various

antlr, jai, regexp

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:

  1. -autoproxy on the command line (since Ant 1.7): Ant sets the java.net.useSystemProxies system 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 in ANT_ARGS.

  2. Explicit JVM properties in ANT_OPTS: -Dhttp.proxyHost, -Dhttp.proxyPort and the matching HTTPS, FTP and SOCKS properties. This is the best option for a machine that does not roam.

  3. The setproxy task 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.

  4. Passing the properties through ANT_ARGS as 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.

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.

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

Buildfile: build.xml does not exist! and Build failed

Ant started and found no build file in the current directory.

Change to the project directory, or pass -f; the installation works.

We cannot run Java, please ensure you have Java installed.

JAVA_HOME or JAVACMD does not point to a runnable java.

Set JAVA_HOME to a JDK, or remove the wrong value so that the launcher can find java.

Problem: failed to create task or type scp with Could not load a dependent class

An optional task’s library is missing.

Put the JAR in ANT_HOME/lib, in ~/.ant/lib or on -lib, or add a task <classpath>; ant -diagnostics lists the tasks that are missing a class.

Ant starts but picks the wrong libraries or the wrong version

A CLASSPATH, a bundled Ant earlier on the PATH, or another ANT_HOME.

ant -noclasspath, ant -diagnostics, and check ant.home.

Downloads hang or fail behind a firewall

The JVM has no proxy settings.

ANT_OPTS with the -D proxy properties, -autoproxy, or the setproxy task.

Cannot execute multiple targets due to the bug in Maven Ant tasks

fetch.xml was given two or more targets.

One target per run, or all.

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.