Running Ant

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 (the 1.10.x line runs on JDK 8 and later). 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 build file is the small build.xml for the tidepool-model library, one module of the scenario used across this section; paths in console output were shortened to /work/tidepool-model. The tool is installed on Installation; the build file itself is explained on Build File Structure.

The shape of the command

ant [options] [target [target2 [target3] ...]]

The ant script (ant on Unix, ant.bat or ant.cmd on Windows) finds Java, builds a minimal class path and starts org.apache.tools.ant.launch.Launcher, which in turn starts Ant proper. Everything after the options is a list of targets. With no targets, Ant runs the default target named on the <project> element; with no -f option it looks for build.xml in the current directory. The command is the same in a terminal and in continuous integration (see CI & IDE Integration).

The examples below use this build file. It has a default target, descriptions, a deliberately failing target and a target that reads from the console, which together exercise the options.

build.xml
<?xml version="1.0" encoding="UTF-8"?>
<project name="tidepool-model" default="jar" basedir=".">
  <description>Tide height model library.</description>

  <property name="src.dir" location="src/main/java"/>
  <property name="build.dir" location="build"/>
  <property name="classes.dir" location="${build.dir}/classes"/>
  <property name="greeting" value="built with Ant ${ant.version}"/>

  <target name="clean" description="delete the build directory">
    <delete dir="${build.dir}"/>
  </target>

  <target name="compile" description="compile the sources">
    <mkdir dir="${classes.dir}"/>
    <javac srcdir="${src.dir}" destdir="${classes.dir}" release="21" includeantruntime="false"/>
  </target>

  <target name="jar" depends="compile" description="package the JAR">
    <jar destfile="${build.dir}/tidepool-model.jar" basedir="${classes.dir}"/>
    <echo message="${greeting}"/>
  </target>

  <target name="broken" description="fails on purpose">
    <fail message="deliberate failure"/>
  </target>

  <target name="after-broken" depends="broken"/>
  <target name="independent" description="does not depend on broken">
    <echo>independent target ran</echo>
  </target>

  <target name="props" description="show where properties come from">
    <echo>greeting=${greeting} tmpdir=${ant.tmpdir} version=${project.version}</echo>
  </target>

  <target name="ask" description="read a value from the console">
    <input message="Station id?" addproperty="station" defaultvalue="SF"/>
    <echo>station=${station}</echo>
  </target>
</project>

Running ant with no arguments compiles and packages, because jar is the default target and depends on compile:

$ ant
Buildfile: /work/tidepool-model/build.xml

compile:
    [mkdir] Created dir: /work/tidepool-model/build/classes
    [javac] Compiling 1 source file to /work/tidepool-model/build/classes

jar:
      [jar] Building jar: /work/tidepool-model/build/tidepool-model.jar
     [echo] built with Ant Apache Ant(TM) version 1.10.18 compiled on September 3 2026

BUILD SUCCESSFUL
Total time: 0 seconds

Options at a glance

ant -help prints the list of options. The output below is the one printed by 1.10.18, including the three script options that only the Unix wrapper accepts.

$ ant -help
ant [script options] [options] [target [target2 [target3] ..]]
Script Options:
  --help, --h            print this message and ant help
  --noconfig             suppress sourcing of /etc/ant.conf,
                         $HOME/.ant/ant.conf, and $HOME/.antrc
                         configuration files
  --usejikes             enable use of jikes by default, unless
                         set explicitly in configuration files
  --execdebug            print ant exec line generated by this
                         launch script

