If you want to check your IDE whether it handles Java modules accurately, then you need to do a command line compilation and see what the JDK's javac compiler says. But how to do this?
Compilation Commands
Since Java 9, the javac compiler supports two kinds of module compilations: single- and multi-module.
Single Module
Assume following source directories and files:
fri.i18n.messages
fri
i18n
messages
When your terminal window stands above the module's root directory fri.i18n.messages
,
following command-line would compile the module's classes into the target directory
given by the -d
option, in this case I named it module-classes
:
javac --module-path module-classes \ -d module-classes/fri.i18n.messages \ `find fri.i18n.messages -name '*.java'`
The --module-path option (also -p) is necessary only when
the module depends on other modules that were already compiled into module-classes
.
It tells the compiler where to search for referenced modules,
being the future replacement for CLASSPATH.
The -d option gives the output directory where to write compiled classes into.
In this case the name of the module (fri.i18n.messages
)
must be appended to achieve a correct output path.
The find command is available on UNIX systems only,
on WINDOWS you have to download and install it.
It searches all .java files inside the
fri.i18n.messages
directory.
The `backquotes` are a UNIX-shell mechanism that puts the output of the
find
command into the javac
command line.
(Not a good solution, because the list of sources could get huge.
See below for a better way to pass sources to the compiler.
The new --module-source-path
option was not made for this!)
This single-module compilation command would result in following:
fri.i18n.messages
fri
i18n
messages
module-classes
fri.i18n.messages
fri
i18n
messages
Multiple Modules
A certain directory structure is required to compile several modules in one run.
All modules must be located below a directory that is given as --module-source-path
,
in this case I named it module-sources
.
Each module's sources are in a directory below that is named like the module.
Mind that this name is duplicated in module-info.java
!
module-sources
fri.text.output
fri
text
output
fri.text.format
fri
text
format
You can compile all modules contained in module-sources
by following command line:
javac --module-path module-classes \ -d module-classes \ --module-source-path module-sources \ `find module-sources -name '*.java'`
Again the --module-path option could be left out when the modules do not depend on precompiled modules.
The --module-source-path option points to the directory where the module directories are below,
in this case it is module-sources
.
Result of this compilation command would be:
module-sources
fri.text.output
fri
text
output
fri.text.format
fri
text
format
module-classes
fri.text.output
fri
text
output
fri.text.format
fri
text
format
Mind that I didn't have to put any module name into the command line, like I had to do for a single module compilation.
Huge List of Sources
Putting the find
command as substitution into the
javac
command line is not a real-world solution.
Most projects have tons of source files, and the shell command buffer may get out of bounds.
There is an option to replace the list of sources by a file name tagged with '@'.
You would let the find
command write into a file,
and then pass the file to javac
.
Here is an example for single module compilation:
find fri.i18n.messages -name '*.java' >sources.list javac --module-path module-classes -d module-classes/fri.i18n.messages @sources.list rm -f sources.list
The according multi-module commands would be:
find module-sources -name '*.java' >sources.list javac --module-path module-classes -d module-classes --module-source-path module-sources @sources.list rm -f sources.list
The '@' (ampersand) before the file name sources.list
does the magic.
Automation Script
Here is a UNIX shell script, named modules-compile.sh
,
that can do both single and multi module compilations.
Put this into the directory above your module source directory like the following:
fri.i18n.messages
fri
i18n
messages
module-sources
fri.text.output
fri
text
output
fri.text.format
fri
text
format
module-classes
Put the list of your module directories into modules.list (no comments allowed here!):
fri.i18n.messages module-sources
These can be single modules (fri.i18n.messages
)
or directories containing multiple modules (module-sources
).
IMPORTANT: keep the list in dependency order, so that basic modules are already compiled when
the modules that depend on them get compiled!
Then put following into modules-compile.sh:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | # compiled classes target directory moduleTargetDir=module-classes srclist=sources.list # sources must be in dependency order! moduleDirs=`cat modules.list` for dir in $moduleDirs do # try to find module-info.java in given directory moduleInfo=`find \$dir -name module-info.java` [ -z "$moduleInfo" ] && { echo "ERROR: Ignoring $dir because it contains no module-info.java!" >&2 continue } # check how many module-info.java files were found modulesCount=`echo \$moduleInfo | wc -w` if [ "$modulesCount" = 1 ] then # try to extract the module name from module-info.java moduleName=`awk '/^[ \t]*module/ {print $2}' \$moduleInfo` [ -z "$moduleName" ] && { echo "ERROR: Could not extract module name from $moduleInfo" >&2 continue } srcdir=`dirname \$moduleInfo` echo "Compiling module $moduleName in $srcdir to $moduleTargetDir ..." >&2 else # the grandparent directory of first module-info.java must be the parent of all firstModulesDir=`echo \$moduleInfo | awk '{print $1}'` srcdir=`dirname \$firstModulesDir` srcdir=`dirname \$srcdir` echo "Multimodule compile in $srcdir to $moduleTargetDir ...." >&2 fi # write sources list into file and use the javac @sources.list option find $srcdir -name '*.java' >$srclist if [ "$modulesCount" = 1 ] then javac --module-path $moduleTargetDir -d $moduleTargetDir/$moduleName @$srclist else javac --module-path $moduleTargetDir -d $moduleTargetDir --module-source-path $srcdir @$srclist fi rm -f $srclist done |
Most likely you'll have to make the script executable then:
chmod 754 modules-compile.sh
Mind that this script requires the name of the module written in same line
as the module
keyword in a module-info.java
of a single module (script line 23).
For multi-module compilation this is not needed.
This script works with both Maven and Eclipse projects.
Eclipse projects have all sources inside a src/
directory,
Maven has them in src/main/java/
and src/test/java/
.
It would take too much space to explain the script. Shell scripts are not well readable, they are quick & dirty, you need an expert to maintain them. But they are quite useful sometimes, so try it out !-)