Paths, Filesets & Patterns

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 and JDK 26. Every example was run on 2026-10-16 in a scratch copy of the tidepool source tree (the three modules tidepool-model, tidepool-engine and tidepool-cli, the data file data/stations.csv and a lib directory with commons-csv, commons-io, commons-codec and picocli). The build files are run from the root of that tree. Paths in the console output were shortened to /work/tidepool, and the Buildfile:, BUILD SUCCESSFUL and Total time: lines are left out of the listings unless they matter. Maven 3.10.0 and Gradle 9.8.1 are named where they do the same job.

Almost everything an Ant build does is "take these files and do something with them", so the way Ant names a set of files is the most reused part of the language. This page covers the whole family: the path-like structures that carry class paths, the patterns that select files, and the resource collections built from them (fileset, dirset, filelist, zipfileset, tarfileset, multirootfileset and classfileset). The selectors that refine a fileset are on Selectors, the other kinds of resource collection are on Resource Collections, and the filters and mappers that transform the selected files are on Filters & Mappers. The way properties such as ${basedir} are resolved is on Properties, and where id and refid fit in a build file is on Build File Structure.

The family at a glance

Ant has no single "file set" class. It has a handful of types that all answer the question "which files?" in a different way, and a handful of tasks that consume the answer. A path-like structure is an ordered list of file system locations (a PATH, a CLASSPATH); a fileset is a group of files found by scanning a directory tree and matching patterns; a dirset is the same for directories; a filelist names files explicitly, whether they exist or not.

flowchart TB PAT["patterns: include, exclude, patternset, default excludes"] SEL["selectors: and, or, not, date, size, ..."] subgraph SCAN["found by scanning a directory tree"] FS["fileset: files below dir"] DS["dirset: directories below dir"] MR["multirootfileset: several roots"] ZF["zipfileset and tarfileset: archive entries or files, with prefix and mode"] CF["classfileset: classes a root class needs"] end FL["filelist: names listed by hand, files may be missing"] PA["path: pathelement, classpath, nested sets, other paths"] PAT --> FS PAT --> DS PAT --> MR PAT --> ZF SEL --> FS SEL --> DS FS --> PA DS --> PA FL --> PA PA --> CON["consumers: javac, java, copy, jar, zip, delete, pathconvert, manifestclasspath"] FS --> CON ZF --> CON CF --> CON

Two ideas run through the whole page. First, a set of files is described once, with an id, and used many times with a refid; the description is evaluated again each time it is used, so the result always reflects the files that exist at that moment. Second, patterns are relative to a base directory and are matched one directory level at a time, which explains almost every surprise a newcomer meets.

Path-like structures

A path-like structure is Ant’s value for "a list of locations separated by the platform’s path separator": the CLASSPATH of javac and java, the module path, the PATH an exec task needs, the list of source directories. The manual’s phrase is that wherever a path-like value is needed, a nested element can be used. Three elements take the same content:

  • <path> is a stand-alone type. It can sit under <project> or <target>, carries an id, and can be referenced.

  • <classpath> (and its siblings <bootclasspath>, <modulepath>, <sourcepath>, <srcdir> and so on) is the same structure nested in a task that needs one.

  • <pathelement> is one entry of either of them.

pathelement, location and path

<pathelement> has two mutually exclusive attributes. location names a single file or directory, relative to the project’s base directory or absolute. path takes a list of locations separated by : or ;, and Ant converts the separators to the ones of the platform it runs on. The manual advises using path only to splice in a predefined path (such as the value of a property that is itself a class path) and preferring several location elements in every other case, which is also what keeps a build file free of platform separators.

Beyond pathelement, a path may contain any resource collection that consists of file-type resources:

Nested element What it adds to the path

<pathelement location="..."/>

one file or directory (since Ant 1.8.2 the last name of a location may end in *, a wildcard that Ant passes through unexpanded; it only works as a Java 6+ CLASSPATH)

<pathelement path="a:b;c"/>

a list of locations; both separators are accepted

<fileset>, <dirset>, <filelist>, <files>, <multirootfileset>

the matching files or directories, in an order that Ant does not define for filesets, dirsets and files

<path refid="other"/>

every entry of another path, which is how a test class path is built on top of a compile class path

The attributes of a stand-alone <path> are small:

Attribute Meaning

id

the name under which the path is registered so that refid can find it

location, path

shortcuts for a single nested pathelement (and the same two shortcuts exist on <classpath>)

refid

makes this path a reference to a path defined elsewhere; no other attribute or nested element may be given

cache

since Ant 1.8.0: when true the path scans its nested resource collections once and assumes they do not change during the build; the default is false, so a path is re-evaluated each time it is used

The cache attribute is worth setting on a path that contains a large fileset and is used by many tasks, such as a test class path, because the default re-scans the file system for every use. It is wrong for a path that must see files created during the build, such as the output directory of an earlier target, for the same reason.

Building paths and looking at them

The build file below defines the class paths of the tidepool build and prints them. ${toString:ref} is a property expansion that turns a path (or any resource collection) reference into a string with the platform separator; pathconvert is described in the next section. The file is paths.xml:

<project name="tidepool-paths" default="show" basedir=".">
  <property name="engine.classes" location="tidepool-engine/build/classes"/>

  <path id="compile.path">
    <pathelement location="${engine.classes}"/>
    <fileset dir="lib" includes="*.jar"/>
  </path>

  <path id="test.path" cache="true">
    <path refid="compile.path"/>
    <pathelement path="tidepool-model/build/classes:tidepool-model/build/test-classes"/>
  </path>

  <path id="shortcut.path" path="a.jar;b.jar:c.jar"/>

  <target name="show">
    <echo>compile.path = ${toString:compile.path}</echo>
    <echo>test.path    = ${toString:test.path}</echo>
    <echo>shortcut     = ${toString:shortcut.path}</echo>
    <pathconvert property="win.path" refid="test.path" targetos="windows">
      <map from="${basedir}" to="C:\work\tidepool"/>
    </pathconvert>
    <echo>windows     = ${win.path}</echo>
    <pathconvert property="jar.list" pathsep=", ">
      <fileset dir="lib" includes="*.jar"/>
      <mapper type="flatten"/>
    </pathconvert>
    <echo>jars        = ${jar.list}</echo>
  </target>
