dagblog.
← Archive

Ant and a little compilation trap


I’ve had this post in my head for a while. I just hadn’t found the time to share an interesting 
 true story. Once upon a time, Simpleton Honza set out to install an application server along 
 with the latest software from Fairy Tale Ltd. Honza was simple, though hardly lazy, so 
 he prepared everything carefully. He made a tidy distribution package from the latest 
 state in CVS with the help of a little ant called Ant.

Fortified by the buns his mother had made, he set off for the kingdom beyond seven rivers and mountains. Honza’s installation went splendidly. A dragon appeared in the sky when he started testing it. The brave warrior fought 
the dragon in vain. His sharp sword, the latest source code that had worked on the training ground, was no help. Honza came home empty-handed. The treachery was revealed the next day. Ant, the wretched servant, was guilty of betraying his master.

After that fairy-tale interlude, on to the problem. We have a class Foo and a class Constant. Constant contains only constants used by Foo.

public class Constant {
public static final String SOMETHING_FISHY = "Something fishy";
}
public class Foo {
public static void main(String args[]){
System.out.println(Constant.SOMETHING_FISHY);
}
}
We have an Ant task to compile the code.
We run the build task and let it compile the source code. Then we run the compiled program.
java Foo
Something fishy
That’s exactly what we expected. We make a small change to the source code and change the value of the constant SOMETHING_FISHY.
public class Constant {
public static final String SOMETHING_FISHY = "Kim Philby";
}
We run the build task and the compiled program again.
java Foo
Something fishy
Instead of the expected Kim Philby, the output is Something fishy. Where’s the bug? Unfortunately, this is a feature. Ant decides to compile a class when it has no corresponding class file or when the class file is older than the source file. That condition does hold in our case, but as an optimization the compiler inlines the constant’s value into Foo. We can check that by decompiling it.
// Decompiled by DJ v3.5.5.77 
// Copyright 2003 Atanas Neshkov Date: 24.11.2004 8:22:41
// Home Page : http://members.fortunecity.com/neshkov/dj.html
// - Check often for new version!
// Decompiler options: packimports(3)
// Source File Name: Foo.java
import java.io.PrintStream;
public class Foo
{
public Foo()
{
}
public static void main(String args[])
{
System.out.println("Something fishy");
}
}
Ant’s depend task might come to the rescue. Unfortunately, at the end of its documentation we find the following paragraph in the Limitations section:
The most obvious example of these limitations is that the task can’t tell which classes to recompile when a constant primitive data type exported by other classes is changed. For example, a change in the definition of something like
public final class Constants {
public final static boolean DEBUG=false;
}
will not be picked up by other classes.
What can I add in closing, other than that this tripped me up badly in my role as Simpleton Honza? One piece of advice: make the first step of the build war task delete the target directory for class files. If you want to check the behavior yourself, the source code is available.

Medium version

Related articles

Related CZ Podcast episodes