ant [options] [target [target2 [target3] ...]]
Options:
  -help, -h              print this message and exit
  -projecthelp, -p       print project help information and exit
  -version               print the version information and exit
  -diagnostics           print information that might be helpful to
                         diagnose or report problems and exit
  -quiet, -q             be extra quiet
  -silent, -S            print nothing but task outputs and build failures
  -verbose, -v           be extra verbose
  -debug, -d             print debugging information
  -emacs, -e             produce logging information without adornments
  -lib <path>            specifies a path to search for jars and classes
  -logfile <file>        use given file for log
    -l     <file>                ''
  -logger <classname>    the class which is to perform logging
  -listener <classname>  add an instance of class as a project listener
  -noinput               do not allow interactive input
  -buildfile <file>      use given buildfile
    -file    <file>              ''
    -f       <file>              ''
  -D<property>=<value>   use value for given property
  -keep-going, -k        execute all targets that do not depend
                         on failed target(s)
  -propertyfile <name>   load all properties from file with -D
                         properties taking precedence
  -inputhandler <class>  the class which will handle input requests
  -find <file>           (s)earch for buildfile towards the root of
    -s  <file>           the filesystem and use it
  -nice  number          A niceness value for the main thread:
                         1 (lowest) to 10 (highest); 5 is the default
  -nouserlib             Run ant without using the jar files from
                         ${user.home}/.ant/lib
  -noclasspath           Run ant without using CLASSPATH
  -autoproxy             Java1.5+: use the OS proxy settings
  -main <class>          override Ant's normal entry point

The sections that follow group the options by what they do. Neither Maven nor Gradle has an equivalent single-letter zoo: the Maven options are on Running Maven (CLI) and the Gradle ones on Command-Line Interface.

Information options

-version prints the version and exits. -help (or -h) prints the table above. -diagnostics prints a report meant to be pasted into a bug report: the Ant version, the jars in ANT_HOME/lib and in ${user.home}/.ant/lib, which optional tasks are unavailable because a library is missing, the XML parser and XSLT processor in use, the system properties, the temporary directory and the proxy settings.

$ ant -version
Apache Ant(TM) version 1.10.18 compiled on September 3 2026

The start of the diagnostics report, and the "Tasks availability" section that explains most "task not found" surprises, look like this (the full report is a few hundred lines; the elisions are marked):

$ ant -diagnostics
------- Ant diagnostics report -------
Apache Ant(TM) version 1.10.18 compiled on September 3 2026

-------------------------------------------
 Implementation Version
-------------------------------------------
core tasks     : 1.10.18 in file:/opt/apache-ant-1.10.18/lib/ant.jar

[...]

-------------------------------------------
 USER_HOME/.ant/lib jar listing
-------------------------------------------
user.home: /home/user
No such directory.

-------------------------------------------
 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
[...]

The user.home line was replaced by a placeholder here; the report is otherwise unedited. The jars to add to fix each missing dependency are in the optional-library table on Installation.

-projecthelp (-p) lists the targets of the build file and exits without running anything. A target with a description attribute is listed under "Main targets"; targets without one are "Other targets" and are shown only when there are no main targets or when -verbose or -debug is also given. The project <description> is printed first.

$ ant -projecthelp
Buildfile: /work/tidepool-model/build.xml
Tide height model library.
Main targets:

 ask          read a value from the console
 broken       fails on purpose
 clean        delete the build directory
 compile      compile the sources
 independent  does not depend on broken
 jar          package the JAR
 props        show where properties come from
Default target: jar

Adding -v also lists after-broken under "Other targets", because it has no description. This is why a build file meant to be used by others gives a description to every target that is a public entry point and none to internal ones; see Targets & Extension Points.

Choosing the build file

-buildfile <file> (also -file and -f) names the build file. If the argument is a directory, Ant uses the build.xml in it. The default is build.xml in the current directory. The file name is arbitrary; the examples run noasc.xml, a one-target file, and the project directory itself:

$ ant -f noasc.xml
Buildfile: /work/tidepool-model/noasc.xml

hi:
     [echo] hi from noasc

BUILD SUCCESSFUL
Total time: 0 seconds

A file that does not exist is not an Ant error with a stack trace; it is a short message and exit code 1:

$ ant -f nofile.xml
Buildfile: nofile.xml does not exist!
Build failed

