1. Overview
1.概述
In some special circumstances, we don’t have a third-party build tool installed, such as Ant or Maven. And, we still need to compile a project with lots of packages and classes.
在一些特殊情况下,我们没有安装第三方构建工具,如Ant或Maven。而且,我们仍然需要编译一个有很多包和类的项目。
In this tutorial, we’re going to use the javac command to accomplish this task with different scenarios.
在本教程中,我们将使用javac命令来完成不同场景下的这项任务。
2. Using File Names
2.使用文件名
Let’s assume we have two directories in the current directory: src and out. The src directory holds our Java source files, and the out directory will contain the corresponding compiled class files.
让我们假设在当前目录下有两个目录。src和out。src目录存放我们的Java源文件,而out目录将包含相应的编译后的类文件。
Let’s start with a simple scenario. The src directory contains a single Java source file named com/baeldung/MyClass.java:
让我们从一个简单的场景开始。src目录包含一个名为com/baeldung/MyClass.java的单一Java源文件。
Then, let’s use javac to compile the MyClass.java file into the out directory:
然后,让我们用javac来编译MyClass.java文件到out目录。
$ javac -d ./out/ ./src/com/baeldung/MyClass.java
In the above command, the -d option specifies the destination directory for the class file. Also, we should note that the exact code of the MyClass.java file isn’t that important, and we only need to ensure it’s a grammar-correct Java file.
在上面的命令中,-d选项指定了类文件的目标目录。另外,我们应该注意,MyClass.java文件的确切代码并不那么重要,我们只需要确保它是一个语法正确的Java文件。
To be a little complicated, let’s add another three Java files – YourClass.java, HerClass.java, and HisClass.java:
说得复杂一点,让我们再添加三个Java文件–YourClass.java, HerClass.java, 和HisClass.java。
To compile all four of the above Java files, we can list each of them on the command line:
为了编译上述所有四个Java文件,我们可以在命令行中列出每一个文件。
$ javac -d ./out/ \
./src/com/baeldung/MyClass.java \
./src/com/baeldung/YourClass.java \
./src/com/baeldung/HerClass.java \
./src/com/baeldung/HisClass.java
Then, let’s add a new Main.java file that refers to the other four Java files, for example, by invoking methods or creating an object instance:
然后,让我们添加一个新的Main.java文件,该文件引用其他四个Java文件,例如,通过调用方法或创建对象实例:
In this case, we only need to compile the Main.java file:
在这种情况下,我们只需要编译Main.java文件。
$ javac -sourcepath ./src/ -d ./out/ ./src/com/baeldung/Main.java
After the above command, the other four class files will also be compiled. That’s because javac will search for the required types and compile the corresponding source files by default. If we don’t want to compile the required types, we can add the -implicit:none option.
执行上述命令后,其他四个类文件也将被编译。这是因为javac会搜索所需的类型并默认编译相应的源文件。如果我们不想编译所需类型,我们可以添加-implicit:none选项。
The -sourcepath option tells the Java compiler where to find the input source files. If the -sourcepath option isn’t specified, javac will utilize the user classpath to search for both the class files and source files. So, we can replace the -sourcepath option with the -classpath or -cp option:
-sourcepath选项告诉Java编译器在哪里找到输入的源文件。如果没有指定-sourcepath选项,javac将利用用户classpath来搜索类文件和源文件。因此,我们可以用-classpath或-cp选项代替-sourcepath选项。
$ javac -cp ./src/ -d ./out/ ./src/com/baeldung/Main.java
However, this approach has its limitations: the javac command only compiles the required types and omits other source files. For example, if we add a new ItsClass.java and the Main.java doesn’t refer to it, then ItsClass.java will not be compiled:
然而,这种方法有其局限性:javac命令只编译所需的类型,而省略了其他源文件。例如,如果我们添加了一个新的ItsClass.java,而Main.java没有引用它,那么ItsClass.java将不会被编译。
To summarize, there are two scenarios suitable to list filenames in the javac command line: when there are just a few Java source files, and when there is a launcher class that refers to other classes recursively.
总而言之,有两种情况适合在javac命令行中列出文件名:当只有几个Java源文件时,以及当有一个启动器类递归地引用其他类时。
3. Using Wildcard
3.使用通配符
The javac command also supports the wildcard character (*) for compiling multiple source files in the same directory.
javac命令还支持通配符(*),用于编译同一目录下的多个源文件。
For example, we can use the wildcard to compile the above source files:
例如,我们可以使用通配符来编译上述源文件。
$ javac -d ./out/ ./src/com/baeldung/*.java
To complicate our scenario further, let’s add four sub-packages (spring, summer, autumn, and winter) and corresponding classes:
为了使我们的方案进一步复杂化,让我们添加四个子包(春、夏、秋、冬)和相应的类。
Now, in the command line, we can list each package with a wildcard to compile all of them:
现在,在命令行中,我们可以用通配符列出每个包,以编译所有的包。
$ javac -d ./out/ \
./src/com/baeldung/*.java \
./src/com/baeldung/spring/*.java \
./src/com/baeldung/summer/*.java \
./src/com/baeldung/autumn/*.java \
./src/com/baeldung/winter/*.java
When there are only a few packages, regardless of the source file number, it’s suitable to use this wildcard approach.
当只有几个软件包时,无论源文件的编号如何,都适合使用这种通配符方式。
4. Using Argument Files
4.使用论据文件
When there are multiple packages to compile, then using the javac command with an argument file comes in handy. An argument file can include both the javac options and source file names.
当有多个软件包需要编译时,使用带有参数文件的javac命令就会很方便。一个参数文件可以包括javac选项和源文件名。
To use an argument file, we need to prepend the at sign (@) leading character before the argument filename:
要使用一个参数文件,我们需要在参数文件名前加上at符号(@)的引导字符。
$ javac -d ./out/ @sources.txt
But how can we generate such a @sources.txt file? That depends on the OS we’re using. In Linux or macOS, we can use the find command:
但我们如何才能生成这样一个@sources.txt文件呢?这取决于我们所使用的操作系统。在Linux或macOS中,我们可以使用find命令。
$ find ./src/ -type f -name "*.java" > sources.txt
In the above command line, the ./src/ is our search starting-point directory, the -type f option filters only regular files, and the -name “*.java” option matches all filenames with the .java extension.
在上述命令行中,./src/是我们的搜索起点目录,-type f选项只过滤常规文件,-name “*.java”选项匹配所有扩展名为.java的文件。
However, in Windows, we can use the dir command:
然而,在Windows中,我们可以使用dir命令。
> dir src /b /s *.java > sources.txt
In the above command line, the src folder is our search path, the /b switch shows the directory and file names without additional information, and the /s option lists all files in a specified directory and all subdirectories.
在上述命令行中,src文件夹是我们的搜索路径,/b开关显示目录和文件名,没有附加信息,/s选项列出指定目录和所有子目录的所有文件。
The shortcoming of this approach is that each time we add a new or remove an existing Java source file, we need to regenerate the sources.txt file.
这种方法的缺点是,每次我们添加一个新的或删除一个现有的Java源文件,我们需要重新生成sources.txt文件。
5. Other Approaches
5.其他方法
Besides the above common approaches, there also exist other OS-dependent approaches, such as using globstar or pipe.
除了上述常见的方法,还存在其他依赖于操作系统的方法,例如使用globstar或pipe。
5.1. Using Globstar
5.1.使用Globstar
Bash version 4.0 adds a new globbing option called globstar that treats the double wildcard (**) differently. With it enabled, Bash will traverse multi-level directories; otherwise, Bash will only search a single-level directory.
Bash 4.0版增加了一个新的globbing选项,叫做globstar,它对双通配符(**)的处理方式有所不同。启用该选项后,Bash将遍历多级目录;否则,Bash将只搜索单级目录。
However, this option is disabled by default. And, we can use the shopt (sh + opt, shell options) command to inspect the Bash option settings. If we execute the shopt command without any argument, it will output a long list of options and their statuses (on or off).
然而,这个选项在默认情况下是禁用的。而且,我们可以使用shopt(sh + opt,shell options)命令来检查Bash的选项设置。如果我们在没有任何参数的情况下执行shopt命令,它将输出一长串选项和它们的状态(on或off)。
Currently, we’re only concerned with the globstar option:
目前,我们只关注globstar选项。
$ shopt globstar
globstar off
To enable it, we use the shopt command with the -s option:
要启用它,我们使用带有-s选项的shopt命令。
$ shopt -s globstar
To disable it, we invoke the shopt command with the -u option:
要禁用它,我们调用带有 -u选项的shopt命令。
$ shopt -u globstar
After enabling this option, we can invoke javac with the double wildcard:
启用这个选项后,我们可以用双通配符调用javac。
$ javac -d ./out/ ./src/**/*.java
5.2. Using a Pipe
5.2.使用管道
Conceptually, a pipe is a connection between two processes. And, we can utilize this pipe mechanism to connect multiple commands to produce our desired results.
从概念上讲,管道是两个进程之间的连接。而且,我们可以利用这种管道机制来连接多个命令,以产生我们想要的结果。
To compile our Java source files, we can combine the find, xargs, and javac commands:
为了编译我们的Java源文件,我们可以结合find、xargs和javac命令。
$ find ./src/ -type f -name "*.java" | xargs javac -cp ./src/ -d ./out/
Also, the find command supports the -exec action:
另外,find命令支持-exec动作。
$ find ./src/ -type f -name "*.java" -exec javac -cp ./src/ -d ./out/ '{}' ';'
The above command line may run a bit slowly. That’s because the javac command will run for each matched file. For more information, we can use the man find command to read the -exec option’s documentation.
上述命令行可能运行得有点慢。这是因为javac命令将为每个匹配的文件运行。关于更多信息,我们可以使用man find命令来阅读-exec选项的文档。
To be a little faster, we can change the semicolon (;) into a plus sign (+). Then, the javac command will collect all the matched files and execute only once:
为了更快一点,我们可以把分号(;)改为加号(+)。然后,javac命令将收集所有匹配的文件,只执行一次。
$ find ./src/ -type f -name "*.java" -exec javac -cp ./src/ -d ./out/ '{}' +
6. Conclusion
6.结语
In this article, we first looked at some common approaches to compiling all Java source files in a directory structure, such as using filenames, wildcards, and an argument file. Then, we looked at some OS-dependent approaches, such as using globstar and pipe.
在这篇文章中,我们首先看了一些在目录结构中编译所有Java源文件的常用方法,如使用文件名、通配符和参数文件。然后,我们看了一些依赖于操作系统的方法,如使用globstar和pipe。