</project>
$ ant -f paths.xml
show:
     [echo] compile.path = /work/tidepool/tidepool-engine/build/classes:/work/tidepool/lib/commons-csv-1.14.1.jar:/work/tidepool/lib/picocli-4.7.7.jar
     [echo] test.path    = /work/tidepool/tidepool-engine/build/classes:/work/tidepool/lib/commons-csv-1.14.1.jar:/work/tidepool/lib/picocli-4.7.7.jar:/work/tidepool/tidepool-model/build/classes:/work/tidepool/tidepool-model/build/test-classes
     [echo] shortcut     = /work/tidepool/a.jar:/work/tidepool/b.jar:/work/tidepool/c.jar
     [echo] windows     = C:\work\tidepool\tidepool-engine\build\classes;C:\work\tidepool\lib\commons-csv-1.14.1.jar;C:\work\tidepool\lib\picocli-4.7.7.jar;C:\work\tidepool\tidepool-model\build\classes;C:\work\tidepool\tidepool-model\build\test-classes
     [echo] jars        = commons-csv-1.14.1.jar, picocli-4.7.7.jar

Four things can be read from the output. Every relative location was resolved against basedir, so the path holds absolute names. The engine.classes directory is in the path although it does not exist yet: a pathelement is not checked, which is why a typo in a class path path shows up later as "package does not exist" and not as a path error. The mixed ; and : separators of shortcut.path came out as the platform separator. The lib directory was expanded to the jar files it holds on the day the path was used, not on the day it was defined.

Wildcards in a pathelement

The wildcard form of location exists for the Java 6 lib/* class path syntax. Ant does not look at the file system for it. The path below keeps the star as written, and only a JVM that understands the syntax expands it:

<path id="w"><pathelement location="lib/*"/></path>
<echo>${toString:w}</echo>
     [echo] /work/tidepool/lib/*

A <fileset dir="lib" includes="*.jar"/> inside the path is the portable choice, because the list is expanded by Ant and works for tasks that do not hand the path to a JVM as a class path (for example copy).

pathconvert

<pathconvert> turns a path, or any resource collection, into a string, and either stores it in a property, writes it to a resource or logs it. Its two jobs are producing a platform-specific path for another program (a script, a manifest, a shell command) and producing a list with a separator of your choice.

Attribute Meaning Required

refid

what to convert: a reference to a path, fileset, dirset or filelist

yes, unless a nested resource collection is given

property

the property that receives the result

no; the result is logged if neither property nor dest is set

dest

since Ant 1.10.13: a resource (a file by default) that receives the result

no

targetos

unix, windows, netware, tandem or os/2; shorthand that sets both separators

no

pathsep

the separator between entries; defaults to the one of the running JVM

no

dirsep

the separator between directory names; defaults to the one of the running JVM

no

setonempty

whether to set the property when the result is empty; default true

no

preserveduplicates

since Ant 1.8: keep duplicate resources; default false

no

The nested elements are <map from="..." to="..."/> (replace a leading prefix; the first matching map wins, so list the longest prefix first; the match is case-insensitive on Windows), a nested <mapper> (since Ant 1.6.2) for any other file-name transformation, and the resource collections themselves when refid is not used. The Windows result in the output above came from targetos="windows" and one map; the second pathconvert used a flatten mapper to keep the file names only and a comma as separator.

An empty result is where the defaults bite. With setonempty="true" (the default) the property exists and is empty, so a later unless="..." test sees it as set; with setonempty="false" the property is not defined:

<pathconvert property="p" setonempty="false"><fileset dir="data" includes="*.xml"/></pathconvert>
<echo>p=${p}</echo>
<pathconvert property="q" setonempty="true"><fileset dir="data" includes="*.xml"/></pathconvert>
<echo>q=[${q}]</echo>
     [echo] p=${p}
     [echo] q=[]

The unexpanded ${p} in the first line is how Ant shows a property that does not exist. Calling pathconvert with nothing to convert is an error:

BUILD FAILED
/work/tidepool/misc.xml:19: You must specify a path to convert

manifestclasspath

A runnable JAR that needs libraries has them listed in its manifest’s Class-Path attribute, as file names relative to the JAR. The <manifestclasspath> task (since Ant 1.7) computes that string from an Ant path, so that the manifest never contains an absolute name of the machine that built it. It is also the standard workaround for the Windows limit on the length of a command line, because a launcher JAR with a long Class-Path replaces a class path of hundreds of entries.

Attribute or element Meaning Required

property

the property to set; it must not already be set

yes

jarfile

the JAR that will carry the manifest; it need not exist, but its parent directory must

yes

maxParentLevels

the maximum number of .. allowed in a relative entry; default 2, and 0 forces every entry into the JAR’s own directory tree

no

nested <classpath>

the path to convert; it must not be empty

yes

The build.xml below compiles all three modules with one javac (whose <src> is a path built from a dirset), copies the libraries next to the JAR, computes the Class-Path, builds tidepool-cli.jar and runs it:

<project name="tidepool" default="run" basedir=".">
  <property name="build.dir"   location="build"/>
  <property name="classes.dir" location="${build.dir}/classes"/>
  <property name="dist.dir"    location="${build.dir}/dist"/>

  <path id="compile.path">
    <fileset dir="lib" includes="*.jar"/>
  </path>

  <path id="main.sources">
    <dirset dir=".">
      <include name="*/src/main/java"/>
    </dirset>
  </path>

  <target name="compile">
    <mkdir dir="${classes.dir}"/>
    <javac destdir="${classes.dir}" release="21" includeantruntime="false">
      <src refid="main.sources"/>
      <classpath refid="compile.path"/>
    </javac>
  </target>

  <target name="jar" depends="compile">
    <mkdir dir="${dist.dir}/lib"/>
    <copy todir="${dist.dir}/lib" flatten="true">
      <path refid="compile.path"/>
    </copy>
    <manifestclasspath property="jar.classpath" jarfile="${dist.dir}/tidepool-cli.jar">
      <classpath>
        <fileset dir="${dist.dir}/lib" includes="*.jar"/>
      </classpath>
    </manifestclasspath>
    <echo>Class-Path value: ${jar.classpath}</echo>
    <jar destfile="${dist.dir}/tidepool-cli.jar" basedir="${classes.dir}">
      <manifest>
        <attribute name="Main-Class" value="io.tidepool.cli.Main"/>
        <attribute name="Class-Path" value="${jar.classpath}"/>
      </manifest>
    </jar>
  </target>

  <target name="run" depends="jar">
    <java jar="${dist.dir}/tidepool-cli.jar" fork="true" failonerror="true">
      <arg value="data/stations.csv"/>
      <arg value="BOS"/>
    </java>
  </target>
</project>
$ ant
compile:
    [mkdir] Created dir: /work/tidepool/build/classes
    [javac] Compiling 7 source files to /work/tidepool/build/classes

jar:
    [mkdir] Created dir: /work/tidepool/build/dist/lib
     [copy] Copying 4 files to /work/tidepool/build/dist/lib
     [echo] Class-Path value: lib/commons-codec-1.19.0.jar lib/commons-csv-1.14.1.jar lib/commons-io-2.20.0.jar lib/picocli-4.7.7.jar
      [jar] Building jar: /work/tidepool/build/dist/tidepool-cli.jar

