Skip to content

Ant Cheatsheet

Overview

Apache Ant is a Java-based build tool that uses XML to describe the build process and its dependencies. It's platform-independent and designed to be extensible through Java classes.

Installation

Package Managers

bash
# macOS
brew install ant

# Ubuntu/Debian
sudo apt install ant

# CentOS/RHEL
sudo yum install ant

# Windows (Chocolatey)
choco install ant

# Manual installation
wget https://archive.apache.org/dist/ant/binaries/apache-ant-1.10.12-bin.tar.gz
tar -xzf apache-ant-1.10.12-bin.tar.gz
export ANT_HOME=/path/to/apache-ant-1.10.12
export PATH=$PATH:$ANT_HOME/bin

Verification

bash
ant -version

Basic Concepts

Key Terms

Build File    # XML file (usually build.xml)
Project       # Root element of build file
Target        # Set of tasks to execute
Task          # Unit of work (compile, copy, etc.)
Property      # Name-value pair for configuration

Project Structure

project/
├── build.xml
├── src/
│   ├── main/
│   │   └── java/
│   └── test/
│       └── java/
├── lib/
├── build/
└── dist/

Basic build.xml

Minimal Example

xml
<?xml version="1.0" encoding="UTF-8"?>
<project name="MyProject" default="compile" basedir=".">
    <description>A simple Java project</description>
    
    <!-- Properties -->
    <property name="src.dir" value="src"/>
    <property name="build.dir" value="build"/>
    <property name="classes.dir" value="${build.dir}/classes"/>
    
    <!-- Targets -->
    <target name="init">
        <mkdir dir="${build.dir}"/>
        <mkdir dir="${classes.dir}"/>
    </target>
    
    <target name="compile" depends="init">
        <javac srcdir="${src.dir}" destdir="${classes.dir}"/>
    </target>
    
    <target name="clean">
        <delete dir="${build.dir}"/>
    </target>
</project>

Complete Example

xml
<?xml version="1.0" encoding="UTF-8"?>
<project name="MyProject" default="build" basedir=".">
    <description>Complete Java project build file</description>
    
    <!-- Properties -->
    <property name="src.dir" value="src/main/java"/>
    <property name="test.src.dir" value="src/test/java"/>
    <property name="resources.dir" value="src/main/resources"/>
    <property name="lib.dir" value="lib"/>
    <property name="build.dir" value="build"/>
    <property name="classes.dir" value="${build.dir}/classes"/>
    <property name="test.classes.dir" value="${build.dir}/test-classes"/>
    <property name="dist.dir" value="dist"/>
    <property name="jar.file" value="${dist.dir}/${ant.project.name}.jar"/>
    
    <!-- Classpath -->
    <path id="classpath">
        <fileset dir="${lib.dir}">
            <include name="*.jar"/>
        </fileset>
    </path>
    
    <path id="test.classpath">
        <path refid="classpath"/>
        <pathelement location="${classes.dir}"/>
        <pathelement location="${test.classes.dir}"/>
    </path>
    
    <!-- Targets -->
    <target name="init">
        <mkdir dir="${build.dir}"/>
        <mkdir dir="${classes.dir}"/>
        <mkdir dir="${test.classes.dir}"/>
        <mkdir dir="${dist.dir}"/>
    </target>
    
    <target name="compile" depends="init">
        <javac srcdir="${src.dir}" 
               destdir="${classes.dir}"
               classpathref="classpath"
               debug="true"
               includeantruntime="false"/>
        <copy todir="${classes.dir}">
            <fileset dir="${resources.dir}"/>
        </copy>
    </target>
    
    <target name="compile-tests" depends="compile">
        <javac srcdir="${test.src.dir}" 
               destdir="${test.classes.dir}"
               classpathref="test.classpath"
               debug="true"
               includeantruntime="false"/>
    </target>
    
    <target name="test" depends="compile-tests">
        <junit printsummary="yes" haltonfailure="yes">
            <classpath refid="test.classpath"/>
            <formatter type="plain"/>
            <batchtest fork="yes" todir="${build.dir}">
                <fileset dir="${test.src.dir}">
                    <include name="**/*Test.java"/>
                </fileset>
            </batchtest>
        </junit>
    </target>
    
    <target name="jar" depends="compile">
        <jar destfile="${jar.file}" basedir="${classes.dir}">
            <manifest>
                <attribute name="Main-Class" value="com.example.Main"/>
                <attribute name="Class-Path" value=". lib/"/>
            </manifest>
        </jar>
    </target>
    
    <target name="build" depends="test,jar"/>
    
    <target name="clean">
        <delete dir="${build.dir}"/>
        <delete dir="${dist.dir}"/>
    </target>
    
    <target name="rebuild" depends="clean,build"/>
