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 |
|---|---|
|
Where javadoc looks for sources. On its own it documents nothing; combine it with |
|
Package names, with a terminating wildcard allowed ( |
nested |
A |
nested |
Individual source files. Use these for the default package or to exclude files. Ant adds the include pattern
|
|
Packages to leave out. They have no effect on nested |
|
Module names, used with |
|
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:
<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
Source: Ant manual, javadoc
The task has many more attributes than the example uses. The ones that matter most, from the manual:
| Attribute | Effect |
|---|---|
|
Output directory. Required unless a doclet is set. |
|
|
|
Where javadoc finds the classes your sources refer to. Missing annotation implementations here are the usual cause of
|
|
Links to other javadoc sets. With |
|
Page text. The nested element form takes HTML in CDATA; nested text with line breaks needs |
|
Source file encoding, output encoding and the charset declared in the HTML. |
|
Heap for the forked javadoc JVM. |
|
Use a specific |
|
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. |
|
Fail the build on a non-zero exit code, or when javadoc reports warnings. |
|
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. |
|
Any other javadoc option, for example |
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):
<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:
<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:
<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.
Source: Ant manual, javadoc
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. Setscanincludeddirectories="false"if that is not what you want. -
A single document uses
inandoutinstead ofbasediranddestdir.
The attributes you will use most:
| Attribute or element | Effect |
|---|---|
|
The stylesheet. A nested |
|
Where results go and how names are computed. The default mapper replaces the extension with |
|
Rewrite targets even if they are newer than the source and the stylesheet. |
|
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 |
A value for an |
nested |
The XSLT output properties ( |
nested |
Choose the |
nested |
Entity and URI resolution (see below). |
|
Which kinds of failure stop the build. All default to |
|
Where to find an XSLT processor other than the JDK’s built-in one (Xalan, Saxon). Only |
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 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> </xsl:text>
<xsl:for-each select="station">
<xsl:value-of select="@id"/><xsl:text> | </xsl:text><xsl:value-of select="@name"/><xsl:text> </xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
<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
Source: Ant manual, xslt (style)
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":
<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.
Source: Ant manual, xslt (style)
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:
<target name="validate-wellformed">
<xmlvalidate lenient="true" file="xml/stations.xml"/>
</target>
BUILD SUCCESSFUL
Total time: 0 seconds
Source: Ant manual, xmlvalidate
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:
<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.
Source: Ant manual, schemavalidate
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:
<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:
<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
Source: Apache Ant manual: XMLCatalog and 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.
<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
Source: Ant manual, echoxml
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 |
|---|---|
|
The XML file. It must be well formed; |
|
Text prepended to each property name. |
|
Keep the root element name as the first segment. Default |
|
Treat attributes like nested elements: |
|
Give a few attribute names special meaning: |
|
Keep the semantic attribute’s name in the property name; the base for |
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:
<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.
Source: Ant manual, xmlproperty
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
xsltwas removed in Ant 1.7; the only value forprocessoristrax. -
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,modulenamesandmodulepath(Ant 1.10.6 and later), and sources that use annotations need their implementations on theclasspath. -
XML Catalog examples often rely on an external catalog file, which needs the xml-commons resolver JAR on the classpath; inline
dtdandentityentries need nothing.
References
-
Apache Ant manual: javadoc
-
Apache Ant manual: xslt / style
-
Apache Ant manual: xmlvalidate
-
Apache Ant manual: schemavalidate
-
Apache Ant manual: XMLCatalog
-
Apache Ant manual: echoxml
-
Apache Ant manual: xmlproperty
-
Apache Ant source, 1.10.18 tag: Javadoc.java