Ant Resource Collections
|
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.
The fileset, path and filelist types of Paths,
Filesets & Patterns all describe files. Ant 1.7 generalised that: a resource is any file-like thing, with a name and, where possible,
a way to read and write its content and to ask when it was last modified and how big it is. A resource collection is
a group of resources. The design rationale is that a task written once against "a collection of resources" works for
files, entries inside archives, URLs, strings and Java constants alike, so copy can copy a ZIP entry and concat can
print a property. The concept was introduced in Ant 1.5.2 and made available for explicit use in Ant 1.7; the books and
tutorials of the Ant 1.6 era describe only the file-based types.
The examples use the tidepool scenario’s data/stations.csv and the tidepool-model sources, described on the
section landing page. The build file declares the two
internal antlib namespaces that the resource selectors and comparators live in; the prefixes rsel and rcmp below are
the ones used in the Ant manual:
<project name="resources-demo" default="all" basedir="."
xmlns:rsel="antlib:org.apache.tools.ant.types.resources.selectors"
xmlns:rcmp="antlib:org.apache.tools.ant.types.resources.comparators">
Source: Apache Ant manual: Resources
Resources
A resource that is not a file cannot be handed to a task that only works on the file system (javac, exec) but is
accepted wherever a task takes a resource. Every resource type is also a one-element resource collection, so it can be
nested wherever a collection is expected. The built-in types:
| Resource | Represents | Key attributes |
|---|---|---|
|
a basic resource, base of the others |
|
|
a file on the local file system |
|
|
a resource loadable through a Java class loader |
|
|
the value of a |
|
|
an entry of a ZIP archive |
|
|
an entry of a TAR archive |
|
|
a wrapper that decompresses (when read) or compresses (when written) one nested resource on the fly |
a single nested resource collection; |
|
a URL |
exactly one of |
|
a Java string; writable once |
|
|
an Ant property |
|
Source: Apache Ant manual: resource, javaresource, javaconstant, zipentry, tarentry, gzipresource, url, string, propertyresource
Reading resources of different kinds
concat reads its nested resources in order and writes them to the console, so it shows what each resource contains.
The first block mixes a string, a property, a Java constant and a resource from Ant’s own class loader; the second reads
a file through a url and cuts it to two lines with a filterchain.
<property name="tide.region" value="north-atlantic"/>
<concat>
<string value="a string: tidepool${line.separator}"/>
<propertyresource name="tide.region"/>
<string value="${line.separator}"/>
<javaconstant name="org.apache.tools.ant.Main.DEFAULT_BUILD_FILENAME"/>
<string value="${line.separator}"/>
<javaresource name="org/apache/tools/ant/version.txt"/>
</concat>
<concat>
<url file="data/stations.csv"/>
<filterchain><headfilter lines="2"/></filterchain>
</concat>
[concat] a string: tidepool
[concat] north-atlantic
[concat] build.xml
[concat] VERSION=1.10.18
[concat] DATE=September 3 2026
[concat] id,name,latitude,longitude,constituents
[concat] SF,San Francisco,37.8063,-122.4659,M2:0.55:120;S2:0.13:150;K1:0.38:210
javaconstant found DEFAULT_BUILD_FILENAME of Ant’s Main class (build.xml), and javaresource found the
version.txt inside ant.jar, which also reports the version this page was written for. A javaconstant is typically
used with loadresource to read a version number out of a class on a classpath, as in the manual’s example.
Source: Apache Ant manual: javaconstant
Archive entries and compressed resources
zipentry and tarentry name one entry inside an archive without extracting it. The archive is given by an attribute or
by a nested single-element collection; zipentry supports only file system resources there. The example builds a JAR
and a TAR first, and then prints one entry of each:
<jar destfile="build/model.jar" basedir="tidepool-model/src/main/java" includes="**/Station.java"/>
<tar destfile="build/data.tar"><tarfileset dir="data" prefix="data"/></tar>
<gzip src="build/data.tar" destfile="build/data.tar.gz"/>
<concat>
<zipentry zipfile="build/model.jar" name="io/tidepool/model/Station.java"/>
</concat>
<concat>
<tarentry archive="build/data.tar" name="data/stations.csv"/>
<filterchain><headfilter lines="1"/></filterchain>
</concat>
[concat] package io.tidepool.model;
[concat]
[concat] import java.util.List;
[concat]
[concat] /** A coastal tide station with its harmonic constituents. */
[concat] public record Station(String id, String name, double latitude, double longitude, List<Constituent> constituents) {
[concat] }
[concat] id,name,latitude,longitude,constituents
gzipresource, bzip2resource and xzresource wrap one resource and transform its stream: reading decompresses,
writing compresses. Here the gzip file made above is copied through a gzipresource, which writes the decompressed
TAR:
<copy tofile="build/data-unpacked.tar">
<gzipresource><file file="build/data.tar.gz"/></gzipresource>
</copy>
<length property="tar.size" file="build/data.tar"/>
<length property="gz.size" file="build/data.tar.gz"/>
<length property="unpacked.size" file="build/data-unpacked.tar"/>
<echo taskname="gzipresource" message="data.tar ${tar.size} bytes, data.tar.gz ${gz.size} bytes, read back through gzipresource ${unpacked.size} bytes"/>
[gzipresource] data.tar 10240 bytes, data.tar.gz 293 bytes, read back through gzipresource 10240 bytes
Getting the direction wrong is the classic error. A first attempt of this example wrapped the plain CSV file in a
gzipresource as the source of a copy, expecting a compressed copy; reading decompresses, so the wrapper tried to
decompress a text file:
BUILD FAILED
<scratch>/build.xml:46: Failed to copy GZip compressed stations.csv to <scratch>/build/stations.csv.gz due to java.util.zip.ZipException Not in GZIP format
xzresource (Ant 1.10.1) needs the XZ library on Ant’s classpath; the list of optional libraries is in
Installation and was not exercised here, nor was bzip2resource
(it follows the same pattern).
Resource collections
Several older datatypes are resource collections: fileset, dirset, multirootfileset, filelist and path expose
file resources; tarfileset and zipfileset expose either files or tarentry/zipentry resources; propertyset
exposes property resources. Even a task can act as one: concat exposes the concatenated result as a resource. The
additional collections of Ant 1.7 and later are the subject of the rest of this page.
resources and union
resources is a generic collection, mainly for holding a reference to a collection of unknown kind (for example the
result of a third-party task) and as a container that keeps the order and the duplicates of its members. union is the
set form of the same idea: it removes duplicates. cache (since 1.8.0, default false on resources) stores the
iteration result.
<resources id="twice">
<string value="x"/>
<string value="y"/>
<string value="x"/>
</resources>
<union id="once"><resources refid="twice"/></union>
<echo taskname="resources" message="${toString:twice}"/>
<echo taskname="union" message="${toString:once}"/>
<resourcecount property="n.twice"><resources refid="twice"/></resourcecount>
<resourcecount property="n.once"><union refid="once"/></resourcecount>
<echo taskname="count" message="resources=${n.twice} union=${n.once}"/>
[resources] x:y:x
[union] x:y
[count] resources=3 union=2
The ${toString:...} property prefix prints a collection joined by the path separator; see
Properties.
Source: Apache Ant manual: resources
files
files is a fileset without a base directory: its includes/excludes patterns are absolute and the file system is
scanned from the patterns alone, so a selector or a filename test can only act after the pattern has matched. Nested
file selectors are combined as with and. It accepts includes, includesfile, excludes, excludesfile,
defaultexcludes, casesensitive and followsymlinks (Windows junctions are detected since Ant 1.10.16), and at least one
include is required. Prefer fileset whenever a base directory exists; files is for patterns that really are absolute.
<files id="abs" includes="${basedir}/data/*.csv ${basedir}/tidepool-model/**/S*.java"/>
<pathconvert property="abs.list" refid="abs" pathsep="${line.separator} ">
<map from="${basedir}${file.separator}" to=""/>
</pathconvert>
<echo taskname="files" message="${line.separator} ${abs.list}"/>
[files]
[files] data/stations.csv
[files] tidepool-model/src/main/java/io/tidepool/model/Station.java
Source: Apache Ant manual: files
restrict: resource selectors
restrict narrows one nested collection with nested resource selectors, combined with AND. They are patterned after the
file selectors of Selectors but work on resources of any kind, and live in the
antlib org.apache.tools.ant.types.resources.selectors, so they need the namespace prefix declared above (or a
xmlns on the element). cache (default true) stores the result; disabling it can hurt performance seriously.
| Resource selector | Selects resources that |
|---|---|
|
match a pattern ( |
|
exist |
|
were modified relative to |
|
are |
|
compare to |
|
are an instance of a |
|
combine nested resource selectors, as the containers of the file selectors; |
|
are the file selectors of the same name, usable here as resource selectors |
|
are files (the resource must be a file) that are readable or writable |
|
compare to the resources of a nested |
The four examples select from the three model records, using the fixture state in which Height.java has a modification
time in 2020 and is the smallest file (118 bytes):
<restrict id="small">
<fileset refid="models"/>
<rsel:size size="160" when="lt"/>
</restrict>
<restrict id="old">
<fileset refid="models"/>
<rsel:date datetime="01/01/2021 12:00 AM" when="before"/>
</restrict>
<restrict id="named">
<fileset refid="models"/>
<rsel:or>
<rsel:name name="**/S*.java"/>
<rsel:name regex=".*Height.*"/>
</rsel:or>
</restrict>
<filelist id="expected" dir="${basedir}" files="data/stations.csv,data/harmonics.csv,build/model.jar,build/model.war"/>
<restrict id="missing">
<filelist refid="expected"/>
<rsel:not><rsel:exists/></rsel:not>
</restrict>
[size] Height.java
[date] Height.java
[name] Height.java, Station.java
[missing] data/harmonics.csv, build/model.war
The last example is the idiom for "which of the files I expect are not there?": a filelist of expected names, filtered
with not and exists. It listed the two files the build had not produced. The models fileset is
<fileset id="models" dir="tidepool-model/src/main/java" includes="**/*.java"/>; the lists above were printed with
pathconvert and a flatten mapper.
compare keeps the resources whose comparison against the control resources holds, using a comparator to order them.
The control resource is named, not a path, because the compared name of a fileset entry is relative to the fileset’s
base directory:
<restrict id="notnewer">
<fileset refid="models"/>
<rsel:compare when="le" against="all">
<control><resource name="io/tidepool/model/Station.java"/></control>
<rcmp:name/>
</rsel:compare>
</restrict>
[compare] by name, less or equal to Station.java: Constituent.java, Height.java, Station.java
An earlier version of this example used <file file="…/Station.java"/> as control. It selected nothing, because the name of
a file resource is its absolute path, which sorts before every relative name. Comparing against the right kind of
resource is the whole trick.
instanceof filters a mixed collection by kind. The union below holds a string, a file and a ZIP entry; the selector
keeps what is a FileResource:
<restrict id="onlyfiles">
<union>
<string value="not a file"/>
<file file="data/stations.csv"/>
<zipentry zipfile="build/model.jar" name="io/tidepool/model/Station.java"/>
</union>
<rsel:instanceof class="org.apache.tools.ant.types.resources.FileResource"/>
</restrict>
[instanceof] data/stations.csv
Source: Apache Ant manual: restrict, compare, name
sort, first, last, allbutfirst and allbutlast
sort orders a nested collection by the resources' natural order, or by one or more nested resource comparators from the
antlib org.apache.tools.ant.types.resources.comparators. With several comparators the first written is the outermost
sort key. The comparators are:
| Comparator | Orders resources by |
|---|---|
|
name |
|
existence; not existing is "less than" existing |
|
modification time |
|
type; directories are "greater" than files |
|
size |
|
content; |
|
reverses the natural order, or that of one nested comparator |
first and last (the latter since Ant 1.7.1) keep the first or last count resources of a nested collection (default 1),
allbutfirst and allbutlast (both since Ant 1.9.5) keep everything else. Together with sort they answer "the three
largest", "all but the newest five" and so on, which is how a build prunes old files.
<sort id="bysize">
<fileset refid="models"/>
<rcmp:size/>
</sort>
<sort id="bydate">
<fileset refid="models"/>
<rcmp:reverse><rcmp:date/></rcmp:reverse>
</sort>
<first id="newest" count="1"><sort refid="bydate"/></first>
<last id="oldest" count="1"><sort refid="bydate"/></last>
<allbutfirst id="notnewest" count="1"><sort refid="bydate"/></allbutfirst>
<allbutlast id="notoldest" count="3"><sort refid="bydate"/></allbutlast>
[sort-size] Height.java, TideEvent.java, Constituent.java, Station.java
[first] Constituent.java
[last] Height.java
[allbutfirst] Station.java, TideEvent.java, Height.java
[allbutlast] Constituent.java
Sorted by size ascending the order is Height (118 bytes), TideEvent (163), Constituent (182), Station (231).
Sorted by date descending, Constituent.java is the newest and Height.java (year 2020) the oldest.
Source: Apache Ant manual: sort, Resource Comparators, first, last, allbutfirst
tokens: lines and words as resources
tokens turns the content of a nested collection into one string resource per token, using the tokenizers of the
tokenfilter (a line tokenizer by default; see Filters & Mappers). Its
encoding defaults to the JVM default. Together with sort, union and restrict it reimplements several Unix tools
on a collection. The examples sort a string’s words, and run sort -u, wc -l and grep -c against a five-line file
(tide, wave, tide, swell, wave):
<pathconvert property="sorted.tokens" pathsep=",">
<sort><tokens><string value="foo bar etc baz"/><stringtokenizer/></tokens></sort>
</pathconvert>
<echo taskname="tokens" message="${sorted.tokens}"/>
<concat taskname="sort-u">
<union>
<sort>
<tokens><file file="build/lines.txt"/><linetokenizer includedelims="true"/></tokens>
</sort>
</union>
</concat>
<resourcecount property="wc.l"><tokens><file file="build/lines.txt"/></tokens></resourcecount>
<resourcecount property="grep.c">
<restrict><tokens><file file="build/lines.txt"/></tokens><rsel:contains text="wave"/></restrict>
</resourcecount>
<echo taskname="wc-l" message="${wc.l}"/>
<echo taskname="grep-c" message="${grep.c}"/>
[tokens] bar,baz,etc,foo
[sort-u] swell
[sort-u] tide
[sort-u] wave
[wc-l] 5
[grep-c] 2
union is what removes the duplicates in the sort -u equivalent. A first attempt at grep -c nested a containsstring
string filter in tokens; the filters belong to tokenfilter, not to tokens, and the build failed with:
BUILD FAILED
<scratch>/build.xml:194: tokens doesn't support the nested "containsstring" element.
The fix is the one shown: restrict the tokens with the contains resource selector.
Set operations: union, intersect and difference
union, intersect and difference combine the nested collections as sets. They take refid and cache (default
true).
<resources id="A"><string value="a"/><string value="b"/></resources>
<resources id="B"><string value="b"/><string value="c"/></resources>
<union id="u"><resources refid="A"/><resources refid="B"/></union>
<intersect id="i"><resources refid="A"/><resources refid="B"/></intersect>
<difference id="d"><resources refid="A"/><resources refid="B"/></difference>
[A] a:b
[B] b:c
[union] a:b:c
[intersect] b
[difference] a:c
difference keeps the elements that occur in only one of the nested collections (a and c), not the elements of A that are
missing from B, which would be only a; the manual’s expected output (a;c) shows the same.
mappedresources, archives and resourcelist
mappedresources (Ant 1.8.0) wraps a collection and renames its resources with a nested mapper (the identity mapper when
none is given). The result is not file-system based, even if the wrapped collection is, so tasks that need files cannot
take it. enablemultiplemappings (since 1.8.1, default false) uses all the target names a mapper produces, and
cache (since 1.8.1, default false) stores the result. Compared with a mapper nested in copy, it can be used wherever a
collection can: in a zip, a war, or inside another collection.
archives (Ant 1.8.0) treats its nested resources as archives and returns their contents. Its nested zips and
tars are unions: everything in zips is read as ZIP, everything in tars as TAR. It generalises zipgroupfileset,
which only the zip family of tasks understands.
resourcelist (Ant 1.8.0) reads its nested resources as lists and returns a resource per line. A line containing a
colon is tried as a URL and otherwise becomes a file name; properties in the lines are expanded. It generalises
filelist, and supports encoding, basedir (since 1.10.4, to resolve relative names), preserveduplicates (since 1.10.10)
and nested filterchain elements that modify the text before it is split into lines.
<copy todir="build/bak">
<mappedresources>
<fileset dir="data"/>
<globmapper from="*" to="*.bak"/>
</mappedresources>
</copy>
<copy todir="build/unpacked">
<archives>
<zips><file file="build/model.jar"/></zips>
<tars><file file="build/data.tar"/></tars>
</archives>
</copy>
<echo file="build/candidates.txt">build/missing.csv${line.separator}data/stations.csv${line.separator}build/data.tar${line.separator}</echo>
<copy todir="build/first">
<first>
<restrict>
<resourcelist><file file="build/candidates.txt"/></resourcelist>
<rsel:exists/>
</restrict>
</first>
</copy>
[mappedresources] stations.csv.bak
[archives] META-INF/MANIFEST.MF;data/stations.csv;io/tidepool/model/Station.java
[resourcelist] stations.csv
The output lines list the files that copy created in each target directory. archives extracted the contents of both
the JAR (including the manifest jar added) and the TAR in one copy. The last example is the manual’s "first reachable
mirror" pattern: candidates.txt lists a file that does not exist, then two that do, and first keeps the first
existing one, so only stations.csv is copied. The manual applies the same shape to URLs on a mirror list; fetching from
real mirrors was not run here.
Equivalents in Maven and Gradle
| Ant | Maven | Gradle |
|---|---|---|
|
|
|
|
the dependency and assembly plugins unpack archives |
|
|
archive plugins |
|
|
none; the lists are composed in the POM |
|
|
none |
ordinary Kotlin or Groovy collection code |
The structural difference is that Gradle collections are lazy and typed objects that can be wired as task inputs, so a Gradle build rarely needs the Ant idea of a collection that is also a resource. The Ant-to-Gradle bridge that runs these types is described in Ant Integration.
Classic mistakes
-
Reading the wrapper backwards. A
gzipresourcedecompresses what it reads; it is shown above with the realNot in GZIP formaterror. -
Forgetting the namespace. The resource selectors and comparators are in internal antlibs. Without the
xmlns:rselandxmlns:rcmpdeclarations,<rsel:size/>is an undefined element. -
Expecting
mappedresourcesto be a file set. It maps names, so it can be copied or archived but not passed to a task that needs the file system. -
Using
tokensfilters as nested elements. The tokenizers belong insidetokens, the string filters insidetokenfilter. -
Comparing against the wrong kind of control. A file resource is named by its absolute path, a fileset entry by its relative path.
-
A
filescollection with a relative pattern.fileshas no base directory; use absolute patterns or preferfileset.