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:

  1. The includes/excludes patterns run first. A selector can only remove files that the patterns already matched; a filename selector cannot add files back.

  2. All the selectors directly nested in a fileset are combined with a logical AND, as if they were wrapped in and.

  3. 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 includes and 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

contains

contain a string

text (required), casesensitive (default true), ignorewhitespace (default false), encoding

encoding 1.9.0

containsregexp

contain a match of a regular expression

expression (required), casesensitive, multiline, singleline (all default false except casesensitive)

the three flags 1.8.2

date

were modified before, after or at a date

datetime or millis (one is required), when (before, after, equal; default equal), granularity, pattern, checkdirs

pattern 1.6.2

depend

are newer than the file a mapper derives from them

targetdir (required), granularity, a nested mapper

depth

are a number of directory levels below the base

min, max (at least one)

different

differ from the equivalent file elsewhere

targetdir (required), ignoreFileTimes (default true), ignoreContents (default false), granularity, a nested mapper

ignoreContents 1.6.3

filename

have a name matching a pattern or regular expression

name or regex (exactly one), casesensitive, negate

present

do (both) or do not (srconly) exist in another tree

targetdir (required), present (default both), a nested mapper

size

are larger, smaller or exactly a size

value (required), units, when (less, more, equal; default equal)

type

are regular files or directories

type (file or dir, required)

modified

have a value that differs from the one stored in a cache

algorithm, cache, comparator, update, seldirs, selres, delayupdate

signedselector

are signed, optionally with a given signature name

name

1.7

readable, writable

the JVM reports as readable or writable

executable

java.nio.file.Files#isExecutable reports as executable

1.10.0

symlink

are symbolic links

1.10.0

ownedBy

are owned by a user

owner (required), followsymlinks (default true)

1.10.0

posixGroup

belong to a POSIX group

group (required), followsymlinks

1.10.4

posixPermissions

have given POSIX permissions

permissions (rwxr-xr-x or octal 755, required), followsymlinks

1.10.4

windowsjunction

are directories that are Windows junctions

1.10.16

scriptselector

a script selects

language (required), manager, src, encoding, setbeans, classpath, classpathref

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.

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.

  • depend selects 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.

  • present selects with present="both" (the default) the files that have a counterpart, and with present="srconly" those that do not. srconly is the same as wrapping the selector in not.

  • different selects a file that has no counterpart, has a different length, or (unless ignoreContents="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 in targetdir are 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

and

every contained selector selects it (stops at the first that does not)

or

at least one contained selector selects it (stops at the first that does)

not

the single contained selector does not select it

none

no contained selector selects it (stops at the first that does)

majority

most contained selectors select it; allowtie (default true) decides when exactly half do

selector

the single contained selector selects it, provided its if/unless conditions hold; the element that can also appear at project level

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

filename, depth, patterns

includes/excludes lists in plugin configuration, for example in the resources plugin; see Resources & Filtering

include, exclude and the PatternFilterable interface on fileTree, copySpec and Copy; see Working with Files

contains, date, size, type, readable, executable

no equivalent in the core; a plugin with its own options, or the antrun plugin to run the Ant selector

a Spec<FileTreeElement> passed to include/exclude, or FileTree.matching { } and filter, written in the build script’s language

depend, different, present, modified

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. date with datetime but no when selects only files modified at exactly that moment.

  • Expecting a selector to add files. filename inside a fileset narrows what includes already found. Use includes to widen.

  • A cache left in the source tree. modified writes cache.properties into the project’s base directory by default (checked here with ant -f ../proj/build.xml from another directory), next to the build file, where it survives a clean and is easy to commit by accident. Set cache.cachefile to a file under build/, as the example above does.

  • Opening every file. A contains placed first in an and or or reads files that a filename test would have rejected without opening them.

  • Time stamps on coarse file systems. date, depend and different need granularity where a file system stores modification times with a resolution of two seconds.

  • Windows and POSIX selectors. posixGroup and posixPermissions need a POSIX file system; on Windows they select nothing, silently.