0

I have a simple java project which I am using ant to build. It has these two classes:

A.java:

public class A {
    public static void main(String[] args) {
        Integer i = 0;
        B.f(i);
    }
}

B.java:

public class B {
    public static void f(int i) {
        System.out.println("hello");
    }
}

Which works fine:

$ ant compile
[...]
$ java -cp bin A
hello

Now, if I change the int parameter in B.f to an Object:

public class B {
    public static void f(Object i) {
        System.out.println("hello");
    }
}

...the code recompiles fine...

$ ant compile
[...]
$ java -cp bin A
Exception in thread "main" java.lang.NoSuchMethodError: B.f(I)V
    at A.main(Unknown Source)

... but it crashes at runtime. Why?


Folder structure before compilation:

bin
build.xml
src
├── A.java
└── B.java

build.xml:

<project>
    <target name="compile">
        <javac srcdir="src" destdir="bin"/>
    </target>
</project>
Dog
  • 7,707
  • 8
  • 40
  • 74

2 Answers2

2

Ant will only compile files that did not change since last compile (unless you clean). That said, only one of your files changed, and the other is making a call expecting the old version. If you remove all the class files before running the ant build i would bet it will work.

It says in the apache documentation for the ant <javac> task:

The source and destination directory will be recursively scanned for Java source files to compile. Only Java files that have no corresponding .class file or where the class file is older than the .java file will be compiled.

The methods may be compatible at a source level, but when compiled into bytecode, they have a specific signature that other classes are looking for. You changed the compiled signature so it cannot find the method it is looking for.

------------------ EDIT ---------------------

A simple clean task can be added to your build:

  <target name="clean"
        description="clean up" >
    <!-- Delete the ${build} and ${dist} directory trees -->
    <delete dir="${build}"/>
    <delete dir="${dist}"/>
  </target>

And you could add a full rebuild:

  <target name="fullReBuild" depends="clean compile">
  </target>

Then issue ant fullReBuild from command line as desired.

Lucas
  • 14,227
  • 9
  • 74
  • 124
  • This sounds rather dubious. That would mean ant's recompilation strategy is almost useless, and you may as well just delete all the classes every time you're going to compile. Nevertheless, shouldn't `A` still work with the new `B`, since the argument is compatible either way? – Dog Mar 28 '13 at 15:08
  • @Dog, i may be wrong, but did you try deleting the class files and running your ant build? – Lucas Mar 28 '13 at 15:14
  • From the link in your comment, it seems one must use the `` task to make recompilation work properly. – Dog Mar 28 '13 at 17:44
  • @Dog, that depends on what you mean by _work properly_. Ant does not recompile things that have not changed in order to allow for fast builds on large projects. Most people create another target for "clean" and use the `ant clean` before `ant` when a full rebuild is desired. See my edit above. – Lucas Mar 28 '13 at 18:08
  • Do you know about ``? It seems to resolve the transitive closure of dependents of the changed class, then recompiles them all, so that should work fine as far as I can see. It fixed the problem I was having. If I make a `clean` target (which I already have), how do I know when to use `clean` instead of recompile? This issue just happened to me and took 2 hours to track down, since things were crashing after random amounts of time. How was I supposed to know I was meant to use `clean` when I made this change? – Dog Mar 28 '13 at 18:14
  • @Dog, no, i did not know about `` and it looks like it could be useful to you. I'm not sure what you mean by when to use clean vs recompile. Clean should be used when you want to clear out all binary. As far as knowing when to use clean... If you use `` it could alleviate the need for clean, otherwise, use clean whenever you need a stable full build. I personally use clean on EVERY build (but i use maven instead of ant). – Lucas Mar 28 '13 at 19:27
0

By default ant does not recompile anything unless the date of the file changed. Solution is to use the <depend> task. This makes ant recompile dependents when their dependencies change.

Dog
  • 7,707
  • 8
  • 40
  • 74