</project>

Properties

Property Definition

xml
<!-- Simple properties -->
<property name="version" value="1.0.0"/>
<property name="src.dir" value="src"/>

<!-- Properties from file -->
<property file="build.properties"/>

<!-- Environment properties -->
<property environment="env"/>
<echo message="Java Home: ${env.JAVA_HOME}"/>

<!-- System properties -->
<property name="java.version" value="${java.version}"/>

<!-- Conditional properties -->
<condition property="isWindows">
    <os family="windows"/>
</condition>

<!-- Property with default value -->
<property name="build.type" value="debug"/>

Property Files

properties
# build.properties
version=1.0.0
src.dir=src/main/java
test.dir=src/test/java
lib.dir=lib
build.dir=build

Tasks

File Operations

xml
<!-- Copy files -->
<copy todir="${build.dir}/resources">
    <fileset dir="${src.dir}/resources"/>
</copy>

<!-- Copy with filtering -->
<copy todir="${build.dir}/config" filtering="true">
    <fileset dir="config"/>
    <filterset>
        <filter token="VERSION" value="${version}"/>
        <filter token="BUILD_DATE" value="${timestamp}"/>
    </filterset>
</copy>

<!-- Move files -->
<move todir="${backup.dir}">
    <fileset dir="${build.dir}">
        <include name="*.log"/>
    </fileset>
</move>

<!-- Delete files -->
<delete dir="${build.dir}"/>
<delete>
    <fileset dir="." includes="**/*.tmp"/>
</delete>

<!-- Create directories -->
<mkdir dir="${build.dir}/classes"/>

<!-- Create archive -->
<zip destfile="${dist.dir}/source.zip">
    <fileset dir="${src.dir}"/>
</zip>

<tar destfile="${dist.dir}/source.tar.gz" compression="gzip">
    <fileset dir="${src.dir}"/>
</tar>

Compilation Tasks

xml
<!-- Java compilation -->
<javac srcdir="${src.dir}" 
       destdir="${classes.dir}"
       classpathref="classpath"
       debug="true"
       deprecation="true"
       optimize="false"
       includeantruntime="false">
    <include name="**/*.java"/>
    <exclude name="**/Test*.java"/>
</javac>

<!-- Create JAR -->
<jar destfile="${dist.dir}/myapp.jar" 
     basedir="${classes.dir}"
     includes="**/*.class">
    <manifest>
        <attribute name="Main-Class" value="com.example.Main"/>
        <attribute name="Implementation-Version" value="${version}"/>
        <attribute name="Built-By" value="${user.name}"/>
        <attribute name="Built-Date" value="${timestamp}"/>
    </manifest>
</jar>

<!-- Create WAR -->
<war destfile="${dist.dir}/myapp.war" webxml="web.xml">
    <fileset dir="webapp"/>
    <lib dir="lib"/>
    <classes dir="${classes.dir}"/>
</war>

Testing Tasks

xml
<!-- JUnit testing -->
<junit printsummary="yes" 
       haltonfailure="yes" 
       haltonerror="yes"
       fork="yes">
    <classpath refid="test.classpath"/>
    <formatter type="plain"/>
    <formatter type="xml"/>
    
    <!-- Run specific test -->
    <test name="com.example.MyTest" todir="${test.reports.dir}"/>
    
    <!-- Run all tests -->
    <batchtest fork="yes" todir="${test.reports.dir}">
        <fileset dir="${test.src.dir}">
            <include name="**/*Test.java"/>
            <exclude name="**/Abstract*Test.java"/>
        </fileset>
    </batchtest>
</junit>

<!-- Generate test reports -->
<junitreport todir="${test.reports.dir}">
    <fileset dir="${test.reports.dir}">
        <include name="TEST-*.xml"/>
    </fileset>
    <report format="frames" todir="${test.reports.dir}/html"/>
</junitreport>

Targets and Dependencies

Target Dependencies

xml
<!-- Simple dependency -->
<target name="compile" depends="init">
    <!-- Compile tasks -->
</target>

<!-- Multiple dependencies -->
<target name="build" depends="clean,compile,test,jar">
    <!-- Build tasks -->
</target>

<!-- Conditional targets -->
<target name="compile-debug" if="debug.enabled">
    <javac srcdir="${src.dir}" destdir="${classes.dir}" debug="true"/>
</target>

<target name="compile-release" unless="debug.enabled">
    <javac srcdir="${src.dir}" destdir="${classes.dir}" optimize="true"/>
