Ant Documentation and XML Tasks

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 covers the Ant tasks that produce documentation and the tasks that read, check, transform or write XML: javadoc, xslt (and its deprecated name style), xmlvalidate, schemavalidate, the xmlcatalog type, echoxml and xmlproperty. It is written against Apache Ant 1.10.18 and was checked on 2026-10-16 with JDK 26 on macOS. Every build output shown below was produced by running the example on that day, on the tidepool-model module of the tidepool project plus a few small XML files; absolute paths are shortened to . and nothing else is edited. Anything that was only read in the manual, and not run, is said so. The tasks used here are all part of the core ant.jar; the tasks that need an extra library (an XSLT processor other than the JDK’s, the xml-commons resolver for external catalogs) say so where they appear.

The build-file basics used in the examples (targets, properties, filesets) are explained on Build File Structure and Paths, Filesets & Patterns. How the same jobs are done in the other two tools is summarised in each section and compared in Comparing Ant, Maven & Gradle.

The javadoc task

javadoc runs the JDK’s javadoc tool over a set of sources and writes HTML. Ant always forks a new JVM for it, because javadoc calls System.exit() and could not run inside Ant’s own JVM without ending the build. The task has no notion of "changed files" (unlike javac): every selected package is processed on each run, so it is normally kept out of the default compile-and-test path and run from a dedicated docs or dist target.

Choosing what to document

You can say which sources to document in several ways, and the result is the union of everything you specify:

Selection Meaning

sourcepath / sourcepathref / nested sourcepath

Where javadoc looks for sources. On its own it documents nothing; combine it with packagenames, a packageset or a fileset.

packagenames, nested package

Package names, with a terminating wildcard allowed (io.tidepool.*).

nested packageset

A dirset: every matched directory that holds Java sources becomes a package name. The set’s base directory must be the root of the package hierarchy.

nested fileset, sourcefiles, source

