desruisseaux opened a new pull request, #271:
URL: https://github.com/apache/maven-compiler-plugin/pull/271

   This is a proposed rewriting of the Maven compiler plugin. This work is for 
Maven 4 only, the plugin for Maven 3 will stay unaffected. A major goal of this 
work is to improve the support of Java Module System in Maven.
   
   
   # Incompatible changes
   This section describes behaviors of the Maven compiler plugin 4 that changed 
in incompatibles way compared to versions 3. Some projects building 
successfully with version 3 of `maven-compiler-plugin` may fail with version 4 
because of those changes. This section describes corrections that can be 
applied to projects for fixing those build failures.
   
   
   ## Cannot use both `--release` and `--source`
   As a general rule, this new Maven plugin does not modify the compiler 
options specified in the configuration. This policy may break projects that 
specify the `<release>` option together with `<source>` and `<target>`. The 
previous version of the plugin applied some heuristic rules for deciding which 
options to pass to the Java compiler. The new version passes all options 
verbatim, which may result in the following message from the `javac` compiler:
   
   > option --source cannot be used together with --release
   
   
   ### Correction if the build fails
   Removes the `<source>` and `<target>` configuration parameters. They may be 
specified indirectly through the `maven.compiler.source` and 
`maven.compiler.target` properties. If those configuration parameters or 
properties are inherited from a super-POM that cannot be modified, clear the 
`<source>` and `<target>` parameters as below:
   
   ```xml
   <project>
     <build>
       <plugins>
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-compiler-plugin</artifactId>
           <configuration>
             <source/>
             <target/>
             <release>17</release>
           </configuration>
         </plugin>
       </plugins>
     </build>
   </project>
   ```
   
   
   ## Use of dependencies with automatic names
   If a dependency does not declare explicitly whether the JAR file should be 
placed on the class-path or on the module-path, the Maven plugin uses heuristic 
rules for making its own decision. Those rules are not obvious, and the 
selected option is not always appropriate for the project. The results may 
sometime be different between this version and the previous version of 
`maven-compiler-plugin`. Some JAR files may placed on the class-path when they 
were previously on the module-path, or conversely.
   
   By default, a dependency is placed on `--module-path` if the project is 
itself modularized (i.e., contains a `module-info.java`) and one of the 
following conditions is true:
   
   * The JAR contains a `module-info.class` entry.
   * The JAR contains a `META-INF/MANIFEST.MF` entry with an 
`Automatic-Module-Name` attribute.
   
   In all other cases, the dependency is placed on `--class-path`. This default 
behavior may be adjusted in future Maven plugin versions depending on 
experience.
   
   
   ### Correction if the build fails
   Developers are strongly encouraged to specify explicitly where they want 
their non-modular JAR files to appear in a modular project. It can be done by 
setting the type of a dependency to `modular-jar` or `classpath-jar`. Example:
   
   ```xml
   <project>
     <dependencies>
       <dependency>
         <groupId>org.codehaus.plexus</groupId>
         <artifactId>plexus-utils</artifactId>
         <version>3.0.24</version>
         <type>modular-jar</type>
       </dependency>
     </dependencies>
   </project>
   ```
   
   Note that specifying the dependency type is usually not needed in other 
cases (e.g., non-modular project, or dependencies that are already modular). It 
should not be needed neither for dependencies with the runtime scope. However, 
it may be a good practice to be explicit anyway for more guarantees.
   
   
   ### Guideline for choosing the dependency type
   If a non-modular dependency is forced to the `modular-jar` type, Java will 
automatically infer a module name from the file name. That name can be shown by 
the `jar --describe-module` command. For example, the following command shows 
the name of above `plexus-utils` dependency on a Unix system:
   
   ```bash
   jar --describe-module --file 
~/.m2/repository/org/codehaus/plexus/plexus-utils/3.0.24/plexus-utils-3.0.24.jar
   ```
   
   The command output contains `plexus.utils@3.0.24 automatic`. That module 