-find [file] (also -s) searches for the build file in the current directory, then in its parent, and so on up to the root of the file system. By default it looks for build.xml. This is how a build can be started from any subdirectory of a single-build-file project. The catch, stated in the manual, is that if more options follow -find, the file name argument is required even when it is build.xml, because the next word would otherwise be consumed as the name. From a subdirectory two levels below the project:

$ ant -find build.xml -q jar
     [echo] built with Ant Apache Ant(TM) version 1.10.18 compiled on September 3 2026

BUILD SUCCESSFUL
Total time: 0 seconds

$ ant -s -q jar
Searching for -q ...
Could not locate a build file!

$ ant -q jar
Buildfile: build.xml does not exist!
Build failed

The second command is the classic mistake: -s swallowed -q as the file name to search for. The third shows plain ant not looking upwards. The same rule applies to -find inside ANT_ARGS, described below.

Ant sets the basedir of the project to the directory containing the build file unless the basedir attribute says otherwise, so relative paths in the build file are relative to the build file, not to the current directory. That is why the same build behaves identically when started with -f ../other/build.xml from elsewhere.

Selecting targets

Targets are listed after the options and run in order, each one after the targets it depends on, and each target at most once per invocation. ant clean jar runs clean, then compile and jar. A target name that does not exist fails the build before anything runs:

$ ant nosuch
Buildfile: /work/tidepool-model/build.xml

BUILD FAILED
Target "nosuch" does not exist in the project "tidepool-model".

Total time: 0 seconds

A failing task stops the build at the first failure. The error line names the build file and the line of the failing task (build.xml:25, the <fail>):

$ ant broken
Buildfile: /work/tidepool-model/build.xml

broken:

BUILD FAILED
/work/tidepool-model/build.xml:25: deliberate failure

Total time: 0 seconds

-keep-going (-k) changes that: after a target fails, Ant carries on with the other requested targets that do not depend on the failed one. It still ends with BUILD FAILED and exit code 1.

$ ant -k after-broken independent
Buildfile: /work/tidepool-model/build.xml

broken:
Target 'broken' failed with message 'deliberate failure'.
Cannot execute 'after-broken' - 'broken' failed or was not executed.

independent:
     [echo] independent target ran

BUILD FAILED
/work/tidepool-model/build.xml:25: deliberate failure

Total time: 0 seconds

-k is useful on a continuous integration server that should report as many independent failures as possible in one run, and in a build with several unrelated test targets. For target conditions (if, unless) and the lifecycle-like extension points, see Targets & Extension Points.

Defining properties

-D<property>=<value> sets a property. Properties set on the command line are user properties: they cannot be overridden by a <property> task in the build file, because properties in Ant are immutable once set and the command line sets them first. That is how a build file’s defaults (<property name="greeting" .../>) are overridden from outside. The props target prints a few:

$ ant -Dgreeting=hello props
Buildfile: /work/tidepool-model/build.xml

props:
     [echo] greeting=hello tmpdir=${ant.tmpdir} version=${project.version}

BUILD SUCCESSFUL
Total time: 0 seconds

An unset property is not an error: the text ${ant.tmpdir} is left as is, which is the most common reason for a path like ${build.dir}/classes appearing literally in a directory listing. ant -v adds a line for each one that is used and unset (Property "ant.tmpdir" has not been set). Environment variables can be passed with -DMYVAR=$MYVAR (Unix) or -DMYVAR=%MYVAR% (Windows), or read inside the build file with <property environment="env"/>; how properties are resolved is on Properties.

-propertyfile <name> loads every property in a file as if each had been given with -D, so they too are user properties. When both are present the -D value wins. With this release.properties:

project.version=1.2.0
greeting=from the property file
$ ant -propertyfile release.properties props
Buildfile: /work/tidepool-model/build.xml

props:
     [echo] greeting=from the property file tmpdir=${ant.tmpdir} version=1.2.0

BUILD SUCCESSFUL
Total time: 0 seconds