run:
     [java] Boston: -0,662 m

(the decimal comma is the locale of the machine that ran the example) and the manifest the jar task wrote, read with unzip -p build/dist/tidepool-cli.jar META-INF/MANIFEST.MF, shows the property re-wrapped at 72 characters as the JAR specification requires:

Manifest-Version: 1.0
Ant-Version: Apache Ant 1.10.18
Created-By: 26+35-2893 (Oracle Corporation)
Main-Class: io.tidepool.cli.Main
Class-Path: lib/commons-codec-1.19.0.jar lib/commons-csv-1.14.1.jar li
 b/commons-io-2.20.0.jar lib/picocli-4.7.7.jar

Two failure modes are worth knowing. The first is a library that is on the compile path but was not copied next to the JAR: the Class-Path is built from the files that exist, so the missing JAR is simply not listed, and the program fails at run time. The first run of this example had commons-io missing from lib (it is a dependency of commons-csv), and Class-Path listed only two libraries:

     [echo] Class-Path value: lib/commons-csv-1.14.1.jar lib/picocli-4.7.7.jar
...
     [java] Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/commons/io/output/AppendableOutputStream
     [java] 	at io.tidepool.engine.StationReader.read(Unknown Source)

The JVM ignores a Class-Path entry whose file is missing without any message, so this failure is the only sign. The second failure mode is an entry that is more than maxParentLevels directories away from the JAR. With a limit of 1, the library directory two levels up is rejected:

BUILD FAILED
/work/tidepool/mcp.xml:3: No suitable relative path from /work/tidepool/build/dist to /work/tidepool/lib/picocli-4.7.7.jar

The relative layout that manifestclasspath assumes must be the layout of the deployed files, which is why the example copies the libraries into build/dist/lib first and computes the value from that directory. Maven does the same job with the addClasspath manifest setting of the maven-jar-plugin and Gradle with a Class-Path attribute computed from runtimeClasspath; both are on the Maven packaging page and the Gradle Java projects page. Packing the libraries inside one JAR is the subject of Packaging Archives.

The system class path: includeantruntime and build.sysclasspath

Every class path Ant hands to a compiler or a forked JVM can be combined with the system class path, the one in effect when Ant itself runs. The build.sysclasspath property chooses how:

Value Meaning

only

only the system class path is used; the class paths of the build file are ignored (the person who runs the build knows the environment better than the author)

ignore

the system class path is ignored; this is the reverse, and the mode the manual recommends for portable scripts

last

the build file’s class path first, the system class path appended

first

the system class path first, then the build file’s class path

The default differs from task to task, and the property also affects the bootclasspath settings since Ant 1.7 (when the property is unset it defaults to ignore there). In day-to-day builds the property is rarely set. What matters more is its consequence for javac: the includeantruntime attribute says whether the Ant libraries themselves are put on the compile class path, and it defaults to "yes, unless build.sysclasspath is set". That default is why a javac without the attribute prints a warning on every build, and why a build that works on one machine can fail on another where Ant is installed differently:

<javac srcdir="tidepool-model/src/main/java" destdir="build/a" release="21"/>
    [javac] /work/tidepool/javac.xml:4: warning: 'includeantruntime' was not set, defaulting to build.sysclasspath=last; set to false for repeatable builds
    [javac] Compiling 4 source files to /work/tidepool/build/a

Setting includeantruntime="false" removes the warning and makes the compile class path exactly what the build file says. A project whose sources really use Ant classes (a custom task, see Writing Custom Tasks) must either set it to true or name the Ant jars. The probe below is a class that imports org.apache.tools.ant.Main; it fails to compile with includeantruntime="false", compiles with true, and compiles with an explicit class path only when both ant.jar and ant-launcher.jar are named:

package demo;

import org.apache.tools.ant.Main;

public class Probe {
    public static String version() throws Exception {
        return Main.getAntVersion();
    }
}
<target name="cp">
  <mkdir dir="build/probe"/>
  <javac srcdir="src-ant" destdir="build/probe" release="21" includeantruntime="false">
    <classpath>
      <pathelement location="${ant.core.lib}"/>
      <pathelement location="${ant.library.dir}/ant-launcher.jar"/>
    </classpath>
  </javac>
</target>
$ ant -f probe.xml without
    [javac] /work/tidepool/src-ant/demo/Probe.java:3: error: package org.apache.tools.ant does not exist
    [javac] import org.apache.tools.ant.Main;
    [javac]                            ^
...
BUILD FAILED
/work/tidepool/probe.xml:4: Compile failed; see the compiler error output for details.

$ ant -f probe.xml with
    [javac] Compiling 1 source file to /work/tidepool/build/probe

BUILD SUCCESSFUL

With only ${ant.core.lib} (the property that points at ant.jar) on the class path, javac stopped with error: cannot access AntMain and class file for org.apache.tools.ant.launch.AntMain not found, because Main is declared in terms of a class that lives in ant-launcher.jar; adding ${ant.library.dir}/ant-launcher.jar fixed it. ant.core.lib, ant.library.dir and ant.home are among the built-in properties listed on Properties. The full treatment of javac is on Compiling Java.

Directory-based tasks and implicit filesets

Many tasks work on a directory tree: javac compiles the .java files under srcdir, delete removes files below a directory, copy copies a subset. For those tasks, the task element is a fileset. The manual’s list of tasks that can act as an implicit fileset includes checksum, delete, dependset, fixcrlf, javac, replace, rmic, xslt, tar and zip, and the deprecated copydir, jlink and jspc. On such a task the attributes includes, excludes, includesfile and excludesfile and the nested elements include, exclude, includesfile and excludesfile describe the subset, exactly as on a fileset.

<project name="implicit" default="all" basedir=".">
  <target name="all">
    <mkdir dir="build/implicit"/>
    <javac srcdir="tidepool-model/src" destdir="build/implicit" release="21" includeantruntime="false"
           includes="main/**" excludes="**/TideEvent.java"/>
    <copy todir="build/implicit/data" verbose="true">
      <fileset dir="data" includes="*.csv"/>
    </copy>
    <delete verbose="true">
      <fileset dir="build/implicit" includes="**/*.class" excludes="**/Height.class"/>
    </delete>
  </target>
</project>
all:
    [mkdir] Created dir: /work/tidepool/build/implicit
    [javac] Compiling 3 source files to /work/tidepool/build/implicit
     [copy] Copying 1 file to /work/tidepool/build/implicit/data
     [copy] Copying /work/tidepool/data/stations.csv to /work/tidepool/build/implicit/data/stations.csv
   [delete] Deleting /work/tidepool/build/implicit/io/tidepool/model/Constituent.class
   [delete] Deleting /work/tidepool/build/implicit/io/tidepool/model/Station.class