name must be declared in the `module-info.java` file.
   
   ```java
   module my.module {
       requires plexus.utils;
   }
   ```
   
   Conversely, if a dependency is forced to the `classpath-jar` type, then the 
`module-info.java` is not modified. Instead, the `pom.xml` file should have the 
following configuration (replace `my.module` by the actual project module name):
   
   ```xml
   <project>
     <build>
       <plugins>
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-compiler-plugin</artifactId>
           <configuration>
             <compilerArgs>
               <arg>--add-reads</arg>
               <arg>my.module=ALL-UNNAMED</arg>
             </compilerArgs>
           </configuration>
         </plugin>
       </plugins>
     </build>
   </project>
   ```
   
   
   ## `jpms.args` generation
   The `META-INF/jpms.args` file is no longer generated.
   
   
   # Changes in compiler parameters
   In its current version, the new plugin does not remove any parameters that 
exist in version 3 of `maven-compiler-plugin`. However, the behavior of some 
parameters has been modified and some parameters are deprecated. This section 
lists the changes.
   
   
   ## Deprecated but still working parameters
   The following parameters are deprecated but are still working:
   
   * `<compilerArgument>`: already replaced by `<compilerArgs>` since Maven 
3.1. The replacement uses a list of strings instead of a single string.
   * `<testCompilerArgument>`: replaced by `<testCompilerArgs>` for the same 
reason as above. Note that the latter is a new parameter introduced in Maven 4, 
see next point.
   * `<testCompilerArguments>`: replaced by `<testCompilerArgs>` for 
consistency with the main plugin. The former parameter was inconsistent in the 
name ("Arguments" suffix instead of "Args"), but also in the value type 
(`Map<String,String>` instead of `List<String>`).
   * `<annotationProcessorPaths>`: replaced by ordinary dependencies with 
`<type>proc</type>`. The latter is a new artifact type introduced in Maven 
4-alpha13.
   * `<useModulePath>`: replaced by `<type>classpath-jar</type>` declarations 
in dependencies.
   
   
   ## Deprecated parameters that are no-op
   The following parameters are marked as deprecated for removal. They all 
became no-op in the new plugin:
   
   * `<forceJavacCompilerUse>`: the documentation is not really explicit, but 
this parameters seems to be about forcing the use of `java.lang.Compiler` API 
instead of `javax.tools.JavaCompiler`. The former class was deprecated since 
Java 9 and no longer exists in Java 21.
   * `<compilerVersion>`: this parameter was passed to 
`org.codehaus.plexus.compiler.CompilerConfiguration`, but it is unclear how the 
Plexus compiler used it. We see no obvious mapping in `javax.tools` or 
`java.lang.Process` API.
   * `<compilerReuseStrategy>`: does not apply well to the 
`javax.tools.JavaCompiler` API that the new plugin is using.
   * `<skipMultiThreadWarning>`: deprecated as a consequence of 
`<compilerReuseStrategy>` deprecation.
   * `<optimize>`: was already deprecated in Maven 3, reported here for 
completness.
   * `<outputFileName>`: producing the final JAR file can be considered as the 
task of separated plugins, for example the JAR plugin. Furthermore, this 
parameter does not work well in the context of Module Source Hierarchy, because 
the output is not a single JAR file.
   * `<outputTimestamp>`: not really applicable to the Maven compiler plugin. 
It was used only as an heuristic rules for guessing if the developer intended 
to have a reproducible build.
   
   
   ## Changes in default values
   The Maven compiler plugin has numerous default values, which sometime differ 
from the standard (JDK) default values. For example, the default value of 
`-source` and `-target` options was hard-coded to "1.8" in Maven 3, while the 
standard (JDK) default is the version of the JDK used for compiling the code. 
The following parameters had their default value modified in the new plugin:
   
   * `<source>` and `<target>`: removed the default value and added a Javadoc 