$ ant -propertyfile release.properties -Dgreeting=cli props
Buildfile: /work/tidepool-model/build.xml

props:
     [echo] greeting=cli tmpdir=${ant.tmpdir} version=1.2.0

BUILD SUCCESSFUL
Total time: 0 seconds

The Maven counterpart is -Dkey=value (system properties read by plugins) and the Gradle one is -Pname=value for project properties and -D for JVM system properties; see Comparing Ant, Maven & Gradle.

Controlling the output

Four options change the amount of logging, in order of increasing volume: -silent (-S) prints only task output and build failures, -quiet (-q) prints less than normal, the default, -verbose (-v) and -debug (-d). The messages Ant prints are produced at the levels error, warn, info, verbose and debug; tasks choose a level for each message (the <echo> task has a level attribute), and the option decides how many levels reach the screen.

$ ant -q clean jar
     [echo] built with Ant Apache Ant(TM) version 1.10.18 compiled on September 3 2026

BUILD SUCCESSFUL
Total time: 0 seconds

$ ant -S clean jar
Buildfile: /work/tidepool-model/build.xml
built with Ant Apache Ant(TM) version 1.10.18 compiled on September 3 2026

-S is the option to use when a script captures Ant’s output: little but what the tasks print remains (here the <echo> text, with no [echo] prefix, after the Buildfile: line that 1.10.18 still prints).

-verbose shows, for this build, the version, the Java and OS detected, how the build file was found, the parsing of the build file, the base directory, the build sequence and every unset property that is used. -debug adds a message for each property set, each class loaded and each task configured. Both are the first thing to try when a build does something unexpected; the build sequence line is a quick way to see which targets will run and in which order.

