Task Dependencies: ant

Steven J Zeil

Last modified: Dec 27, 2023
Contents:

Abstract

ant is a build manager based upon a task dependency graph expressed in an XML file

In this lesson we look at how ant addresses a number of shortcomings of make.

We will look at the task-based build model implemented by ant at how this differs from the dependency-based model of make, and at how to describe projects to the ant tool.

We will look at how ant could be applied to some of our sample projects.


ant


What’s Wrong with make?

ant is actually an acronym for Another Neat Tool.

But why do we need “another” tool, however neat, for build management?


Other Criticisms

1 The ant Command

 


ant Options

Some useful options:

-k, -keep-going
“Keep going.” Don’t stop the build at the first failue, but continue building any required targets that do not depend on the one whose construction has failed.
-f filename
Use filename instead of the default build.xml. Also -file or -buildfile
-Dproperty=value
Sets a property (similar to make’s variables)

2 Build Files

The ant build file is an XML file.

<project name="382Website" default="deploy">
     <description>
        Extract Metadata Extractor - top level
    </description>
  ⋮
</project>

2.1 Targets

At its heart, a build file is a collection of targets.

ant targets correspond, roughly, to make’s “artificial targets”.


Example of Targets

simplebuild.xml.listing
<project name="JavaBuild" default="deploy"> ➀
  <description>
    Example of a simple project build
  </description>

  <target name="compile" description="Compile src/.../*.java into bin/"> ➁
    <mkdir dir="bin" />                    ➂
    <javac srcdir="src" destdir="bin"
           debug="true" includeantruntime="false"/>
    <echo>compiled </echo>
  </target>

  <target name="unittest" depends="compile" unless="test.skip"> ➃
    <mkdir dir="test-reports" />
    <junit printsummary="on" haltonfailure="true" 
           fork="true" forkmode="perTest">
      <formatter type="plain" />
      <batchtest todir="test-reports">
        <fileset dir="bin">
          <include name="**/Test*.class" />
          <exclude name="**/Test*$*.class" />
        </fileset>
      </batchtest>
    </junit>
  </target>

  <target name="deploy" depends="unittest" description="Create project's Jar file">
    <jar destfile="myProject.jar">
      <fileset dir="bin"/>
    </jar>
  </target>
</project>

The project has a name and default target

A basic target. It is named “compile” and has a description (which may be picked up by some IDEs)

This target has 3 tasks. It creates a directory, compiles Java source code, and prints a message when completed.

This target illustrates both a dependency and a condition.


Task versus File Dependencies

ant targets correspond, roughly, to make’s “artificial targets”.

So this build file

simplebuild.xml.listing
<project name="JavaBuild" default="deploy"> ➀
  <description>
    Example of a simple project build
  </description>

  <target name="compile" description="Compile src/.../*.java into bin/"> ➁
    <mkdir dir="bin" />                    ➂
    <javac srcdir="src" destdir="bin"
           debug="true" includeantruntime="false"/>
    <echo>compiled </echo>
  </target>

  <target name="unittest" depends="compile" unless="test.skip"> ➃
    <mkdir dir="test-reports" />
    <junit printsummary="on" haltonfailure="true" 
           fork="true" forkmode="perTest">
      <formatter type="plain" />
      <batchtest todir="test-reports">
        <fileset dir="bin">
          <include name="**/Test*.class" />
          <exclude name="**/Test*$*.class" />
        </fileset>
      </batchtest>
    </junit>
  </target>

  <target name="deploy" depends="unittest" description="Create project's Jar file">
    <jar destfile="myProject.jar">
      <fileset dir="bin"/>
    </jar>
  </target>
</project>

is roughly equivalent to this makefile

simplemake.listing
JAVAFILESsrc=$(shell find src/ -name '*.java')
JAVAFILES=$(JAVAFILESsrc:src/%=%)
CLASSFILES=$(JAVAFILES:%.java=%.class)
TESTFILES=$(shell find src/ -name 'Test*.java')
TESTS=$(TESTFILES:src/%.java=%)

deploy: unittest
	cd bin; jar cvf myProject.jar `find . -name '*.class'`

unittest: build
	cd bin; for test in $(TESTS); do \
	   java $$test; \
	done
build:
	cd src; javac -d ../bin -g $(JAVAFILES)

though a “real” makefile author would probably write this:

simplemake2.listing
JAVAFILESsrc=$(shell find src/ -name '*.java')
JAVAFILES=$(JAVAFILESsrc:src/%=%)
CLASSFILES=$(JAVAFILES:%.java=%.class)
TESTFILES=$(shell find src/ -name 'Test*.java')
TESTS=$(TESTFILES:src/%.java=%)

deploy: myProject.jar
unittest: testReport.txt
build: $(CLASSFILES)

myProject.jar: testReport.txt $(CLASSFILES)
	cd bin; jar cvf myProject.jar `find . -name '*.class'`

testReport.txt: $(CLASSFILES)
	-rm testReport.txt
	cd bin; for test in $(TESTS); do \
	   java $$test >> testReport.txt; \
	done


bin/%.class: src/%.java
	cd src; javac -d ../bin -g $*.java

2.2 Ant Building Blocks and Concepts

(Optional reading. Read on if you actually want to use Ant. Otherwise, jump to Case Studies.)

2.2.1 Properties

Properties are named string values.


The <property Task

Two basic modes:


Additional <property Variants

2.2.2 File Sets and Lists


File Sets

