Skip to content

Commit 203cb86

Browse files
authored
Add documentation for multi-release and multi-module projects (#976)
1 parent d958a62 commit 203cb86

File tree

9 files changed

+596
-9
lines changed

9 files changed

+596
-9
lines changed

src/site/fml/faq.fml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,7 @@ under the License.
3434
in the build of your project for source generation (in that way the sources are generated each time).
3535
</p>
3636
<p>For more information about Modello, please visit the
37-
<a href="https://codehaus-plexus.github.io/modello/">Modello website</a>
38-
.
37+
<a href="https://codehaus-plexus.github.io/modello/">Modello website</a>.
3938
</p>
4039
</answer>
4140
</faq>

src/site/markdown/examples/annotation-processor.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ under the License.
2222
[Annotation processing](https://docs.oracle.com/en/java/javase/23/docs/specs/man/javac.html#annotation-processing) is used to let the compiler generate source code based on annotations.
2323
For example, the [Hibernate Processor](https://hibernate.org/orm/processor/) provides an annotation processor to generate the JPA metamodel.
2424

25+
2526
## Recommended way to activate annotation processing
27+
2628
Up to JDK 23, the compiler automatically scanned the classpath for annotation processors and executed all found by default.
2729
For security reasons, this got disabled by default since JDK 23 and annotation processing needs to be activated explicitly.
2830
The recommended way for this is to list all desired processors using either the `<annotationProcessors>` plugin configuration
@@ -31,7 +33,9 @@ Only those processors will get executed by the compiler.
3133

3234
The following example shows how to activate the Hibernate Processor.
3335

36+
3437
### Maven 3
38+
3539
When using Maven 3 and Maven Compiler Plugin version 3.x you do this using the following configuration.
3640

3741
```xml
@@ -59,7 +63,9 @@ When using Maven 3 and Maven Compiler Plugin version 3.x you do this using the f
5963
</project>
6064
```
6165

66+
6267
### Maven 4
68+
6369
With Maven 4 and Maven Compiler Plugin 4.x the way described above got deprecated and will be removed in a future version of the plugin.
6470
Configuration now makes use of the new `processor` dependency type to shorten the configuration,
6571
give control over the placement on class-path or module-path, and make the information available to other plugins.

src/site/markdown/examples/compile-using-different-jdk.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,9 @@ or sets an environment variable, so that the build remains portable.
7979
[...]
8080
<profile>
8181
<id>compiler</id>
82-
<properties>
83-
<JAVA_11_HOME>/usr/lib/jvm/java-11-openjdkk</JAVA_11_HOME>
84-
</properties>
82+
<properties>
83+
<JAVA_11_HOME>/usr/lib/jvm/java-11-openjdkk</JAVA_11_HOME>
84+
</properties>
8585
</profile>
8686
</profiles>
8787
[...]

src/site/markdown/index.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,15 @@ Of course, patches are welcome, too.
5252
Contributors can check out the project from our [source repository](./scm.html) and will find supplementary information
5353
in the [guide to helping with Maven](http://maven.apache.org/guides/development/guide-helping.html).
5454

55+
The following pages describes how to use the plugin beyond the default
56+
"one source directory, one module, one release" default configuration:
57+
58+
* [Declaration of source directories](./sources.html)
59+
* [Multi-release project](./multirelease.html)
60+
* [Modular project](./modules.html)
61+
* [Module-info patch for tests](./module-info-patch.html)
62+
63+
5564
# Examples
5665

5766
To provide you with better understanding on some usages of the Compiler Plugin,
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
<!--
2+
Licensed to the Apache Software Foundation (ASF) under one
3+
or more contributor license agreements. See the NOTICE file
4+
distributed with this work for additional information
5+
regarding copyright ownership. The ASF licenses this file
6+
to you under the Apache License, Version 2.0 (the
7+
"License"); you may not use this file except in compliance
8+
with the License. You may obtain a copy of the License at
9+
10+
http://www.apache.org/licenses/LICENSE-2.0
11+
12+
Unless required by applicable law or agreed to in writing,
13+
software distributed under the License is distributed on an
14+
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
KIND, either express or implied. See the License for the
16+
specific language governing permissions and limitations
17+
under the License.
18+
-->
19+
20+
# Module-info patch
21+
22+
For white box testing, it is necessary to use compiler options such as
23+
`--patch-module`, `--add-modules`, `--add-reads`, `--add-exports` and `--add-opens`.
24+
Writing these options inside the Maven `<compilerArgs>` XML element is tedious, redundant
25+
(the name of the module to patch is repeated in every occurrence of some options), error prone,
26+
and must be repeated in every plugins that depends on the tests (Surefire, Javadoc for test documentation, _etc._).
27+
An alternative is to put a `module-info.java` file in the tests which *replace* the `module-info.java` file of the main code.
28+
However, it forces the developer to repeat all the content of the main `module-info.java`
29+
into the test `module-info.java` before to add test-specific statements.
30+
This is tedious if the main `module-info.java` is large, and risky if the two files become out of sync.
31+
32+
Instead of defining a `module-info.java` file in test, Maven projects can define a `module-info-patch.maven`.
33+
The content of `module-info-patch.maven` uses the same syntax as Java, C/C++, JavaScript, Groovy, _etc._
34+
(comments between `/*``*/` or after `//`, blocks between `{``}`, statements ending with `;`)
35+
but is not Java, hence the `.maven` file suffix.
36+
The general principles are:
37+
38+
* Everything that a developer would like to change in a `module-info.java` file for testing purposes is declared in `module-info-patch.maven`.
39+
* Everything that is not in `module-info.java` is not in `module-info-patch.maven` neither.
40+
In particular, everything that specify paths to JAR files or paths to source code stay in the `pom.xml` file.
41+
* All keywords except `patch-module`, `SUBPROJECT-MODULES` and `TEST-MODULE-PATH`
42+
map directly to Java compiler or Java launcher options.
43+
44+
Compared to declaring options in `<compilerArgs>` XML elements, the `module-info-patch.maven` file is more readable,
45+
keep the options in separated files for each module on which the options are applied, is less redundant as it avoids
46+
the need to repeat the module name in every `--add-reads`, `--add-exports` and `--add-opens` options,
47+
and is more flexibly as it is translated in slightly different options for compilation and test executions
48+
(e.g. `TEST-MODULE-PATH` means modules having `test` and `test-only` Maven's scope at compilation time,
49+
but means modules having `test` and `test-runtime` Maven's scope at execution time).
50+
51+
52+
## Syntax
53+
54+
The syntax is:
55+
56+
* The same styles of comment as Java (`/*``*/` and `//`) are accepted.
57+
* The first tokens, after comments, shall be `patch-module` followed by the name of the module to patch.
58+
* All keywords inside `patch-module` are Java compiler or Java launcher options without the leading `--` characters.
59+
* Each option value ends at the `;` character, which is mandatory.
60+
61+
The accepted keywords are `add-modules`, `limit-modules`, `add-reads`, `add-exports` and `add-opens`.
62+
Note that they are options where the values are package or module names, not paths to source or binary files.
63+
Options with path values (`--module-path`, `--module-source-path`, `--patch-module`, _etc._)
64+
continue to be derived from the dependencies declared in the POM.
65+
66+
67+
### Options applying to all modules
68+
69+
All options declared in a `module-info-patch.maven` file apply only to the module declared after the `patch-module` token,
70+
except the `--add-modules` and `--limit-modules` options.
71+
These two options apply to all modules in a multi-modules project,
72+
because these options given to `java` or `javac` expect no module name.
73+
Therefore, it is not necessary to repeat `add-modules TEST-MODULE-PATH` in all modules:
74+
declaring that particular option in only one module of a multi-modules project is sufficient.
75+
If the `--add-modules` or `--limit-modules` options are declared in many `module-info-patch.maven` files of a multi-modules project,
76+
then the effective value is the union of the values declared in each file, without duplicated values.
77+
78+
79+
### Special option values
80+
81+
The following option values have special meanings:
82+
83+
* `SUBPROJECT-MODULES`: all other modules in the current Maven (sub)project.
84+
* This is Maven-specific, not a standard value recognized by Java tools.
85+
* Allowed in: `add-exports`.
86+
* `TEST-MODULE-PATH`: all dependencies having a test scope in the build tools.
87+
* This is specific to this format, not a standard value recognized by Java tools.
88+
* Allowed in: `add-modules`, `add-reads` and `add-exports` options.
89+
* `ALL-MODULE-PATH`: everything on the module path, regardless if test or main.
90+
* This is a standard value accepted by the Java compiler.
91+
* Allowed in: `add-modules` option.
92+
* `ALL-UNNAMED`: all non-modular dependencies.
93+
* This is a standard value accepted by the Java compiler.
94+
* Allowed in: `add-exports` option.
95+
96+
97+
## Example
98+
99+
Below is an example of a `module-info-patch.maven` file content
100+
for modifying the `module-info` of a module named `org.foo.bar`:
101+
102+
```java
103+
/*
104+
* The same comments as in Java are allowed.
105+
*/
106+
patch-module org.foo.bar { // Put here the name of the module to patch.
107+
add-modules TEST-MODULE-PATH; // Recommended value in the majority of cases.
108+
109+
add-reads org.junit.jupiter.api, // Frequently used dependency for tests.
110+
my.product.test.fixture; // Put here any other dependency needed for tests.
111+
112+
add-exports org.foo.bar.internal // Name of a package which is normally not exported.
113+
to org.junit.jupiter.api, // Any module that need access to above package for testing.
114+
org.something.else; // Can export to many modules, as a coma-separated list.
115+
116+
add-exports org.foo.bar.fixtures // Another package to export. It may be a package defined in the tests.
117+
to org.foo.bar.other; // Another module of this project which may want to reuse test fixtures.
118+
}
119+
```
120+
121+
122+
### How module info patches are compiled
123+
124+
`module-info-patch.maven` are compiled into a file of options in the following ways:
125+
126+
* `add-modules org.foo, org.bar;` is translated to `--add-modules org.foo,org.bar`.
127+
* Note: spaces between `org.foo` and `org.bar` are removed for interpreting the option values as a single argument.
128+
* `limit-modules org.foo, org.bar;` is translated to `--limit-modules org.foo,org.bar`.
129+
* Note: idem regarding spaces removal.
130+
* `add-reads org.foo, org.bar;` is translated to `--add-reads org.patched=org.foo,org.bar`
131+
where `org.patched` is the module name declared in the first statement of the `module-info-patch` file.
132+
* `add-exports com.biz to org.foo, org.bar;` is translated to `--add-exports org.patched/com.biz=org.foo,org.bar`
133+
where `org.patched` is as above.
134+
* `add-opens com.biz to org.foo, org.bar;` is translated to `--add-opens org.patched/com.biz=org.foo,org.bar`
135+
like above but only for runtime execution, not for compilation.
136+
137+
There is a separated `module-info-patch.maven` file for each module,
138+
and the Maven compiler plugin merges them in a single set of options for `java` and `javac`.
139+
While this format does not require the use of module source hierarchy, it fits nicely in that hierarchy.
140+
141+
The results of the translation to compiler options can be seen in the `target/javac.args` and `target/javac-test.args` files.
142+
Those files are produced when the build failed or when Maven was executed with the `--verbose` command-line option.
143+
In addition, a slightly different set of options, suitable for tests execution, is written in the
144+
`target/test-classes/META-INF/maven/module-info-patch.args` file.

src/site/markdown/modules.md

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
<!--
2+
Licensed to the Apache Software Foundation (ASF) under one
3+
or more contributor license agreements. See the NOTICE file
4+
distributed with this work for additional information
5+
regarding copyright ownership. The ASF licenses this file
6+
to you under the Apache License, Version 2.0 (the
7+
"License"); you may not use this file except in compliance
8+
with the License. You may obtain a copy of the License at
9+
10+
http://www.apache.org/licenses/LICENSE-2.0
11+
12+
Unless required by applicable law or agreed to in writing,
13+
software distributed under the License is distributed on an
14+
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
KIND, either express or implied. See the License for the
16+
specific language governing permissions and limitations
17+
under the License.
18+
-->
19+
20+
# Modular projects
21+
22+
The Maven 3 way to make a modular project is to put a `module-info.java` file in the root directory of Java source files.
23+
Because the compilation and execution of tests usually require an amended version of module information,
24+
Maven 3 allows to overwrite that file with another `module-info.java` file placed in the test source directory.
25+
While this approach is still supported in Maven 4 for compatibility reasons,
26+
it is deprecated and may no longer be supported in a future version.
27+
Developers are encouraged to migrate to the approach described below.
28+
29+
30+
## Maven 3
31+
32+
The directory layout of a modular project in Maven 3 was as below:
33+
34+
```
35+
src
36+
├─ main
37+
│ └─ java
38+
│ ├─ module-info.java
39+
│ └─ org/foo/bar/*.java
40+
├─ test
41+
│ └─ java
42+
│ ├─ module-info.java (optional)
43+
│ └─ org/foo/bar/*.java
44+
└─ target
45+
└─ classes
46+
└─ org/foo/bar/*.class
47+
```
48+
49+
An alternative to the `test/java/module-info.java` file is to declare compiler arguments
50+
such as `--add-reads` in the `<testCompilerArgs>` element of the plugin configuration.
51+
52+
53+
## Maven 4 with package hierarchy
54+
55+
Maven 4 allows the same directory layout as Maven 3.
56+
However, the `module-info.java` file in the test directory *should* be
57+
replaced by a `module-info-patch.maven` file in the same directory.
58+
59+
```
60+
src
61+
├─ main
62+
│ └─ java
63+
│ ├─ module-info.java
64+
│ └─ org/foo/bar/*.java
65+
├─ test
66+
│ └─ java
67+
│ ├─ module-info-patch.maven (optional)
68+
│ └─ org/foo/bar/*.java
69+
└─ target
70+
└─ classes
71+
└─ org/foo/bar/*.class
72+
```
73+
74+
The Maven compiler automatically adds `--patch-module`, `--add-modules` and `--add-reads` arguments for compiling the tests.
75+
If more `--add-reads` arguments are needed, or if `--add-modules`, `--add-exports` or `--add-opens` arguments are also needed,
76+
then a `module-info-patch.maven` file (syntax described below) can be placed in the `test/java` directory.
77+
This Maven file is preferred to a `module-info.java` file in the test directory because the Maven file
78+
*completes* the main `module-info.class` (using compiler arguments) instead of *replacing* it.
79+
80+
81+
### Limitation
82+
83+
When using the package hierarchy, problems may occur if the module name is a single name without `.` separator
84+
(for example, `foo` or `bar` but not `foo.bar`) and that name is identical to a package name.
85+
In such case, the hack implemented in the Maven compiler plugin for Maven 3 compatibility
86+
become confused about whether a directory named `foo` represents the module or the package.
87+
For avoiding ambiguity, use module names containing at least one `.` character
88+
(as it should be when using the reverse domain name convention)
89+
or use the module source hierarchy described below.
90+
91+
92+
## Maven 4 with module source hierarchy
93+
94+
The [module source hierarchy](https://docs.oracle.com/en/java/javase/17/docs/specs/man/javac.html#directory-hierarchies)
95+
introduces one additional directory level in the paths to source Java files and to compiled classes.
96+
The name of this directory is the Java module name,
97+
and the directory is always present even in projects containing only one module.
98+
More than one Java module can be present in the same Maven sub-project.
99+
Such multi-module projects have advantages such as resolving compiler warnings
100+
in forward references to dependent modules and easier sharing of test code between modules.
101+
For example, a Maven project for a single Java module named `org.foo.bar` would have the following directory layout:
102+
103+
```
104+
src
105+
├─ org.foo.bar
106+
│ ├─ main
107+
│ │ └─ java
108+
│ │ ├─ module-info.java
109+
│ │ └─ org/foo/bar/*.java
110+
│ └─ test
111+
│ └─ java
112+
│ ├─ module-info-patch.maven (optional)
113+
│ └─ org/foo/bar/*.java
114+
└─ target
115+
└─ classes
116+
└─ org.foo.bar
117+
└─ org/foo/bar/*.class
118+
```
119+
120+
Note that the output directory also contains an `org.foo.bar` directory level.
121+
That directory level is generated by `javac`, this is not a convention invented by Maven.
122+
123+
Above layout can be declared with the following fragment in the `pom.xml` file.
124+
Since this example uses the default directory layout for modular projects,
125+
the `<directory>` elements do not need to be specified.
126+
127+
```xml
128+
<build>
129+
<sources>
130+
<source>
131+
<module>org.foo.bar</module>
132+
</source>
133+
<source>
134+
<scope>test</scope>
135+
<module>org.foo.bar</module>
136+
</source>
137+
</sources>
138+
</build>
139+
```
140+
141+
## Black Box testing
142+
143+
"Black Box testing" refers to tests executed without access to the internal code of the project to test.
144+
Internal codes include package-private classes, interfaces, methods and fields, and also all non-exported packages.
145+
Because the module source hierarchy allows any number of Java modules in the same Maven sub-project,
146+
it is easy to add an `org.foo.bar.test` module which will test the `org.foo.bar` module as if it was
147+
an ordinary client application.
148+
149+
150+
## White Box testing
151+
152+
"White Box testing" refers to tests which have an access to the internal classes of the project to test.
153+
For any `<source>` element with the `test` scope, all Java code placed in the directory managed by that
154+
element is automatically white box testing for the module declared in the `<module>` child element.
155+
Access to package-private types and members is granted by placing the code in the same package as the code to test.
156+
Access to non-exported modules is implicit, but only for the module where the tests belong.
157+
158+
159+
## Reusing test fixtures of another module
160+
161+
The Maven 3 way (`test-jar`) is still supported in Maven 4.
162+
However, when using module source hierarchy, it is easier to place test fixtures
163+
in the test code of any module which is required by all modules that need these fixtures.
164+
The test fixtures can be in any package, not necessarily a package that exists in the main code.
165+
Then, the `module-info-patch.maven` file can export that package to the other modules.
166+
For example if the test fixtures are placed in the `org.foo.bar.test` package of the `org.foo.bar` module:
167+
168+
```java
169+
patch-module org.foo.bar { // Put here the name of the module to patch.
170+
add-modules TEST-MODULE-PATH; // Recommended value in the majority of cases.
171+
add-reads TEST-MODULE-PATH;
172+
173+
add-exports org.foo.bar.test // The package that contains the test fixtures.
174+
to SUBPROJECT-MODULES; // The other modules which want to use those test fixtures.
175+
}
176+
```
177+
178+
`SUBPROJECT-MODULES` is a Maven-specific keyword for exporting to all other Java modules
179+
in the Maven (sub)project being compiled. It can be replaced by an explicit list of modules.
180+
That's all, no need to deploy or install a test JAR.

0 commit comments

Comments
 (0)