Ant Properties
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. The reference material consulted while preparing these pages is listed in the section bibliography. |
This page is written against Apache Ant 1.10.18. Every example was run on JDK 26 on 2026-10-16 with that release, in a
scratch directory that mimics the tidepool model module. Paths in the console output were shortened to
/work/properties/..., and the Ant installation to /opt/apache-ant-1.10.18. Ant’s own warnings and error messages are
quoted exactly as printed.
Properties are the one feature every Ant build uses on its first line and the one whose rules surprise newcomers most. They look like variables, and they are not: they are named, string-valued constants that, once set, cannot be changed. Almost every "why does my build ignore this setting?" question about Ant comes down to the rules explained here. For the other pieces of a build file see Build File Structure and Targets & Extension Points; for the command-line switches that define properties see Running Ant.
What a property is
A property is a case-sensitive name with a string value. Wherever a task attribute or a task’s nested text contains
${name}, Ant replaces it with the value at the moment the task is configured. Ant also exposes every JVM system property
(${os.name}, ${user.home}, ${java.version}) as if it had been set with the property task.
Three rules govern everything else:
-
Properties are immutable. The first definition wins; every later attempt to set the same name is silently ignored. The manual says it directly: whoever sets a property first freezes it for the rest of the build, and properties are "most definitely not variables".
-
Properties are global. Once defined they are visible to every task in every target that runs afterwards, and the child builds started by
ant,antcallandsubantcannot set a property that the calling build then sees. The one exception to global scope is thelocaltask, described below. -
Properties are expanded when they are set or read, once. A value that contains a reference to a property that is not yet defined keeps the text
${...}literally.
Source: Ant manual: Properties
Source: Ant manual: Property task
First definition wins
<project name="tidepool-model" default="show" basedir=".">
<property name="build.dir" value="build"/>
<property name="build.dir" value="out"/>
<property name="classes.dir" value="${build.dir}/classes"/>
<property name="dollar.demo" value="$${build.dir} is ${build.dir}"/>
<target name="show">
<echo>build.dir = ${build.dir}</echo>
<echo>classes.dir = ${classes.dir}</echo>
<echo>${dollar.demo}</echo>
<echo>$${not.defined} stays: ${not.defined}</echo>
</target>
</project>
Run with no arguments (ant):
Buildfile: /work/properties/ex1/build.xml
show:
[echo] build.dir = build
[echo] classes.dir = build/classes
[echo] ${build.dir} is build
[echo] ${not.defined} stays: ${not.defined}
BUILD SUCCESSFUL
Total time: 0 seconds
The second build.dir definition (out) was ignored. Nothing was printed about it at the default log level; running
with -v or -d adds the line Override ignored for property "build.dir", which is the only trace. The last two
echo lines show the escape rule (next section) and an undefined property left as literal text.
Source: Ant manual: Property task
Source: Ant manual: Properties
Command-line properties always win
A property given with -D on the command line, or loaded with -propertyfile, is set before the build file is parsed, so
no property task in the file can change it. The same happens if the arguments come from the ANT_ARGS environment
variable. Run the same build as ant -Dbuild.dir=cli-wins:
Buildfile: /work/properties/ex1/build.xml
show:
[echo] build.dir = cli-wins
[echo] classes.dir = cli-wins/classes
[echo] ${build.dir} is cli-wins
[echo] ${not.defined} stays: ${not.defined}
BUILD SUCCESSFUL
Total time: 0 seconds
With -propertyfile ci.properties (containing build.dir=from-file and classes.dir=file-classes) together with
-Dbuild.dir=from-cli, the output was build.dir = from-cli and classes.dir = file-classes: both sources beat the
build file, and -D beats -propertyfile, exactly as the usage text promises ("load all properties from file with -D
properties taking precedence"). Setting ANT_ARGS="-Dbuild.dir=from-ant-args" gave build.dir = from-ant-args.
This is by design: a build file states defaults, and the caller (a developer, a continuous integration job) overrides
them. It is also the single most useful idiom in Ant: write property for every tunable value and let people pass
-D.
Source: Ant manual: Running Ant, options
Where a value comes from: the order of precedence
Two details complete the picture:
-
Top-level tasks run first. Since Ant 1.6 every project has an implicit target that holds all tasks written directly under
<project>. It runs as part of initialisation, before any named target and even forant -p. That is why the customary properties block at the top of a build file works, and why properties file loading at the top acts as "defaults" that every target sees. -
Order of definition is the only way to prioritise. To let a per-user file override the project’s defaults, load the user’s file first. The next example does exactly that.
Source: Ant manual: Using Ant, projects
Property expansion
${name} and the $$ escape
In its default configuration Ant expands ${name} to the value of the property name. If no property of that name
exists, the text is left untouched (as the last echo above shows) and nothing is reported. A typo in a property name is
therefore a silent bug.
To write a literal ${...}, double the dollar sign: $${name} expands to ${name} and suppresses expansion of the
text after it. The manual’s own example is a diagnostic message such as <echo>$${builddir}=${builddir}</echo>, which
prints ${builddir}=build/classes. A lone $ that is not part of a property-like construct is kept literally for
backward compatibility, but the manual advises always escaping it, and warns that mixing the two styles gives
unpredictable results ($$$ yields $$).
Expansion happens when the property is set
The value of a property task is expanded at the moment the task executes. A property that is defined later is not seen:
<target name="c">
<property name="x" value="${y}"/>
<property name="y" value="late"/>
<echo>x = ${x}</echo>
</target>
c:
[echo] x = ${y}
Properties loaded from a file are different: Ant resolves references between the entries of one file regardless of their
order. In the example below, tide.url refers to tide.host and tide.port, which are defined after it, and still
expands correctly.
Source: Ant manual: Property task
No nested expansion
The default expander finds the name between ${ and the first } and does not balance braces, so
${${which}} cannot look up a property whose name is held in another property. With which set to nested:
e:
[echo] ${${which}}
No error is raised; the text simply stays as written. The Props Antlib (below) installs an expander that supports nesting.
The property task
The property task sets one property or many. The manual lists seven ways, and recommends using only one per task
because combining them depends on evaluation order:
| Form | Effect |
|---|---|
|
Sets |
|
Sets |
|
Copies a reference defined elsewhere. Reasonable only for path-like structures and other properties. |
|
Loads a |
|
Loads properties-format text from a URL. |
|
Loads a properties file from the classpath ( |
|
Defines one property per environment variable, prefixed with the given name and a dot. |
|
Since 1.10.4: defines |
The remaining attributes modify how the values are produced:
| Attribute | Meaning |
|---|---|
|
Prepended to every property loaded from |
|
Since 1.8.2. Whether the prefix is also applied when expanding references on the right-hand side of the loaded
entries. Default |
|
Since 1.8.0. With |
|
Where to look for a |
From Ant 1.8.0, file, url and resource can also read the XML properties format when the name ends in .xml.
Nested properties inside the loaded file are expanded when they are loaded.
A tour
The following build file uses every common form. It sits beside a conf/ directory:
# defaults shared by all modules
tide.station=ES-0001
tide.datum=MSL
tide.url=https://${tide.host}:${tide.port}/api
tide.host=localhost
tide.port=8443
tide.station=ES-9999
db.host=localhost
db.url=jdbc:h2:tcp://${db.host}/tide
<project name="props-tour" default="tour" basedir=".">
<property name="greeting" value="ahoy"/>
<property name="motto">fair winds</property>
<property name="data.csv" location="data/stations.csv"/>
<path id="cp">
<pathelement location="lib/a.jar"/>
<pathelement location="lib/b.jar"/>
</path>
<property name="cp.copy" refid="cp"/>
<!-- the first definition wins: read the overrides before the defaults -->
<property file="conf/override.properties"/>
<property file="conf/tidepool.properties"/>
<property file="conf/db.properties" prefix="plain"/>
<property file="conf/db.properties" prefix="pv" prefixValues="true"/>
<property name="rel" location="conf/tidepool.properties" relative="true" basedir="conf"/>
<target name="tour">
<echo>greeting = ${greeting}</echo>
<echo>motto = ${motto}</echo>
<echo>data.csv = ${data.csv}</echo>
<echo>cp.copy = ${cp.copy}</echo>
<echo>tide.station = ${tide.station}</echo>
<echo>tide.url = ${tide.url}</echo>
<echo>plain.db.url = ${plain.db.url}</echo>
<echo>pv.db.url = ${pv.db.url}</echo>
<echo>rel = ${rel}</echo>
</target>
</project>
Buildfile: /work/properties/ex2/build.xml
tour:
[echo] greeting = ahoy
[echo] motto = fair winds
[echo] data.csv = /work/properties/ex2/data/stations.csv
[echo] cp.copy = /work/properties/ex2/lib/a.jar:/work/properties/ex2/lib/b.jar
[echo] tide.station = ES-9999
[echo] tide.url = https://localhost:8443/api
[echo] plain.db.url = jdbc:h2:tcp://${db.host}/tide
[echo] pv.db.url = jdbc:h2:tcp://localhost/tide
[echo] rel = tidepool.properties
BUILD SUCCESSFUL
Total time: 0 seconds
What the output proves:
-
tide.stationisES-9999: the override file was read first, so it froze the value, and the default intidepool.propertieswas ignored. -
tide.urlexpanded althoughtide.hostis defined after it in the same file. -
plain.db.urlkept the unresolved${db.host}: the file was loaded withprefix="plain", so its entries becameplain.db.hostandplain.db.url, and the reference todb.hostfound nothing.prefixValues="true"makes the right-hand side use the prefix too, sopv.db.urlresolved. -
A
pathcopied withrefidbecomes a single string using the platform’s path separator.
The environment attribute works the same way. After <property environment="env"/> the variable HOME is available as
${env.HOME} (the run on this machine printed the home directory). Names are case-sensitive even where the operating system
is not: on Windows the variable Path appears as env.Path, not env.PATH. Since Ant 1.8.2 on Java 5 and later Ant reads
System.getenv(); the old trick of running a platform command to list variables remains only for OpenVMS and as a
fallback. The manual’s classic layered-configuration idiom is worth copying: read the user’s file, then the
environment-specific file, then the project file, so the most specific definition wins:
<property environment="env"/>
<property file="${user.name}.properties"/>
<property file="${env.STAGE}.properties"/>
<property file="build.properties"/>
Source: Ant manual: Property task
Built-in properties
Besides the JVM system properties, Ant defines a handful of properties of its own. The run below printed all of them for
a project named tidepool-model, invoked as ant builtins refs.
| Property | Value |
|---|---|
|
Absolute path of the project’s base directory (the |
|
Absolute path of the build file. |
|
The version string of Ant. |
|
The |
|
The |
|
Comma-separated list of the targets requested on the command line, by an IDE or by an |
|
The Java version Ant detected. |
|
Absolute path of |
|
Ant’s home directory. Set by the launcher script, so it can be missing inside an IDE. |
|
The directory Ant’s jars were loaded from, normally |
Buildfile: /work/properties/ex3/build.xml
builtins:
[echo] basedir = /work/properties/ex3
[echo] ant.file = /work/properties/ex3/build.xml
[echo] ant.version = Apache Ant(TM) version 1.10.18 compiled on September 3 2026
[echo] ant.project.name = tidepool-model
[echo] ant.project.default-target = builtins
[echo] ant.project.invoked-targets = builtins,refs
[echo] ant.java.version = 26
[echo] java.version = 26
[echo] ant.core.lib = /opt/apache-ant-1.10.18/lib/ant.jar
[echo] ant.home = /opt/apache-ant-1.10.18
[echo] ant.library.dir = /opt/apache-ant-1.10.18/lib
Note that ant.java.version is 26 here. The manual’s list of possible values ends at 9 and 1.8 and is out of date
in that respect: since Ant 1.10.0 the property holds 9 rather than 1.9 on Java 9, and on later JDKs it is simply the
major version. Compare with ant.project.invoked-targets: invoking ant with no argument gives builtins (the default
target), and invoking ant refs builtins gives refs,builtins, in command-line order.
Some other "magic" properties are read by particular tasks and are documented with them:
| Property | Since | Effect |
|---|---|---|
|
1.7 |
Default |
|
1.10.8 |
Directory for temporary files; see Running Ant. |
|
1.10.2 |
Fix the time |
|
1.10.9 |
Turns off the Nashorn compatibility mode Ant enables for GraalVM JavaScript; see Scripting. |
Local properties
Since Ant 1.8 the local task creates a property whose scope is the current target (or the body of a sequential,
parallel branch or macrodef). It shadows a global property of the same name from that point on, and is gone when the
scope ends. The task only declares the name; the property is then set by an ordinary property task, which obeys the same
"first wins" rule inside the scope.
<project name="scopes" default="all" basedir=".">
<property name="mode" value="release"/>
<target name="step1">
<echo>before local: mode is ${mode}</echo>
<local name="mode"/>
<property name="mode" value="debug"/>
<echo>after local: mode is ${mode}</echo>
</target>
<target name="step2" depends="step1">
<echo>in step2: mode is ${mode}</echo>
</target>
<target name="mutate">
<property name="mode" value="changed"/>
<echo>after a second property task: mode is still ${mode}</echo>
</target>
<target name="child">
<ant antfile="child.xml" inheritAll="false">
<property name="mode" value="passed explicitly"/>
</ant>
<echo>from.child in the parent: ${from.child}</echo>
</target>
<target name="all" depends="step2,mutate,child"/>
</project>
child.xml echoes mode, then sets from.child:
<project name="child" default="run" basedir=".">
<target name="run">
<echo>child sees mode = ${mode}</echo>
<property name="from.child" value="set in the child"/>
</target>
</project>
Buildfile: /work/properties/ex4/build.xml
step1:
[echo] before local: mode is release
[echo] after local: mode is debug
step2:
[echo] in step2: mode is release
mutate:
[echo] after a second property task: mode is still release
child:
run:
[echo] child sees mode = passed explicitly
[echo] from.child in the parent: ${from.child}
all:
BUILD SUCCESSFUL
Total time: 0 seconds
Four lessons in one run: local changed mode inside step1 only; a plain second property task cannot change a
global value; with inheritAll="false" a child build sees only the properties passed as nested property elements
(by default inheritAll is true and all properties are passed down); and nothing a child sets travels back to the
parent.
The place local pays for itself is the macro. Without it, a temporary property inside a macrodef is global and
is frozen by the first invocation, so every later call reuses the first call’s value. The common workaround before 1.8
was to put an attribute into the name (parent.@{file}), which pollutes the global namespace. Declare it local instead
(see Macros & Presets):
<macrodef name="makeparentdir">
<attribute name="file"/>
<sequential>
<local name="parent"/>
<dirname property="parent" file="@{file}"/>
<mkdir dir="${parent}"/>
</sequential>
</macrodef>
Since Ant 1.10.13 several names can be declared in one task with nested <name> elements. Since 1.10.9 a propertyset also
sees in-scope local properties.
Source: Ant manual: Local task
Source: Ant manual: Ant task
if and unless
Targets, and some tasks (fail, the test element of junit, the if: and unless: namespaces covered on
Targets & Extension Points), take if
and unless attributes. Their meaning changed in Ant 1.8.0:
-
Before 1.8.0 the value was always a property name, and the item was enabled if that property was defined, even as
falseor empty. -
From 1.8.0 the value is first expanded:
true,onoryesenables,false,offornodisables, and any other string is still treated as a property name.unlessis the inverse.
Because the value is expanded, the condition can be overridden from the command line, which the old form could only do positively.
<project name="conditional" default="all" basedir=".">
<target name="check-csv">
<available property="csv.present" type="file" file="data/stations.csv"/>
</target>
<target name="validate" depends="check-csv" if="${csv.present}">
<echo>validating data/stations.csv</echo>
</target>
<target name="skippable" unless="${skip.extras}">
<echo>extras run</echo>
</target>
<target name="old-style" if="skip.extras">
<echo>old style: runs when the property is merely defined, even as false</echo>
</target>
<target name="all" depends="validate,skippable,old-style"/>
</project>
With no CSV file and no skip.extras, running ant printed:
check-csv:
validate:
skippable:
[echo] extras run
old-style:
all:
After creating data/stations.csv and running ant -Dskip.extras=false, validate, skippable and old-style all ran
(the last one because the old form only asks whether the property exists). With -Dskip.extras=true, skippable was skipped,
yet old-style still ran, which is the trap of mixing the two styles. A skipped target still prints its header line
without any task output; a target skipped by if or unless does not skip the targets it depends on.
Reading properties from files and resources
loadproperties
loadproperties is the property file|resource task with two differences: it accepts a nested filterchain
(see Filters & Mappers) and nested resources, and it
fails the build when the source is missing, where property only warns at verbose level. Its attributes are srcFile,
resource, encoding, classpath/classpathref, prefix (since 1.8.1) and prefixValues (since 1.8.2, default
true, unlike property).
The tidepool build keeps its release settings in release.properties:
# tidepool release settings
import.csv=data/stations.csv
import.datum=MSL
export.dir=build/export
version.major=1
version.minor=4
To import only the keys that start with import.:
<loadproperties srcFile="release.properties">
<filterchain>
<linecontains>
<contains value="import."/>
</linecontains>
</filterchain>
</loadproperties>
The missing-file behaviour, with a srcFile that does not exist, is a hard failure:
BUILD FAILED
/work/properties/ex7/build.xml:3: Source resource does not exist: /work/properties/ex7/missing.properties
Source: Ant manual: LoadProperties task
loadfile and loadresource
loadfile reads a whole text file into one property; loadresource does the same for any resource (a URL, a file in a
zip, a classpath entry), and loadfile is the file-only shorthand. If the content is empty after filtering, the property is
not set. Both support encoding (the default is the platform encoding, so set it explicitly for portable builds),
failonerror (default true), quiet (since 1.7.0; implies failonerror="false") and nested filterchain. Because
properties are immutable, neither overwrites an existing property.
<loadfile property="banner" srcFile="banner.txt">
<filterchain>
<headfilter lines="1"/>
</filterchain>
</loadfile>
Source: Ant manual: LoadFile task and LoadResource task
xmlproperty
xmlproperty flattens a well-formed XML file into dotted property names, with the root element as the first segment
(keepRoot, default true). Attributes become name(attribute) unless collapseAttributes="true". Plain
xmlproperty does not expand ${...} references; set semanticAttributes="true" for that and for the special
attributes id, location, refid, value and pathid. Other attributes are prefix, validate,
includeSemanticAttribute, rootDirectory and delimiter (since 1.7.1, default ,).
With stations.xml:
<stations source="harbour-office">
<station id="ES-0001">
<name>Vigo</name>
<constituent name="M2" amplitude="1.12"/>
</station>
</stations>
this build file target loads all three sources (the full target is load in the example’s build.xml):
<xmlproperty file="stations.xml" collapseAttributes="true"/>
<echo>stations.source = ${stations.source}</echo>
<echo>stations.station.id = ${stations.station.id}</echo>
<echo>stations.station.name = ${stations.station.name}</echo>
<echo>constituent.amplitude = ${stations.station.constituent.amplitude}</echo>
load:
[echo] import.csv = data/stations.csv
[echo] export.dir = ${export.dir}
[echo] banner = tidepool
[echo] stations.source = harbour-office
[echo] stations.station.id = ES-0001
[echo] stations.station.name = Vigo
[echo] constituent.amplitude = 1.12
export.dir was filtered out by the linecontains filter, so its reference stays literal; banner holds only the first
line of banner.txt.
Source: Ant manual: LoadProperties task
Source: Ant manual: LoadFile task
Source: Ant manual: LoadResource task
Writing and inspecting properties
propertyfile
propertyfile edits a properties file in place. It is an optional task, built for "bump the build number" steps. Nested
entry elements describe each change: key (required), value, default, type (string, int or date),
operation (= default, +, - for int and date, or del), pattern for formatting numbers and dates and unit
(millisecond through year, default day) for date arithmetic. The task header attributes are file, comment and
jdkproperties. Since 1.8.0 it preserves the comments and layout of the original file (set jdkproperties="true" to
get the old behaviour back, which discards them) and since 1.8.2 it preserves the line-ending style. Since 1.10.18 the timestamp comment honours
SOURCE_DATE_EPOCH, ant.tstamp.now and ant.tstamp.now.iso (only when jdkproperties is false), so reproducible
builds stop getting a new timestamp on every run.
<propertyfile file="release.properties">
<entry key="version.minor" type="int" operation="+" value="1"/>
<entry key="version.patch" type="int" default="0"/>
</propertyfile>
<concat><file file="release.properties"/></concat>
Run as SOURCE_DATE_EPOCH=1767225600 ant bump against the file shown earlier:
bump:
Honouring environment variable SOURCE_DATE_EPOCH which has been set to 1767225600
[propertyfile] Updating property file: /work/properties/ex6/release.properties
[concat] #Thu, 01 Jan 2026 00:00:00 +0000
[concat] # tidepool release settings
[concat] import.csv=data/stations.csv
[concat] import.datum=MSL
[concat] export.dir=build/export
[concat] version.major=1
[concat] version.minor=5
[concat]
[concat] version.patch=0
The comments survived, version.minor went from 4 to 5, and version.patch was added with its default because it did not
exist. The task wrote a timestamp comment first (here the fixed date, as requested) and, as this run shows, a blank line
before the new key; the file is still a valid properties file.
Source: Ant manual: PropertyFile task
echoproperties
echoproperties prints all properties, or a subset, to the log or to destfile. It is meant for debugging a build file.
Attributes: destfile, prefix, regex, failonerror (default true) and format (text or xml); the
properties can also be selected with a nested propertyset. Since 1.10.14 the output is sorted on JDK 9 and later.
propertyset
propertyset (since 1.6) groups properties so a task can take them as a block: for example to give a selection to
echoproperties or, as a syspropertyset, to a forked java. Attributes are dynamic (re-evaluate on every
use, default true), negate (since 1.6.2) and refid. Nested propertyref elements select by name, prefix, regex
or builtin (all, system, commandline); nested propertyset elements form a union, and at most one mapper
renames keys.
<property name="tide.a" value="1"/>
<property name="tide.b" value="2"/>
<propertyset id="tide-props">
<propertyref prefix="tide."/>
<mapper type="glob" from="tide.*" to="cfg.*"/>
</propertyset>
<echoproperties prefix="tide."/>
<echo>${toString:tide-props}</echo>
<echoproperties destfile="build.props.out" prefix="tide."/>
<concat><file file="build.props.out"/></concat>
dump:
[echoproperties] #Ant properties
[echoproperties] #Fri Oct 16 11:36:54 CEST 2026
[echoproperties] tide.a=1
[echoproperties] tide.b=2
[echo] cfg.a=1, cfg.b=2
[concat] #Ant properties
[concat] #Fri Oct 16 11:36:54 CEST 2026
[concat] tide.a=1
[concat] tide.b=2
The file written by echoproperties is a normal properties file, so a later property file= can read it back, which is
the "return values from a child build" trick the manual mentions as a contrived use.
Source: Ant manual: PropertyFile task
Source: Ant manual: Echoproperties task
Source: Ant manual: PropertySet type
References as strings: toString: and ant.refid:
Two built-in prefixes read a project reference instead of a property.
-
${toString:id}expands to thetoString()of the referenced object. The manual’s example lists a fileset’s files; the run on a small tree printeda/One.java;a/Two.javafor${toString:sources}; a fileset always joins its entries with;, whatever the platform, while apathuses the platform path separator (:on macOS and Linux). For a third-party type there is no guarantee thattoString()is meaningful. -
${ant.refid:id}expands to the referenced object itself, which matters only for tasks whose attribute setters accept a type other thanString(aResource, for instance). Surrounded by other text it behaves liketoString:.
<fileset id="sources" dir="src" includes="**/*.java"/>
<echo>toString: ${toString:sources}</echo>
refs:
[echo] toString: a/One.java;a/Two.java
Source: Ant manual: Properties, toString
PropertyHelper and its delegates
Behind every ${...} is one org.apache.tools.ant.PropertyHelper instance attached to the project. Since Ant 1.8 it is
a thin coordinator that consults a chain of delegates, which makes property handling pluggable:
| Delegate interface | Responsibility |
|---|---|
|
Finds a property name inside a string. The default one extracts |
|
Turns a name into an object. |
|
Writes a property; the other half of the storage of local properties. |
|
Since 1.10.9: lists the property names a delegate manages, so tasks like |
The propertyhelper task (since 1.8.0) installs a delegate or a whole replacement helper at run time. Delegates are
consulted in last-in, first-out order, so the one added last runs before the built-in ones. This is advanced usage that
needs Java code; the manual’s example registers an evaluator class with componentdef and then wraps it in
<propertyhelper>. Not run here, because it needs a compiled delegate class; a worked custom class belongs to
Writing Custom Tasks.
The Props Antlib
The Apache Props Antlib is a library of ready-made delegates. It provides nested (a PropertyExpander that supports
${${double-expand-me}}), stringops (shell-like string operations), types (a type-aware evaluator) and others,
and is installed with propertyhelper. Install a Props Antlib release into one of Ant’s library locations (see
Antlibs & Namespaces). Not run here: the Antlib is a separate download that
the section’s fixed set of tools does not include. In practice most builds never need it; if you need computed property
names, a macrodef or the script task is usually simpler to maintain.
Source: Ant manual: PropertyHelper task
Source: Apache Props Antlib
The ant.build.javac.* properties
javac has no default source and target; the compiler’s defaults depend on the JDK that runs the build, so a build
file that omits them can stop compiling when the JDK is upgraded. When you cannot edit the file, two properties supply the
defaults, and are ignored when the attributes are set explicitly: ant.build.javac.source (since 1.7; also used by
javadoc) and ant.build.javac.target (since 1.7).
Given a one-line Height record and a build file with a bare javac task and no source or target, the run on JDK 26 with
ant -Dant.build.javac.source=17 -Dant.build.javac.target=17 compile printed:
compile:
[mkdir] Created dir: /work/properties/ex8/build/classes
[javac] Compiling 1 source file to /work/properties/ex8/build/classes
[javac] warning: [options] location of system modules is not set in conjunction with -source 17
[javac] not setting the location of system modules may lead to class files that cannot run on JDK 17
[javac] --release 17 is recommended instead of -source 17 -target 17 because it sets the location of system modules automatically
[javac] 1 warning
[echo] ant.build.javac.source = 17
[echo] ant.build.javac.target = 17
BUILD SUCCESSFUL
javap confirmed class file major version 61 (Java 17). The compiler’s own warning is the lesson: since Ant 1.9.8 javac
has a release attribute that passes --release, and it is the better choice on JDK 9 and later because it also
selects the matching platform API. Use these two properties only as a stop-gap and put release in the build file (see
Compiling Java). Without the properties and without
the attributes, a verbose run (-v) shows the property lookup failing with Property "ant.build.javac.source" has not been set.
Classic pitfalls
-
"My setting is ignored." Something defined the same name earlier: another
property, a-D, an imported build file that runs before yours, or a property file loaded above your task. Run with-vand look forOverride ignored for property "name". The fix is to order the definitions, not to repeat the task. -
"My
${...}is printed literally." The name is misspelt, the property is defined later, or the property is defined only in alocalscope that has ended. Ant never reports it. A defensive build usesfailwithunlessfor required properties:<fail message="Set -Dtide.station=... or add it to conf/tidepool.properties" unless="tide.station"/> -
Properties as loop variables. Because they are immutable, a property cannot be a counter. Use a macro with
local, a loop from the third-party Ant-Contrib library, or thescripttask; do not look for an "assign" task in core Ant. -
antcallandantdo not return values. A child build cannot set a property for its caller. Use anechopropertiesfile, a shared properties file, or restructure withdepends,importand macros. -
Environment name case. On Windows the variable
Pathisenv.Path; scripts written on Linux withenv.PATHfind nothing. -
Encoding. Properties files are read as ISO-8859-1; write other characters as
\uXXXX, or load the file withloadpropertiesand an explicitencoding. -
Trailing spaces. They are part of the value in a properties file, as in
java.util.Properties. -
Missing optional file.
property file=is silent about a missing file (intentionally);loadpropertiesfails. Choose according to whether the file is optional. -
Syntax error at runtime. An unclosed reference stops the build with
Syntax error in property: ${b; the error names the line of the build file.
Where older Ant material says that top-level code may contain only property, typedef and taskdef, or describes
ant.java.version as one of 1.2 to 1.9, it is out of date: since Ant 1.6 every task may appear outside a target, and the
property now holds the JDK’s own major version.
Source: Ant manual: Fail task
Properties in Maven and Gradle
| Concept | Ant | Maven | Gradle |
|---|---|---|---|
Define a value |
|
|
|
Override from the command line |
|
|
|
Environment |
|
|
|
Scoped value |
|
a plugin parameter or profile |
a task or project property |
See POM Fundamentals and Profiles for Maven properties, and Build Environment & Properties for Gradle’s. The Gradle build can also call Ant tasks and read Ant properties; see Ant Integration.
Where to go next
-
Running Ant for the full list of command-line switches and environment variables.
-
Paths, Filesets & Patterns for the types that properties often point to.
-
Conditions & Control Flow for
condition,availableandfail. -
Multi-File Builds: import, include & subant for how properties flow between files.