javac compiled three of the four model sources, because TideEvent.java was excluded; the explicit <fileset> in copy and delete is the other way to write the same thing, and the only way when a task needs more than one set (the explicit form is the only one copy accepts, because it is not in the manual’s list of implicit-fileset tasks). When a task is an implicit fileset, the selectors of Selectors can be nested directly inside it.

Patterns

A pattern selects files by their path relative to the base directory of the set, and only files below that directory are ever scanned. A pattern such as ../foo.java is syntactically legal and never matches anything, because the parent of the base directory is not scanned.

The matching rules are few:

  • * matches zero or more characters within one name, and ? matches exactly one character.

  • Matching is done one directory level at a time: the first directory of the pattern is matched against the first directory of the path, then the second, and so on.

  • **, when it is the whole name of a directory, matches zero or more directories. That is the only way to cross directory levels.

  • A pattern that ends with / or \ has ** appended: mypackage/test/ means mypackage/test/**.

  • Patterns are case sensitive unless the set has casesensitive="false".

  • A set with no include pattern includes everything (with the exception of the default excludes below); a file is selected when it matches at least one include pattern and no exclude pattern.

The manual’s own table of examples is reproduced in spirit by the next listing, run for real. The list macro prints the files of the set it is given, with their paths relative to the project directory (the macrodef is explained on Macros & Presets). The file is patterns.xml:

<project name="tidepool-patterns" default="all" basedir=".">

  <macrodef name="list">
    <attribute name="title"/>
    <attribute name="refid"/>
    <sequential>
      <pathconvert property="out.@{refid}" refid="@{refid}" pathsep="${line.separator}  ">
        <map from="${basedir}/" to=""/>
      </pathconvert>
      <echo>@{title}${line.separator}  ${out.@{refid}}</echo>
    </sequential>
  </macrodef>

  <fileset id="all.java" dir="." includes="**/*.java"/>
  <fileset id="main.java" dir="." includes="**/src/main/**/*.java"/>
  <fileset id="tests.only" dir="." includes="**/*Test.java"/>
  <fileset id="six.letters" dir="tidepool-model/src/main/java/io/tidepool/model" includes="??????.java"/>
  <fileset id="not.tests" dir="." includes="**/*.java" excludes="**/*Test*"/>
  <fileset id="slash.dir" dir="tidepool-engine" includes="src/test/"/>
  <fileset id="mixed.case" dir="tidepool-model" includes="**/HEIGHT.JAVA" casesensitive="false"/>
  <fileset id="data.default" dir="data"/>
  <fileset id="data.all" dir="data" defaultexcludes="false"/>

  <target name="all">
    <list title="**/*.java" refid="all.java"/>
    <list title="**/src/main/**/*.java" refid="main.java"/>
    <list title="**/*Test.java" refid="tests.only"/>
    <list title="six-letter names (??????.java)" refid="six.letters"/>
    <list title="excludes **/*Test*" refid="not.tests"/>
    <list title="src/test/ (trailing slash)" refid="slash.dir"/>
    <list title="HEIGHT.JAVA, casesensitive=false" refid="mixed.case"/>
    <list title="data with default excludes" refid="data.default"/>
    <list title="data, defaultexcludes=false" refid="data.all"/>
  </target>
</project>
$ ant -f patterns.xml
all:
     [echo] **/*.java
     [echo]   tidepool-cli/src/main/java/io/tidepool/cli/Main.java
     [echo]   tidepool-engine/src/main/java/io/tidepool/engine/StationReader.java
     [echo]   tidepool-engine/src/main/java/io/tidepool/engine/TideEngine.java
     [echo]   tidepool-engine/src/test/java/io/tidepool/engine/StationReaderTest.java
     [echo]   tidepool-engine/src/test/java/io/tidepool/engine/TideEngineTest.java
     [echo]   tidepool-model/src/main/java/io/tidepool/model/Constituent.java
     [echo]   tidepool-model/src/main/java/io/tidepool/model/Height.java
     [echo]   tidepool-model/src/main/java/io/tidepool/model/Station.java
     [echo]   tidepool-model/src/main/java/io/tidepool/model/TideEvent.java
     [echo]   tidepool-model/src/test/java/io/tidepool/model/HeightTest.java
     [echo] **/src/main/**/*.java
     [echo]   tidepool-cli/src/main/java/io/tidepool/cli/Main.java
     [echo]   tidepool-engine/src/main/java/io/tidepool/engine/StationReader.java
     [echo]   tidepool-engine/src/main/java/io/tidepool/engine/TideEngine.java
     [echo]   tidepool-model/src/main/java/io/tidepool/model/Constituent.java
     [echo]   tidepool-model/src/main/java/io/tidepool/model/Height.java
     [echo]   tidepool-model/src/main/java/io/tidepool/model/Station.java
     [echo]   tidepool-model/src/main/java/io/tidepool/model/TideEvent.java
     [echo] **/*Test.java
     [echo]   tidepool-engine/src/test/java/io/tidepool/engine/StationReaderTest.java
     [echo]   tidepool-engine/src/test/java/io/tidepool/engine/TideEngineTest.java
     [echo]   tidepool-model/src/test/java/io/tidepool/model/HeightTest.java
     [echo] six-letter names (??????.java)
     [echo]   tidepool-model/src/main/java/io/tidepool/model/Height.java
     [echo] excludes **/*Test*
     [echo]   tidepool-cli/src/main/java/io/tidepool/cli/Main.java
     [echo]   tidepool-engine/src/main/java/io/tidepool/engine/StationReader.java
     [echo]   tidepool-engine/src/main/java/io/tidepool/engine/TideEngine.java
     [echo]   tidepool-model/src/main/java/io/tidepool/model/Constituent.java
     [echo]   tidepool-model/src/main/java/io/tidepool/model/Height.java
     [echo]   tidepool-model/src/main/java/io/tidepool/model/Station.java
     [echo]   tidepool-model/src/main/java/io/tidepool/model/TideEvent.java
     [echo] src/test/ (trailing slash)
     [echo]   tidepool-engine/src/test/java/io/tidepool/engine/StationReaderTest.java
     [echo]   tidepool-engine/src/test/java/io/tidepool/engine/TideEngineTest.java
     [echo] HEIGHT.JAVA, casesensitive=false
     [echo]   tidepool-model/src/main/java/io/tidepool/model/Height.java
     [echo] data with default excludes
     [echo]   data/stations.csv
     [echo] data, defaultexcludes=false
     [echo]   data/.DS_Store
     [echo]   data/.gitignore
     [echo]   data/stations.csv
     [echo]   data/stations.csv~