</target>

<!-- Target with description -->
<target name="help" description="Show available targets">
    <echo message="Available targets:"/>
    <echo message="  compile - Compile source code"/>
    <echo message="  test    - Run unit tests"/>
    <echo message="  jar     - Create JAR file"/>
    <echo message="  clean   - Clean build directory"/>
</target>

Conditional Execution

xml
<!-- Check conditions -->
<target name="check-java-version">
    <condition property="java.version.ok">
        <or>
            <contains string="${java.version}" substring="1.8"/>
            <contains string="${java.version}" substring="11"/>
            <contains string="${java.version}" substring="17"/>
        </or>
    </condition>
    <fail unless="java.version.ok" 
          message="Java 8, 11, or 17 required. Found: ${java.version}"/>
</target>

<!-- Platform-specific targets -->
<target name="init-windows" if="isWindows">
    <property name="script.ext" value=".bat"/>
</target>

<target name="init-unix" unless="isWindows">
    <property name="script.ext" value=".sh"/>
</target>

Macros and Custom Tasks

Macrodefs

xml
<!-- Define reusable macro -->
<macrodef name="compile-module">
    <attribute name="module"/>
    <attribute name="srcdir" default="src/@{module}/java"/>
    <attribute name="destdir" default="build/@{module}/classes"/>
    <sequential>
        <mkdir dir="@{destdir}"/>
        <javac srcdir="@{srcdir}" 
               destdir="@{destdir}"
               classpathref="classpath"
               includeantruntime="false"/>
    </sequential>
</macrodef>

<!-- Use macro -->
<target name="compile-all">
    <compile-module module="core"/>
    <compile-module module="web"/>
    <compile-module module="api"/>
</target>

<!-- Macro with nested elements -->
<macrodef name="run-tests">
    <attribute name="module"/>
    <element name="test-elements" implicit="true"/>
    <sequential>
        <junit printsummary="yes" haltonfailure="yes">
            <classpath refid="test.classpath.@{module}"/>
            <test-elements/>
        </junit>
    </sequential>
</macrodef>

Custom Tasks

xml
<!-- Load custom task -->
<taskdef name="mytask" classname="com.example.MyTask">
    <classpath>
        <pathelement location="lib/custom-tasks.jar"/>
    </classpath>
</taskdef>

<!-- Use custom task -->
<target name="custom-operation">
    <mytask param1="value1" param2="value2"/>
</target>

Advanced Features

Parallel Execution

xml
<!-- Parallel execution -->
<target name="build-parallel">
    <parallel>
        <sequential>
            <antcall target="compile-core"/>
            <antcall target="test-core"/>
        </sequential>
        <sequential>
            <antcall target="compile-web"/>
            <antcall target="test-web"/>
        </sequential>
    </parallel>
</target>

Subprojects

xml
<!-- Build subprojects -->
<target name="build-all">
    <subant target="build">
        <fileset dir="." includes="*/build.xml"/>
    </subant>
</target>

<!-- Call specific target in subproject -->
<target name="clean-all">
    <ant dir="core" target="clean"/>
    <ant dir="web" target="clean"/>
    <ant dir="api" target="clean"/>
</target>

Import and Include

xml
<!-- Import external build file -->
<import file="common-build.xml"/>

<!-- Include external file -->
<include file="properties.xml"/>

<!-- Import with override -->
<import file="base-build.xml"/>
<target name="compile" depends="base.compile">
    <!-- Override or extend base compile target -->
    <echo message="Custom compilation step"/>
</target>

Command Line Usage

Basic Commands

bash
# Run default target
ant

# Run specific target
ant compile
ant clean
ant test

# Run multiple targets
ant clean compile test

# List available targets
ant -projecthelp
ant -p

# Verbose output
ant -verbose compile
ant -v compile

# Debug output
ant -debug compile
ant -d compile

# Quiet output
ant -quiet compile
ant -q compile

Setting Properties

bash
# Set properties from command line
ant -Dversion=2.0.0 -Ddebug.enabled=true compile

# Use different build file
ant -buildfile mybuild.xml compile
ant -f mybuild.xml compile

# Set logger
ant -logger org.apache.tools.ant.DefaultLogger compile

# Find build file
ant -find build.xml compile

Advanced Options

bash
# Keep going on failure
ant -keep-going build

# Use input handler
ant -inputhandler org.apache.tools.ant.input.DefaultInputHandler

# Set log level
ant -loglevel info compile

# Disable input
ant -noinput compile

# Show version
ant -version

Integration

IDE Integration

