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 FooSomething fishyThat’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 FooSomething fishyInstead 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.javaimport 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.
Related articles
Related CZ Podcast episodes
- #261 · Leadership
On leading people and teams. · Czech audio
- #3 · Java 6 SE, Vývoj webových aplikací
Dagi and Filemon discuss Java 6 and web application frameworks. · Czech audio
- #1 · Vývojová prostředí v Javě
The first CZ Podcast episode looks at Java development environments. · Czech audio