What the listing shows, pattern by pattern:

  • **/*.java matched sources at every depth, because ** also matches zero directories.

  • **/src/main/**/*.java selected the production sources of all three modules and none of the tests: the src/main part is matched as two directory names and the second ** crosses java/io/tidepool/....

  • ??????.java matched Height.java and nothing else in the directory: six letters and the extension.

  • **/*Test* as an exclude removed the three test classes from the **/*.java set.

  • src/test/ (trailing slash) behaved as src/test/** and selected the two test classes of the engine.

  • casesensitive="false" let HEIGHT.JAVA match Height.java. The default is true on every platform, including the file systems of macOS and Windows that are case-insensitive, so a pattern that works because the disk ignores case breaks on Linux.

  • The last two sets differ by the default excludes, described next.

A * inside a name never matches a /, so *.java selects only the files directly in the base directory, and a frequent beginner error is to expect it to find the sources of a package tree. The mirror error is a fileset whose base directory is already the package directory but whose pattern still starts with src/.

Default excludes

Directory-based tasks skip a fixed list of files that are rarely meant to be part of a build: the temporary files of editors and the metadata of version control systems. The list in Ant 1.10.18 is:

Patterns Origin

**/*~, **/#*#, **/.#*, **/%*%, **/._*

editor backups, lock and temporary files (._* are macOS resource-fork files)

**/CVS, **/CVS/**, **/.cvsignore

CVS

**/SCCS, **/SCCS/**, **/vssver.scc

SCCS and Visual SourceSafe

**/.svn, **/.svn/**

Subversion

**/.DS_Store

macOS Finder

**/.git, **/.git/**, **/.gitattributes, **/.gitignore, **/.gitmodules

Git (added in Ant 1.8.2)

**/.hg, **/.hg/**, **/.hgignore, **/.hgsub, **/.hgsubstate, **/.hgtags

Mercurial (added in Ant 1.8.2)

**/.bzr, **/.bzr/**, **/.bzrignore

Bazaar (added in Ant 1.8.2)

Both runs of data in the previous listing show the effect: .DS_Store, .gitignore and stations.csv~ are in the directory, but only stations.csv is part of the default set. A single set can switch the list off with defaultexcludes="false". The list for a whole build can be changed with the <defaultexcludes> task, which alters the default excludes for all subsequent processing:

Attribute Meaning

echo

print the current list; true is required if no other attribute is given

add

add a pattern to the list

remove

remove a pattern from the list

default

go back to the hard-wired list

<target name="excludes">
  <defaultexcludes add="**/*.bak"/>
  <defaultexcludes remove="**/*~"/>
  <fileset id="data" dir="data"/>
  <list title="after add **/*.bak and remove **/*~" refid="data"/>
  <defaultexcludes default="true"/>
</target>
$ touch data/x.bak
$ ant -f sets.xml excludes
excludes:
     [echo] after add **/*.bak and remove **/*~
     [echo]   data/stations.csv
     [echo]   data/stations.csv~

The backup file stations.csv~ came back into the set, and x.bak stayed out. The task is global and sticky for the rest of the build (and of the JVM, if Ant is embedded), so a build file that changes the list restores it with default="true". With echo="true" the task prints the current list (in the order of a hash set, not the order of the table above); in 1.10.18 its first line carries a literal %n where a newline was meant:

[defaultexcludes] Current Default Excludes:%n  **/%*%
[defaultexcludes]   **/.git/**
[defaultexcludes]   **/SCCS
[defaultexcludes]   **/.bzr
[defaultexcludes]   **/.hg/**
...

A frequent reason to touch the list is the Subversion _svn convention: remove **/.svn and **/.svn/** and add **/_svn and **/_svn/**, as the manual shows. In practice, the commonest default-excludes surprise has the opposite cause: a file that should be packaged, such as a .gitignore shipped inside a project template, is left out because it is in the list. The fix is defaultexcludes="false" on that one fileset, not a global change.

Gradle uses the same Ant DirectoryScanner underneath its file trees and therefore the same default excludes; it lets a settings script change them with DirectoryScanner.removeDefaultExclude(...) and does not allow changing them during the execution phase.

fileset

A <fileset> is a group of files found in a directory tree below a base directory and matched by patterns and selectors. It holds an implicit patternset, so it takes the patternset’s attributes and nested elements directly. It also acts as an <and> selector container, so any nested selector must select a file for it to belong to the set.

Attribute Meaning Required

dir

the root of the directory tree

exactly one of dir or file

file

shortcut for a single-file set

exactly one of dir or file

includes, excludes

comma- or space-separated patterns

no; defaults to all files

includesfile, excludesfile

a file whose every line is a pattern; an empty includesfile with no other pattern includes all files

no

defaultexcludes

whether the default excludes apply (yes|no)

no; yes

casesensitive

whether patterns are case sensitive

no; true

followsymlinks

whether symbolic links and (since Ant 1.10.16) Windows junctions are followed

no; true

erroronmissingdir

since Ant 1.7.1: what happens when dir does not exist; false makes the set empty

no; true

id, refid

register the set, or make this element a reference to a set defined elsewhere

no

erroronmissingdir defaults to true "for backward compatibility", so the first thing a clean checkout does with a javac that points at a not-yet-created generated-sources directory is fail. Setting it to false on the sets that describe optional directories, for example a src/generated directory that only exists after a code-generation step, keeps the build file valid before and after:

BUILD FAILED
/work/tidepool/errs.xml:3: /work/tidepool/no-such-dir does not exist.

Symbolic links deserve a sentence of their own. followsymlinks="false" excludes symbolic links and junctions completely, so a link that points at a directory of sources makes the whole subtree vanish from the set; true lets selectors with their own followsymlinks attribute decide whether to look at the link or its target. Before Ant 1.10.2 "symbolic link" meant any file whose canonical path differs from its path, which gave false results on some platforms; Ant 1.10.2 and later use Files#isSymbolicLink. With a link data/model-link to ../tidepool-model the difference is the whole module:

<fileset id="f1" dir="data"><include name="**/*.java"/></fileset>
<fileset id="f2" dir="data" followsymlinks="false"><include name="**/*.java"/></fileset>
<resourcecount property="n1"><fileset refid="f1"/></resourcecount>
<resourcecount property="n2"><fileset refid="f2"/></resourcecount>
<echo>followsymlinks=true: ${n1} files, false: ${n2} files</echo>
     [echo] followsymlinks=true: 5 files, false: 0 files

Many errors come from the way the element is written, and Ant’s messages are precise about them:

Message Cause

fileset doesn't support the "include" attribute

include is the nested element; the attribute is includes

No directory specified for fileset.

a fileset with neither dir nor file nor refid

you can only specify one of the dir and file attributes

both given

Reference nope not found.

a refid whose set is not defined (yet), see the section on references