saying "As of Java 9, the --release option is preferred."
   * `<release>`: no default value. Therefor, the standard (JDK) default 
applies.
     * If both `<release>` and `<target>` are unspecified, a warning is logged.
   * `<debugLevel>`: omitting this parameter now means "JDK default debug info" 
instead of "all debug info".
     * A new `all` level (Maven-specific) has been added for meaning "all debug 
info".
   * `<createMissingPackageInfoClass>`: default changed from `true` to `false`,
     because this workaround is no longer necessary for the new incremental 
build system.
   * `<showWarnings>`: default to `true` for consistency with the JDK default 
value.
   
   ### Default values not yet changed
   The following changes are in consideration, but not yet applied. The intend 
for those changes would be to have the same default values as the `javac` tool:
   
   * `<meminitial>` and `<maxmem>`: if no unit is specified, change the default 
from 'M' to bytes as interpreted by the `javac` tool.
   
   ## Changes in parameters validation
   * Include/exclude filters accept also glob and regex syntax (see 
[implementation changes](Implementation-changes)).
   * All parameters for memory settings accept also 'K' and 'G' suffix in 
addition of 'M'.
   * `<debugLevel>` values unknown to the plugin but known by the compiler are 
now accepted.
   
   
   # Changes in implementation
   This section lists some noticeable implementation changes.
   
   ## Plexus dependencies removed
   The major change is the replacement of Plexus compiler API by the standard 
`javax.tools.JavaCompiler` API introduced in Java 6. This changes required an 
almost full rewrite of the compiler plugin. Other Plexus dependencies such as 
`StringUtils` could also be removed because they now have replacements in the 
standard Java API.
   
   
   ## ASM no longer used for generating package-info
   The previous compiler plugin used ASM for generating the 
`package-info.class` files that were omitted by the Java compiler because 
empty. This issue is moot because the `package-info.class` files were generated 
as a workaround for the way that Maven incremental compilation worked. The new 
plugin no longer generate those classes by default (i.e., the 
`<createMissingPackageInfoClass>` default value has been changed to `false`). 
If empty `package-info.class` files are nevertheless desired, they are 
generated by the Java compiler itself with the `-Xpkginfo:always` option. If 
that extra option is not supported, then a warning is logged and no option is 
added.
   
   
   ## Heuristic removed
   The previous plugin tried to be clever by intercepting and modifying the 
values of user-supplied `--patch-module` compiler arguments, by adding 
`--path-module` and `--add-reads <module>=ALL-UNNAMED` arguments on its own 
initiative, _etc._ The result was saved in a `META-INF/jpms.args` file added to 
the JAR file. Above-cited heuristics have been removed from the new compiler 
plugin, as this plugin aims to give more configuration control to the users. 
For example, developers are encouraged to set a dependency type to 
`modular-jar` or `classpath-jar` instead of `jar`. Some heuristics have been 
kept for compatibility with current widespread practice, in particular when the 
dependency type is only `jar`. But most heuristics should not be applied when 
developers use the most explicit ways to configure Maven.
   
   
   ## Include/exclude filters
   The Plexus scranners used for include/exclude filters have been replaced by 
