Introduction to Ant dependency checking

Posted by buildmeister on May 17th, 2006
Filed under:  java  build scripting 
There are 1 comments on this article.
Bookmark and Share

Introduction

By defaut, Apache Ant uses a simple Build Avoidance mechanism whereby it works out what to compile based on basic date and time out-of-date rules: only .java files that have no corresponding .class file or where the .class file is older than the .java file will be compiled. It will not, however, rebuild classes where the files that they depend upon change, such as a parent class, an imported class or a change in interface. This can lead to runtime errors – something you obviously want to try to avoid. Most projects accept this situation and subsequently create and make judicious use of an Ant clean target to remove all the .class files before compilation. Although this is good practice every now and again and also for specific builds, such as when a Release Build is being created, it can be a bit of overkill for projects where the compilation process takes a significant amount of time and builds are required frequently.

One of the ways to reduce the compilation time and improve Java dependency checking is to use Ant’s <depend> task. Rather than looking at simple date and time stamps, the <depend> task analyses the .class files passed to it to determine which of them are out of date with respect to their source code. It then subsequently removes the .class files of any classes which depend on an out-of-date class.

An example

As a simple example, consider the following two classes (which would be held in the source files A.java and B.java:

public class A {
    protected void sayHello(String name) {
        System.out.println(name);
    }
    
    public class B extends A {
        public void test() {
            sayHello("Kevin");
        }
}

If you compiled this source code and then subsequently introduced a change in class A’s interface, for example:

public class A {
    protected void sayHello(String salutation, String name) {
        System.out.println(salutation + " " + name);
    }
}

then without executing a clean build first, Ant by default would only recompile one file as follows:

>ant compile
Buildfile: build.xml
    [echo] creating directory structure
    [mkdir] Created dir: C:\code\build
compile:
    [javac] Compiling 1 source files to C:\code\build
BUILD SUCCESSFUL
Total time: 1 second

In this case only A.java would be recompiled as it is the only class whose date and time had changed. If you cleaned out all the classes first and then compiled, the compiler would notify you of the error that has been made by changing the interface in once class and not in another. However, there are other more insidious dependencies which might not cause a compiler error and pose some seriously difficult to find runtime bugs.

To enable dependency checking, simply pass the .java source and .class build files to the <depend> task as shown below. In this example I have also made a reference to a cache called depcache. This is used to cache the dependency information; a cache can be utilised because the dependencies for a class only change when the class itself changes and only those class files that change will have their dependency information re-analysed.

<project name="ant-depend" default="compile" basedir=".">

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

<target name="init" description="initialise directory structure">
    <mkdir dir="${dir.build}"/>
</target>

<target name="clean" description="remove generated files">
    <delete dir="${dir.build}"/>
    <delete dir="depcache"/>
</target>

<target name="compile" depends="init" description="compile source code">
    <depend srcdir="${dir.src}" destdir="${dir.build}" cache="depcache"> 
        <include name= "**/*.java"/> 
    </depend> 

<javac destdir="${dir.build}" > <src path="${dir.src}"/> </javac> </target> </project>

With dependency checking in place, compiling the example from above would mean the automatic deletion of the dependent .class files and the exposure of the interface compilation problem as follows:

>ant compile
Buildfile: build.xml
init:
    [echo] creating directory structure
compile:
    [depend] Deleted 2 out of date files in 0 seconds
    [javac] Compiling 2 source files to C:\code\build
    [javac] C:\code\src\B.java:7: sayHello(java.lang.String,java.lang.String
in A cannot be applied to (java.lang.String)
    [javac] sayHello("Kevin");
    [javac]           ^
    [javac] 1 error
BUILD FAILED
Total time: 1 second

Because of the intricacies of Java coding, dependency checking will never be infallible, therefore it is good strategy to carry out a clean build every now and again. This is something you can automatically setup with a tool such as CruiseControl where you can create a schedule so that one in every five builds is a clean build.

Bookmark and Share

Comments

Posted by clarkht

Greate Article! Really helped me gain more insight about Ant Compile Policy

Posted on March 13th, 2010

Back to Top

Submit a new comment

All fields in bold are required.