Individual source files. Use these for the default package or to exclude files. Ant adds the include pattern **/*.java itself.

excludepackagenames, nested excludepackage

Packages to leave out. They have no effect on nested packageset or fileset elements; those carry their own patterns.

modulenames, nested module (Ant 1.10.6)

Module names, used with modulesourcepath.

packagelist

A file listing packages, for migrating from a Makefile. The packages in it are not checked, so the task succeeds even if some are missing.

If you mix sourcepath with a packageset that points at the same directory, excludepackagenames does not apply unless it agrees with the packageset’s own excludes; the manual calls this out because the union is easy to forget.

A first run on tidepool-model

The tidepool-model records Height, Station, Constituent and TideEvent carry a one-line class comment. Height also uses a custom block tag, @tidepool.units. The target below documents the whole package, registers that tag, adds a heading, and groups the package on the overview page:

build.xml (javadoc target)
<target name="javadoc" description="API documentation for tidepool-model">
  <javadoc destdir="${build.dir}/api" sourcepath="${model.src}"
           packagenames="io.tidepool.*" windowtitle="tidepool-model API"
           author="false" use="true" failonerror="true" failonwarning="true">
    <doctitle><![CDATA[<h1>tidepool-model</h1>]]></doctitle>
    <tag name="tidepool.units" scope="types" description="Units:"/>
    <group title="Model" packages="io.tidepool.model*"/>
  </javadoc>
</target>

Running ant -q javadoc (the -q flag hides the per-file Generating … lines in this listing):

  [javadoc] ./tidepool-model/src/main/java/io/tidepool/model/Constituent.java:4: warning: no @param for name
  [javadoc] ./tidepool-model/src/main/java/io/tidepool/model/Constituent.java:4: warning: no @param for amplitude
  [javadoc] ./tidepool-model/src/main/java/io/tidepool/model/Constituent.java:4: warning: no @param for phase
  [javadoc] ./tidepool-model/src/main/java/io/tidepool/model/Station.java:6: warning: no @param for id
  [javadoc] ./tidepool-model/src/main/java/io/tidepool/model/Station.java:6: warning: no @param for name
  [javadoc] ./tidepool-model/src/main/java/io/tidepool/model/Station.java:6: warning: no @param for latitude
  [javadoc] ./tidepool-model/src/main/java/io/tidepool/model/Station.java:6: warning: no @param for longitude
  [javadoc] ./tidepool-model/src/main/java/io/tidepool/model/Station.java:6: warning: no @param for constituents
  [javadoc] ./tidepool-model/src/main/java/io/tidepool/model/TideEvent.java:6: warning: no @param for time
  [javadoc] ./tidepool-model/src/main/java/io/tidepool/model/TideEvent.java:6: warning: no @param for height
  [javadoc] ./tidepool-model/src/main/java/io/tidepool/model/TideEvent.java:6: warning: no @param for high
  [javadoc] 11 warnings

BUILD SUCCESSFUL
Total time: 0 seconds

The task has many more attributes than the example uses. The ones that matter most, from the manual:

Attribute Effect

destdir

Output directory. Required unless a doclet is set.

access

public, protected (the default), package or private: the lowest visibility to document.

classpath / classpathref / nested classpath

Where javadoc finds the classes your sources refer to. Missing annotation implementations here are the usual cause of ClassCastException: com.sun.tools.javadoc.ClassDocImpl (see the manual’s bug 6442982 note).

link, linkoffline, nested link

Links to other javadoc sets. With offline="true" plus packagelistLoc the task does not need the network at build time.

doctitle, header, footer, bottom, windowtitle

Page text. The nested element form takes HTML in CDATA; nested text with line breaks needs useexternalfile="true".

encoding, docencoding, charset

Source file encoding, output encoding and the charset declared in the HTML.

maxmemory

Heap for the forked javadoc JVM.

executable

Use a specific javadoc binary instead of the one in the JDK running Ant. You are responsible for it supporting the options you set.

useexternalfile

Write the file list (and, since Ant 1.7, all other options) to a temporary file. Try this first when javadoc fails with an odd error on a large project: an over-long command line is a common cause and the error message does not say so.

failonerror, failonwarning

Fail the build on a non-zero exit code, or when javadoc reports warnings. failonwarning appeared in Ant 1.9.4.

postProcessGeneratedJavadocs

On by default. Checks the output for the 2013 frame-injection hole (CVE-2013-1571, JDKs older than Java 7 update 25) and patches it. It is irrelevant on a current JDK.

additionalparam, nested arg

Any other javadoc option, for example -Xdoclint:-missing.

Some attributes and names are kept only for compatibility: old has no effect unless an external javadoc older than JDK 1.4 is used, and the task name javadoc2 is an alias that the manual says will be removed.

Grouping, tags and taglets

A nested group splits the overview page’s package table. Its packages list is colon-separated; a nested form with title and package children is needed when a title contains a space or comma. A nested tag registers a custom block tag: name, description, scope (a comma-separated subset of overview, packages, types, constructors, methods, fields, or all) and enabled. A tag with a dir attribute behaves as an implicit fileset whose files hold one tag definition per line. A nested taglet names a taglet class (name) and the path to load it from. A nested doclet swaps the whole doclet and takes param children. Taglets and doclets are ordinary javadoc extensions that run in the forked javadoc JVM, so they need nothing from Ant itself; only their class path (path, docletpath) has to be right.

failonwarning and a count that stops at one digit

A strict documentation build is the usual reason to set failonwarning="true". The build above is the trap: it has failonwarning="true", printed 11 warnings and still said BUILD SUCCESSFUL. The check in Ant 1.10.18 looks for the summary line with the regular expression (\d) warning[s]?$ applied with String.matches, which accepts exactly one digit. A run that reports 1 to 9 warnings fails the build; a run that reports 10 or more matches nothing and passes. The source at the 1.10.18 tag confirms it, and the two builds below show both sides on the same sources.

First, one warning (an unregistered tag, with the missing-@param checks switched off by -Xdoclint:-missing):

build.xml (javadoc-strict target)
<target name="javadoc-strict" description="Fails: one warning, an unregistered custom tag">
  <javadoc destdir="${build.dir}/api-strict" sourcepath="${model.src}"
           packagenames="io.tidepool.*" failonerror="true" failonwarning="true">
    <arg value="-Xdoclint:-missing"/>
  </javadoc>
</target>
  [javadoc] ./tidepool-model/src/main/java/io/tidepool/model/Height.java:6: warning: unknown tag. Unregistered custom tag?
  [javadoc]  * @tidepool.units metres
  [javadoc] 1 warning

BUILD FAILED
./build.xml:18: Javadoc issued warnings.

Total time: 0 seconds

Registering the tag fixes it, and the build is quiet and green:

build.xml (javadoc-clean target)
<target name="javadoc-clean" description="Passes: tag registered and missing-comment checks off">
  <javadoc destdir="${build.dir}/api-clean" sourcepath="${model.src}"
           packagenames="io.tidepool.*" failonerror="true" failonwarning="true">
    <tag name="tidepool.units" scope="types" description="Units:"/>
    <arg value="-Xdoclint:-missing"/>
  </javadoc>
</target>
BUILD SUCCESSFUL
Total time: 0 seconds

The practical lessons: do not rely on failonwarning as a gate when a project can produce ten or more warnings. Use -Xdoclint options to switch off the checks you do not want (as above) so that the count stays low, and check the log yourself if the gate matters. The gap is in the pattern Ant uses, not in your build file.

Source: Ant manual, javadoc; the check itself is in Javadoc.java at the rel/1.10.18 tag (the JavadocOutputStream class).

Documenting a module

Since Ant 1.10.6 the task understands the module system: modulesourcepath, modulepath and their …ref forms, plus modulenames or nested module elements. A throwaway copy of the model with a module-info.java (module io.tidepool.model, exporting the one package) is documented like this:

build.xml (javadoc-module target)
<target name="javadoc-module" description="Documents a named module">
  <javadoc destdir="${build.dir}/api-module" modulesourcepath="mods"
           modulenames="io.tidepool.model" failonerror="true" failonwarning="true">
    <tag name="tidepool.units" scope="types" description="Units:"/>
    <arg value="-Xdoclint:-missing"/>
  </javadoc>
</target>
BUILD SUCCESSFUL
Total time: 0 seconds

The output goes to build/api-module/io.tidepool.model/…, one subdirectory per module. Dependencies on other modules (requires) are found through modulepath, which takes the directories or JARs of the other modules.

In the other tools: Maven runs maven-javadoc-plugin (goal javadoc:javadoc, configured under Site and Reporting in the Maven part of this section) and Gradle’s java plugin adds a javadoc task, covered on Java Projects. Both build the argument list for you; Ant gives you every flag and no defaults.

The xslt task

xslt applies an XSLT stylesheet to documents, which is useful for turning XML documentation into HTML or for generating code and reports. style is the old name of the same task; Ant 1.10.18 still accepts it but prints a deprecation warning, as the run below shows. Prefer xslt in new build files.

The task is a directory-based task: it forms an implicit fileset (the dir attribute is called basedir) with the usual includes, excludes and defaultexcludes, and since Ant 1.7 it also accepts any nested resource collection. Two details catch people out:

  • Matched directories are processed too, and all the files under them. With the default include pattern ** that means every file. Set scanincludeddirectories="false" if that is not what you want.

  • A single document uses in and out instead of basedir and destdir.

The attributes you will use most:

Attribute or element Effect

style, nested style

The stylesheet. A nested style takes any Ant resource, so it can be a classpath resource or a URL.

destdir, extension, nested mapper

Where results go and how names are computed. The default mapper replaces the extension with extension (.html by default).

force

Rewrite targets even if they are newer than the source and the stylesheet.

granularity

Milliseconds of leeway before a file counts as out of date (Ant 1.10.16). The default is 1 second, 2 on DOS systems. It helps when the clocks of two machines or a file system’s timestamp resolution disagree.

nested param

A value for an xsl:param. type (Ant 1.9.3) can be STRING (the default), BOOLEAN, INT, LONG, DOUBLE, or one of the XPATH_* types, where the expression is evaluated by XPath and Ant properties are visible as XPath variables ($name). ${name} is substituted by Ant before XPath sees it. Attributes if and unless make a param conditional.

nested outputproperty

The XSLT output properties (method, indent, encoding, standalone).

nested factory (Ant 1.9.8), attribute, feature

Choose the TransformerFactory and set processor-specific attributes. The only feature every implementation must support is http://javax.xml.XMLConstants/feature/secure-processing.

nested xmlcatalog

Entity and URI resolution (see below).

failOnError, failOnTransformationError, failOnNoResources (Ant 1.8.0)

Which kinds of failure stop the build. All default to true; the last two have no effect if failOnError is false.

classpath, processor

Where to find an XSLT processor other than the JDK’s built-in one (Xalan, Saxon). Only trax is accepted as the processor name; Xalan 1 support was removed in Ant 1.7.

Extension functions are restricted when the JDK’s secure processing is on (the default since Java 7), and the old Ant workaround stops working on Java 9 and later. The manual points to the http://www.oracle.com/xml/jaxp/properties/enableExtensionFunctions feature, or to the jdk.xml.transform.extensionClassLoader factory attribute, for extension functions that are not part of the Java class library.

Example: stations to a text report

The stylesheet xml/stations.xsl lists each station, preceded by a heading taken from the heading parameter:

xml/stations.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text"/>
  <xsl:param name="heading" select="'Stations'"/>
  <xsl:template match="/stations">
    <xsl:value-of select="$heading"/>
    <xsl:text>&#10;</xsl:text>
    <xsl:for-each select="station">
      <xsl:value-of select="@id"/><xsl:text> | </xsl:text><xsl:value-of select="@name"/><xsl:text>&#10;</xsl:text>
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>
build.xml (xslt target)
<target name="xslt">
  <xslt in="xml/stations.xml" out="${build.dir}/stations.txt" style="xml/stations.xsl">
    <param name="heading" expression="Stations in ${ant.project.name}"/>
  </xslt>
  <loadfile srcfile="${build.dir}/stations.txt" property="stations.txt"/>
  <echo>${stations.txt}</echo>
</target>
     [echo] Stations in tidepool-docs
     [echo] SF | San Francisco
     [echo] BOS | Boston
     [echo] HNL | Honolulu

BUILD SUCCESSFUL
Total time: 0 seconds

Up-to-date checks, granularity and the deprecated name

The target below calls xslt twice in a row with the same inputs, then calls style with force="true":

build.xml (xslt-incremental target)
<target name="xslt-incremental">
  <delete file="${build.dir}/s.txt"/>
  <xslt in="xml/stations.xml" out="${build.dir}/s.txt" style="xml/stations.xsl" granularity="2000"/>
  <xslt in="xml/stations.xml" out="${build.dir}/s.txt" style="xml/stations.xsl" granularity="2000"/>
  <style in="xml/stations.xml" out="${build.dir}/s.txt" style="xml/stations.xsl" force="true"/>
</target>
Buildfile: ./build.xml

xslt-incremental:
     [xslt] Processing ./xml/stations.xml to ./build/s.txt
     [xslt] Loading stylesheet stations.xsl
    [style] Warning: the task name <style> is deprecated. Use <xslt> instead.
    [style] Processing ./xml/stations.xml to ./build/s.txt
    [style] Loading stylesheet stations.xsl

BUILD SUCCESSFUL

The second xslt produced no output at all: the target was newer than both the document and the stylesheet, so the task skipped it. force="true" made the style call run again, and the deprecation warning appears only for the old name.

In the other tools: Maven has no XSLT in the core; the Mojo xml-maven-plugin provides a transform goal. Gradle has no XSLT task either; a build script can use the JDK’s javax.xml.transform directly or call Ant’s xslt through the Ant integration.

Validating XML

Three pieces cooperate: the validating tasks, the parser they use, and the catalog that tells the parser where to find DTDs and schemas locally.

xmlvalidate

xmlvalidate checks that files are well formed or valid. It uses the JAXP SAX2 parser (normally the JDK’s built-in Xerces) and accepts file or a nested fileset. lenient="true" reduces the check to well-formedness; warn logs parser warnings; failonerror defaults to true; classname and classpath select another parser. Nested dtd, xmlcatalog, attribute (parser features such as http://xml.org/sax/features/validation) and property (parser properties, for example the Xerces external-noNamespaceSchemaLocation that points at an XSD) configure it.

The manual flags one trap: the JAXP parser Ant creates is not namespace aware, so a document that uses a xsi:noNamespaceSchemaLocation attribute needs <attribute name="http://xml.org/sax/features/namespaces" value="true"/>. That is also why the manual recommends schemavalidate for XSD validation.

Well-formedness check, run against xml/stations.xml:

build.xml (validate-wellformed target)
<target name="validate-wellformed">
  <xmlvalidate lenient="true" file="xml/stations.xml"/>
</target>
BUILD SUCCESSFUL
Total time: 0 seconds

schemavalidate

schemavalidate extends xmlvalidate for W3C XML Schema. It makes the parser validating, namespace aware and schema-aware by itself. A schema for documents without a namespace comes from noNamespaceFile or noNamespaceURL; namespaced schemas are bound with nested schema elements (namespace plus file or url). fullchecking (slow but strict) defaults to true, and disableDTD turns DTD support off. Only SAX2 parsers are allowed.

The schema xml/stations.xsd requires four attributes on every station, with latitude and longitude typed xs:decimal. A valid file passes silently:

build.xml (validate-xsd target)
<target name="validate-xsd">
  <schemavalidate noNamespaceFile="xml/stations.xsd" file="xml/stations.xml"/>
</target>
BUILD SUCCESSFUL
Total time: 0 seconds

Running the same task on xml/stations-bad.xml, where one station has latitude="north", fails with the parser’s messages and a generic summary line:

[schemavalidate] ./xml/stations-bad.xml:3:81: cvc-datatype-valid.1.2.1: 'north' is not a valid value for 'decimal'.
[schemavalidate] ./xml/stations-bad.xml:3:81: cvc-attribute.3: The value 'north' of attribute 'latitude' on element 'station' is not valid with respect to its type, 'decimal'.

BUILD FAILED
./build.xml:60: ./xml/stations-bad.xml is not a valid XML document.

Total time: 0 seconds

The 3:81 is line and column in the data file; the cvc-… codes come from the parser, so you can look them up in the XML Schema specification. The summary line names the file but not the cause, so always read the lines above it.

The manual also shows a presetdef (see Macros & Presets) that wraps schemavalidate with a set of schema bindings, so that a project validates SOAP or WSDL files with one short name. That example needs the schema files from W3C and is not run here.

XML catalogs

A DTD or schema location in a document is often a web URL. Validating then needs the network, is slow, and breaks when the host is gone. An xmlcatalog maps public identifiers to local copies. It can be declared inside a task or, as an id-ed element at the same level as targets, shared by xmlvalidate, schemavalidate, xslt and xmlproperty (xslt uses it for both entity and URI resolution).

Inline entries are dtd and entity (both publicId plus location; a relative location is resolved against the project base directory). Ant looks a location up in this order: the file system, the classpath, the xml-commons resolver if its JAR is on the classpath, and finally as a URL. External catalog files in a nested catalogpath are ignored, with a warning, unless the xml-commons resolver version 1.1 or later is available; inline entries work either way. Catalogs can nest, and refid points to another one.

The file xml/stations-dtd.xml declares its DTD by a public identifier and a system identifier on a reserved, unresolvable host:

<!DOCTYPE stations PUBLIC "-//Tidepool//DTD Stations 1.0//EN" "http://dtd.tidepool.invalid/stations.dtd">

Without a catalog the parser tries the URL and the build fails. At the default log level the cause is hidden behind a generic message; -v adds the stack trace but not the host name:

build.xml (validate-dtd-nocatalog target)
<target name="validate-dtd-nocatalog">
  <xmlvalidate file="xml/stations-dtd.xml"/>
</target>
BUILD FAILED
./build.xml:42: Could not validate document ./xml/stations-dtd.xml

Total time: 0 seconds

With a catalog that maps the public identifier to the local xml/stations.dtd, the same document validates:

build.xml (xmlcatalog and validate-dtd)
<xmlcatalog id="tidepool.catalog">
  <dtd publicId="-//Tidepool//DTD Stations 1.0//EN" location="xml/stations.dtd"/>
</xmlcatalog>

<target name="validate-dtd">
  <xmlvalidate file="xml/stations-dtd.xml">
    <xmlcatalog refid="tidepool.catalog"/>
  </xmlvalidate>
</target>
BUILD SUCCESSFUL
Total time: 0 seconds

Running the failing target with -v also prints Apache resolver library not found, internal resolver will be used, which is the expected line when no xml-commons resolver JAR is present and only inline entries are used. Sources: Ant manual, XMLCatalog, Ant manual, xmlvalidate

echoxml and xmlproperty

echoxml

echoxml writes the XML nested inside it, as XML, to the log or to a file (file, append). Since Ant 1.8 the namespacePolicy attribute (ignore, the default, elementsOnly or all) controls how namespaces are written. It is the simplest way to generate a build file or a configuration file from a template without <concat> and escaping.

build.xml (echoxml target)
<target name="echoxml">
  <mkdir dir="${build.dir}"/>
  <echoxml file="${build.dir}/subbuild.xml">
    <project default="foo">
      <target name="foo"><echo>foo</echo></target>
    </project>
  </echoxml>
  <loadfile srcfile="${build.dir}/subbuild.xml" property="subbuild"/>
  <echo>${subbuild}</echo>
</target>
     [echo] <?xml version="1.0" encoding="UTF-8"?>
     [echo] <project default="foo">
     [echo]   <target name="foo">
     [echo]     <echo>foo</echo>
     [echo]   </target>
     [echo] </project>

BUILD SUCCESSFUL
Total time: 0 seconds

xmlproperty

xmlproperty loads the values of an XML file into properties: the element path becomes the property name. By default it does no processing, so a reference such as ${foo} inside the file is not expanded. The attributes that change the result:

Attribute Effect

file or nested resource

The XML file. It must be well formed; validate="true" also checks it against its DTD.

prefix

Text prepended to each property name.

keepRoot

Keep the root element name as the first segment. Default true. With false, the root element’s own attributes are not turned into properties.

collapseAttributes

Treat attributes like nested elements: a(b) becomes a.b. Default false.

semanticAttributes

Give a few attribute names special meaning: value sets the property, location makes a file path, refid copies another property, id and pathid define references and paths; property references such as ${name} are resolved. Default false.

includeSemanticAttribute, rootDirectory, delimiter

Keep the semantic attribute’s name in the property name; the base for location; and the separator for multiple values (comma by default, since Ant 1.7.1).

With this file, xml/build-info.xml:

<build-info release="true">
  <version value="1.2.0"/>
  <target java="21"/>
  <notes>nightly</notes>
</build-info>

the target reads it three ways:

build.xml (xmlproperty target)
<target name="xmlproperty">
  <xmlproperty file="xml/build-info.xml"/>
  <echo>build-info.version(value)=${build-info.version(value)}</echo>
  <echo>build-info.notes=${build-info.notes}</echo>
  <xmlproperty file="xml/build-info.xml" prefix="b" keepRoot="false" collapseAttributes="true"/>
  <echo>b.version.value=${b.version.value}  b.target.java=${b.target.java}  b.release=${b.release}</echo>
  <xmlproperty file="xml/build-info.xml" prefix="s" keepRoot="false" semanticAttributes="true"/>
  <echo>s.version=${s.version}</echo>
</target>
     [echo] build-info.version(value)=1.2.0
     [echo] build-info.notes=nightly
     [echo] b.version.value=1.2.0  b.target.java=21  b.release=${b.release}
     [echo] s.version=1.2.0

The first call keeps the default names, where an attribute is written element(attribute). The second collapses attributes into dotted names. Its last item shows that with keepRoot="false" the root’s own attribute (release) produced no property, so the reference is left as written. The third uses semanticAttributes: <version value="1.2.0"/> becomes the single property s.version.

Properties are immutable once set (see Properties), so a second xmlproperty with the same names changes nothing; use a prefix, as above, when you read the same file twice.

Where the books and older build files are outdated

Older build files, and books written around Ant 1.6 to 1.8, show a few patterns that no longer apply on 1.10.18:

  • <style> is still accepted but warns; <xslt> is the name to use.

  • Xalan 1 support in xslt was removed in Ant 1.7; the only value for processor is trax.

  • Javadoc examples written for JDK 8 link to the JDK 8 API URL and know nothing about Java modules; documenting a modular project needs modulesourcepath, modulenames and modulepath (Ant 1.10.6 and later), and sources that use annotations need their implementations on the classpath.

  • XML Catalog examples often rely on an external catalog file, which needs the xml-commons resolver JAR on the classpath; inline dtd and entity entries need nothing.

References