`java.nio.file.PathMatcher`. The set of allowed syntax contains at least "glob" 
and "regex". See `FileSystem.getPathMatcher(String)` Javadoc for a description 
of the "glob" syntax. If no syntax is specified, then the default syntax is 
"glob" with the following modification:
   
   * Unless escaped by `\`, all occurrences of the `/` character are replaced 
by the platform-specific separator.
   
   The list of files to process is built by applying the path matcher on each 
regular (non directory) files. The walk in file trees has the following 
characteristics:
   
   * Symbolic links are followed.
   * Hidden files and hidden directories are ignored.
   
   
   ## Incremental compilation
   The code for detecting which files to recompile has been rewritten. The 
previous code had an issue with list of files generated and compared in one 
case as absolute files, and in other case as relative files. Consequently, the 
plugin always considered that all files changed, which may explain the 
performance issue reported in 
[MCOMPILER-209](https://issues.apache.org/jira/browse/MCOMPILER-209).
   
   The new implementation saves the list of source files, together with their 
last modification times, in a binary file with the `.cache` extension instead 
of a text file with the `.lst` extension. This custom binary encoding is more 
compact than the previous text files (because of less redundancies) and has 
more information. The incremental compilation behavior is modified as below:
   
   * Changes in compiler options cause a recompilation of all files. Those 
changes are detected on a "best effort" basis only.
   * By default, addition of new source files does not cause the recompilation 
of all files, because this is usually not necessary.
   * The modification times of source files are compared with the modification 
times of the same source files during the previous build (saved in above-cited 
binary file), instead of compared with the modification times of the class 
files.
   
   The last point avoids the problem that the `<createMissingPackageInfoClass>` 
parameter tried to solve. Because the new algorithm does not depend on `.class` 
timestamps, there is no need to force their creation.
   
   If the `<useIncrementalCompilation>` parameter is set to `false`, the plugin 
does not use the binary cache file and compare modification times of source 
files with modification times of class files. This approach reproduces the 
previous behavior.
   
   ### Detection of changes in dependencies
   When checking if a dependency changed, the new implementation compares the 
JAR timestamps against the start time of the previous build saved in the binary 
file instead of comparing against the value given by `session.getStartTime()`. 
The reason for this change is as below:
   
   * Consider a project with 3 modules named _A_, _B_ and _C_ where modules _B_ 
and _C_ depend on module _A_.
   * Module _A_ is modified and `mvn install` is executed on the command-line.
   * `A.jar` get a modification time which is after the build start time. 
Module _B_ and _C_ detect that fact and perform a "clean and build all". This 
is the current behavior of Maven 3 incremental compilation.
   
   But let assume that the compilation failed in module _B_ because of the 
change in _A_. The developer fixes the compilarion error in _B_, then executes 
`mvn install` a second time:
   
   * Compilation of _A_ is skipped because nothing changed. Consequently, the 
timestamp of `A.jar` is unchanged.
   * With the Maven 3 implementation, module _C_ will not detect that module 
_A_ changed because the modification time of `A.jar` is before the second build 
start time.
   
   By using the timestamp saved in the binary file instead, the new algorithm 
will detect that `A.jar` has been updated since the last build of _C_.
   
   
   ## Overwriting `module-info` in tests
   For modular projects having a `module-info.java` file in their main sources, 
Maven 3 recommended to declare the test dependencies in another 
`module-info.java` file located in the test sources. The latter was basically a 
copy of the former with the addition of, for example, `requires 
org.junit.jupiter.api` statements. This approach **is deprecated** in Maven 4. 
Java does not let us overwrite `module-info` easily, maybe for security 
reasons. Maven 3 supported that approach by putting the project upside-down: 
the test classes were considered the project's main code (so that the test's 
`module-info` takes precedence), and the main classes were considered the patch 
added on top of the tests. This new compiler plugin keeps the main classes as 
main and the test classes as patch, and instead executes the following steps:
   
   1. Compile the test `module-info.java` file alone.
   2. Temporarily rename the main `module-info.class` file as 
`module-info.class.bak`.
   3. Temporarily move the test `module-info.class` file (compiled in step 1) 
in place of the main one (saved in step 2).
   4. Temporarily rename the test `module-info.java` file as 
`module-info.java.bak` (otherwise the compiler seems to get confused).
   5. Compile all test classes except the test `module-info.java` file which 
has been compiled at step 1.
   6. Restore all `*.bak` files to their original location.
   
   A shutdown hook is registered for executing step 6 even if the user 
interrupted the compilation with \[Ctrl-C\]. Above hack works, but is obviously 
fragile and should be used in last resort only. The preferred approach is to 
use options formally supported by the JDK such as `--add-reads`. Their use 
should be easier with this new plugin than with Maven 3, since Maven 4 
automatizes some of those options.
   
   
   ## Compiler options validation
   The way to handle compiler options has been modified. Previously, the Maven 
plugin validated some options before to pass them to the compiler. For example, 
if the `<debuglevel>` value contains anything else than `lines`, `vars` or 
`source`, the plugin raised an error. The intend was to provide more 
informative message. But in the `javax.tools.JavaCompiler` interface, there is 
an API telling us whether an option is supported. Therefor, the new plugin 
version first asks to the compiler whether the option is supported, and only if 
the compiler said "no", the validation is performed for producing the error 
message. Consequently, if the compiler claims to support the `-g:foo` option, 
then the plugin will no longer block the use of the `foo` value in 
`<debuglevel>` even if the plugin does not know that value.
   
   
   ## Miscellaneous
   when the plugin proposes a code snippet (e.g. for specifying the target Java 
release), the Maven compiler plugin version shown in the snippet is fetched 
from `META-INF/MANIFEST.MF` instead of 
`META-INF/maven/org.apache.maven.plugins/maven-compiler-plugin/pom.properties`.
   
   
   # Changes in tests
   This section lists some noticeable changes in the tests.
   
   ## Changes in POM parameters
   Moved or added the following configuration parameters in the `pom.xml` files 
of some tests:
   
   * The `<outputDirectory>` and `<testOutputDirectory>` parameters declared 
under `<configuration>` moved to `<build>`, because those properties are 
read-only in the configuration.
   * Many `<source>` and `<target>` parameters have been either removed or 
replaced by `<release>`.
   * For some tests using a non-modular JAR is a modular project, 
`<type>modular-jar</type>` has been added in the dependency declaration.
   
   ## Changes to met new assumptions
   The plugin incremental compilation algorithm depends on the convention that 
Java source files are located in directories of the same name as their package 
names, with the `.` separator replaced by path separator (`/` or `\`). This is 
a very common convention, but not strictly required by the Java compiler. For 
example, if the `src/main/java/MyCode.java` file contains the `package foo` 
statement, the compiled class will be located in 
`target/classes/foo/MyCode.class` — note the `foo` additional directory. In 
such case, the incremental build algorithm will not track correctly the 
changes. The following tests have been made compliant with the convention for 
allowing the algorithm to work:
   
   * `mcompiler-182` in integration tests.
   
   Note that due to 
[MCOMPILER-209](https://jira.codehaus.org/browse/MCOMPILER-209), the old 
algorithm was compiling everything without really detecting change. So this 
issue is maybe not really a regression. To reproduce the old behavior, users 
can just disable the incremental compilation.
   
   
   ## Removed tests
   
   ### JUnit tests
   Removed the following directories and associated test methods:
   
   * `compiler-one-output-file-test2` because it was redundant with 
`compiler-one-output-file-test`.
   
   The only difference was the addition of include/exclude filters, but that 
difference had no effect because the compiler mock used in this test was 
ignoring all sources anyway. This test has been replaced by 
`compiler-modular-project`.
   
   ### Integration tests
   The tests in the following directories were already disabled and have been 
removed:
   
   * `MCOMPILER-197` because it ran only on Java 8 while the build now requires 
Java 17.
   * `groovy-project-with-new-plexus-compiler` because it ran only on Java 8 
and the plexus compiler has been removed.
   
   The tests in the following directores are not supported anymore and have 
been removed:
   
   * `release-without-profile` because the plugin no longer try to chose 
automatically which parameters to use between `--source` and `--release`. This 
is justified by the fact that the plugin cannot run on Java 8.
   * `release-without-profile-fork` for the same reason as above.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscr...@maven.apache.org

For queries about this service, please contact Infrastructure at:
us...@infra.apache.org

Reply via email to