Reputation: 99408
When compiling several .java files with some dependency relation between them, do we need to compile them in some order?
Must a dependency be .class file? Or can a dependency be a .java file?
Specifically, when A.java depends on B.class file compiled from B.java file, but B.class hasn't been created (i.e. B.java file hasn't been compiled into B.class), can we compile A.java by specifying the directory for B.java in java -cp
? Or do we need to compile B.java into B.class first, and then specify the directory for B.class in java -cp
when compiling A.java?
For example, from https://dzone.com/articles/java-8-how-to-create-executable-fatjar-without-ide, ./src/main/java/com/exec/one/Main.java
depends on ./src/main/java/com/exec/one/service/MagicService.java
, and both haven't been compiled yet.
Why does the following compilation fail?
$ javac ./src/main/java/com/exec/one/*.java -d ./out/
./src/main/java/com/exec/one/Main.java:3: error: package com.exec.one.service does not exist
import com.exec.one.service.MagicService;
^
./src/main/java/com/exec/one/Main.java:8: error: cannot find symbol
MagicService service = new MagicService();
^
symbol: class MagicService
location: class Main
./src/main/java/com/exec/one/Main.java:8: error: cannot find symbol
MagicService service = new MagicService();
^
symbol: class MagicService
location: class Main
3 errors
Why does the following compilation succeed? How can one compile them in one javac
command? How is -cp ./src/main/java
used in the compilation? What happens in the compilation process?
$ javac -cp ./src/main/java ./src/main/java/com/exec/one/*.java ./src/main/java/com/exec/one/**/*.java
./src/main/java/com/exec/one/Main.java
package com.exec.one;
import com.exec.one.service.MagicService;
public class Main {
public static void main(String[] args){
System.out.println("Main Class Start");
MagicService service = new MagicService();
System.out.println("MESSAGE : " + service.getMessage());
}
}
./src/main/java/com/exec/one/service/MagicService.java
package com.exec.one.service;
public class MagicService {
private final String message;
public MagicService(){
this.message = "Magic Message";
}
public String getMessage(){
return message;
}
}
Upvotes: 3
Views: 3980
Reputation: 159086
TL;DR As documented below, if you compile with this simpler command, where you only ask to compile the Main
class, the compiler will still locate and compile the required MagicService
class, because it can find the source file on the classpath.
javac -cp ./src/main/java ./src/main/java/com/exec/one/Main.java
See the "Searching for Types" section of the compiler documentation page.
Quoting it all here for your convenience, with highlights (bold and/or italic) added by me:
To compile a source file, the compiler often needs information about a type, but the type definition is not in the source files specified on the command line. The compiler needs type information for every class or interface used, extended, or implemented in the source file. This includes classes and interfaces not explicitly mentioned in the source file, but that provide information through inheritance.
For example, when you create a subclass
java.applet.Applet
, you are also using the ancestor classes of Applet:java.awt.Panel
,java.awt.Container
,java.awt.Component
, andjava.lang.Object
.When the compiler needs type information, it searches for a source file or class file that defines the type. The compiler searches for class files first in the bootstrap and extension classes, then in the user class path (which by default is the current directory). The user class path is defined by setting the
CLASSPATH
environment variable or by using the-classpath
option.If you set the
-sourcepath
option, then the compiler searches the indicated path for source files. Otherwise, the compiler searches the user class path for both class files and source files.You can specify different bootstrap or extension classes with the
-bootclasspath
and the-extdirs
options. See Cross-Compilation Options.A successful type search may produce a class file, a source file, or both. If both are found, then you can use the
-Xprefer
option to instruct the compiler which to use. Ifnewer
is specified, then the compiler uses the newer of the two files. Ifsource
is specified, the compiler uses the source file. The default isnewer
.If a type search finds a source file for a required type, either by itself, or as a result of the setting for the
-Xprefer
option, then the compiler reads the source file to get the information it needs. By default the compiler also compiles the source file. You can use the-implicit
option to specify the behavior. Ifnone
is specified, then no class files are generated for the source file. Ifclass
is specified, then class files are generated for the source file.The compiler might not discover the need for some type information until after annotation processing completes. When the type information is found in a source file and no
-implicit
option is specified, the compiler gives a warning that the file is being compiled without being subject to annotation processing. To disable the warning, either specify the file on the command line (so that it will be subject to annotation processing) or use the-implicit
option to specify whether or not class files should be generated for such source files.
Upvotes: 3
Reputation: 3531
Why does the following compilation fail?
$ javac ./src/main/java/com/exec/one/*.java -d ./out/
Because Main.java
(the only file picked up in that command) makes use of a class called com.exec.one.service.MagicService
that is not available on the classpath nor is one of the files being compiled.
Why does the following compilation succeed?
$ javac -cp ./src/main/java ./src/main/java/com/exec/one/*.java ./src/main/java/com/exec/one/**/*.java
Because Main.java
makes use of a class called com.exec.one.service.MagicService
that is also one of the files being compiled.
How can one compile them in one
javac
command?
What you have is already one command. The javac
program accepts a list of source files to compile
Usage: javac <options> <source files>
How is
-cp ./src/main/java
used in the compilation?
It's used to set the classpath, ie. it includes class files that may be needed during compilation. In your example, it's useless.
However, if you had compiled MagicService
separately and pointed the -cp
to wherever the corresponding MagicServe.class
file was located (taking into account the directory structure that matches its containing package), it would have been useful. This is how 3rd party libraries are included in Java projects.
The Java compiler doesn't impose an ordering. Simply, at compilation time, all required classes must be available, either through a source file being compiled or a class available in the classpath.
Upvotes: 3
Reputation: 4303
It looks like you should start inside the path "/src/main/java". Only below that you have packages (com.exec.one) matching your folder names. So do a "cd src/main/java" and try:
javac ./com/exec/one/*.java
Upvotes: 1