You must not specify more than one attribute when using refid

refid combined with another attribute such as includes

You must not specify nested elements when using refid

refid combined with a nested include

patternset

A <patternset> groups patterns so that they can be named and shared. It may appear nested in a fileset or in a directory-based task, and also stand-alone at the same level as <target> (a child of <project> or of <target>).

Attribute or element Meaning

includes, excludes

comma- or space-separated patterns

includesfile, excludesfile

a file of patterns, one per line; the nested <includesfile name="..."> and <excludesfile name="..."> may repeat and accept if, unless and (since Ant 1.10.4) encoding

nested <include name="...">, <exclude name="...">

exactly one pattern each, with optional if and unless properties

nested <patternset>

adds the nested patterns to the parent patternset

nested <invert>

since Ant 1.7.1: a nested patternset that selects everything except what it would select

refid

a reference to a patternset defined elsewhere

The comma-or-space convenience of the attributes does not apply to the nested element: a nested include takes one pattern. The if and unless attributes of include and exclude test whether a named property is set (not its value), which is the cheap way to make a source list depend on the command line. In the file sets.xml:

<patternset id="sources">
  <include name="*/src/main/java/**/*.java"/>
  <include name="*/src/test/java/**/*.java" if="with.tests"/>
  <exclude name="tidepool-cli/**" unless="with.cli"/>
</patternset>

<fileset id="selected" dir=".">
  <patternset refid="sources"/>
</fileset>
$ ant -q -f sets.xml
     [echo] selected sources
     [echo]   tidepool-engine/src/main/java/io/tidepool/engine/StationReader.java
     [echo]   tidepool-engine/src/main/java/io/tidepool/engine/TideEngine.java
     [echo]   tidepool-model/src/main/java/io/tidepool/model/Constituent.java
     [echo]   tidepool-model/src/main/java/io/tidepool/model/Height.java
     [echo]   tidepool-model/src/main/java/io/tidepool/model/Station.java
     [echo]   tidepool-model/src/main/java/io/tidepool/model/TideEvent.java

$ ant -q -f sets.xml -Dwith.cli=true -Dwith.tests=true
     [echo] selected sources
     [echo]   tidepool-cli/src/main/java/io/tidepool/cli/Main.java
     [echo]   tidepool-engine/src/main/java/io/tidepool/engine/StationReader.java
     [echo]   tidepool-engine/src/main/java/io/tidepool/engine/TideEngine.java
     [echo]   tidepool-engine/src/test/java/io/tidepool/engine/StationReaderTest.java
     [echo]   tidepool-engine/src/test/java/io/tidepool/engine/TideEngineTest.java
     [echo]   tidepool-model/src/main/java/io/tidepool/model/Constituent.java
     [echo]   tidepool-model/src/main/java/io/tidepool/model/Height.java
     [echo]   tidepool-model/src/main/java/io/tidepool/model/Station.java
     [echo]   tidepool-model/src/main/java/io/tidepool/model/TideEvent.java
     [echo]   tidepool-model/src/test/java/io/tidepool/model/HeightTest.java

A patternset also reads patterns from a file. The file extra.patterns below has a comment line, and the example shows that the file has no comment syntax: every line is a pattern, so the comment became a pattern that matches nothing (harmless here, but a source of confusion). The pattern *.csv matched nothing under . either, because it is relative to the base directory and * does not cross directories:

*.csv
# comment line
**/*Test.java
<fileset id="from.file" dir="." includesfile="extra.patterns"/>
     [echo] includesfile extra.patterns
     [echo]   tidepool-engine/src/test/java/io/tidepool/engine/StationReaderTest.java
     [echo]   tidepool-engine/src/test/java/io/tidepool/engine/TideEngineTest.java
     [echo]   tidepool-model/src/test/java/io/tidepool/model/HeightTest.java

The <invert> element is only accepted inside a patternset, not directly in a fileset (a direct nesting fails with fileset doesn't support the nested "invert" element):

<fileset id="inverted" dir="tidepool-model/src/main/java/io/tidepool/model">
  <patternset>
    <invert>
      <patternset includes="H*.java,S*.java"/>
    </invert>
  </patternset>
</fileset>
     [echo] invert
     [echo]   tidepool-model/src/main/java/io/tidepool/model/Constituent.java
     [echo]   tidepool-model/src/main/java/io/tidepool/model/TideEvent.java

Gradle’s counterpart is PatternFilterable (the include, exclude, setIncludes family used by fileTree, copy specs and the source sets), with the same Ant-style patterns; Maven’s counterpart is the <includes> and <excludes> list of a resource directory or a plugin configuration.

dirset

A <dirset> is the directory counterpart of a fileset: a group of directories matched by patterns and selectors below a base directory. It takes the same attributes and nested elements as a fileset except file (its dir is required), and it is an <and> selector container as well. A dirset contains the directories only; the files inside them are not part of the set, which is why it is most often seen nested in a path (a source path, a list of class directories):

<dirset id="packages" dir=".">
  <include name="*/src/main/java/io/tidepool/*"/>
</dirset>
     [echo] dirset packages
     [echo]   tidepool-cli/src/main/java/io/tidepool/cli
     [echo]   tidepool-engine/src/main/java/io/tidepool/engine
     [echo]   tidepool-model/src/main/java/io/tidepool/model

The build file in the manifestclasspath section used a dirset for the opposite purpose: its <path id="main.sources"> wraps the dirset of */src/main/java, which javac took as a <src refid>. A bare <dirset id> cannot be passed as a refid where a path is expected; the type error is

BUILD FAILED
/work/tidepool/build.xml:16: main.sources doesn't denote a path

and the fix is to wrap it in a <path>, as the example does. The manual’s last dirset example, a present selector with a glob mapper, selects every directory that contains a marker file, which is also the way to find every module directory in a multi-module tree; selectors are on Selectors.

filelist

A <filelist> is an explicit list of files relative to a directory. Where a fileset filters what exists, a filelist lists names whether or not the files exist, and does not expand wildcards (a * in a name is kept as written). That makes it the right type for ordered lists (the files of a documentation set, the chapters of a book, the modules a script must run in order), and for the expected outputs of a build that has not run yet.

Attribute or element Meaning Required

dir

the base directory

yes

files

names separated by whitespace or commas

yes, unless nested file elements are used

nested <file name="...">

since Ant 1.6.2: one name; allows spaces and commas in the name

no

id, refid

register the list, or refer to one

no

<filelist id="release.files" dir=".">
  <file name="README.txt"/>
  <file name="build/dist/tidepool-cli.jar"/>
  <file name="docs/user guide.pdf"/>
</filelist>
     [echo] filelist release.files
     [echo]   README.txt
     [echo]   build/dist/tidepool-cli.jar
     [echo]   docs/user guide.pdf

