Originally published on 2009-01-28; fully revised and updated.

Apache Ant is a Java-based build automation tool that defines repeatable build steps in XML. This guide is for engineers maintaining Java build pipelines who want a clear refresher on Ant fundamentals: build file anatomy, dependency-driven execution, command-line usage, packaging, filesystem operations, and custom task extension.
Apache Ant at a glance
Ant is most useful when you need explicit control over build steps and cross-platform behavior without relying on shell-specific scripts. The original tutorial compared Ant to make; in modern teams, Ant is also often evaluated alongside Maven and Gradle.
| Tool | Configuration style | Typical strength | Common trade-off |
|---|---|---|---|
| Ant | Explicit XML tasks and targets | Fine-grained control of build steps | Verbose configuration for large projects |
| Make | Rules plus shell commands | Simple and language-agnostic | Portability issues across shells and platforms |
| Maven | Convention-driven lifecycle and plugins | Standardized Java project structure | Less explicit control over each step |
| Gradle | Programmable DSL (Groovy or Kotlin) | Flexible build logic and ecosystem integration | Higher complexity for teams new to DSL-based builds |
Anatomy of a minimal build.xml
An Ant build file has one top-level project, with one or more target elements. Targets contain tasks (such as javac, jar, copy, delete) that perform the actual work.
<?xml version='1.0'?>
<project name='demo' default='hello' basedir='.'>
<target name='hello'>
<echo message='Hello from Ant'/>
</target>
</project>Code language: HTML, XML (xml)
Properties are immutable once set, and are typically used for paths and reusable values.
<property name='src.dir' location='src'/>
<property name='build.dir' location='build'/>
<target name='compile'>
<javac srcdir='${src.dir}' destdir='${build.dir}' debug='true'/>
</target>Code language: HTML, XML (xml)
Dependency-driven build flow
Ant runs targets based on dependencies, not file order. If dist depends on compile, and compile depends on init, Ant resolves that chain automatically.
flowchart TD
A[Run ant dist] --> B[Resolve dependencies]
B --> C[init target]
C --> D[compile target]
D --> E[dist target]
E --> F[Jar artifact in dist directory]
<target name='init'>
<mkdir dir='build'/>
</target>
<target name='compile' depends='init'>
<javac srcdir='src' destdir='build'/>
</target>
<target name='dist' depends='compile'>
<jar destfile='dist/app.jar' basedir='build'/>
</target>Code language: HTML, XML (xml)
Running Ant from the command line
The default command is simply ant, which executes the build file’s default target. You can then override file names, set properties, or call specific targets.
| Command or option | What it does | Example |
|---|---|---|
| ant | Runs default target in build.xml | ant |
| -f or -buildfile | Uses a non-default build file name | ant -f build-prod.xml dist |
| -Dname=value | Sets a property before parsing build.xml | ant -Denv=staging clean dist |
| -p | Prints available targets | ant -p |
| target names | Runs one or more explicit targets | ant clean compile dist |
ant
ant -f build.xml clean dist
ant -Dmetal=beryllium compile
ant -p
The original material also showed Eclipse integration (Ant editor, target execution, and console output). Those concepts remain useful, though exact UI steps vary by Eclipse version.
Complete example: compile, package, and timestamp a Java build
This example mirrors the classic Ant workflow: initialize directories, compile Java sources, and create a date-stamped JAR.
<project name='java-example' default='dist' basedir='.'>
<description>Simple Java project build</description>
<property name='src.dir' location='src'/>
<property name='build.dir' location='build'/>
<property name='dist.dir' location='dist'/>
<target name='clean'>
<delete dir='${build.dir}'/>
<delete dir='${dist.dir}'/>
</target>
<target name='init'>
<tstamp/>
<mkdir dir='${build.dir}'/>
<mkdir dir='${dist.dir}'/>
</target>
<target name='compile' depends='init'>
<javac srcdir='${src.dir}' destdir='${build.dir}' debug='true'/>
</target>
<target name='dist' depends='compile'>
<jar destfile='${dist.dir}/package-${DSTAMP}.jar' basedir='${build.dir}'>
<manifest>
<attribute name='Main-Class' value='com.example.Main'/>
</manifest>
</jar>
</target>
</project>Code language: HTML, XML (xml)
ant clean dist
java -jar dist/package-*.jar
Filesystem tasks, patterns, and selectors
Ant includes strong cross-platform filesystem primitives. Common tasks include mkdir, delete, copy, move, zip, jar, and replace. Pattern-based filesets and selectors let you work on groups of files safely and repeatably.
- Use fileset include/exclude for wildcard matching such as
**/*.java. - Use selectors (size, date, contains, and logical containers) for precise file filtering.
- Keep path handling portable by favoring Ant path handling and relative locations.
<copy todir='${build.dir}/resources'>
<fileset dir='src'>
<include name='**/*.properties'/>
<exclude name='**/*test*'/>
</fileset>
</copy>
<replace file='config/app.properties' token='@BUILD_DATE@' value='${DSTAMP}'/>Code language: HTML, XML (xml)
<fileset dir='src'>
<include name='**/*.txt'/>
<and>
<size value='512' when='more'/>
<contains text='hello' casesensitive='no'/>
</and>
</fileset>Code language: HTML, XML (xml)
Extending Ant with a custom task
Ant can be extended by writing Java classes that extend org.apache.tools.ant.Task and implement execute(). This is useful when shelling out with exec would make builds less portable or harder to maintain.
import java.io.File;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;
public class FileSorter extends Task {
private File file;
private File tofile;
public void setFile(File file) { this.file = file; }
public void setTofile(File tofile) { this.tofile = tofile; }
@Override
public void execute() throws BuildException {
// read file, sort lines, write to tofile
}
}Code language: JavaScript (javascript)
<project name='custom-task-demo' default='main' basedir='.'>
<taskdef name='filesorter' classname='FileSorter' classpath='.'/>
<target name='main'>
<filesorter file='input.txt' tofile='output.txt'/>
</target>
</project>Code language: HTML, XML (xml)
javac -cp $ANT_HOME/lib/ant.jar FileSorter.java
ant -f build-custom.xml mainCode language: PHP (php)
Resources and attribution
This topic was originally presented as an IBM developerWorks tutorial by Matt Chapman, and this revised version keeps the core concepts while modernizing structure and examples.
- Apache Ant home page
- Ant IDE and editor integration
- Eclipse
- Cygwin
- CVS
- Original reference link from the legacy post: http://www.ibm.com/developerworks/edu/j-dw-java-apant-i.html
Best practices and common pitfalls
- Split large builds into logical targets and use
dependsfor order, not file position. - Use properties for directories and artifact names; avoid hard-coded absolute paths.
- Keep a
cleantarget to remove generated output and support reproducible rebuilds. - Prefer filesets and selectors over ad hoc file loops for clarity and portability.
- Use custom tasks when behavior is reused across builds; otherwise keep logic in standard tasks.
- Treat older Ant, CVS, and IDE-specific guidance as historical context and validate against your current toolchain.
I’m Rajesh Kumar, a DevOps, SRE, DevSecOps, Cloud, and Platform Engineering expert passionate about sharing practical knowledge, real-world experiences, and industry best practices. I have worked at Cotocus and regularly write about technology, travel, investing, health, product reviews, and digital marketing through my various platforms.
I publish technical articles at DevOps School, travel stories at Holiday Landmark, stock market insights at Stocks Mantra, health and fitness guidance at My Medic Plus, product reviews at TrueReviewNow, and SEO and digital marketing strategies at Wizbrand.
Find Trusted Cardiac Hospitals
Compare heart hospitals by city and services — all in one place.
Explore Hospitals
Really enjoyed this post — you explained Apache Ant in such a clear, beginner-friendly way that it didn’t feel intimidating at all. Loved how you broke things down without overcomplicating them. Super helpful read!