<fileset file="src/main.cpp"/>
<fileset dir="src"
    includes="main.cpp utility.h utility.cpp"/>
<fileset dir="src" includes="*.cpp,*.h"/>

File Lists

<filelist dir="src"
    files="main.cpp utilities.h utilities.cpp"/>

Mappers


Selectors

Selectors provide more options for selecting files than simple include/exclude based on the names.

2.2.3 Path Sets

Used to specify a sequence of paths, usually to be searched.

<classpath>
    <pathelement path="${env.CLASSPATH}"/>
    <fileset dir="target/classes">
       <include name="**/*.class"/>
    </fileset>
    <filelist refid="third-party_jars"/>
</classpath>

Referencing Path Sets

2.2.4 Filters

Filters are used to modify the outputs of some commands by performing various substitutions:

<copy file="../../templates/@{format}.tex"
      tofile="${doc}-@{format}.ltx">
  <filterset>
    <filter token="doc" value="${doc}"/>
    <filter token="relPath" value="${relPath}"/>
    <filter token="format" value="@{format}"/>
  </filterset>
</copy>

A filter set replaces tokens like @doc@ by a string, in this case the value of the property ${doc}


Filter Chains

Filter chains offer a variety of more powerful options, e.g.,

<loadfile property="doctitle" srcfile="${doc}.info.tex">
  <filterchain>
    <linecontains>
      <contains value="\title{"/>
    </linecontains>
    <tokenfilter>
      <replaceregex pattern=" *\\title[{]([^}]*)[}]"
                    replace="\1"/>
    </tokenfilter>
  </filterchain>
</loadfile>

2.3 Tasks

The Ant Manual has a good breakdown on these.


Extending Ant

<project name="code2html" default="build">

  <taskdef classpath="JFlex.jar"
           classname="JFlex.anttask.JFlexTask"
           name="jflex" />
     ⋮

  <target name="generateSource">
    <mkdir dir="src/main/java"/>
    <jflex file="src/main/jflex/code2html.flex"
	   destdir="src/main/java"/>
    <jflex file="src/main/jflex/code2tex.flex"
  	       destdir="src/main/java"/>
       ⋮

Finding Extensions

3 Case Study

3.1 Simple Java Build

build.xml.sjb.listing
<project name="codeAnnotation" basedir="." default="build"
	 xmlns:ivy="antlib:org.apache.ivy.ant">

  <record name="ant.log" action="start" append="false" />  ➀
	
  <path id="testCompilationPath">                         ➁
    <fileset dir="lib" includes="*.jar"/>
    <pathelement path="target/classes"/>
  </path>

  <path id="testExecutionPath">
    <fileset dir="lib" includes="*.jar"/>
    <pathelement path="target/classes"/>
    <pathelement path="target/test-classes"/>
  </path>

  <property name="build.dir" value="build"/>
  <property name="src.dir" value="src"/>

	
  <import file="ivyBuild.xml" as="ivy"/>                 ➂


  <target name="compile" depends="ivy.resolve-ivy"        ➃
	description=
  	"Compile all source code, including the lexical
	 analysis code generated using jflex." 
  	>
    <mkdir dir="target/classes"/>
    <javac srcdir="src/main/java" destdir="target/classes"
	   source="1.8" includeantruntime="false"/>
  </target>

  <target name="compile-tests" depends="compile"          ➄
  	description="Compile JUnit tests"
  	>
    <mkdir dir="target/test-classes"/>
    <javac srcdir="src/test/java" destdir="target/test-classes"
	   source="1.8" includeantruntime="false">
      <classpath refid="testCompilationPath"/>
    </javac>
  </target>

  <target name="test" depends="compile-tests"           
  	description="Run all JUnit tests, producing a
  	summary report in target/test-results."
  	>
    <mkdir dir="target/test-results/details"/>
    <junit printsummary="yes"                             ➅
	   haltonfailure="no" fork="no"                  
	   >
        <classpath refid="testExecutionPath"/>
      <formatter type="xml"/>
      <batchtest todir="target/test-results/details">
	     <fileset dir="target/test-classes">
	        <include name="**/*Test*.class"/>
	     </fileset>
      </batchtest>
    </junit>
    <junitreport todir="target/test-results">              ➆
      <fileset dir="target/test-results/details">
        <include name="TEST-*.xml"/>
      </fileset>
      <report format="frames" styledir="junit-stylesheets" 
         todir="target/test-results/html"/>
    </junitreport>
  </target>

  <target name="build"  depends="test"
  	description="Construct a jar file with the
  	compiled code and a zip file with the project
  	source code.">
  	
  	<jar destfile="codeAnnotation.jar" basedir="target/classes">  ➇
  	    <manifest>
  		    <attribute name="Main-Class"
  		               value="edu.odu.cs.code2html.Code2HTML"/>
  		</manifest>
  	</jar>
  </target>


  <target name="clean">                                           ➈
    <delete dir="target"/>
  </target>

  
</project>

You can find this entire project, with the Ant files, here.

Eclipse is generally ant-friendly.


Eclipse Builders

Eclipse supports multiple, plug-able builders.

4 Overall

Goals scripting make ant
easy to use? Y Y Y
easy to set up for a given project? N N N
efficient: avoid redundant/unnecessary actions N Y ?
efficient: detect and abort bad builds in progress ? Y ?
incremental: allow focused/partial builds ? Y Y
flexible: allow for a variety of build actions N Y Y
flexible: builds on/for a variety of platforms N N Y
configurable: permit the management of multiple artifact configurations ? ? Y