Ant Utility Tasks
|
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 collects the small tasks that do not belong to a larger topic but appear in almost every real build file: echo,
input (with input handlers), record, tstamp, checksum, buildnumber, hostinfo, diagnostics, antversion,
whichresource, antstructure and sleep. It is written against Apache Ant 1.10.18 and was checked on 2026-10-16 with JDK
26 on macOS. Every example was run on that day on a scratch copy of the tidepool project’s data/stations.csv; output is
shown as Ant printed it, with absolute paths shortened to . or ... and nothing else edited. All tasks are in Ant’s core
ant.jar (no extra JARs); antversion and whichresource also exist as conditions, described on
Conditions & Control Flow. Properties used
below follow the rules on Properties, in particular that a
property, once set, cannot be changed.
echo
echo writes a message to the build’s loggers, or to a file. The message comes from message or from the element’s text, and
a task with neither prints a blank line. Whitespace in element text is kept, including the newline right after the opening
tag, which is why multi-line echo bodies usually start on the same line as the tag.
| Attribute | Effect |
|---|---|
|
What to print. |
|
|
|
Write to a file, or ( |
|
Character encoding of the file (Ant 1.7), and overwrite a read-only destination file (Ant 1.8.2). |
Log levels
A build is filtered by the command-line level: -quiet (-q), none, -verbose (-v), -debug (-d). The target below
echoes one message at every level:
<target name="echo-levels">
<echo message="This is error message." level="error"/>
<echo message="This is warning message."/>
<echo message="This is info message." level="info"/>
<echo message="This is verbose message." level="verbose"/>
<echo message="This is debug message." level="debug"/>
</target>
Run with each switch (only the [echo] lines are kept):
$ ant -q echo-levels
[echo] This is error message.
[echo] This is warning message.
$ ant echo-levels
[echo] This is error message.
[echo] This is warning message.
[echo] This is info message.
$ ant -v echo-levels
[echo] This is error message.
[echo] This is warning message.
[echo] This is info message.
[echo] This is verbose message.
$ ant -d echo-levels
[echo] This is error message.
[echo] This is warning message.
[echo] This is info message.
[echo] This is verbose message.
[echo] This is debug message.
This matches the table in the manual. The practical rule: use info for normal progress, verbose for detail that helps when
something goes wrong, debug for dumps, and error only for messages that must show even in -quiet runs. The same levels
govern the -verbose and -debug output of every other task, as explained on
Running Ant.
Source: Ant manual, echo
Echoing to a file and the double dollar
Generating a script is a common use. In the element text, a single $ starts a property reference, so to write a literal $
(here the shell’s $*) you write $$:
<target name="echo-file">
<mkdir dir="${build.dir}"/>
<echo file="${build.dir}/runner.sh" append="false">#!/bin/sh
java -Xmx512m -jar tidepool-cli.jar $$*
</echo>
<echo file="${build.dir}/runner.sh" append="true">echo "built by ${ant.project.name}"
</echo>
<loadfile srcfile="${build.dir}/runner.sh" property="runner"/>
<echo>${runner}</echo>
</target>
[echo] #!/bin/sh
[echo] java -Xmx512m -jar tidepool-cli.jar $*
[echo] echo "built by tidepool-utils"
echo does not set the executable bit; follow it with chmod (on Unix) or setpermissions
(File Operations). The echoxml task, for XML content,
is on Documentation & XML.
Source: Ant manual, echo
input and input handlers
input asks the user for a value through the project’s input handler. It takes a message (or element text), optional
validargs (a comma-separated, case-sensitive list; input outside it is rejected), addproperty (the property to create) and
defaultvalue (used when the user just presses Return). input behaves like property: an existing property is not
overridden, and since Ant 1.6 the task does not even prompt if the property is already set.
Prompting and validating
The target asks a yes/no question, and re-asks until the answer is valid:
<target name="input-yesno">
<input message="Publish to the local repository (y/n)?" validargs="y,n" addproperty="do.publish"/>
<echo>do.publish=${do.publish}</echo>
</target>
<target name="input-default">
<input message="Station id:" addproperty="station.id" defaultvalue="SF"/>
<echo>station.id=${station.id}</echo>
</target>
With the answer piped on standard input (echo y | ant input-yesno):
input-yesno:
[input] Publish to the local repository (y/n)? (y, n)
[echo] do.publish=y
Typing maybe first (printf 'maybe\ny\n' | ant input-yesno) makes the default handler ask again:
input-yesno:
[input] Publish to the local repository (y/n)? (y, n)
[input] Publish to the local repository (y/n)? (y, n)
[echo] do.publish=y
With an empty line the default is used, and a property set beforehand skips the prompt altogether:
$ echo "" | ant input-default
input-default:
[input] Station id: [SF]
[echo] station.id=SF
$ ant -Dstation.id=BOS input-default
input-default:
[input] skipping input as property station.id has already been set.
[echo] station.id=BOS
The second form is how a build that normally asks questions is made to run unattended: define the answer as a property.
Source: Ant manual, input
What -noinput really does
-noinput ("do not allow interactive input") does not choose answers for you. It makes standard input unreadable, so the
default handler fails when it tries to read:
$ ant -noinput input-yesno
input-yesno:
[input] Publish to the local repository (y/n)? (y, n)
BUILD FAILED
./build.xml:25: Failed to read input from Console.
and, importantly, the same happens when a defaultvalue is set:
$ ant -noinput input-default
input-default:
[input] Station id: [SF]
BUILD FAILED
./build.xml:30: Failed to read input from Console.
A defaultvalue is applied only when the handler reads an empty line, never when there is no input at all. For CI, either
define the property (-Dstation.id=SF, or a <property> before the input) so the task is skipped, or select a handler that
does not read the console. The manual’s recommendation for IDEs that cannot give a prompt is the same: put the answers in a
property file and load it before input.
Choosing an input handler
The handler is chosen for the whole build with -inputhandler class, or per task with a nested handler (Ant 1.7):
Handler (type) |
Behaviour |
|---|---|
|
Prompts on the console and reads a line. |
|
Looks the prompt up in the properties file named by the Java system property |
|
Reads standard input until end of file, instead of one line (per its Javadoc). |
|
Reads without echoing the characters, through Java’s |
|
Your own |
The propertyfile handler suits CI. The file’s keys are the prompt texts, with the characters that are special in a properties
key escaped:
Publish\ to\ the\ local\ repository\ (y/n)?=y
Station\ id\:=HNL
The handler reads a Java system property, not an Ant property, so -Dant.input.properties=… on the Ant command line (which
sets an Ant property) is not enough: the run failed with System property ant.input.properties for PropertyFileInputHandler
not set. Pass it through ANT_OPTS instead:
$ ANT_OPTS="-Dant.input.properties=inputs.properties" ant -inputhandler org.apache.tools.ant.input.PropertyFileInputHandler input-yesno input-default
input-yesno:
[echo] do.publish=y
input-default:
[echo] station.id=HNL
The greedy handler took the line SF from a pipe and set the property as expected. The secure handler could not be run
here: without a terminal, System.console() is null, and the task failed with a NullPointerException from
SecureInputHandler. Run it from a real console. input is not usable from IDEs that do not forward standard input; in that
case use a property file as above.
A related point for passwords: avoid typing them on a command line (they land in shell history and process listings) and avoid
writing them in the build file. The secure handler, an environment variable read with <property environment="env"/>, or a
property file with restricted permissions are the usual routes, covered with other credentials on
Supply Chain Security.
Custom handlers, listeners and loggers are documented on Listeners, Loggers & Input Handlers.
Source: Ant manual, input
record
record attaches a recorder, a build listener that writes the log to a file. Each file name identifies one recorder: the
first record with a new name creates and starts it; later calls with the same name change its state.
| Attribute | Effect |
|---|---|
|
The file to record to. Required. |
|
|
|
|
|
|
|
|
|
|
The recorder writes what the build logs at its level between start and stop; its stream is flushed after targets and tasks
and closed at the end of the build. This target records one stretch of the build at the verbose
level, then stops and reads the file back:
<target name="record">
<record name="${build.dir}/steps.log" action="start" loglevel="verbose"/>
<echo>first step</echo>
<echo level="verbose">only in the recording</echo>
<record name="${build.dir}/steps.log" action="stop"/>
<echo>not recorded</echo>
<loadfile srcfile="${build.dir}/steps.log" property="steps"/>
<echo>--- steps.log ---${line.separator}${steps}</echo>
</target>
[echo] first step
[echo] not recorded
[echo] --- steps.log ---
[echo] [echo] first step
[echo] [echo] only in the recording
The console (run at the default level) shows two messages; the file holds the first one and the verbose one, and not
"not recorded". The 1.10.18 attribute relativeToBaseDir matters when Ant is started from another directory. Running
ant -f ../ant-utility-tasks/build.xml record-relative from a sibling directory with one recorder per setting:
<target name="record-relative">
<record name="steps-cwd.log" action="start"/>
<record name="steps-base.log" action="start" relativeToBaseDir="yes"/>
<echo>hello recorders</echo>
<record name="steps-cwd.log" action="stop"/>
<record name="steps-base.log" action="stop" relativeToBaseDir="yes"/>
</target>
produced one file in each place, each holding the one [echo] line:
steps-cwd.log in the working directory (28 bytes)
steps-base.log in the project's base directory (28 bytes)
Every record call for that file has to repeat relativeToBaseDir="yes", because the attribute is part of how the name is
resolved. A first version of this target left it off the stop call: that call resolved steps-base.log against the
working directory, created a second, empty recorder there (a zero-byte steps-base.log), and the real recorder in the base
directory was never stopped, so it went on recording to the end of the build (46 bytes, with BUILD SUCCESSFUL).
Source: Ant manual, record; Ant 1.10.18 WHATSNEW
tstamp
tstamp sets DSTAMP (yyyyMMdd), TSTAMP (HHmm) and TODAY (MMMM d yyyy) in the project, and can set more properties
with nested format elements: property, pattern (a SimpleDateFormat pattern), timezone, locale, and offset with a
unit (millisecond to year). prefix prefixes all properties. Put it in an initialisation target.
<target name="tstamp">
<tstamp/>
<tstamp prefix="start">
<format property="iso" pattern="yyyy-MM-dd'T'HH:mm:ss'Z'" timezone="UTC"/>
<format property="yesterday" pattern="yyyy-MM-dd" offset="-1" unit="day" timezone="UTC"/>
</tstamp>
<echo>DSTAMP=${DSTAMP} TSTAMP=${TSTAMP} TODAY=${TODAY}</echo>
<echo>start.iso=${start.iso} start.yesterday=${start.yesterday}</echo>
</target>
[echo] DSTAMP=20261016 TSTAMP=1144 TODAY=October 16 2026
[echo] start.iso=2026-10-16T09:44:53Z start.yesterday=2026-10-15
(The local time zone on the test machine was CEST, two hours ahead of UTC, which is why TSTAMP and start.iso differ.)
Source: Ant manual, tstamp
Reproducible dates
A build that stamps the current time cannot be reproduced. Ant lets you fix the time:
-
the magic property
ant.tstamp.now(seconds since the epoch) andant.tstamp.now.iso(ISO-8601), since Ant 1.10.2; -
the
SOURCE_DATE_EPOCHenvironment variable, since Ant 1.10.8 (an invalid value logs a debug message and the current date is used); -
since Ant 1.10.18, when
SOURCE_DATE_EPOCHis the source, the time zone ofDSTAMP,TSTAMPandTODAYis UTC, and nestedformatelements default to UTC and theen_USlocale unless they say otherwise.
The same target with the fixed values:
$ SOURCE_DATE_EPOCH=0 ant -q tstamp
[echo] DSTAMP=19700101 TSTAMP=0000 TODAY=January 1 1970
[echo] start.iso=1970-01-01T00:00:00Z start.yesterday=1969-12-31
$ ant -q -Dant.tstamp.now.iso=1972-04-17T08:07:00Z tstamp
[echo] DSTAMP=19720417 TSTAMP=0907 TODAY=April 17 1972
[echo] start.iso=1972-04-17T08:07:00Z start.yesterday=1972-04-16
The second run shows the difference: with ant.tstamp.now.iso the standard TSTAMP is still in the local time zone (0907 for
08:07 UTC on a machine one hour ahead then), whereas with SOURCE_DATE_EPOCH the 1.10.18 rule makes it UTC (0000). For
reproducible builds set SOURCE_DATE_EPOCH and give explicit timezone attributes to your own formats. Maven has project.build.outputTimestamp and Gradle reproducible archives for the same goal, compared on
Build Automation Fundamentals.
Source: Ant manual, tstamp
checksum
checksum creates or verifies checksum files and properties. The attributes:
| Attribute | Effect |
|---|---|
|
What to checksum. Nested collections must be file-system resources. |
|
A |
|
A JCE provider for algorithms the JDK lacks. |
|
Extension of the checksum file. The default is |
|
Write checksum files to another directory. |
|
Without |
|
Set to |
|
|
|
A property for a checksum of all checksums and relative paths, with |
|
Overwrite newer checksum files; buffer size for large files. |
The target computes a SHA-256 checksum into a property, writes a *.sha256 file in GNU format, verifies it, then modifies the
file and verifies again:
<target name="checksum">
<mkdir dir="${build.dir}"/>
<copy file="data/stations.csv" todir="${build.dir}/ck"/>
<checksum file="${build.dir}/ck/stations.csv" algorithm="SHA-256" property="stations.sha256"/>
<echo>sha256=${stations.sha256}</echo>
<checksum file="${build.dir}/ck/stations.csv" algorithm="SHA-256" fileext=".sha256" format="MD5SUM"/>
<loadfile srcfile="${build.dir}/ck/stations.csv.sha256" property="sumfile"/>
<echo>${sumfile}</echo>
<checksum file="${build.dir}/ck/stations.csv" algorithm="SHA-256" fileext=".sha256" format="MD5SUM" verifyproperty="ck.ok1"/>
<echo>verify unchanged: ${ck.ok1}</echo>
<echo file="${build.dir}/ck/stations.csv" append="true">XXX,tampered,0,0,
</echo>
<checksum file="${build.dir}/ck/stations.csv" algorithm="SHA-256" fileext=".sha256" format="MD5SUM" verifyproperty="ck.ok2"/>
<echo>verify tampered: ${ck.ok2}</echo>
</target>
[echo] sha256=37a3148aacc8e1738a9c923806fd0a973c61b955f404c9c5e1af721bebe7baa7
[echo] 37a3148aacc8e1738a9c923806fd0a973c61b955f404c9c5e1af721bebe7baa7 *stations.csv
[echo] verify unchanged: true
[echo] verify tampered: false
The digest equals the one printed by the operating system’s tool, shasum -a 256 data/stations.csv:
37a3148aacc8e1738a9c923806fd0a973c61b955f404c9c5e1af721bebe7baa7. The *stations.csv form in the file is what
shasum -a 256 -c (binary mode) reads, so the file can be checked outside Ant.
The extension keeps the case of the algorithm name. With a nested fileset and algorithm="SHA-256" and no fileext:
$ ls build/ck2
stations.csv
stations.csv.SHA-256
The manual warns about exactly this: ask for SHA1 and you get .SHA1, ask for sha1 and you get .sha1. On a
case-insensitive file system (the macOS default, used here) two runs with different spellings write the same file, so pick
one spelling or set fileext explicitly. Use checksums to verify downloads, as in the get example on
Network & Remote Tasks, and to publish them with
your release; the wider picture, including signatures and Gradle’s dependency verification, is on
Supply Chain Security.
Source: Ant manual, checksum
buildnumber
buildnumber reads a number from a file (build.number in the working directory by default), sets the property
build.number to it (0 when the file is missing), increments it and writes it back. The file attribute names another file.
The propertyfile task gives finer control (other property names, number formats).
Because the property is immutable, a second buildnumber in the same build does not change build.number, although it still
increments the file:
<target name="buildnumber">
<buildnumber file="${build.dir}/build.number"/>
<echo>build.number=${build.number}</echo>
</target>
Running the target twice in one build (ant buildnumber buildnumber; Ant executed it twice, as the output shows), starting
from no file, is the same as having two buildnumber tasks:
[echo] build.number=0
[echo] build.number=0
$ cat build/build.number
#Build Number for ANT. Do not edit!
#Fri Oct 16 11:45:02 CEST 2026
build.number=2
Both messages print 0, yet the file holds 2. The first call set the property to 0 and wrote 1; the second could not change
the property and wrote 2. Run the task once per build, early, and read the property afterwards. A build number kept in a file
that is committed to version control makes every build a change; many projects prefer a CI-provided number passed as a
property.
Source: Ant manual, buildnumber
hostinfo
hostinfo sets NAME, DOMAIN, ADDR4 and ADDR6 for a host (the local machine by default), with an optional prefix. If
the host cannot be found, the values fall back to the name given, localhost/localdomain, 0.0.0.0 or 127.0.0.1, and ::
or ::1. It is useful for host-stamped file names and for writing the build machine into a manifest. Run for localhost so no
real host name appears:
<target name="hostinfo">
<hostinfo prefix="local" host="localhost"/>
<echo>local.NAME=${local.NAME} local.DOMAIN=${local.DOMAIN} local.ADDR4=${local.ADDR4} local.ADDR6=${local.ADDR6}</echo>
</target>
[echo] local.NAME=localhost local.DOMAIN=localdomain local.ADDR4=127.0.0.1 local.ADDR6=0:0:0:0:0:0:0:1
The manual’s own example resolves www.apache.org and was not run here, because it would query the network.
Source: Ant manual, hostinfo
diagnostics
diagnostics runs Ant’s -diagnostics report inside the build, which helps when Ant is started from an IDE and you need to
know what it can see. The same report is printed by ant -diagnostics. The report has these sections (taken from a real run;
the content of the long ones, which contain paths and system properties of the machine, is not reproduced): Implementation
Version, ANT PROPERTIES, ANT_HOME/lib jar listing, USER_HOME/.ant/lib jar listing, Tasks availability, XML Parser
information, XSLT Processor information, Symlinking information, System properties, Temp dir, Locale information and Proxy
information.
The most useful section is the task availability list. On the test machine, with no extra libraries installed, it printed:
[diagnostics] Tasks availability
[diagnostics] -------------------------------------------
[diagnostics] sshsession : Missing dependency com.jcraft.jsch.Logger
[diagnostics] scp : Missing dependency com.jcraft.jsch.Logger
[diagnostics] image : Missing dependency javax.media.jai.PlanarImage
[diagnostics] jdepend : Missing dependency jdepend.textui.JDepend
[diagnostics] xz : Missing dependency org.tukaani.xz.FilterOptions
[diagnostics] sshexec : Missing dependency com.jcraft.jsch.Logger
[diagnostics] ftp : Missing dependency org.apache.commons.net.ftp.FTPConnectionClosedException
[diagnostics] A task being missing/unavailable should only matter if you are trying to use it
This is the quickest way to find out which JAR a task is missing; the network tasks in the list are the ones described on Network & Remote Tasks.
Source: Ant manual, diagnostics
antversion
antversion stores the running Ant’s version in a property (property, required as a task) and can check it: atleast or
exactly (format major.minor.point). It is also a condition. The property is set only if the check passes. Use it to fail
early when a build needs a task or attribute from a newer Ant:
<target name="antversion">
<antversion property="ant.is.recent" atleast="1.10.14"/>
<antversion property="ant.version.found"/>
<echo>antversion=${ant.version.found} atleast1.10.14=${ant.is.recent}</echo>
<condition property="ant.exactly.1.10.18">
<antversion exactly="1.10.18"/>
</condition>
<echo>exactly 1.10.18: ${ant.exactly.1.10.18}</echo>
<antversion property="never" atleast="1.11.0"/>
<echo>atleast 1.11.0: ${never}</echo>
</target>
[echo] antversion=1.10.18 atleast1.10.14=1.10.18
[echo] exactly 1.10.18: true
[echo] atleast 1.11.0: ${never}
The last line shows the unset property left as written. To turn a missing minimum version into an error, combine it with
fail and unless:
<antversion property="ant.ok" atleast="1.10.18"/>
<fail unless="ant.ok" message="Ant 1.10.18 or later is required (CVE-2026-78254)"/>
Source: Ant manual, antversion
whichresource
whichresource finds a class (class) or resource (resource) on the supplied classpath (classpath, classpathref or a
nested classpath) or on the system class path, and sets property to its URL if found. It answers "which JAR does this class
come from?", the common cause of NoSuchMethodError and duplicate-library problems.
<target name="whichresource">
<whichresource property="ant.project.loc" class="org.apache.tools.ant.Project"/>
<echo>${ant.project.loc}</echo>
<whichresource property="missing.loc" class="io.tidepool.NoSuchClass"/>
<echo>missing: ${missing.loc}</echo>
</target>
[echo] jar:file:/.../apache-ant-1.10.18/lib/ant.jar!/org/apache/tools/ant/Project.class
[echo] missing: ${missing.loc}
Source: Ant manual, whichresource
antstructure
antstructure writes a DTD describing every task and type Ant currently knows (output is required). The manual cautions
that it is not a real DTD for build files, because the same element can take different attributes in different places, that
it does not know which attributes are required, and that it only knows what is defined now (add your own definitions with
taskdef or typedef first). A custom structure printer (an implementation of
org.apache.tools.ant.taskdefs.AntStructure.StructurePrinter declared with typedef and nested in the task, Ant 1.7) can emit
another format.
<target name="antstructure">
<mkdir dir="${build.dir}"/>
<antstructure output="${build.dir}/project.dtd"/>
</target>
On 1.10.18 the file was 210,814 bytes. Its entry for echo shows the style (every attribute #IMPLIED; the file as a whole
has 4,499 #IMPLIED and only 2 #REQUIRED markers):
<!ELEMENT echo (#PCDATA)>
<!ATTLIST echo
id ID #IMPLIED
level (error | warn | warning | info | verbose | debug) #IMPLIED
force %boolean; #IMPLIED
encoding CDATA #IMPLIED
append %boolean; #IMPLIED
(Only the first lines of the ATTLIST are shown.) Treat the file as documentation of what exists, not as a validating schema; the elements of a build file itself are covered
on Build File Structure.
Source: Ant manual, antstructure
sleep
sleep pauses the build. The time is the sum of hours, minutes, seconds and milliseconds; individual values may be
negative if the total is positive. failonerror controls whether an error (for example an interruption) breaks the build. The
manual notes that sleep times are hints: small values may be rounded up, and clock granularity makes measuring them unreliable.
<target name="sleep">
<sleep seconds="1" milliseconds="500"/>
</target>
sleep:
BUILD SUCCESSFUL
Total time: 1 second
Ant reports whole seconds, so the 1.5 seconds shows as 1. A fixed sleep is a poor way to wait for a service: use waitfor
with a condition and a timeout, described on
Conditions & Control Flow.
Source: Ant manual, sleep
The same jobs in Maven and Gradle
| Job | Maven | Gradle |
|---|---|---|
Print a message |
|
|
Ask the user |
Nothing in the core; use |
A |
Build date |
|
Reproducible archives by default since Gradle 9.0 |
Checksum files |
Generated for installed and deployed artifacts by the install and deploy machinery |
Generated for published artifacts by the publishing plugins |
Run Ant tasks as they are |
|
The |
The Maven and Gradle cells are summaries; the details live on the pages listed in Comparing Ant, Maven & Gradle, and the Gradle way of calling these Ant tasks is on Ant integration.
Where the books and older build files are outdated
-
The
tstampexamples in older books use the current time only. Ant 1.10.2, 1.10.8 and 1.10.18 addedant.tstamp.now,SOURCE_DATE_EPOCHsupport and the UTC rule for it; use them for reproducible builds. -
checksumexamples default to MD5 and show.MD5files; useSHA-256or stronger and setfileextexplicitly. -
The
inputtask’s long-standing complaint (passwords echoed on the console) was addressed in Java 6, and Ant 1.7.1 and 1.8 added thesecurehandler; the advice to "just prompt for the password" in older material should use it. -
Advice that
-noinputletsdefaultvalueanswer for you is wrong, as shown above: it makes the read fail. -
diagnosticsdid not exist before Ant 1.7.0; build files written for earlier versions test for problems by hand.
References
-
Apache Ant manual: echo
-
Apache Ant manual: input
-
Apache Ant manual: record
-
Apache Ant manual: tstamp
-
Apache Ant manual: checksum
-
Apache Ant manual: buildnumber
-
Apache Ant manual: hostinfo
-
Apache Ant manual: diagnostics
-
Apache Ant manual: antversion
-
Apache Ant manual: whichresource
-
Apache Ant manual: antstructure
-
Apache Ant manual: sleep
-
Reproducible Builds: SOURCE_DATE_EPOCH specification