$ ant -v props
Apache Ant(TM) version 1.10.18 compiled on September 3 2026
Trying the default build file: build.xml
Buildfile: /work/tidepool-model/build.xml
Detected Java version: 26 in: /Library/Java/JavaVirtualMachines/jdk-26.jdk/Contents/Home
Detected OS: Mac OS X
parsing buildfile /work/tidepool-model/build.xml with URI = file:/work/tidepool-model/build.xml
Project base dir set to: /work/tidepool-model
parsing buildfile jar:file:/opt/apache-ant-1.10.18/lib/ant.jar!/org/apache/tools/ant/antlib.xml with URI = jar:file:/opt/apache-ant-1.10.18/lib/ant.jar!/org/apache/tools/ant/antlib.xml from a zip file
Build sequence for target(s) `props' is [props]
Complete build sequence is [props, broken, after-broken, compile, jar, ask, independent, clean, ]

props:
Property "ant.tmpdir" has not been set
Property "project.version" has not been set
     [echo] greeting=built with Ant Apache Ant(TM) version 1.10.18 compiled on September 3 2026 tmpdir=${ant.tmpdir} version=${project.version}

BUILD SUCCESSFUL
Total time: 0 seconds

-emacs (-e) removes the [task] prefix and the indentation so that each message starts at column 0. It was designed for editors that parse compiler messages from the log and for tools that dislike the leading spaces:

$ ant -e clean jar
Buildfile: /work/tidepool-model/build.xml

clean:
Deleting directory /work/tidepool-model/build

compile:
Created dir: /work/tidepool-model/build/classes
Compiling 1 source file to /work/tidepool-model/build/classes

jar:
Building jar: /work/tidepool-model/build/tidepool-model.jar
built with Ant Apache Ant(TM) version 1.10.18 compiled on September 3 2026

BUILD SUCCESSFUL
Total time: 0 seconds

-logfile <file> (also -l) sends the output to a file instead of the console. Nothing is printed to the terminal, and the file receives exactly what the console would have shown at the chosen level:

$ ant -logfile build.log -q jar
$ cat build.log
     [echo] built with Ant Apache Ant(TM) version 1.10.18 compiled on September 3 2026

BUILD SUCCESSFUL
Total time: 0 seconds

Loggers, listeners and input handlers

A logger formats and prints the build’s messages; a listener is notified of build events (target started, task finished, message logged) and may do anything. -logger <classname> replaces the default logger (org.apache.tools.ant.DefaultLogger) and there can be only one. -listener <classname> adds a listener, and there can be several. Ant ships several in org.apache.tools.ant.listener (for example TimestampedLogger, AnsiColorLogger, MailLogger, BigProjectLogger, ProfileLogger) and org.apache.tools.ant (XmlLogger, NoBannerLogger); -logger takes the first kind:

$ ant -logger org.apache.tools.ant.listener.TimestampedLogger -q jar
     [echo] built with Ant Apache Ant(TM) version 1.10.18 compiled on September 3 2026

BUILD SUCCESSFUL - at 16/10/26, 11:34
Total time: 0 seconds

Passing a logger class to -listener is the classic mistake. Loggers are also listeners, but a logger given to -listener is never handed an output stream (that is done for the logger chosen with -logger), and the build ends with a stack trace after the "BUILD SUCCESSFUL" line and exit code 1:

$ ant -listener org.apache.tools.ant.listener.TimestampedLogger -q jar
     [echo] built with Ant Apache Ant(TM) version 1.10.18 compiled on September 3 2026

BUILD SUCCESSFUL
Total time: 0 seconds
Caught an exception while logging the end of the build.  Exception was:
java.lang.NullPointerException: Cannot invoke "java.io.PrintStream.println(String)" because "stream" is null
	at org.apache.tools.ant.DefaultLogger.printMessage(DefaultLogger.java:325)
	at org.apache.tools.ant.DefaultLogger.buildFinished(DefaultLogger.java:184)
	at org.apache.tools.ant.Project.fireBuildFinished(Project.java:2116)
	at org.apache.tools.ant.Main.runBuild(Main.java:830)
	at org.apache.tools.ant.Main.startAnt(Main.java:223)
	at org.apache.tools.ant.launch.Launcher.run(Launcher.java:284)
	at org.apache.tools.ant.launch.Launcher.main(Launcher.java:101)

Writing your own logger or listener is covered on Listeners, Loggers & Input Handlers, which also lists every provided class.

-noinput tells Ant that nobody is at the keyboard, so tasks such as <input> must not wait. With -noinput the ask target fails at once instead of hanging a CI job:

$ ant -noinput ask
Buildfile: /work/tidepool-model/build.xml

ask:
    [input] Station id? [SF]

BUILD FAILED
/work/tidepool-model/build.xml:38: Failed to read input from Console.

Total time: 0 seconds

Without the option, a piped answer is read from the standard input:

$ echo 'X' | ant ask
Buildfile: /work/tidepool-model/build.xml

ask:
    [input] Station id? [SF]
     [echo] station=X

BUILD SUCCESSFUL
Total time: 0 seconds

-inputhandler <class> chooses the class that answers the <input> task. The provided PropertyFileInputHandler reads the answers from a Java properties file, using the prompt text as the key. The file is named by the Java system property ant.input.properties, not by an Ant property, so it is passed through ANT_OPTS; the same build with -D on the Ant command line fails (the property exists for Ant, but the handler reads the JVM’s):

$ ant -Dant.input.properties=answers.properties -inputhandler org.apache.tools.ant.input.PropertyFileInputHandler ask
Buildfile: /work/tidepool-model/build.xml

ask:

BUILD FAILED
/work/tidepool-model/build.xml:38: System property ant.input.properties for PropertyFileInputHandler not set

With answers.properties containing the escaped key Station\ id?=BOS:

$ ANT_OPTS="-Dant.input.properties=answers.properties" ant -inputhandler org.apache.tools.ant.input.PropertyFileInputHandler ask
Buildfile: /work/tidepool-model/build.xml

ask:
     [echo] station=BOS

BUILD SUCCESSFUL
Total time: 0 seconds

If the key is missing the build fails with Unable to find input for 'Station id?'. The <input> task itself is on Utility Tasks.

Library directories and -lib

Since Ant 1.6 the jars that Ant itself, its optional tasks and third-party tasks run on come from library directories, not from the CLASSPATH. Two directories are scanned by default, ANT_HOME/lib and ${user.home}/.ant/lib; the first is shared by every user of the installation and the second is private to one user, so an administrator can lock the installation down while a developer still adds, say, the JSch jar for <scp>. More can be added with -lib <path>, a path of jars and directories (: or ; separated as the platform requires, or the option repeated). The order in which they are searched is:

  1. jars from the -lib options, in the order given;

  2. jars from ${user.home}/.ant/lib, unless -nouserlib is set;

  3. jars from ANT_HOME/lib.

The CLASSPATH environment variable is converted into one more -lib option, because the launcher starts Ant itself with a minimal class path. Ant is happy with an empty CLASSPATH; -noclasspath enforces that, and -nouserlib skips the user directory. The documentation recommends never setting CLASSPATH for Ant: most class path problems reported against Ant (and many quoting problems) come from it. Typical uses:

ant -lib /home/ant/extras
ant -lib one.jar -lib another.jar
ant -nouserlib -noclasspath      # run with exactly the jars in ANT_HOME/lib

A directory that does not exist is ignored without a message, so a typo in -lib surfaces later as an unknown task or missing class rather than at the option itself:

$ ant -lib /nonexistent -q props
     [echo] greeting=built with Ant Apache Ant(TM) version 1.10.18 compiled on September 3 2026 tmpdir=${ant.tmpdir} version=${project.version}

BUILD SUCCESSFUL
Total time: 0 seconds

The proper way for a project to bring its own task jars is to load them from the build file with a <taskdef> and a class path, or from an antlib (see Antlibs & Namespaces); -lib is for jars that must be on Ant’s own class loader. Downloading the libraries for the optional tasks is described on Installation; resolving a project’s own dependencies is the job of Ivy or the Maven Resolver Ant Tasks (see Ivy Overview and Maven Resolver Ant Tasks).

Other options

  • -nice <number> sets the priority of the thread that runs the build, from 1 (lowest) to 10 (highest); 5 is the default. It is accepted and has no visible output, and is mostly useful on a shared build server.

  • -autoproxy makes Ant use the operating system’s proxy settings; the -diagnostics report shows what the JVM found ("Direct connection" in the run above). Explicit proxy configuration is in the <setproxy> task and ANT_OPTS="-Dhttp.proxyHost=<host> -Dhttp.proxyPort=<port>".

  • -main <class> replaces Ant’s entry point (org.apache.tools.ant.Main) with a class implementing AntMain. It is how an embedding tool or a wrapper changes startup behaviour. The manual’s example of changing the exit behaviour is to subclass Main, override exit(int) and start Ant with -lib pointing at the class; this was not run here.

Files read by the wrapper script

On Unix the ant script sources (reads and evaluates) /etc/ant.conf, $HOME/.ant/ant.conf and $HOME/.antrc before it does anything else; on Windows the batch file runs %HOME%\antrc_pre.bat first and %HOME%\antrc_post.bat last. They are the place to set variables that only Ant should see, such as JAVA_HOME for a different JDK or ANT_OPTS. The --noconfig script option suppresses them. A one-line .antrc in a scratch home directory:

$ cat fakehome/.antrc
echo "antrc sourced" >&2; ANT_OPTS="-Dfrom.antrc=yes"

$ HOME=$PWD/fakehome ant -q props
antrc sourced
     [echo] greeting=built with Ant Apache Ant(TM) version 1.10.18 compiled on September 3 2026 tmpdir=${ant.tmpdir} version=${project.version}

BUILD SUCCESSFUL
Total time: 0 seconds

$ HOME=$PWD/fakehome ant --noconfig -q props
     [echo] greeting=built with Ant Apache Ant(TM) version 1.10.18 compiled on September 3 2026 tmpdir=${ant.tmpdir} version=${project.version}

BUILD SUCCESSFUL
Total time: 0 seconds

A hidden .antrc is a classic reason why the same build behaves differently for two developers; --noconfig and --execdebug (below) are the way to find out.

Environment variables

The wrapper scripts use these variables when they are set. The installation variables JAVA_HOME and ANT_HOME are described on Installation; Ant 1.10.x finds ANT_HOME from the script’s own location, so only JAVA_HOME is normally needed.

Variable Purpose

JAVACMD

Full path of the Java executable, to use a JVM other than JAVA_HOME/bin/java.

ANT_OPTS

Arguments for the JVM that runs Ant: heap size (-Xmx), system properties (-Dname=value), proxy settings. These are Java system properties, different from the Ant properties set with -D after ant.

ANT_ARGS

Ant command-line arguments added to every invocation: for example a different logger, a listener or -find. If it contains -find, include the build file name, as above.

--execdebug prints the Java command the script is about to run, which shows precisely how the variables were applied:

$ ANT_OPTS="-Dfoo=bar -Xmx64m" ant --execdebug props
arg       : props
quoted_arg: "props"
exec "$JAVACMD" -Dfoo=bar -Xmx64m -classpath "$LOCALCLASSPATH" -Dant.home="$ANT_HOME" -Dant.library.dir="$ANT_LIB"  org.apache.tools.ant.launch.Launcher  -cp "$CLASSPATH"  "props"
Buildfile: /work/tidepool-model/build.xml

props:
     [echo] greeting=built with Ant Apache Ant(TM) version 1.10.18 compiled on September 3 2026 tmpdir=${ant.tmpdir} version=${project.version}

BUILD SUCCESSFUL
Total time: 0 seconds

ANT_ARGS prepends options to every run, which is a convenient way to set a team-wide default and a surprising one for a build that behaves differently on a machine that has it set:

$ ANT_ARGS="-q -Dgreeting=fromenv" ant props
     [echo] greeting=fromenv tmpdir=${ant.tmpdir} version=${project.version}

BUILD SUCCESSFUL
Total time: 0 seconds

A wrong JAVACMD produces the script’s own message, not an Ant one, and exit code 1:

$ JAVACMD=/nonexistent/java ant props
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.

Java system properties and the temporary directory

Some of Ant’s core classes read Java system properties. They are set with ANT_OPTS (or java -Dname=value when starting Ant by hand), because they must reach the JVM, not Ant’s own property table. A few of them are listed here; the manual has the full table.

Property Meaning

ant.build.javac.source, ant.build.javac.target

Default source and target for <javac> and <javadoc>.

build.compiler

Default compiler adapter for <javac>.

ant.executor.class

Class that invokes targets (default org.apache.tools.ant.helper.DefaultExecutor).

ant.file, ant.file.<project>

Read-only: full file name of the build file, and per project, of every imported file.

ant.input.properties

File read by PropertyFileInputHandler.

org.apache.tools.ant.ProjectHelper

Class that parses build files (default ProjectHelper2).

ant.tstamp.now, ant.tstamp.now.iso

Value used as "now" by <tstamp>, for reproducible builds (seconds since the epoch, or an ISO-8601 string).

ant.disable.graal.nashorn.compat

Since Ant 1.10.9: set to true to turn off the Nashorn compatibility mode of GraalVM JavaScript for <script>.

Properties starting with ant. or org.apache.tools.ant. are reserved: the manual asks build files to avoid inventing names in those prefixes because future releases may add them.

Temporary directory. Tasks that need temporary files use java.io.tmpdir by default, and tmp directories shared between users can leak sensitive information or let an attacker inject source files into the build. Since Ant 1.10.8 the Ant property ant.tmpdir takes precedence over java.io.tmpdir when set, and, unlike the JVM property, can be set from the command line or from the build file. Since Ant 1.10.9, when ant.tmpdir is not set, Ant creates a build-specific directory under java.io.tmpdir, accessible only to the current user where the file system allows it. The manual recommends pointing ant.tmpdir at a directory owned by the user running Ant with 0700 permissions. Tasks that are not shipped with Ant ignore ant.tmpdir unless they were adapted to the 1.10.8 API.

$ ant -Dant.tmpdir=/tmp/antwork props
Buildfile: /work/tidepool-model/build.xml

props:
     [echo] greeting=built with Ant Apache Ant(TM) version 1.10.18 compiled on September 3 2026 tmpdir=/tmp/antwork version=${project.version}

BUILD SUCCESSFUL
Total time: 0 seconds

Running Ant through Java

The scripts are a convenience. Ant can be started from any process with a JVM, which is how an IDE or a build server does it when it does not use the scripts. There are two entry points:

  • org.apache.tools.ant.launch.Launcher (in ant-launcher.jar) is what the script runs. It sets up the library directories and so supports -lib; it needs -Dant.home=<installation> so that it can find lib.

  • org.apache.tools.ant.Main is the traditional entry point. It supports no -lib: every jar must be on the CLASSPATH, at a minimum ant.jar and ant-launcher.jar, an XML parser (the JDK has one) and what the tasks need.

$ java -cp "$ANT_HOME/lib/ant-launcher.jar" -Dant.home="$ANT_HOME" org.apache.tools.ant.launch.Launcher -q props
     [echo] greeting=built with Ant Apache Ant(TM) version 1.10.18 compiled on September 3 2026 tmpdir=${ant.tmpdir} version=${project.version}

BUILD SUCCESSFUL
Total time: 0 seconds

$ java -cp "$ANT_HOME/lib/ant.jar:$ANT_HOME/lib/ant-launcher.jar" org.apache.tools.ant.Main -q props
     [echo] greeting=built with Ant Apache Ant(TM) version 1.10.18 compiled on September 3 2026 tmpdir=${ant.tmpdir} version=${project.version}

BUILD SUCCESSFUL
Total time: 0 seconds

Handing -lib to Main is rejected, with an explanation of the common cause (a mismatch between the script and Ant itself, for instance a newer ant script on the PATH with an older ANT_HOME):

$ java -cp "$ANT_HOME/lib/ant.jar:$ANT_HOME/lib/ant-launcher.jar" org.apache.tools.ant.Main -lib x -q props
Ant's Main method is being handed an option -lib that is only for the launcher class.
This can be caused by a version mismatch between the ant script/.bat file and Ant itself.

Starting Ant from Java code, with a Project object and a build listener, is on Embedding Ant. Calling another build from a build file is the <ant> and <subant> tasks, on Multi-File Builds: import, include & subant.

Exit codes

The start-up scripts return the exit code of the Java process: 0 for a successful build and a non-zero value, in practice 1, for a failed one. That covers every case shown above: a failed task (ant broken), an unknown target, a missing build file, -k with a failure, an unusable JAVACMD, and a logging exception as in the -listener mistake. A CI job needs nothing more than that status: the failure of the job follows the failure of the command. To change the exit behaviour, the manual suggests subclassing org.apache.tools.ant.Main, overriding exit(int) (for example to not exit the JVM when Ant is run inside another program) and starting Ant with -lib pointing at that class (not run here). The <fail> task has a status attribute for a build that wants a specific exit code.

Practical recipes

  • A quiet, scriptable build: ant -S -noinput target.

  • A diagnosable build: ant -v -logfile build.log target, keeping the log as a CI artifact.

  • A reproducible command line for a release: ant -propertyfile release.properties -Dant.tmpdir=build/tmp dist.

  • Starting from a sub-project: ant -find build.xml jar.

  • Find which build file or option set is in play: ant -v, ant --execdebug, and a look at ANT_ARGS, ANT_OPTS and the .antrc files.

The same ideas under Maven and Gradle are on Running Maven (CLI) and Command-Line Interface; the tool-independent view is on Build Automation Fundamentals. The properties that a build reads are the subject of Properties.