docs/user guide.pdf does not exist, and it is in the list anyway, with a space in its name that the files attribute could not have carried. This is useful with pathconvert, as in the manual’s example that converts a filelist into a Unix path with a map, and with the available and uptodate tasks that check whether the listed files exist.

zipfileset and tarfileset

<zipfileset> and <tarfileset> are filesets with extra attributes for building archives. Each has two modes: with dir it is populated with files from the file system; with src (or, since Ant 1.7, a nested resource) it is populated with the entries of an archive, which is how one archive is merged into another. They take every attribute of <fileset> plus:

Attribute Meaning Applies to

prefix

every file is placed under this path in the archive

both

fullpath

the single file of the set is placed at exactly this path; cannot be combined with prefix

both

src

a zip (or tar) file whose entries are included

both

filemode

three-digit octal permissions of plain files; default 644

both (zipfileset since Ant 1.5.2)

dirmode

three-digit octal permissions of directories; default 755

both (zipfileset since Ant 1.5.2)

encoding

encoding of the entry names (tarfileset since Ant 1.9.5)

both

erroronmissingarchive

since Ant 1.8.0: whether a missing src archive is an error (default true)

both

username, group, uid, gid

owner of the tar entries

tarfileset only

Only the tar and zip tasks use the permission attributes, and only tar uses the ownership attributes. A zipfileset can be defined once with an id and used in several archives with refid (since Ant 1.6; for tarfileset since Ant 1.7). The file archives.xml:

<project name="archives" default="all" basedir=".">
  <property name="out" location="build/dist"/>

  <zipfileset id="libs" dir="lib" includes="*.jar" prefix="app/lib" filemode="640"/>

  <target name="all">
    <mkdir dir="${out}"/>
    <zip destfile="${out}/bundle.zip">
      <zipfileset dir="data" prefix="app/data"/>
      <zipfileset refid="libs"/>
      <zipfileset dir="." includes="README.txt" fullpath="app/docs/READ-ME.txt"/>
      <zipfileset src="${out}/tidepool-cli.jar" includes="io/tidepool/model/**" prefix="app/classes"/>
    </zip>
    <tar destfile="${out}/bundle.tar" longfile="posix">
      <tarfileset dir="data" prefix="app/data" filemode="600" username="tide" group="pool" uid="1001" gid="1002"/>
      <tarfileset dir="." includes="README.txt" fullpath="app/bin/run" filemode="755"/>
    </tar>
  </target>
</project>
$ ant -f archives.xml
all:
      [zip] Building zip: /work/tidepool/build/dist/bundle.zip
      [tar] Building tar: /work/tidepool/build/dist/bundle.tar

$ unzip -Z build/dist/bundle.zip     (directories and the summary line left out)
-rw-r--r--  app/data/stations.csv
-rw-r-----  app/lib/commons-codec-1.19.0.jar
-rw-r-----  app/lib/commons-csv-1.14.1.jar
-rw-r-----  app/lib/commons-io-2.20.0.jar
-rw-r-----  app/lib/picocli-4.7.7.jar
-rw-r--r--  app/docs/READ-ME.txt
-rw-r--r--  app/classes/io/tidepool/model/Constituent.class
-rw-r--r--  app/classes/io/tidepool/model/Height.class
-rw-r--r--  app/classes/io/tidepool/model/Station.class
-rw-r--r--  app/classes/io/tidepool/model/TideEvent.class

$ tar tvf build/dist/bundle.tar
-rw-------  0 tide   pool      239 Oct 16 11:33 app/data/stations.csv
-rwxr-xr-x  0 0      0           6 Oct 16 11:36 app/bin/run

The reading guide: prefix moved data and lib under app/; fullpath renamed README.txt to app/docs/READ-ME.txt; the src form copied only the model classes out of tidepool-cli.jar (the build of the earlier section); filemode="640" shows as -rw-r----- and the tar entry for stations.csv carries mode 600 and the owner names. The default-excluded stations.csv~ is not in either archive. Two messages catch the usual mistakes:

/work/tidepool/errs2.xml:10: Cannot set both fullpath and prefix attributes
/work/tidepool/errs2.xml:13: fullpath attribute may only be specified for filesets that specify a single file.

The jar, war and ear tasks accept the same nested sets, plus zipgroupfileset to merge the contents of many archives, and are the subject of Packaging Archives. Merging is also how Ant builds an uber JAR; Maven uses the Shade or Assembly plugins and Gradle a Jar task with from(zipTree(...)) (see Uber JARs with Shade & Assembly).

multirootfileset

A <multirootfileset> (since Ant 1.9.4) is a union of filesets and dirsets that share the same patterns and selectors: the same include and exclude rules applied to several base directories. It takes everything a fileset takes except dir, and adds:

Attribute or element Meaning

basedirs

a comma-separated list of root directories

nested <basedir file="...">

one root each; merged with basedirs when both are given

type

file (default), dir or both

cache

whether to cache the result; the default is true and turning it off can hurt performance seriously

<multirootfileset id="roots" basedirs="tidepool-model/src,tidepool-engine/src" type="file">
  <include name="main/**/*.java"/>
  <exclude name="**/Tide*"/>
</multirootfileset>
     [echo] multirootfileset roots
     [echo]   tidepool-model/src/main/java/io/tidepool/model/Constituent.java
     [echo]   tidepool-model/src/main/java/io/tidepool/model/Height.java
     [echo]   tidepool-model/src/main/java/io/tidepool/model/Station.java
     [echo]   tidepool-engine/src/main/java/io/tidepool/engine/StationReader.java

The result lists the model sources first and the engine sources next, the order of basedirs, and the Tide* exclude dropped TideEvent.java and TideEngine.java. The two-module union would otherwise need two filesets and the same patterns twice (or a shared patternset). Gradle’s equivalent is a files(...) collection of several fileTree objects, and Maven spreads the same filter over <sourceDirectory> and the build-helper plugin’s add-source goal.

classfileset

A <classfileset> is a fileset computed from the dependencies of some root classes: given a directory of class files and one or more roots, it includes every class file the roots need. It is typically used to build a JAR containing only what an application requires. It needs the BCEL library, which Ant does not ship; the optional ant-apache-bcel.jar in ANT_HOME/lib is the glue, and the BCEL jar (and its own dependencies) must be on Ant’s class path.

Attribute or element Meaning

dir, and every fileset attribute

the directory of class files to draw from

rootclass

a single root class name

nested <root classname="...">

one more root, by fully-qualified name

nested <rootfileset dir="...">

roots given as a fileset of class files; the class name is derived from the relative path

<classfileset id="needed" dir="build/classes">
  <root classname="io.tidepool.engine.StationReader"/>
</classfileset>