xml
<!-- Eclipse integration -->
<target name="eclipse" description="Generate Eclipse project files">
    <copy file="templates/.project" tofile=".project"/>
    <copy file="templates/.classpath" tofile=".classpath"/>
    <replace file=".classpath" token="@PROJECT_NAME@" value="${ant.project.name}"/>
</target>

<!-- IntelliJ integration -->
<target name="idea" description="Generate IntelliJ project files">
    <mkdir dir=".idea"/>
    <copy todir=".idea">
        <fileset dir="templates/idea"/>
    </copy>
</target>

CI/CD Integration

xml
<!-- Jenkins integration -->
<target name="ci-build" description="Continuous integration build">
    <antcall target="clean"/>
    <antcall target="compile"/>
    <antcall target="test"/>
    <antcall target="package"/>
    <antcall target="deploy"/>
</target>

<!-- Generate reports for CI -->
<target name="ci-reports">
    <junitreport todir="${reports.dir}">
        <fileset dir="${test.reports.dir}">
            <include name="TEST-*.xml"/>
        </fileset>
        <report format="frames" todir="${reports.dir}/junit"/>
    </junitreport>
</target>

Version Control

xml
<!-- Git integration -->
<target name="git-info">
    <exec executable="git" outputproperty="git.revision">
        <arg value="rev-parse"/>
        <arg value="HEAD"/>
    </exec>
    <exec executable="git" outputproperty="git.branch">
        <arg value="rev-parse"/>
        <arg value="--abbrev-ref"/>
        <arg value="HEAD"/>
    </exec>
    <echo message="Git branch: ${git.branch}"/>
    <echo message="Git revision: ${git.revision}"/>
</target>

Best Practices

Project Organization

xml
<!-- Use consistent naming -->
<property name="src.main.java" value="src/main/java"/>
<property name="src.test.java" value="src/test/java"/>
<property name="build.main.classes" value="build/main/classes"/>
<property name="build.test.classes" value="build/test/classes"/>

<!-- Separate concerns -->
<import file="build-compile.xml"/>
<import file="build-test.xml"/>
<import file="build-package.xml"/>
<import file="build-deploy.xml"/>

<!-- Use meaningful target names -->
<target name="compile-main-sources" depends="init"/>
<target name="compile-test-sources" depends="compile-main-sources"/>
<target name="run-unit-tests" depends="compile-test-sources"/>

Error Handling

xml
<!-- Fail on missing dependencies -->
<target name="check-dependencies">
    <available file="${lib.dir}/junit.jar" property="junit.present"/>
    <fail unless="junit.present" message="JUnit JAR not found in ${lib.dir}"/>
</target>

<!-- Conditional execution -->
<target name="deploy" if="deploy.enabled">
    <echo message="Deploying to ${deploy.target}"/>
    <!-- Deployment tasks -->
</target>

<!-- Try-catch equivalent -->
<target name="safe-operation">
    <trycatch>
        <try>
            <antcall target="risky-operation"/>
        </try>
        <catch>
            <echo message="Operation failed, continuing with fallback"/>
            <antcall target="fallback-operation"/>
        </catch>
    </trycatch>
</target>

Troubleshooting

Common Issues

bash
# Check Ant installation
ant -version

# Verify Java installation
java -version

# Check classpath issues
ant -debug compile 2>&1 | grep -i classpath

# Validate build file
ant -projecthelp

# Check property values
ant -Dant.echo.properties=true compile

Debug Techniques

xml
<!-- Debug properties -->
<target name="debug-properties">
    <echoproperties/>
</target>

<!-- Debug paths -->
<target name="debug-paths">
    <pathconvert property="classpath.debug" refid="classpath"/>
    <echo message="Classpath: ${classpath.debug}"/>
</target>

<!-- Conditional debugging -->
<target name="debug" if="debug.enabled">
    <echo message="Debug mode enabled"/>
    <echo message="Source directory: ${src.dir}"/>
    <echo message="Build directory: ${build.dir}"/>
</target>

Performance Tips

xml
<!-- Use uptodate checks -->
<target name="compile" depends="init">
    <uptodate property="compile.notRequired" targetfile="${jar.file}">
        <srcfiles dir="${src.dir}" includes="**/*.java"/>
    </uptodate>
    <antcall target="do-compile" unless="compile.notRequired"/>
</target>

<!-- Parallel compilation -->
<target name="compile-parallel">
    <parallel>
        <javac srcdir="${src.dir}/module1" destdir="${build.dir}/module1"/>
        <javac srcdir="${src.dir}/module2" destdir="${build.dir}/module2"/>
    </parallel>
</target>

Resources