3
votes

The java 12.0.1 compiler (my tests here are all run in MS Windows) has some really odd behavior when using the -sourcepath argument, with regards to what it decides to compile. The easiest way to explain this is to provide two examples and cite the differences in behavior.

Example 1:

Source file "A.java"

public class A {
    public static void main(String[] args) {
        System.out.println("Hello World");
        B.myMethod();
    }
}

Source file "B.java"

public class B {
    public static void myMethod() {
        System.out.println("Goodbye!");
    }
}

To compile example 1, we just use the following:

javac -sourcepath . A.java

This will compile both A.java and B.java and create A.class and B.class. We expect it to also compile B.java because A depends on it. Now wait a second or so and without modifying either ".java" source file, simply re-run the compilation command above. You will find that it re-compiles A.java and a new A.class is created (with updated timestamp), but B.class is not re-compiled. Ok, this is pretty much what one might expect. Now let's compare this to the next example below.

Example 2:

Source file "example2/A.java"

package example2;
public class A {
    public static void main(String[] args) {
        System.out.println("Hello World");
        B.myMethod();
    }
}

Source file "example2/B.java"

package example2;
public class B {
    public static void myMethod() {
        System.out.println("Goodbye!");
    }
}

The source files are the same, except everything is moved into a package. We want to compile the two source files while currently in the "example2" folder. So we use the following command:

javac -sourcepath .. A.java

This will again compile both A.java and B.java and create A.class and B.class. No problems here, same as before. Note that -sourcepath is now ".." because that is the "root" source folder now that everything is in a package. Now wait a second or so and without modifying either source file, simply re-run the compilation command above. You will find that it re-compiles BOTH A.java and B.java and a new A.class and B.class file are created (with updated timestamps).

Note the difference in compilation behavior when the javac command is run the second time. When -sourcefile was "." and the files were not in a package, the second "javac" command only compiles the source file specified on the command line. But when -sourcefile is ".." and the classes are in a package, the second "javac" command ALWAYS compiles all the dependent source files, regardless if the unspecified source files need to be recompiled or not.

The question is why? What arguments can I pass to the javac command-line to stop example 2 from unconditionally recompiling all the dependent source files, if their class files have a newer timestamp than the source files?

3
Here's a third experiment, try compiling just B in example 2 :) - yhyrcanus
@yhyrcanus Then it only recompiles B, as expected. Attempts to recompile A only, result in B also being recompiled because there is a dependency that javac detects. Problem is, B doesn't need to be recompiled because it's already up to date. This isn't a big deal with 2 source files, but when there's dozens of source files, it matters. - deltamind106
You're doing it wrong. You should be in .., that is to say in the directory that contains the head of the package structure, and the command should be javac example2/A.java etc. - user207421
@user207421 Then what's the purpose of the -sourcepath option? If you "must" be in the root source folder when compiling, why even have a command-line option that allows you to be in a different folder? Clearly that's the whole point of -sourcepath, i.e. to allow you to compile from whatever folder you want. - deltamind106

3 Answers

2
votes

This issue occurs because javac compares timestamps between the .java file and the .class file. It uses -sourcepath to find the source files, and -classpath to find the class files. Therefore, your issue can be fixed by specifying the classpath:

javac -sourcepath .. -classpath .. A.java
0
votes

This seems related to ... If you go to the source root, the default package:

cd ..
javac -sourcepath . ex/A.java

everything works (at my place): no recompile of B.java.

It might be due to the -classpath too, but I would rather think of some esoteric problem with package paths.

0
votes

EDIT: I've removed my suggestion that this is a bug, since I've accepted another answer as the best explanation.

As an aside, there seems to be no sensible way to report this as a bug to the OpenJDK. I googled and searched around the OpenJDK website and it seems that the bugs.openjdk.java.net is only for trusted developers. I guess I could throw it up on the OpenJDK mailing list, but that seems like a hack. So I am not even sure where to report this bug, sadly.