-
Notifications
You must be signed in to change notification settings - Fork 0
User guide
This page gives an introduction to the Maven compiler plugin 4.0. For differences compared to the previous compiler plugin, see migration from Maven 3 to Maven 4.
The recommended way to declare source directories is with <source> elements.
Note that these sources are declared in the <build> element and therefore can apply to all plugins.
If no source is declared, the default values relevant to the compiler plugin are as below:
<build>
<sources>
<source>
<lang>java</lang>
<scope>main</scope>
<directory>src/main/java</directory>
</source>
<source>
<lang>java</lang>
<scope>test</scope>
<directory>src/test/java</directory>
</source>
</sources>
<build>If a <sources> element is declared, its content will replace the above default values.
Therefore, the above defaults may need to be copied if the developer wants to add sources
instead of replacing them.
The <source> element can be repeated as many times as desired for the same language and scope.
Optionally, an enabled flag allows to include or exclude the whole directory according a property value.
The following example adds the extension directory only if the value of the include.extension property is set to true.
For brevity, this example uses the default values of the <scope> and <lang> elements when applicable.
These defaults are main and java respectively.
<build>
<sources>
<source>
<directory>src/main/java</directory>
</source>
<source>
<directory>src/extension/java</directory>
<enabled>${include.extension}</enabled>
</source>
<source>
<scope>test</scope>
<directory>src/test/java</directory>
</source>
</sources>
<build>For each source directory, the list of source files to compile can be filtered.
If <include> elements are specified, only the files that match at least one include filters may be compiled.
If no <include> element is specified, then the default filter is glob:**/*.java.
Next, if <exclude> elements are specified, all included elements matching at least one exclude filter become excluded.
The filtering implementation uses java.nio.file.PathMatcher. The syntax is described in the
Javadoc of standard Java.
Various syntaxes are possible, including glob and regex.
If no syntax is specified, Maven defaults to the glob syntax where
/ is the path separator regardless the platform (including Windows),
* matches any filename inside a directory and ** matches any number of directories
(see above-cited Javadoc for more detailed explanation).
All paths to be matched are relative to the path specified in the <directory> element.
Example:
<build>
<sources>
<source>
<directory>src/main/java</directory>
<excludes>
<exclude>**/Foo*.java</exclude>
</excludes>
</source>
<source>
<scope>test</scope>
<directory>src/test/java</directory>
</source>
</sources>
</build>If "glob:" or other syntax was specified as a prefix of a pattern, the pattern is used as-is without any change. If no syntax is specified, then the pattern is modified as below before to be used as a glob syntax:
- The platform-specific separator (
\on Windows) is replaced by/. - Trailing
/is completed as/**. - The
**wildcard is interpreted as "0 or more directories" instead of "1 or more directories". This is implemented by adding variants of the pattern with**progressively removed. - Bracket characters
[,],{and}are escaped. - On Unix, the escape character
\is itself escaped (on Windows, it was the path separator converted to/).
The <source> element can be repeated many times with different Java releases.
The lowest release value is given to the --release compiler option for the base classes,
and the source files in all directories associated to that version are compiled together.
Then, the sources of all other releases are compiled in separated javac executions,
one execution for each release, in the order of increasing release values.
For each new execution, the output directories of all previous executions are added to the class-path with highest releases first,
and the output is written in the META-INF/versions/${release}/ sub-directory.
Example:
<build>
<sources>
<source>
<directory>src/main/java</directory>
<targetVersion>17</targetVersion>
</source>
<source>
<directory>src/main/java_21</directory>
<targetVersion>21</targetVersion>
</source>
<source>
<scope>test</scope>
<directory>src/test/java</directory>
</source>
</sources>
</build>Note: For targeting Java 8, the version number shall be 8, not 1.8.
The recommended way to build a project using the Java Module System is to declare the module name(s)
together with the sources, as in the example below. The name declared inside the <module> element
must match the name declared inside the module-info.java file of that module.
More than one module can be declared if desired.
It means that for Java modular projects, there is no longer a one-to-one relationship between a
Maven project or subproject and a Java module, unless the developer chooses to restrict herself
to exactly one Java module per Maven subproject. Note, however, that there is still a one-to-one
relationship between Java modules and Maven artifacts (the JAR files identified by Maven coordinates).
See the maven-jar-plugin for more information.
(TODO: maven-jar-plugin has not yet been updated for Java module support.)
<build>
<sources>
<source>
<module>my.product.foo</module>
<directory>src/java/my.product.foo/main</directory>
</source>
<source>
<module>my.product.foo</module>
<directory>src/java/my.product.foo/test</directory>
<scope>test</scope>
</source>
<source>
<module>my.product.bar</module>
<directory>src/java/my.product.bar/main</directory>
</source>
<source>
<module>my.product.bar</module>
<directory>src/java/my.product.bar/test</directory>
<scope>test</scope>
</source>
</sources>
</build>While it is a common practice to have a sub-directory of the same name as the module, this is not mandatory.
It is also a common practice to place the main and test sub-directories after the java sub-directory
instead of before it (in order to group them per module), but this is not mandatory neither.
It is also possible to specify multiple source directories for the same module.
For a modular project, the compiler writes the class files of each module in a directory of the same name as the module.
For example, the classes of the my.product.foo module will be written in the target/classes/my.product.foo/ directory
rather than directly in target/classes/. This is the standard javac behavior, not a Maven particularity.
Technically, the behavior of the Maven compiler plugin is straightforward: if a <module> element is present,
the plugin declares the source directories using the --module-source-path compiler option,
which implies the above-cited new output directory.
Otherwise, the plugin declares the source directories with the --source-path option.
The plugin does nothing more.
If that new output directory is not desired, the Maven 3 way to do a modular project is still supported:
setup the Maven project has if it was non-modular (without <module> element),
but keep adding a module-info.java file in the sources.
However, migration to a fully modular project is recommended when applicable.
By default, each dependency of a modular project is placed on the module-path if the dependency contains
a module-info.class file or an Automatic-Module-Name entry in the META-INF/MANIFEST.MF file,
otherwise the dependency is placed on the class-path.
This heuristic rule can be overridden by specifying explicitly the type of the dependency.
For example, the following snippet forces the placement of a dependency on the module-path
even if that dependency has no module-info or manifest entry:
<dependencies>
<dependency>
<groupId>my.dependency</groupId>
<artifactId>foo</artifactId>
<type>modular-jar</type>
<version>1.0</version>
</dependency>
</dependencies>If the version is managed by a <dependencyManagement> section,
the managed dependency must contains the same <type> element,
otherwise Maven will consider that this is not the same artifact.
For the reverse operation
(force placement on the class-path instead of the module-path even if the dependency is modular),
replace modular-jar type by classpath-jar.
By default, the maven compiler plugin automatically adds the following options when compiling the tests:
-
--patch-moduleoptions for each<source>elements having atestscope. -
--add-modulesoption with a value set to all dependencies having thetestortest-onlyscope. -
--add-readsoptions (repeated for each Java module to patch) for all dependencies having thetestortest-onlyscope.
In many cases, this is sufficient and there is no need to configure the plugin with additional compiler arguments.
However, sometime there is a need for more control.
In particular, there is often a need to export or open more packages for whitebox testing.
While it can be done by specifying options such as --add-exports in the <compilerArgs> element of the plugin configuration,
this is tedious, error prone and need to be repeated in other plugins such as Surefire.
The Maven compiler plugin offers a more convenient mechanism, described below.
This is a variation of the deprecated practice consisting in overwriting the
module-info.java of the main code with another module-info.java defined in the test directory.
Instead, the module-info.java file formerly defined in the test
should be replaced by a module-info-patch.txt file in the same directory.
The later is Maven-specific: the content of module-info-patch.txt looks like module-info.java,
but is not standard Java, hence the .txt file suffix.
The principles are:
- Everything that a developer would like to change in a
module-info.javafile for testing purposes is declared inmodule-info-patch.txt. - Everything that is not in
module-info.javais not inmodule-info-patch.txtneither. In particular, everything that specify paths to JAR files stay in thepom.xmlfile. - All keywords inside
patch-modulemap directly to Java compiler or launcher options.
The syntax is:
- The same styles of comment as Java (
/*…*/and//) are accepted. - The first tokens, after comments, shall be
patch-modulefollowed by the name of the module to patch. - All keywords inside
patch-moduleare Java compiler or Java launcher options without the leading--characters. - Each option value ends at the
;character, which is mandatory.
The accepted options are limit-modules, add-reads, add-exports and add-opens.
Note that they are options where the value is not a path to source or binary files.
Options with path values should be handled as Maven dependencies or sources instead.
Below is an example of a module-info-patch.txt file content
for modifying the module-info of a module named my.product.foo:
/*
* The same comments as in Java are allowed.
*/
patch-module my.product.foo { // Put here the name of the module to patch.
add-reads org.junit.jupiter.api, // Frequently used dependency for tests.
my.product.test.fixture; // Put here any other dependency needed for tests.
add-exports my.product.foo.internal // Name of a package which is normally not exported.
to org.junit.jupiter.api, // Any module that need access to above package for testing.
my.product.test.fixture; // Can export to many modules, as a coma-separated list.
add-exports my.product.foo.mock // Another package to export. It may be a package defined in the tests.
to my.product.biz; // Another module of this project which may want to reuse test classes.
}The following values have special meanings:
-
TEST-MODULE-PATH: all dependencies having a test scope.- This is Maven-specific, not a standard value recognized by Java tools.
- Allowed in:
add-reads,add-exports.
-
ALL-MODULE-PATH: everything on the module path, regardless if test or main.- This is a standard value accepted by the Java compiler.
- Allowed in:
--add-modulesspecified in<compilerArgs>element of plugin configuration.
-
ALL-UNNAMED: all non-modular dependencies.- This is a standard value accepted by the Java compiler.
- Allowed in:
add-exports.
If no module-info-patch.txt file is present, the default behavior of the Maven compiler plugin
is as if a file was present with the following content:
patch-module <module name> {
add-reads TEST-MODULE-PATH;
}If a module-info-patch.txt file is present, then it should contain the above
add-reads line if the default behavior is desired.
However, it is recommended to put values more specific than TEST-MODULE-PATH.
These values can be easily determined by compiling the tests without add-reads option.
The compiler error messages are explicit about which reads are missing.
If the compilation failed, the Maven compiler plugin writes the options that it used in a javac.args or
javac-test.args file (for compilation of main code and test code respectively) in the target directory.
The compilation can be executed on the command-line as below.
Note that the paths to source files inside javac.args are relative to the project directory.
Therefore, that command must be executed in the same directory as the pom.xml file,
or in a directory containing a copy or a branch of that project.
javac @target/javac.argsBy default, javac.args is not written if the compilation succeed.
Users can force the plugin to write that file in the following ways:
- Execute
mvnwith the--verboseoption (more exactly: enable logging messages at the debug level). - Or provide the
<verbose>true</verbose>option in the plugin configuration.