Search code examples
javashellcompilationjavac

Javac ignores first line of sourcepath file and doesn't compile class


I have a project with such structure (shortened for simplicity):

enter image description here

and I'm trying to compile it with terminal.

I'm in src directory and firstly doing this:

find * -name "*.java" > sources.txt

After the first command a sources.txt file is generated:

aircraft/Baloon.java
aircraft/Aircraft.java
aircraft/JetPlane.java
aircraft/AircraftFactory.java
aircraft/Coordinates.java
aircraft/Flyable.java
aircraft/Helicopter.java
exception/WrongNumberArgsException.java
simulator/Simulator.java

Further I'm doing:

javac -sourcepath @sources.txt

And it generates all .class files, EXCEPT the first one - Baloon.java is totally ignored by javac.

If I do:

javac -sourcepath @sources.txt src/aircraft/Baloon.java

a Baloon.class file is generated as well with other .class files.

If I manually change first row of sources.txt, for example switch first and second rows, then when I recompile again first .java file in sources.txt is ignored by javac and respective .class file is not created.

Alternatively if I compile with Intellij Idea - everything is fine, no problems occur.

It doesn't matter if I compile in the root directory of project, or in src directory, result is the same - first line of sources.txt is ignored.

So the question is - what I'm doing wrong with above 2 terminal commands? or maybe it's a bug of javac?

javac version - 1.8.0_221

project files themself: https://github.com/Dman-89/42_avaj_launcher


Solution

  • It sounds like your aim is to create a list of source files, and then pass them all to javac to compile. If that is your aim, -sourcepath is not what you want.

    You want one of two things:

    Do what all real java programmers do

    'real' defined as: They do it for money and/or eyeballs, and not as an academic exercise.

    Use a build system; gradle or maven are the common choices. They will take care of all this for you far better than a slapdash effort to hack some bash scripts together.

    Just pass the java source files

    Just remove the -sourcepath part. javac @sources.txt is what you want.

    What is going on?

    The first 'argument' (first line in your sources.txt file) is the 'value' for the -sourcepath argument, and javac doesn't compile this, because -sourcepath doesn't mean 'compile this stuff' (see later). The rest (lines 2 and further) are just arguments, which is actually what javac will compile.

    So what is sourcepath?

    To compile source files with javac, you'd write javac foo/A.java bar/B.java. However, what if, say, A.java contains: import lombok.Value;? Javac is now not capable of compiling this file unless javac knows about lombok.Value. Usually such dependencies are already compiled (you have class files in a directory or a jar file), in which case you'd use the -classpath option of javac to tell javac about where to find this stuff.

    -sourcepath is similar, except, it's for not-yet-compiled stuff. That means javac will gain awareness of the existence of anything in the sourcepath, in case it comes up that any of that needs to be compiled first, and will only do so if it is needed by any of the actual files you specified for compilation.

    Like any -xpath option to javac or java, if you want to specify more than 1 entry, use colons (semicolons on windows) as a separator, not space. Furthermore, the idea is to pass directories and not actual files. And just like -classpath, passing invalid (e.g. non-existing) paths is fine. Then they are just ignored.

    Example

    javac -sourcepath deps:deps2 src/com/mypkg/Main.java src/com/mypkg/Extra.java
    

    This will tell javac to compile Main and Extra (not deps or deps2 or any files inside). However, if, say, Main.java contains the line: Object o = new bar.baz.Hello();, and the file deps/bar/baz/Hello.java exists, then this command will end up also compiling Hello.java. If deps/bar/baz/Whatever.java also exists, that won't be compiled, unless Whatever is mentioned somewhere in Hello.java, Main.java, or Extra.java. And not in either a comment or just an import statement (import if java-ese for alias, it doesn't actually import anything).