Search code examples
javajavac

How To Force Java Compiler to ONLY compile the source file I specify?


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?


Solution

  • 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