The first run failed because BCEL is not installed in Ant 1.10.18 (the stack trace is long; this is its first line):

BUILD FAILED
/work/tidepool/classfs.xml:7: java.lang.NoClassDefFoundError: org/apache/bcel/classfile/Visitor

BCEL 6.12.0 (the version in Ant 1.10.18’s libraries.properties) was fetched from Maven Central into a directory extlib, which is passed to Ant with -lib. That run failed again, with NoClassDefFoundError: org/apache/commons/io/IOUtils, because BCEL 6.12.0 itself needs commons-lang3 (3.20.0) and commons-io (2.21.0). With the three jars in extlib:

$ ant -lib extlib -f classfs.xml
all:
     [echo]
     [echo]   io/tidepool/model/Station.class
     [echo]   io/tidepool/engine/StationReader.class
     [echo]   io/tidepool/model/Constituent.class
      [jar] Building jar: /work/tidepool/build/dist/minimal.jar

The root StationReader needs Station and, through it, Constituent; the other classes of the model (Height, TideEvent) and of the engine were left out of minimal.jar. Static analysis of the byte code cannot see classes loaded by reflection or by name, so a classfileset is an optimisation for small applications, not a replacement for a dependency declaration. Gradle and Maven reach the same goal with the Shade plugin’s minimizeJar and ProGuard-style tools, not with a core feature.

References: id and refid

Any element of a project can be given an id, and an element of the same type can then be a reference to it with refid. The mechanism is not specific to sets and paths, but they are its main use, and a build file that defines compile.path once and refers to it from javac, java and junitlauncher is more than tidy: it cannot drift.

Rules that matter in practice:

  • The reference is by type. <classpath refid="x"/> needs a path; handing it a dirset ends with x doesn't denote a path, as above.

  • An element with refid can have no other attributes and no nested elements (the two messages are in the table in the fileset section).

  • A reference to something that does not exist fails with Reference nope not found., and the order in which the file is read is not the order in which targets run. An element directly under <project> is processed when the file is parsed, so every target sees it. An element inside a <target> is registered only when that target executes, so a path defined in one target and used by another that did not depend on it is not there. With only the use target the expansion stays literal:

    <target name="define"><path id="late"><pathelement location="lib"/></path></target>
    <target name="use"><echo>${toString:late}</echo></target>
    $ ant -q -f misc.xml use
         [echo] ${toString:late}
    $ ant -q -f misc.xml define use
         [echo] /work/tidepool/lib
  • The same id defined twice is not an error; the later definition silently replaces the earlier. Within a macro or a subant call the references of the calling project are copied or not depending on the inheritrefs attribute (see Multi-File Builds: import, include & subant).

  • A reference is not a snapshot. A fileset is rescanned and a cache="false" path re-evaluated each time it is used, which is what keeps a refid to the output directory correct after the compile target ran.

The <arg> element of the tasks that start programs has a path form for the same purpose: <arg path="..."/> is one argument written with either separator and converted to the platform’s, and <arg pathref="..."/> takes a path reference. <arg line="..."/> splits on spaces and the manual recommends avoiding it. ${toString:ref} is the string form of any reference, used in the first example.

The same ideas in Maven and Gradle

Neither Maven nor Gradle has a type called "path": a class path is derived from the declared dependencies. The sets of files do have counterparts:

Ant Maven 3.10.0 Gradle 9.8.1

path / classpath

computed from the dependencies and their scopes (see Dependencies, Scopes & Mediation); maven-antrun-plugin can expose it to Ant tasks

a Configuration is a FileCollection; asPath gives the string (see Declaring Dependencies & Configurations)

fileset, patternset

<includes> and <excludes> of <resource>, of the compiler plugin and of most other plugins (see Resources & Filtering)

fileTree(dir) { include(...); exclude(...) }, PatternFilterable, copy specs (see Working with Files)

dirset, multirootfileset

several <resource> entries or <sourceDirectory> plus build-helper

files(fileTree(a), fileTree(b)), srcDirs(...) of a source set

filelist

not available; list the names in the plugin configuration

files("a", "b"), an ordered FileCollection that need not exist

zipfileset, tarfileset

assembly descriptor <fileSet> with <outputDirectory> and <fileMode>, Shade resource transformers

Zip/Tar/Jar tasks with from, into("prefix"), filePermissions, zipTree, tarTree

default excludes

fixed by the plugin’s scanner

the Ant DirectoryScanner default excludes, changeable in settings.gradle(.kts)

manifestclasspath

the addClasspath setting in the archive configuration of the maven-jar-plugin

Class-Path attribute computed from runtimeClasspath

The Maven row for path is the one that most often surprises Ant users: in Maven the class path is not something one builds, it is a consequence of the POM, and a "wrong" class path is fixed by the dependency declaration and not by a pathelement. The Gradle page on Ant Integration shows how an Ant build’s paths and filesets can be reused from a Gradle build, and the page Migrating Between Tools takes the tidepool Ant build to the other two tools.

Release history of the features on this page

The features are older than most builds, so the release numbers are mostly of interest when a build file must also run on a very old Ant:

Release Change

1.5.2

filemode and dirmode of zipfileset

1.6

${toString:ref} path shortcut; <defaultexcludes> task; id and refid for zipfileset; <map> and <mapper> in pathconvert (mapper since 1.6.2); nested <file> of filelist (1.6.2)

1.7

resource collections; manifestclasspath; tarfileset as a stand-alone type; <invert> (1.7.1) and erroronmissingdir (1.7.1)

1.8.0

cache on path; preserveduplicates on pathconvert; erroronmissingarchive

1.8.1, 1.8.2

the default excludes grow to the current list; the Git, Mercurial and Bazaar patterns arrive in 1.8.2, and so does the lib/* wildcard in a pathelement

1.9.4

multirootfileset

1.10.2

symbolic links detected with Files#isSymbolicLink

1.10.4

encoding on includesfile and excludesfile

1.10.13

dest on pathconvert

1.10.16

Windows junctions detected like symbolic links

Nothing on this page is deprecated in Ant 1.10.18. The nearby tasks copydir, jlink and jspc that the manual lists as implicit-fileset users are deprecated; the migration advice is on Deprecations & Migration.

Where the books are outdated

Java Development with Ant (written for Ant 1.5, 2002) teaches paths and filesets as the only vocabulary, because resource collections, multirootfileset, cache on paths, erroronmissingdir and the includeantruntime warning did not exist yet; its examples compile without includeantruntime, which prints a warning today and is something to set to false on every javac. Its default-excludes list predates the Git, Mercurial and Bazaar entries, so the .gitignore surprise above cannot be found in it. The path and fileset rules themselves have not changed, and the book’s chapter on them is still a good introduction. The Maven 2 and Gradle books do not cover Ant paths.