Ant Selectors
|
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 marked as run was executed on JDK 26 (macOS) on 2026-10-16
in a scratch directory; the output is shown as it was printed, with the long absolute path of the scratch directory
shortened to <scratch>. Anything that could not be run here is labelled "not run here" in the text.
A pattern in a fileset (see Paths, Filesets & Patterns)
can only look at a file’s path. A selector is a nested element of a fileset that decides on any other criterion: what
the file contains, how old it is, how big it is, whether a class file already exists for it, whether it is executable.
Selectors are a design answer to a limit of the pattern language: patterns are cheap and declarative, so the scanner
applies them first and walks only the directories they can match, and everything that needs to open or stat a file is
left to a second, explicit step. The same elements also work as resource selectors inside restrict, which is
the subject of Resource Collections.
The running data is the tidepool scenario described on the section
landing page, but selectors do not fit a single module well, so the examples use a small standalone build file over the
tidepool-model, tidepool-engine and tidepool-cli sources and a few generated fixture files (a docs directory, a
build/classes directory with an old and a new class file, and a build/fs directory with different permissions).
How a selector combines with the patterns
Three rules explain every result on this page:
-
The
includes/excludespatterns run first. A selector can only remove files that the patterns already matched; afilenameselector cannot add files back. -
All the selectors directly nested in a
filesetare combined with a logical AND, as if they were wrapped inand. -
A selector is evaluated for every candidate file, in the order written, after the cheaper pattern test. Put the patterns that exclude the most files in
includesand let selectors do only the finishing.
Tasks that process directories as well as files (copy, delete) offer them to the selectors too. Use type when
a selector should be restricted to one kind.
Every selector can also be written in a project-level selector element with an id and used later by refid; see
Selector references and conditional selection.
The core selectors
| Selector | Selects files that | Main attributes | Since |
|---|---|---|---|
|
contain a string |
|
|
|
contain a match of a regular expression |
|
the three flags 1.8.2 |
|
were modified before, after or at a date |
|
|
|
are newer than the file a mapper derives from them |
|
|
|
are a number of directory levels below the base |
|
|
|
differ from the equivalent file elsewhere |
|
|
|
have a name matching a pattern or regular expression |
|
|
|
do ( |
|
|
|
are larger, smaller or exactly a size |
|
|
|
are regular files or directories |
|
|
|
have a value that differs from the one stored in a cache |
|
|
|
are signed, optionally with a given signature name |
|
1.7 |
|
the JVM reports as readable or writable |
||
|
|
1.10.0 |
|
|
are symbolic links |
1.10.0 |
|
|
are owned by a user |
|
1.10.0 |
|
belong to a POSIX group |
|
1.10.4 |
|
have given POSIX permissions |
|
1.10.4 |
|
are directories that are Windows junctions |
1.10.16 |
|
|
a script selects |
|
1.7 |
Three groups of these were added after the Ant versions that most printed material describes. Books and articles written
for Ant 1.7 or 1.8 do not know executable, symlink and ownedBy (1.10.0), posixGroup and posixPermissions
(1.10.4) or windowsjunction (1.10.16), and describe modified and different the way they worked then. The manual of
the release you run is the reference.
Selecting by content
contains and containsregexp open every candidate file and read it with the JVM default encoding (encoding on
contains overrides it since Ant 1.9.0). containsregexp uses the regular expression implementation Ant is configured
with, which is java.util.regex unless a regexp implementation is chosen (see
Filters & Mappers). With the default flags ^ and $
match only at the start and end of the whole file, so a line-anchored pattern needs multiline="true".
<fileset id="by.contains" dir="." includes="tidepool-*/src/**/*.java">
<contains text="record" casesensitive="true"/>
</fileset>
<fileset id="by.regexp" dir="." includes="tidepool-*/src/**/*.java">
<containsregexp expression="^package io\.tidepool\.(model|engine);" multiline="true"/>
</fileset>
The first selects the four model records, the second selects every file whose package line names the model or the
engine, which leaves out the CLI:
[contains]
[contains] tidepool-model/src/main/java/io/tidepool/model/Constituent.java
[contains] tidepool-model/src/main/java/io/tidepool/model/Height.java
[contains] tidepool-model/src/main/java/io/tidepool/model/Station.java
[contains] tidepool-model/src/main/java/io/tidepool/model/TideEvent.java
[containsregexp]
[containsregexp] tidepool-engine/src/main/java/io/tidepool/engine/StationReader.java
[containsregexp] tidepool-engine/src/main/java/io/tidepool/engine/TideEngine.java
[containsregexp] tidepool-model/src/main/java/io/tidepool/model/Constituent.java
[containsregexp] tidepool-model/src/main/java/io/tidepool/model/Height.java
[containsregexp] tidepool-model/src/main/java/io/tidepool/model/Station.java
[containsregexp] tidepool-model/src/main/java/io/tidepool/model/TideEvent.java
The listing comes from a small show macro defined in the build file; it prints the sorted members of a fileset with
pathconvert, so only the file lists are real output of the selectors and the macro itself is plumbing.
Selecting by date, size, depth, name and type
date takes datetime in the format MM/dd/yyyy hh:mm a with the US locale unless pattern supplies another
SimpleDateFormat pattern, or an absolute millis. when defaults to equal, which is rarely what a build wants: write
before or after. granularity is the leeway in milliseconds for file systems with coarse timestamps and defaults to
zero, or two seconds on DOS-like systems.
size counts bytes. With units="k" the multiplier is 1000, with the IEC spelling units="Ki" it is 1024 (M/Mi,
G/Gi likewise). depth counts directory levels below the base directory of the fileset: a file directly in the base
is at depth 0. filename is the selector form of include and exclude, useful because it can sit inside a container
with other selectors; negate="true" turns it into an exclude. type chooses file or dir.
<fileset id="by.date" dir="." includes="tidepool-*/src/**/*.java">
<date datetime="01/01/2021 12:00 AM" when="before"/>
</fileset>
<fileset id="by.depth" dir="." includes="docs/**">
<depth max="1"/>
</fileset>
<fileset id="by.size" dir="." includes="tidepool-*/src/**/*.java">
<size value="1" units="Ki" when="more"/>
</fileset>
<fileset id="by.filename" dir="." includes="docs/**">
<filename name="**/*.png"/>
</fileset>
The fixture target had set the modification time of Height.java to the year 2020, so it is the only file date finds.
docs/index.txt is one level below the base directory, docs/img/logo.png two, so depth max="1" keeps only the
first. Only the CLI Main.java and StationReader.java are larger than 1024 bytes:
[date]
[date] tidepool-model/src/main/java/io/tidepool/model/Height.java
[depth]
[depth] docs/index.txt
[size]
[size] tidepool-cli/src/main/java/io/tidepool/cli/Main.java
[size] tidepool-engine/src/main/java/io/tidepool/engine/StationReader.java
[filename]
[filename] docs/img/logo.png
Selecting directories with type
A fileset that is used as a resource collection iterates files only, so a fileset with <type type="dir"/> looks empty
when printed. Tasks such as copy and delete ask the scanner for directories as well, and there type has an effect.
This copy creates the directories of docs and no files, because the selector only lets directories through:
<copy todir="build/dirs" includeemptydirs="true">
<fileset dir="docs">
<type type="dir"/>
</fileset>
</copy>
<dirset id="copied.dirs" dir="build/dirs"/>
<echo taskname="type" message="copied: ${toString:copied.dirs}"/>
[type] copied: ;img
The dirset lists the base directory itself (the empty entry before the ;) and img. The other way around, the
manual’s own example for type combines it with present inside an and, to select files that also exist in a
template directory without selecting the empty directories that present would otherwise let through.
Source: Apache Ant manual: Type Selector
Comparing with another tree: depend, present and different
Three selectors answer "does the other tree already have this?" and are the building blocks of hand-written incremental
builds. Each takes a targetdir and an optional nested mapper (the identity mapper when absent) that turns a source
name into the name to look for in targetdir. See Filters & Mappers for the mappers.
-
dependselects a file that is newer than its counterpart, or that has no counterpart. A source with a stale class file is selected, a source with an up-to-date class file is not. -
presentselects withpresent="both"(the default) the files that have a counterpart, and withpresent="srconly"those that do not.srconlyis the same as wrapping the selector innot. -
differentselects a file that has no counterpart, has a different length, or (unlessignoreContents="true") differs byte for byte. By default timestamps are ignored (ignoreFileTimes="true"); set it to false to treat a different timestamp as a difference. Files that exist only intargetdirare ignored.
The fixture target gives Station.class a timestamp in 2020 (older than Station.java), makes Height.class newer than
Height.java, and creates no class file for the other two sources. For different it copies the model sources to
build/snapshot, appends a line to the snapshot’s Height.java and gives the snapshot’s TideEvent.java a timestamp in
the future.
<fileset id="stale" dir="tidepool-model/src/main/java" includes="**/*.java">
<depend targetdir="${classes}">
<mapper type="glob" from="*.java" to="*.class"/>
</depend>
</fileset>
<fileset id="uncompiled" dir="tidepool-model/src/main/java" includes="**/*.java">
<present present="srconly" targetdir="${classes}">
<mapper type="glob" from="*.java" to="*.class"/>
</present>
</fileset>
<fileset id="differs" dir="tidepool-model/src/main/java" includes="**/*.java">
<different targetdir="build/snapshot"/>
</fileset>
<fileset id="differs.times" dir="tidepool-model/src/main/java" includes="**/*.java">
<different targetdir="build/snapshot" ignoreFileTimes="false" ignoreContents="true"/>
</fileset>
[depend]
[depend] tidepool-model/src/main/java/io/tidepool/model/Constituent.java
[depend] tidepool-model/src/main/java/io/tidepool/model/Station.java
[depend] tidepool-model/src/main/java/io/tidepool/model/TideEvent.java
[present]
[present] tidepool-model/src/main/java/io/tidepool/model/Constituent.java
[present] tidepool-model/src/main/java/io/tidepool/model/TideEvent.java
[different]
[different] tidepool-model/src/main/java/io/tidepool/model/Height.java
[different-times]
[different-times] tidepool-model/src/main/java/io/tidepool/model/Height.java
[different-times] tidepool-model/src/main/java/io/tidepool/model/TideEvent.java
depend skipped Height.java (its class file is newer) and selected the stale Station.java and the two sources
without a class file. present="srconly" found only the two sources without a class file. different with its defaults
reports only the file whose content changed; with ignoreFileTimes="false" and ignoreContents="true" it also reports
TideEvent.java, whose content is identical but whose timestamp is not, and Height.java, whose length is different.
different exists for the cases the other two cannot handle: a predecessor that always rewrites its output (a download,
a generator), where a timestamp comparison would always say "newer". Comparing content makes the dependency depend on the
absolute state of the files. Setting preservelastmodified="true" on a copy that uses it keeps the timestamps equal so
later comparisons stay cheap.
The modified selector
modified is the only selector with state. For each file it computes a value with an algorithm, reads the value stored
for the file’s absolute path in a cache, compares the two with a comparator, updates the cache when they differ and
selects the file when they differ. The defaults are the digest algorithm (MD5), the propertyfile cache (a
cache.properties file in the project’s base directory, also when Ant is started from another directory with -f) and the equal comparator. The other algorithms are hashvalue (the
hashCode of the content as a String), checksum (CRC or ADLER) and lastmodified; custom algorithm, cache and
comparator classes are set with algorithmclass, cacheclass and comparatorclass and found through a nested
classpath. The rule comparator (java.text.RuleBasedCollator) is documented as unusable: its manual section says
you should not use it because of a problem in its initialization.
Attributes can also be given as nested param elements, including the algorithm and cache specific ones such as
algorithm.algorithm (MD5, SHA) and cache.cachefile. The cache is written when the first build event after the change
arrives (delayupdate defaults to true); set delayupdate="false" to store after every change. update="false" makes
the selector read-only. Because the cache is keyed by absolute path, a cache file copied to another checkout
selects everything again.
<fileset id="changed" dir="docs">
<modified>
<param name="cache.cachefile" value="build/cache.properties"/>
</modified>
</fileset>
<copy todir="build/site"><fileset refid="changed"/></copy>
<resourcecount property="first.count"><fileset dir="build/site"/></resourcecount>
<echo taskname="modified" message="first run copied ${first.count} files"/>
<echo file="docs/index.txt" message="Tidepool 4.3 notes"/>
<copy todir="build/site2"><fileset refid="changed"/></copy>
<pathconvert property="second.list" pathsep=",">
<fileset dir="build/site2"/>
<mapper type="flatten"/>
</pathconvert>
<echo taskname="modified" message="after editing index.txt: ${second.list}"/>
<loadfile property="cache.text" srcfile="build/cache.properties"/>
<echo taskname="cache" message="${cache.text}"/>
[modified] first run copied 3 files
[modified] after editing index.txt: index.txt
[cache] #Fri Oct 16 11:35:15 CEST 2026
[cache] <scratch>/docs/img/logo.png=bff139fa05ac583f685a523ab3d110a0
[cache] <scratch>/docs/img/photo.jpg=c36bbd258b7ee694eb987221b2b197b0
[cache] <scratch>/docs/index.txt=e35fd0c0166481310eaba8aaaed68d0a
The first pass found no cache, so every file differed and all three were copied. After index.txt was edited, only that
file was selected, and the cache now holds the MD5 digests. The classic use is uploading only changed files to a
server (ftp, scp) from a generated site. modified can also be a resource selector inside restrict; for a
resource that is not a file it first copies the content to a temporary file.
Selecting by file-system attributes
readable, writable, executable, symlink, ownedBy, posixGroup, posixPermissions and windowsjunction ask
the file system through java.io.File or java.nio.file.Files and select the file only when the JVM can tell. If the
platform does not support the question (POSIX attributes on Windows, for instance) they select nothing, except readable
and writable, which the manual says still select a file when the JVM cannot detect that it is unreadable or
unwritable. ownedBy, posixGroup and posixPermissions take followsymlinks (default true).
posixPermissions accepts either the nine-character form (rwxr-xr-x) or octal (755). Note that rw------- and 600
describe exactly one permission set, so a file with rw-r--r-- is not selected by either. A fileset with
followsymlinks="false" does not return symbolic links at all, so combining it with symlink selects nothing; leave
followsymlinks at its default to find links.
<chmod file="build/fs/run.sh" perm="755"/>
<chmod file="build/fs/notes.txt" perm="644"/>
<chmod file="build/fs/secret.key" perm="600"/>
<symlink link="build/fs/latest.txt" resource="notes.txt" failonerror="true"/>
<fileset id="exec" dir="build/fs"><executable/></fileset>
<fileset id="perm600" dir="build/fs"><posixPermissions permissions="rw-------"/></fileset>
<fileset id="perm644" dir="build/fs"><posixPermissions permissions="644" followsymlinks="false"/></fileset>
<fileset id="links" dir="build/fs"><symlink/></fileset>
<fileset id="mine" dir="build/fs"><ownedBy owner="${user.name}"/></fileset>
<fileset id="rw" dir="build/fs"><and><readable/><writable/></and></fileset>
[executable]
[executable] build/fs/run.sh
[posixPermissions]
[posixPermissions] build/fs/secret.key
[octal-nofollow]
[octal-nofollow] build/fs/notes.txt
[symlink]
[symlink] build/fs/latest.txt
[ownedBy] files owned by the current user: 4
[readable+writable] 4 files
latest.txt is a link to notes.txt. With the default followsymlinks="true" the selector sees the permissions of the
target, so a separate run with permissions="644" and no followsymlinks listed both latest.txt and notes.txt. With
followsymlinks="false" it looks at the link itself and does not select it, so octal-nofollow lists only notes.txt. windowsjunction (Ant 1.10.16) and signedselector were not run here: the first needs Windows, the second a
signed JAR.
Container selectors
A container combines the answers of the selectors nested in it. Containers can nest to any depth.
| Container | Selects a file when |
|---|---|
|
every contained selector selects it (stops at the first that does not) |
|
at least one contained selector selects it (stops at the first that does) |
|
the single contained selector does not select it |
|
no contained selector selects it (stops at the first that does) |
|
most contained selectors select it; |
|
the single contained selector selects it, provided its |
Because and, or and none stop early, put cheap selectors (filename, depth, size) before expensive ones
(contains, containsregexp) so a file is rarely opened.
<fileset id="and" dir="." includes="tidepool-*/src/**/*.java">
<and>
<filename name="tidepool-model/**"/>
<contains text="record"/>
<size value="150" when="more"/>
</and>
</fileset>
<fileset id="or" dir="docs">
<or>
<depth max="0"/>
<filename name="**/*.png"/>
</or>
</fileset>
<fileset id="not" dir="." includes="tidepool-*/src/**/*.java">
<not><filename name="tidepool-model/**"/></not>
</fileset>
<fileset id="none" dir="." includes="tidepool-*/src/**/*.java">
<none>
<filename name="tidepool-model/**"/>
<filename name="tidepool-cli/**"/>
</none>
</fileset>
<fileset id="majority" dir="." includes="tidepool-*/src/**/*.java">
<majority>
<contains text="record"/>
<filename name="tidepool-engine/**"/>
<size value="1000" when="more"/>
</majority>
</fileset>
[and]
[and] tidepool-model/src/main/java/io/tidepool/model/Constituent.java
[and] tidepool-model/src/main/java/io/tidepool/model/Station.java
[and] tidepool-model/src/main/java/io/tidepool/model/TideEvent.java
[or]
[or] docs/img/logo.png
[or] docs/index.txt
[not]
[not] tidepool-cli/src/main/java/io/tidepool/cli/Main.java
[not] tidepool-engine/src/main/java/io/tidepool/engine/StationReader.java
[not] tidepool-engine/src/main/java/io/tidepool/engine/TideEngine.java
[none]
[none] tidepool-engine/src/main/java/io/tidepool/engine/StationReader.java
[none] tidepool-engine/src/main/java/io/tidepool/engine/TideEngine.java
[majority]
[majority] tidepool-engine/src/main/java/io/tidepool/engine/StationReader.java
Height.java is only 118 bytes, so the and excludes it. Only StationReader.java satisfies two of the three
majority conditions (it is in the engine and is larger than 1000 bytes). Of the three, TideEngine.java is in the
engine but is 916 bytes, and the model records contain record but fail the other two.
Selector references and conditional selection
The selector element wraps one selector (which can be a container) and gives it an id, so it can be defined once at
project level and reused with <selector refid="..."/> in any fileset. It also takes if and unless, the same property
tests as on targets and on include/exclude in a patternset, which makes the choice of files depend on the properties
set when Ant starts: -Dwith.cli=true adds the CLI sources below, -Dwithout.engine=true removes the engine.
<selector id="large.model">
<and>
<filename name="tidepool-model/**"/>
<size value="200" when="more"/>
</and>
</selector>
<fileset id="viaref" dir="." includes="tidepool-*/src/**/*.java">
<selector refid="large.model"/>
</fileset>
<fileset id="viaif" dir="." includes="tidepool-*/src/**/*.java">
<or>
<selector if="with.cli"><filename name="tidepool-cli/**"/></selector>
<selector unless="without.engine"><filename name="tidepool-engine/**"/></selector>
</or>
</fileset>
[refid]
[refid] tidepool-model/src/main/java/io/tidepool/model/Station.java
[if-unless]
[if-unless] tidepool-engine/src/main/java/io/tidepool/engine/StationReader.java
[if-unless] tidepool-engine/src/main/java/io/tidepool/engine/TideEngine.java
With neither property set, if="with.cli" is false and unless="without.engine" is true, so only the engine is selected.
Only Station.java (231 bytes) passes the size test of the shared selector.
Custom selectors
custom plugs in your own class that implements org.apache.tools.ant.types.selectors.FileSelector, configured through
nested param elements. Its attributes are classname (required), classpath and classpathref; with neither
classpath the class must be on Ant’s own classpath. A few core selectors can be used through custom with their
attributes written as param elements: contains, date, depth, filename and size, plus modified. The depth
example below uses it with the core class, which proves the mechanism without compiling anything:
<fileset id="custom.depth" dir="." includes="docs/**">
<custom classname="org.apache.tools.ant.types.selectors.DepthSelector">
<param name="max" value="1"/>
</custom>
</fileset>
[custom]
[custom] docs/index.txt
This is the same result as <depth max="1"/> above. Writing the Java class, extending BaseExtendSelector and
registering a selector with typedef are covered in
Custom Types, Conditions & Selectors.
The script selector
scriptselector runs a script for every candidate file, in any language that Apache BSF or JSR 223 supports. The script
sees the bean self (call self.setSelected(true) to select the file), filename, file and basedir, plus the usual
project, properties and targets. If src is not set, the script is the element’s text. The Ant manual’s example
selects the files with an even-length name:
<fileset id="even" dir="docs" includes="**">
<scriptselector language="javascript">
self.setSelected((filename.length%2)==0);
</scriptselector>
</fileset>
This was not run successfully here: Ant 1.10.18 on JDK 26 has no JavaScript engine, because Java 15 removed Nashorn, and the build stops with the message below. A script engine such as GraalVM JavaScript (or any other JSR 223 language) must be added to Ant’s classpath first; see Installation for where libraries go and Scripting for the script task, its engines and the security implications.
Java 15 has removed Nashorn, you must provide an engine for running JavaScript yourself. GraalVM JavaScript currently is the preferred option.
BUILD FAILED
<scratch>/script.xml:8: Unable to create javax script engine for javascript
Equivalents in Maven and Gradle
Ant is the only one of the three tools whose file collections accept arbitrary predicates as elements; Maven and Gradle reduce the question to patterns and, in Gradle, to code.
| Ant | Maven | Gradle |
|---|---|---|
|
|
|
|
no equivalent in the core; a plugin with its own options, or the |
a |
|
the compiler and resources plugins do their own staleness detection; no general selector |
task input and output tracking (see Incremental Build) replaces the hand-written comparison |
An Ant selector that has no equivalent is where the usual migration advice applies: in Gradle, the build script is code, so the predicate is a lambda; in Maven, the cleanest route is a small plugin or a profile that changes the pattern lists. The selector-by-selector view is in Migrating Between Tools.
Classic mistakes
-
Selecting nothing because of the default
when.datewithdatetimebut nowhenselects only files modified at exactly that moment. -
Expecting a selector to add files.
filenameinside a fileset narrows whatincludesalready found. Useincludesto widen. -
A cache left in the source tree.
modifiedwritescache.propertiesinto the project’s base directory by default (checked here withant -f ../proj/build.xmlfrom another directory), next to the build file, where it survives acleanand is easy to commit by accident. Setcache.cachefileto a file underbuild/, as the example above does. -
Opening every file. A
containsplaced first in anandororreads files that afilenametest would have rejected without opening them. -
Time stamps on coarse file systems.
date,dependanddifferentneedgranularitywhere a file system stores modification times with a resolution of two seconds. -
Windows and POSIX selectors.
posixGroupandposixPermissionsneed a POSIX file system; on Windows they select nothing, silently.