View Javadoc
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  package org.apache.maven.plugin.compiler;
20  
21  import java.io.File;
22  import java.io.IOException;
23  import java.io.InputStream;
24  import java.lang.reflect.InvocationTargetException;
25  import java.lang.reflect.Method;
26  import java.nio.charset.Charset;
27  import java.nio.file.Files;
28  import java.nio.file.Path;
29  import java.nio.file.Paths;
30  import java.time.Instant;
31  import java.time.temporal.ChronoUnit;
32  import java.util.ArrayList;
33  import java.util.Arrays;
34  import java.util.Collections;
35  import java.util.Comparator;
36  import java.util.Date;
37  import java.util.HashSet;
38  import java.util.Iterator;
39  import java.util.LinkedHashSet;
40  import java.util.List;
41  import java.util.Map;
42  import java.util.Objects;
43  import java.util.Optional;
44  import java.util.Properties;
45  import java.util.Set;
46  import java.util.stream.Collectors;
47  import java.util.stream.Stream;
48  
49  import org.apache.maven.RepositoryUtils;
50  import org.apache.maven.artifact.handler.ArtifactHandler;
51  import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
52  import org.apache.maven.execution.MavenExecutionRequest;
53  import org.apache.maven.execution.MavenSession;
54  import org.apache.maven.model.Dependency;
55  import org.apache.maven.model.DependencyManagement;
56  import org.apache.maven.plugin.AbstractMojo;
57  import org.apache.maven.plugin.MojoExecution;
58  import org.apache.maven.plugin.MojoExecutionException;
59  import org.apache.maven.plugins.annotations.Component;
60  import org.apache.maven.plugins.annotations.Parameter;
61  import org.apache.maven.project.MavenProject;
62  import org.apache.maven.shared.incremental.IncrementalBuildHelper;
63  import org.apache.maven.shared.incremental.IncrementalBuildHelperRequest;
64  import org.apache.maven.shared.utils.StringUtils;
65  import org.apache.maven.shared.utils.logging.MessageBuilder;
66  import org.apache.maven.shared.utils.logging.MessageUtils;
67  import org.apache.maven.toolchain.Toolchain;
68  import org.apache.maven.toolchain.ToolchainManager;
69  import org.codehaus.plexus.compiler.Compiler;
70  import org.codehaus.plexus.compiler.CompilerConfiguration;
71  import org.codehaus.plexus.compiler.CompilerException;
72  import org.codehaus.plexus.compiler.CompilerMessage;
73  import org.codehaus.plexus.compiler.CompilerOutputStyle;
74  import org.codehaus.plexus.compiler.CompilerResult;
75  import org.codehaus.plexus.compiler.manager.CompilerManager;
76  import org.codehaus.plexus.compiler.manager.NoSuchCompilerException;
77  import org.codehaus.plexus.compiler.util.scan.InclusionScanException;
78  import org.codehaus.plexus.compiler.util.scan.SourceInclusionScanner;
79  import org.codehaus.plexus.compiler.util.scan.mapping.SingleTargetSourceMapping;
80  import org.codehaus.plexus.compiler.util.scan.mapping.SourceMapping;
81  import org.codehaus.plexus.compiler.util.scan.mapping.SuffixMapping;
82  import org.codehaus.plexus.languages.java.jpms.JavaModuleDescriptor;
83  import org.codehaus.plexus.languages.java.version.JavaVersion;
84  import org.codehaus.plexus.util.FileUtils;
85  import org.eclipse.aether.RepositorySystem;
86  import org.eclipse.aether.artifact.Artifact;
87  import org.eclipse.aether.artifact.ArtifactTypeRegistry;
88  import org.eclipse.aether.artifact.DefaultArtifact;
89  import org.eclipse.aether.collection.CollectRequest;
90  import org.eclipse.aether.graph.Exclusion;
91  import org.eclipse.aether.resolution.DependencyRequest;
92  import org.eclipse.aether.resolution.DependencyResult;
93  import org.eclipse.aether.util.artifact.JavaScopes;
94  import org.objectweb.asm.ClassWriter;
95  import org.objectweb.asm.Opcodes;
96  
97  /**
98   * TODO: At least one step could be optimized, currently the plugin will do two
99   * scans of all the source code if the compiler has to have the entire set of
100  * sources. This is currently the case for at least the C# compiler and most
101  * likely all the other .NET compilers too.
102  *
103  * @author others
104  * @author <a href="mailto:trygvis@inamo.no">Trygve Laugst&oslash;l</a>
105  * @since 2.0
106  */
107 public abstract class AbstractCompilerMojo extends AbstractMojo {
108     protected static final String PS = System.getProperty("path.separator");
109 
110     private static final String INPUT_FILES_LST_FILENAME = "inputFiles.lst";
111 
112     static final String DEFAULT_SOURCE = "1.8";
113 
114     static final String DEFAULT_TARGET = "1.8";
115 
116     // Used to compare with older targets
117     static final String MODULE_INFO_TARGET = "1.9";
118 
119     // ----------------------------------------------------------------------
120     // Configurables
121     // ----------------------------------------------------------------------
122 
123     /**
124      * Indicates whether the build will continue even if there are compilation errors.
125      *
126      * @since 2.0.2
127      */
128     @Parameter(property = "maven.compiler.failOnError", defaultValue = "true")
129     private boolean failOnError = true;
130 
131     /**
132      * Indicates whether the build will continue even if there are compilation warnings.
133      *
134      * @since 3.6
135      */
136     @Parameter(property = "maven.compiler.failOnWarning", defaultValue = "false")
137     private boolean failOnWarning;
138 
139     /**
140      * Set to <code>true</code> to include debugging information in the compiled class files.
141      */
142     @Parameter(property = "maven.compiler.debug", defaultValue = "true")
143     private boolean debug = true;
144 
145     /**
146      * Set to <code>true</code> to generate metadata for reflection on method parameters.
147      * @since 3.6.2
148      */
149     @Parameter(property = "maven.compiler.parameters", defaultValue = "false")
150     private boolean parameters;
151 
152     /**
153      * Set to <code>true</code> to Enable preview language features of the java compiler
154      * @since 3.10.1
155      */
156     @Parameter(property = "maven.compiler.enablePreview", defaultValue = "false")
157     private boolean enablePreview;
158 
159     /**
160      * Set to <code>true</code> to show messages about what the compiler is doing.
161      */
162     @Parameter(property = "maven.compiler.verbose", defaultValue = "false")
163     private boolean verbose;
164 
165     /**
166      * Sets whether to show source locations where deprecated APIs are used.
167      */
168     @Parameter(property = "maven.compiler.showDeprecation", defaultValue = "false")
169     private boolean showDeprecation;
170 
171     /**
172      * Set to <code>true</code> to optimize the compiled code using the compiler's optimization methods.
173      * @deprecated This property is a no-op in {@code javac}.
174      */
175     @Deprecated
176     @Parameter(property = "maven.compiler.optimize", defaultValue = "false")
177     private boolean optimize;
178 
179     /**
180      * Set to <code>false</code> to disable warnings during compilation.
181      */
182     @Parameter(property = "maven.compiler.showWarnings", defaultValue = "true")
183     private boolean showWarnings;
184 
185     /**
186      * <p>The -source argument for the Java compiler.</p>
187      *
188      * <p><b>NOTE: </b></p>
189      * <p>Since 3.8.0 the default value has changed from 1.5 to 1.6</p>
190      * <p>Since 3.9.0 the default value has changed from 1.6 to 1.7</p>
191      * <p>Since 3.11.0 the default value has changed from 1.7 to 1.8</p>
192      */
193     @Parameter(property = "maven.compiler.source", defaultValue = DEFAULT_SOURCE)
194     protected String source;
195 
196     /**
197      * <p>The -target argument for the Java compiler.</p>
198      *
199      * <p><b>NOTE: </b></p>
200      * <p>Since 3.8.0 the default value has changed from 1.5 to 1.6</p>
201      * <p>Since 3.9.0 the default value has changed from 1.6 to 1.7</p>
202      * <p>Since 3.11.0 the default value has changed from 1.7 to 1.8</p>
203      */
204     @Parameter(property = "maven.compiler.target", defaultValue = DEFAULT_TARGET)
205     protected String target;
206 
207     /**
208      * The -release argument for the Java compiler, supported since Java9
209      *
210      * @since 3.6
211      */
212     @Parameter(property = "maven.compiler.release")
213     protected String release;
214 
215     /**
216      * The -encoding argument for the Java compiler.
217      *
218      * @since 2.1
219      */
220     @Parameter(property = "encoding", defaultValue = "${project.build.sourceEncoding}")
221     private String encoding;
222 
223     /**
224      * Sets the granularity in milliseconds of the last modification
225      * date for testing whether a source needs recompilation.
226      */
227     @Parameter(property = "lastModGranularityMs", defaultValue = "0")
228     private int staleMillis;
229 
230     /**
231      * The compiler id of the compiler to use. See this
232      * <a href="non-javac-compilers.html">guide</a> for more information.
233      */
234     @Parameter(property = "maven.compiler.compilerId", defaultValue = "javac")
235     private String compilerId;
236 
237     /**
238      * Version of the compiler to use, ex. "1.3", "1.5", if {@link #fork} is set to <code>true</code>.
239      */
240     @Parameter(property = "maven.compiler.compilerVersion")
241     private String compilerVersion;
242 
243     /**
244      * Allows running the compiler in a separate process.
245      * If <code>false</code> it uses the built in compiler, while if <code>true</code> it will use an executable.
246      */
247     @Parameter(property = "maven.compiler.fork", defaultValue = "false")
248     private boolean fork;
249 
250     /**
251      * Initial size, in megabytes, of the memory allocation pool, ex. "64", "64m"
252      * if {@link #fork} is set to <code>true</code>.
253      *
254      * @since 2.0.1
255      */
256     @Parameter(property = "maven.compiler.meminitial")
257     private String meminitial;
258 
259     /**
260      * Sets the maximum size, in megabytes, of the memory allocation pool, ex. "128", "128m"
261      * if {@link #fork} is set to <code>true</code>.
262      *
263      * @since 2.0.1
264      */
265     @Parameter(property = "maven.compiler.maxmem")
266     private String maxmem;
267 
268     /**
269      * Sets the executable of the compiler to use when {@link #fork} is <code>true</code>.
270      */
271     @Parameter(property = "maven.compiler.executable")
272     private String executable;
273 
274     /**
275      * <p>
276      * Sets whether annotation processing is performed or not. Only applies to JDK 1.6+
277      * If not set, both compilation and annotation processing are performed at the same time.
278      * </p>
279      * <p>Allowed values are:</p>
280      * <ul>
281      * <li><code>none</code> - no annotation processing is performed.</li>
282      * <li><code>only</code> - only annotation processing is done, no compilation.</li>
283      * </ul>
284      *
285      * @since 2.2
286      */
287     @Parameter
288     private String proc;
289 
290     /**
291      * <p>
292      * Names of annotation processors to run. Only applies to JDK 1.6+
293      * If not set, the default annotation processors discovery process applies.
294      * </p>
295      *
296      * @since 2.2
297      */
298     @Parameter
299     private String[] annotationProcessors;
300 
301     /**
302      * <p>
303      * Classpath elements to supply as annotation processor path. If specified, the compiler will detect annotation
304      * processors only in those classpath elements. If omitted, the default classpath is used to detect annotation
305      * processors. The detection itself depends on the configuration of {@code annotationProcessors}.
306      * </p>
307      * <p>
308      * Each classpath element is specified using their Maven coordinates (groupId, artifactId, version, classifier,
309      * type). Transitive dependencies are added automatically. Exclusions are supported as well. Example:
310      * </p>
311      *
312      * <pre>
313      * &lt;configuration&gt;
314      *   &lt;annotationProcessorPaths&gt;
315      *     &lt;path&gt;
316      *       &lt;groupId&gt;org.sample&lt;/groupId&gt;
317      *       &lt;artifactId&gt;sample-annotation-processor&lt;/artifactId&gt;
318      *       &lt;version&gt;1.2.3&lt;/version&gt; &lt;!-- Optional - taken from dependency management if not specified --&gt;
319      *       &lt;!-- Optionally exclude transitive dependencies --&gt;
320      *       &lt;exclusions&gt;
321      *         &lt;exclusion&gt;
322      *           &lt;groupId&gt;org.sample&lt;/groupId&gt;
323      *           &lt;artifactId&gt;sample-dependency&lt;/artifactId&gt;
324      *         &lt;/exclusion&gt;
325      *       &lt;/exclusions&gt;
326      *     &lt;/path&gt;
327      *     &lt;!-- ... more ... --&gt;
328      *   &lt;/annotationProcessorPaths&gt;
329      * &lt;/configuration&gt;
330      * </pre>
331      *
332      * <b>Note:</b> Exclusions are supported from version 3.11.0.
333      *
334      * @since 3.5
335      */
336     @Parameter
337     private List<DependencyCoordinate> annotationProcessorPaths;
338 
339     /**
340      * <p>
341      * Whether to use the Maven dependency management section when resolving transitive dependencies of annotation
342      * processor paths.
343      * </p>
344      * <p>
345      * This flag does not enable / disable the ability to resolve the version of annotation processor paths
346      * from dependency management section. It only influences the resolution o transitive dependencies of those
347      * top-level paths.
348      * </p>
349      *
350      * @since 3.12.0
351      */
352     @Parameter(defaultValue = "false")
353     private boolean annotationProcessorPathsUseDepMgmt;
354 
355     /**
356      * <p>
357      * Sets the arguments to be passed to the compiler (prepending a dash).
358      * </p>
359      * <p>
360      * This is because the list of valid arguments passed to a Java compiler varies based on the compiler version.
361      * </p>
362      * <p>
363      * Note that {@code -J} options are only passed through if {@link #fork} is set to {@code true}.
364      * </p>
365      * <p>
366      * To pass <code>-Xmaxerrs 1000 -Xlint -Xlint:-path -Averbose=true</code> you should include the following:
367      * </p>
368      *
369      * <pre>
370      * &lt;compilerArguments&gt;
371      *   &lt;Xmaxerrs&gt;1000&lt;/Xmaxerrs&gt;
372      *   &lt;Xlint/&gt;
373      *   &lt;Xlint:-path/&gt;
374      *   &lt;Averbose&gt;true&lt;/Averbose&gt;
375      * &lt;/compilerArguments&gt;
376      * </pre>
377      *
378      * @since 2.0.1
379      * @deprecated use {@link #compilerArgs} instead.
380      */
381     @Parameter
382     @Deprecated
383     protected Map<String, String> compilerArguments;
384 
385     /**
386      * <p>
387      * Sets the arguments to be passed to the compiler.
388      * </p>
389      * <p>
390      * Note that {@code -J} options are only passed through if {@link #fork} is set to {@code true}.
391      * </p>
392      * Example:
393      * <pre>
394      * &lt;compilerArgs&gt;
395      *   &lt;arg&gt;-Xmaxerrs&lt;/arg&gt;
396      *   &lt;arg&gt;1000&lt;/arg&gt;
397      *   &lt;arg&gt;-Xlint&lt;/arg&gt;
398      *   &lt;arg&gt;-J-Duser.language=en_us&lt;/arg&gt;
399      * &lt;/compilerArgs&gt;
400      * </pre>
401      *
402      * @since 3.1
403      */
404     @Parameter
405     protected List<String> compilerArgs;
406 
407     /**
408      * <p>
409      * Sets the unformatted single argument string to be passed to the compiler. To pass multiple arguments such as
410      * <code>-Xmaxerrs 1000</code> (which are actually two arguments) you have to use {@link #compilerArgs}.
411      * </p>
412      * <p>
413      * This is because the list of valid arguments passed to a Java compiler varies based on the compiler version.
414      * </p>
415      * <p>
416      * Note that {@code -J} options are only passed through if {@link #fork} is set to {@code true}.
417      * </p>
418      */
419     @Parameter
420     protected String compilerArgument;
421 
422     /**
423      * Sets the name of the output file when compiling a set of
424      * sources to a single file.
425      * <p/>
426      * expression="${project.build.finalName}"
427      */
428     @Parameter
429     private String outputFileName;
430 
431     /**
432      * Keyword list to be appended to the <code>-g</code> command-line switch. Legal values are none or a
433      * comma-separated list of the following keywords: <code>lines</code>, <code>vars</code>, and <code>source</code>.
434      * If debug level is not specified, by default, nothing will be appended to <code>-g</code>.
435      * If debug is not turned on, this attribute will be ignored.
436      *
437      * @since 2.1
438      */
439     @Parameter(property = "maven.compiler.debuglevel")
440     private String debuglevel;
441 
442     /**
443      * Keyword to be appended to the <code>-implicit:</code> command-line switch.
444      *
445      * @since 3.10.2
446      */
447     @Parameter(property = "maven.compiler.implicit")
448     private String implicit;
449 
450     /**
451      *
452      */
453     @Component
454     private ToolchainManager toolchainManager;
455 
456     /**
457      * <p>
458      * Specify the requirements for this jdk toolchain for using a different {@code javac} than the one of the JRE used
459      * by Maven. This overrules the toolchain selected by the
460      * <a href="https://maven.apache.org/plugins/maven-toolchains-plugin/">maven-toolchain-plugin</a>.
461      * </p>
462      * (see <a href="https://maven.apache.org/guides/mini/guide-using-toolchains.html"> Guide to Toolchains</a> for more
463      * info)
464      *
465      * <pre>
466      * &lt;configuration&gt;
467      *   &lt;jdkToolchain&gt;
468      *     &lt;version&gt;11&lt;/version&gt;
469      *   &lt;/jdkToolchain&gt;
470      *   ...
471      * &lt;/configuration&gt;
472      *
473      * &lt;configuration&gt;
474      *   &lt;jdkToolchain&gt;
475      *     &lt;version&gt;1.8&lt;/version&gt;
476      *     &lt;vendor&gt;zulu&lt;/vendor&gt;
477      *   &lt;/jdkToolchain&gt;
478      *   ...
479      * &lt;/configuration&gt;
480      * </pre>
481      * <strong>note:</strong> requires at least Maven 3.3.1
482      *
483      * @since 3.6
484      */
485     @Parameter
486     private Map<String, String> jdkToolchain;
487 
488     // ----------------------------------------------------------------------
489     // Read-only parameters
490     // ----------------------------------------------------------------------
491 
492     /**
493      * The directory to run the compiler from if fork is true.
494      */
495     @Parameter(defaultValue = "${basedir}", required = true, readonly = true)
496     private File basedir;
497 
498     /**
499      * The target directory of the compiler if fork is true.
500      */
501     @Parameter(defaultValue = "${project.build.directory}", required = true, readonly = true)
502     private File buildDirectory;
503 
504     /**
505      * Plexus compiler manager.
506      */
507     @Component
508     private CompilerManager compilerManager;
509 
510     /**
511      * The current build session instance. This is used for toolchain manager API calls.
512      */
513     @Parameter(defaultValue = "${session}", readonly = true, required = true)
514     private MavenSession session;
515 
516     /**
517      * The current project instance. This is used for propagating generated-sources paths as compile/testCompile source
518      * roots.
519      */
520     @Parameter(defaultValue = "${project}", readonly = true, required = true)
521     private MavenProject project;
522 
523     /**
524      * Strategy to re use javacc class created:
525      * <ul>
526      * <li><code>reuseCreated</code> (default): will reuse already created but in case of multi-threaded builds, each
527      * thread will have its own instance</li>
528      * <li><code>reuseSame</code>: the same Javacc class will be used for each compilation even for multi-threaded build
529      * </li>
530      * <li><code>alwaysNew</code>: a new Javacc class will be created for each compilation</li>
531      * </ul>
532      * Note this parameter value depends on the os/jdk you are using, but the default value should work on most of env.
533      *
534      * @since 2.5
535      */
536     @Parameter(defaultValue = "${reuseCreated}", property = "maven.compiler.compilerReuseStrategy")
537     private String compilerReuseStrategy = "reuseCreated";
538 
539     /**
540      * @since 2.5
541      */
542     @Parameter(defaultValue = "false", property = "maven.compiler.skipMultiThreadWarning")
543     private boolean skipMultiThreadWarning;
544 
545     /**
546      * compiler can now use javax.tools if available in your current jdk, you can disable this feature
547      * using -Dmaven.compiler.forceJavacCompilerUse=true or in the plugin configuration
548      *
549      * @since 3.0
550      */
551     @Parameter(defaultValue = "false", property = "maven.compiler.forceJavacCompilerUse")
552     private boolean forceJavacCompilerUse;
553 
554     /**
555      * @since 3.0 needed for storing the status for the incremental build support.
556      */
557     @Parameter(defaultValue = "${mojoExecution}", readonly = true, required = true)
558     private MojoExecution mojoExecution;
559 
560     /**
561      * File extensions to check timestamp for incremental build.
562      *
563      * @since 3.1
564      */
565     @Parameter(defaultValue = "class,jar")
566     private Set<String> fileExtensions;
567 
568     /**
569      * <p>to enable/disable incremental compilation feature.</p>
570      * <p>This leads to two different modes depending on the underlying compiler. The default javac compiler does the
571      * following:</p>
572      * <ul>
573      * <li>true <strong>(default)</strong> in this mode the compiler plugin determines whether any JAR files the
574      * current module depends on have changed in the current build run; or any source file was added, removed or
575      * changed since the last compilation. If this is the case, the compiler plugin recompiles all sources.</li>
576      * <li>false <strong>(not recommended)</strong> this only compiles source files which are newer than their
577      * corresponding class files, namely which have changed since the last compilation. This does not
578      * recompile other classes which use the changed class, potentially leaving them with references to methods that no
579      * longer exist, leading to errors at runtime.</li>
580      * </ul>
581      *
582      * @since 3.1
583      */
584     @Parameter(defaultValue = "true", property = "maven.compiler.useIncrementalCompilation")
585     private boolean useIncrementalCompilation = true;
586 
587     /**
588      * Package info source files that only contain javadoc and no annotation on the package
589      * can lead to no class file being generated by the compiler.  This causes a file miss
590      * on the next compilations and forces an unnecessary recompilation. The default value
591      * of <code>true</code> causes an empty class file to be generated.  This behavior can
592      * be changed by setting this parameter to <code>false</code>.
593      *
594      * @since 3.10
595      */
596     @Parameter(defaultValue = "true", property = "maven.compiler.createMissingPackageInfoClass")
597     private boolean createMissingPackageInfoClass = true;
598 
599     @Parameter(defaultValue = "false", property = "maven.compiler.showCompilationChanges")
600     private boolean showCompilationChanges = false;
601 
602     /**
603      * Timestamp for reproducible output archive entries, either formatted as ISO 8601
604      * <code>yyyy-MM-dd'T'HH:mm:ssXXX</code> or as an int representing seconds since the epoch (like
605      * <a href="https://reproducible-builds.org/docs/source-date-epoch/">SOURCE_DATE_EPOCH</a>).
606      * @since 3.12.0
607      */
608     @Parameter(defaultValue = "${project.build.outputTimestamp}")
609     private String outputTimestamp;
610 
611     /**
612      * Resolves the artifacts needed.
613      */
614     @Component
615     private RepositorySystem repositorySystem;
616 
617     /**
618      * Artifact handler manager.
619      */
620     @Component
621     private ArtifactHandlerManager artifactHandlerManager;
622 
623     protected abstract SourceInclusionScanner getSourceInclusionScanner(int staleMillis);
624 
625     protected abstract SourceInclusionScanner getSourceInclusionScanner(String inputFileEnding);
626 
627     protected abstract List<String> getClasspathElements();
628 
629     protected abstract List<String> getModulepathElements();
630 
631     protected abstract Map<String, JavaModuleDescriptor> getPathElements();
632 
633     protected abstract List<String> getCompileSourceRoots();
634 
635     protected abstract void preparePaths(Set<File> sourceFiles);
636 
637     protected abstract File getOutputDirectory();
638 
639     protected abstract String getSource();
640 
641     protected abstract String getTarget();
642 
643     protected abstract String getRelease();
644 
645     protected abstract String getCompilerArgument();
646 
647     protected abstract Map<String, String> getCompilerArguments();
648 
649     protected abstract File getGeneratedSourcesDirectory();
650 
651     protected abstract String getDebugFileName();
652 
653     protected final MavenProject getProject() {
654         return project;
655     }
656 
657     protected final Optional<Path> getModuleDeclaration(final Set<File> sourceFiles) {
658         for (File sourceFile : sourceFiles) {
659             if ("module-info.java".equals(sourceFile.getName())) {
660                 return Optional.of(sourceFile.toPath());
661             }
662         }
663         return Optional.empty();
664     }
665 
666     private boolean targetOrReleaseSet;
667 
668     @Override
669     public void execute() throws MojoExecutionException, CompilationFailureException {
670         // ----------------------------------------------------------------------
671         // Look up the compiler. This is done before other code than can
672         // cause the mojo to return before the lookup is done possibly resulting
673         // in misconfigured POMs still building.
674         // ----------------------------------------------------------------------
675 
676         Compiler compiler;
677 
678         getLog().debug("Using compiler '" + compilerId + "'.");
679 
680         try {
681             compiler = compilerManager.getCompiler(compilerId);
682         } catch (NoSuchCompilerException e) {
683             throw new MojoExecutionException("No such compiler '" + e.getCompilerId() + "'.");
684         }
685 
686         // -----------toolchains start here ----------------------------------
687         // use the compilerId as identifier for toolchains as well.
688         Toolchain tc = getToolchain();
689         if (tc != null) {
690             getLog().info("Toolchain in maven-compiler-plugin: " + tc);
691             if (executable != null) {
692                 getLog().warn("Toolchains are ignored, 'executable' parameter is set to " + executable);
693             } else {
694                 fork = true;
695                 // TODO somehow shaky dependency between compilerId and tool executable.
696                 executable = tc.findTool(compilerId);
697             }
698         }
699         // ----------------------------------------------------------------------
700         //
701         // ----------------------------------------------------------------------
702 
703         List<String> compileSourceRoots = removeEmptyCompileSourceRoots(getCompileSourceRoots());
704 
705         if (compileSourceRoots.isEmpty()) {
706             getLog().info("No sources to compile");
707 
708             return;
709         }
710 
711         // Verify that target or release is set
712         if (!targetOrReleaseSet) {
713             MessageBuilder mb = MessageUtils.buffer()
714                     .a("No explicit value set for target or release! ")
715                     .a("To ensure the same result even after upgrading this plugin, please add ")
716                     .newline()
717                     .newline();
718 
719             writePlugin(mb);
720 
721             getLog().warn(mb.toString());
722         }
723 
724         // ----------------------------------------------------------------------
725         // Create the compiler configuration
726         // ----------------------------------------------------------------------
727 
728         CompilerConfiguration compilerConfiguration = new CompilerConfiguration();
729 
730         compilerConfiguration.setOutputLocation(getOutputDirectory().getAbsolutePath());
731 
732         compilerConfiguration.setOptimize(optimize);
733 
734         compilerConfiguration.setDebug(debug);
735 
736         compilerConfiguration.setDebugFileName(getDebugFileName());
737 
738         compilerConfiguration.setImplicitOption(implicit);
739 
740         if (debug && (debuglevel != null && !debuglevel.isEmpty())) {
741             String[] split = StringUtils.split(debuglevel, ",");
742             for (String aSplit : split) {
743                 if (!(aSplit.equalsIgnoreCase("none")
744                         || aSplit.equalsIgnoreCase("lines")
745                         || aSplit.equalsIgnoreCase("vars")
746                         || aSplit.equalsIgnoreCase("source"))) {
747                     throw new IllegalArgumentException("The specified debug level: '" + aSplit + "' is unsupported. "
748                             + "Legal values are 'none', 'lines', 'vars', and 'source'.");
749                 }
750             }
751             compilerConfiguration.setDebugLevel(debuglevel);
752         }
753 
754         compilerConfiguration.setParameters(parameters);
755 
756         compilerConfiguration.setEnablePreview(enablePreview);
757 
758         compilerConfiguration.setVerbose(verbose);
759 
760         compilerConfiguration.setShowWarnings(showWarnings);
761 
762         compilerConfiguration.setFailOnWarning(failOnWarning);
763 
764         if (failOnWarning && !showWarnings) {
765             getLog().warn("The property failOnWarning is set to true, but showWarnings is set to false.");
766             getLog().warn("With compiler's warnings silenced the failOnWarning has no effect.");
767         }
768 
769         compilerConfiguration.setShowDeprecation(showDeprecation);
770 
771         compilerConfiguration.setSourceVersion(getSource());
772 
773         compilerConfiguration.setTargetVersion(getTarget());
774 
775         compilerConfiguration.setReleaseVersion(getRelease());
776 
777         compilerConfiguration.setProc(proc);
778 
779         File generatedSourcesDirectory = getGeneratedSourcesDirectory();
780         compilerConfiguration.setGeneratedSourcesDirectory(
781                 generatedSourcesDirectory != null ? generatedSourcesDirectory.getAbsoluteFile() : null);
782 
783         if (generatedSourcesDirectory != null) {
784             if (!generatedSourcesDirectory.exists()) {
785                 generatedSourcesDirectory.mkdirs();
786             }
787 
788             String generatedSourcesPath = generatedSourcesDirectory.getAbsolutePath();
789 
790             compileSourceRoots.add(generatedSourcesPath);
791 
792             if (isTestCompile()) {
793                 getLog().debug("Adding " + generatedSourcesPath + " to test-compile source roots:\n  "
794                         + StringUtils.join(project.getTestCompileSourceRoots().iterator(), "\n  "));
795 
796                 project.addTestCompileSourceRoot(generatedSourcesPath);
797 
798                 getLog().debug("New test-compile source roots:\n  "
799                         + StringUtils.join(project.getTestCompileSourceRoots().iterator(), "\n  "));
800             } else {
801                 getLog().debug("Adding " + generatedSourcesPath + " to compile source roots:\n  "
802                         + StringUtils.join(project.getCompileSourceRoots().iterator(), "\n  "));
803 
804                 project.addCompileSourceRoot(generatedSourcesPath);
805 
806                 getLog().debug("New compile source roots:\n  "
807                         + StringUtils.join(project.getCompileSourceRoots().iterator(), "\n  "));
808             }
809         }
810 
811         compilerConfiguration.setSourceLocations(compileSourceRoots);
812 
813         compilerConfiguration.setAnnotationProcessors(annotationProcessors);
814 
815         compilerConfiguration.setProcessorPathEntries(resolveProcessorPathEntries());
816 
817         compilerConfiguration.setSourceEncoding(encoding);
818 
819         compilerConfiguration.setFork(fork);
820 
821         if (fork) {
822             if (!(meminitial == null || meminitial.isEmpty())) {
823                 String value = getMemoryValue(meminitial);
824 
825                 if (value != null) {
826                     compilerConfiguration.setMeminitial(value);
827                 } else {
828                     getLog().info("Invalid value for meminitial '" + meminitial + "'. Ignoring this option.");
829                 }
830             }
831 
832             if (!(maxmem == null || maxmem.isEmpty())) {
833                 String value = getMemoryValue(maxmem);
834 
835                 if (value != null) {
836                     compilerConfiguration.setMaxmem(value);
837                 } else {
838                     getLog().info("Invalid value for maxmem '" + maxmem + "'. Ignoring this option.");
839                 }
840             }
841         }
842 
843         compilerConfiguration.setExecutable(executable);
844 
845         compilerConfiguration.setWorkingDirectory(basedir);
846 
847         compilerConfiguration.setCompilerVersion(compilerVersion);
848 
849         compilerConfiguration.setBuildDirectory(buildDirectory);
850 
851         compilerConfiguration.setOutputFileName(outputFileName);
852 
853         if (CompilerConfiguration.CompilerReuseStrategy.AlwaysNew.getStrategy().equals(this.compilerReuseStrategy)) {
854             compilerConfiguration.setCompilerReuseStrategy(CompilerConfiguration.CompilerReuseStrategy.AlwaysNew);
855         } else if (CompilerConfiguration.CompilerReuseStrategy.ReuseSame.getStrategy()
856                 .equals(this.compilerReuseStrategy)) {
857             if (getRequestThreadCount() > 1) {
858                 if (!skipMultiThreadWarning) {
859                     getLog().warn("You are in a multi-thread build and compilerReuseStrategy is set to reuseSame."
860                             + " This can cause issues in some environments (os/jdk)!"
861                             + " Consider using reuseCreated strategy."
862                             + System.getProperty("line.separator")
863                             + "If your env is fine with reuseSame, you can skip this warning with the "
864                             + "configuration field skipMultiThreadWarning "
865                             + "or -Dmaven.compiler.skipMultiThreadWarning=true");
866                 }
867             }
868             compilerConfiguration.setCompilerReuseStrategy(CompilerConfiguration.CompilerReuseStrategy.ReuseSame);
869         } else {
870 
871             compilerConfiguration.setCompilerReuseStrategy(CompilerConfiguration.CompilerReuseStrategy.ReuseCreated);
872         }
873 
874         getLog().debug("CompilerReuseStrategy: "
875                 + compilerConfiguration.getCompilerReuseStrategy().getStrategy());
876 
877         compilerConfiguration.setForceJavacCompilerUse(forceJavacCompilerUse);
878 
879         boolean canUpdateTarget;
880 
881         IncrementalBuildHelper incrementalBuildHelper = new IncrementalBuildHelper(mojoExecution, session);
882 
883         final Set<File> sources;
884 
885         IncrementalBuildHelperRequest incrementalBuildHelperRequest = null;
886 
887         if (useIncrementalCompilation) {
888             getLog().debug("useIncrementalCompilation enabled");
889             try {
890                 canUpdateTarget = compiler.canUpdateTarget(compilerConfiguration);
891 
892                 sources = getCompileSources(compiler, compilerConfiguration);
893 
894                 preparePaths(sources);
895 
896                 incrementalBuildHelperRequest = new IncrementalBuildHelperRequest().inputFiles(sources);
897 
898                 // Strategies used to detect modifications.
899                 String immutableOutputFile = (compiler.getCompilerOutputStyle()
900                                         .equals(CompilerOutputStyle.ONE_OUTPUT_FILE_FOR_ALL_INPUT_FILES)
901                                 && !canUpdateTarget)
902                         ? "immutable single output file"
903                         : null;
904                 String dependencyChanged = isDependencyChanged() ? "changed dependency" : null;
905                 String sourceChanged = isSourceChanged(compilerConfiguration, compiler) ? "changed source code" : null;
906                 String inputFileTreeChanged = hasInputFileTreeChanged(incrementalBuildHelper, sources)
907                         ? "added or removed source files"
908                         : null;
909 
910                 // Get the first cause for the rebuild compilation detection.
911                 String cause = Stream.of(immutableOutputFile, dependencyChanged, sourceChanged, inputFileTreeChanged)
912                         .filter(Objects::nonNull)
913                         .findFirst()
914                         .orElse(null);
915 
916                 if (cause != null) {
917                     getLog().info("Recompiling the module because of "
918                             + MessageUtils.buffer().strong(cause) + ".");
919                     compilerConfiguration.setSourceFiles(sources);
920                 } else {
921                     getLog().info("Nothing to compile - all classes are up to date.");
922                     return;
923                 }
924             } catch (CompilerException e) {
925                 throw new MojoExecutionException("Error while computing stale sources.", e);
926             }
927         } else {
928             getLog().debug("useIncrementalCompilation disabled");
929 
930             Set<File> staleSources;
931             try {
932                 staleSources =
933                         computeStaleSources(compilerConfiguration, compiler, getSourceInclusionScanner(staleMillis));
934 
935                 canUpdateTarget = compiler.canUpdateTarget(compilerConfiguration);
936 
937                 if (compiler.getCompilerOutputStyle().equals(CompilerOutputStyle.ONE_OUTPUT_FILE_FOR_ALL_INPUT_FILES)
938                         && !canUpdateTarget) {
939                     getLog().info("RESCANNING!");
940                     // TODO: This second scan for source files is sub-optimal
941                     String inputFileEnding = compiler.getInputFileEnding(compilerConfiguration);
942 
943                     staleSources = computeStaleSources(
944                             compilerConfiguration, compiler, getSourceInclusionScanner(inputFileEnding));
945                 }
946 
947             } catch (CompilerException e) {
948                 throw new MojoExecutionException("Error while computing stale sources.", e);
949             }
950 
951             if (staleSources.isEmpty()) {
952                 getLog().info("Nothing to compile - all classes are up to date.");
953                 return;
954             }
955 
956             compilerConfiguration.setSourceFiles(staleSources);
957 
958             try {
959                 // MCOMPILER-366: if sources contain the module-descriptor it must be used to define the modulepath
960                 sources = getCompileSources(compiler, compilerConfiguration);
961 
962                 if (getLog().isDebugEnabled()) {
963                     getLog().debug("#sources: " + sources.size());
964                     for (File file : sources) {
965                         getLog().debug(file.getPath());
966                     }
967                 }
968 
969                 preparePaths(sources);
970             } catch (CompilerException e) {
971                 throw new MojoExecutionException("Error while computing stale sources.", e);
972             }
973         }
974 
975         // Dividing pathElements of classPath and modulePath is based on sourceFiles
976         compilerConfiguration.setClasspathEntries(getClasspathElements());
977 
978         compilerConfiguration.setModulepathEntries(getModulepathElements());
979 
980         compilerConfiguration.setIncludes(getIncludes());
981 
982         compilerConfiguration.setExcludes(getExcludes());
983 
984         Map<String, String> effectiveCompilerArguments = getCompilerArguments();
985 
986         String effectiveCompilerArgument = getCompilerArgument();
987 
988         if ((effectiveCompilerArguments != null) || (effectiveCompilerArgument != null) || (compilerArgs != null)) {
989             if (effectiveCompilerArguments != null) {
990                 for (Map.Entry<String, String> me : effectiveCompilerArguments.entrySet()) {
991                     String key = me.getKey();
992                     String value = me.getValue();
993                     if (!key.startsWith("-")) {
994                         key = "-" + key;
995                     }
996 
997                     if (key.startsWith("-A") && (value != null && !value.isEmpty())) {
998                         compilerConfiguration.addCompilerCustomArgument(key + "=" + value, null);
999                     } else {
1000                         compilerConfiguration.addCompilerCustomArgument(key, value);
1001                     }
1002                 }
1003             }
1004             if (!(effectiveCompilerArgument == null || effectiveCompilerArgument.isEmpty())) {
1005                 compilerConfiguration.addCompilerCustomArgument(effectiveCompilerArgument, null);
1006             }
1007             if (compilerArgs != null) {
1008                 for (String arg : compilerArgs) {
1009                     compilerConfiguration.addCompilerCustomArgument(arg, null);
1010                 }
1011             }
1012         }
1013 
1014         // ----------------------------------------------------------------------
1015         // Dump configuration
1016         // ----------------------------------------------------------------------
1017         if (getLog().isDebugEnabled()) {
1018             getLog().debug("Classpath:");
1019 
1020             for (String s : getClasspathElements()) {
1021                 getLog().debug(" " + s);
1022             }
1023 
1024             if (!getModulepathElements().isEmpty()) {
1025                 getLog().debug("Modulepath:");
1026                 for (String s : getModulepathElements()) {
1027                     getLog().debug(" " + s);
1028                 }
1029             }
1030 
1031             getLog().debug("Source roots:");
1032 
1033             for (String root : getCompileSourceRoots()) {
1034                 getLog().debug(" " + root);
1035             }
1036 
1037             try {
1038                 if (fork) {
1039                     if (compilerConfiguration.getExecutable() != null) {
1040                         getLog().debug("Excutable: ");
1041                         getLog().debug(" " + compilerConfiguration.getExecutable());
1042                     }
1043                 }
1044 
1045                 String[] cl = compiler.createCommandLine(compilerConfiguration);
1046                 if (cl != null && cl.length > 0) {
1047                     StringBuilder sb = new StringBuilder();
1048                     sb.append(cl[0]);
1049                     for (int i = 1; i < cl.length; i++) {
1050                         sb.append(" ");
1051                         sb.append(cl[i]);
1052                     }
1053                     getLog().debug("Command line options:");
1054                     getLog().debug(sb);
1055                 }
1056             } catch (CompilerException ce) {
1057                 getLog().debug(ce);
1058             }
1059         }
1060 
1061         List<String> jpmsLines = new ArrayList<>();
1062 
1063         // See http://openjdk.java.net/jeps/261
1064         final List<String> runtimeArgs = Arrays.asList(
1065                 "--upgrade-module-path", "--add-exports", "--add-reads", "--add-modules", "--limit-modules");
1066 
1067         // Custom arguments are all added as keys to an ordered Map
1068         Iterator<Map.Entry<String, String>> entryIter =
1069                 compilerConfiguration.getCustomCompilerArgumentsEntries().iterator();
1070         while (entryIter.hasNext()) {
1071             Map.Entry<String, String> entry = entryIter.next();
1072 
1073             if (runtimeArgs.contains(entry.getKey())) {
1074                 jpmsLines.add(entry.getKey());
1075 
1076                 String value = entry.getValue();
1077                 if (value == null) {
1078                     entry = entryIter.next();
1079                     value = entry.getKey();
1080                 }
1081                 jpmsLines.add(value);
1082             } else if ("--patch-module".equals(entry.getKey())) {
1083                 String value = entry.getValue();
1084                 if (value == null) {
1085                     entry = entryIter.next();
1086                     value = entry.getKey();
1087                 }
1088 
1089                 String[] values = value.split("=");
1090 
1091                 StringBuilder patchModule = new StringBuilder(values[0]);
1092                 patchModule.append('=');
1093 
1094                 Set<String> patchModules = new LinkedHashSet<>();
1095                 Set<Path> sourceRoots = new HashSet<>(getCompileSourceRoots().size());
1096                 for (String sourceRoot : getCompileSourceRoots()) {
1097                     sourceRoots.add(Paths.get(sourceRoot));
1098                 }
1099 
1100                 String[] files = values[1].split(PS);
1101 
1102                 for (String file : files) {
1103                     Path filePath = Paths.get(file);
1104                     if (getOutputDirectory().toPath().equals(filePath)) {
1105                         patchModules.add("_"); // this jar
1106                     } else if (getOutputDirectory().toPath().startsWith(filePath)) {
1107                         // multirelease, can be ignored
1108                         continue;
1109                     } else if (sourceRoots.contains(filePath)) {
1110                         patchModules.add("_"); // this jar
1111                     } else {
1112                         JavaModuleDescriptor descriptor = getPathElements().get(file);
1113 
1114                         if (descriptor == null) {
1115                             if (Files.isDirectory(filePath)) {
1116                                 patchModules.add(file);
1117                             } else {
1118                                 getLog().warn("Can't locate " + file);
1119                             }
1120                         } else if (!values[0].equals(descriptor.name())) {
1121                             patchModules.add(descriptor.name());
1122                         }
1123                     }
1124                 }
1125 
1126                 StringBuilder sb = new StringBuilder();
1127 
1128                 if (!patchModules.isEmpty()) {
1129                     for (String mod : patchModules) {
1130                         if (sb.length() > 0) {
1131                             sb.append(", ");
1132                         }
1133                         // use 'invalid' separator to ensure values are transformed
1134                         sb.append(mod);
1135                     }
1136 
1137                     jpmsLines.add("--patch-module");
1138                     jpmsLines.add(patchModule + sb.toString());
1139                 }
1140             }
1141         }
1142 
1143         if (!jpmsLines.isEmpty()) {
1144             Path jpmsArgs = Paths.get(getOutputDirectory().getAbsolutePath(), "META-INF/jpms.args");
1145             try {
1146                 Files.createDirectories(jpmsArgs.getParent());
1147 
1148                 Files.write(jpmsArgs, jpmsLines, Charset.defaultCharset());
1149             } catch (IOException e) {
1150                 getLog().warn(e.getMessage());
1151             }
1152         }
1153 
1154         // ----------------------------------------------------------------------
1155         // Compile!
1156         // ----------------------------------------------------------------------
1157 
1158         if (StringUtils.isEmpty(compilerConfiguration.getSourceEncoding())) {
1159             getLog().warn("File encoding has not been set, using platform encoding "
1160                     + MessageUtils.buffer().strong(Charset.defaultCharset())
1161                     + ", i.e. build is platform dependent!");
1162         }
1163 
1164         CompilerResult compilerResult;
1165 
1166         if (useIncrementalCompilation) {
1167             incrementalBuildHelperRequest.outputDirectory(getOutputDirectory());
1168 
1169             // MCOMPILER-333: Cleanup the generated source files created by annotation processing
1170             // to avoid issues with `javac` compiler when the source code is rebuild.
1171             if (getGeneratedSourcesDirectory() != null) {
1172                 try (Stream<Path> walk =
1173                         Files.walk(getGeneratedSourcesDirectory().toPath())) {
1174                     walk.sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete);
1175                     // MCOMPILER-567: The directory must already exist because javac does not create it.
1176                     Files.createDirectories(getGeneratedSourcesDirectory().toPath());
1177                 } catch (IOException ex) {
1178                     getLog().warn("I/O error deleting the annotation processing generated files: " + ex.getMessage());
1179                 }
1180             }
1181 
1182             incrementalBuildHelper.beforeRebuildExecution(incrementalBuildHelperRequest);
1183 
1184             getLog().debug("incrementalBuildHelper#beforeRebuildExecution");
1185         }
1186 
1187         try {
1188             compilerResult = compiler.performCompile(compilerConfiguration);
1189         } catch (Exception e) {
1190             // TODO: don't catch Exception
1191             throw new MojoExecutionException("Fatal error compiling", e);
1192         }
1193 
1194         if (createMissingPackageInfoClass
1195                 && compilerResult.isSuccess()
1196                 && compiler.getCompilerOutputStyle() == CompilerOutputStyle.ONE_OUTPUT_FILE_PER_INPUT_FILE) {
1197             try {
1198                 SourceMapping sourceMapping = getSourceMapping(compilerConfiguration, compiler);
1199                 createMissingPackageInfoClasses(compilerConfiguration, sourceMapping, sources);
1200             } catch (Exception e) {
1201                 getLog().warn("Error creating missing package info classes", e);
1202             }
1203         }
1204 
1205         if (outputTimestamp != null && (outputTimestamp.length() > 1 || Character.isDigit(outputTimestamp.charAt(0)))) {
1206             // if Reproducible Builds mode, apply workaround
1207             patchJdkModuleVersion(compilerResult, sources);
1208         }
1209 
1210         if (useIncrementalCompilation) {
1211             if (incrementalBuildHelperRequest.getOutputDirectory().exists()) {
1212                 getLog().debug("incrementalBuildHelper#afterRebuildExecution");
1213                 // now scan the same directory again and create a diff
1214                 incrementalBuildHelper.afterRebuildExecution(incrementalBuildHelperRequest);
1215             } else {
1216                 getLog().debug(
1217                                 "skip incrementalBuildHelper#afterRebuildExecution as the output directory doesn't exist");
1218             }
1219         }
1220 
1221         List<CompilerMessage> warnings = new ArrayList<>();
1222         List<CompilerMessage> errors = new ArrayList<>();
1223         List<CompilerMessage> others = new ArrayList<>();
1224         for (CompilerMessage message : compilerResult.getCompilerMessages()) {
1225             switch (message.getKind()) {
1226                 case ERROR:
1227                     errors.add(message);
1228                     break;
1229                 case WARNING:
1230                 case MANDATORY_WARNING:
1231                     warnings.add(message);
1232                     break;
1233                 default:
1234                     others.add(message);
1235                     break;
1236             }
1237         }
1238 
1239         if (failOnError && !compilerResult.isSuccess()) {
1240             for (CompilerMessage message : others) {
1241                 assert message.getKind() != CompilerMessage.Kind.ERROR
1242                         && message.getKind() != CompilerMessage.Kind.WARNING
1243                         && message.getKind() != CompilerMessage.Kind.MANDATORY_WARNING;
1244                 getLog().info(message.toString());
1245             }
1246             if (!warnings.isEmpty()) {
1247                 getLog().info("-------------------------------------------------------------");
1248                 getLog().warn("COMPILATION WARNING : ");
1249                 getLog().info("-------------------------------------------------------------");
1250                 for (CompilerMessage warning : warnings) {
1251                     getLog().warn(warning.toString());
1252                 }
1253                 getLog().info(warnings.size() + ((warnings.size() > 1) ? " warnings " : " warning"));
1254                 getLog().info("-------------------------------------------------------------");
1255             }
1256 
1257             if (!errors.isEmpty()) {
1258                 getLog().info("-------------------------------------------------------------");
1259                 getLog().error("COMPILATION ERROR : ");
1260                 getLog().info("-------------------------------------------------------------");
1261                 for (CompilerMessage error : errors) {
1262                     getLog().error(error.toString());
1263                 }
1264                 getLog().info(errors.size() + ((errors.size() > 1) ? " errors " : " error"));
1265                 getLog().info("-------------------------------------------------------------");
1266             }
1267 
1268             if (!errors.isEmpty()) {
1269                 throw new CompilationFailureException(errors);
1270             } else {
1271                 throw new CompilationFailureException(warnings);
1272             }
1273         } else {
1274             for (CompilerMessage message : compilerResult.getCompilerMessages()) {
1275                 switch (message.getKind()) {
1276                     case NOTE:
1277                     case OTHER:
1278                         getLog().info(message.toString());
1279                         break;
1280                     case ERROR:
1281                         getLog().error(message.toString());
1282                         break;
1283                     case MANDATORY_WARNING:
1284                     case WARNING:
1285                     default:
1286                         getLog().warn(message.toString());
1287                         break;
1288                 }
1289             }
1290         }
1291     }
1292 
1293     private void createMissingPackageInfoClasses(
1294             CompilerConfiguration compilerConfiguration, SourceMapping sourceMapping, Set<File> sources)
1295             throws InclusionScanException, IOException {
1296         for (File source : sources) {
1297             String path = source.toString();
1298             if (path.endsWith(File.separator + "package-info.java")) {
1299                 for (String root : getCompileSourceRoots()) {
1300                     root = root + File.separator;
1301                     if (path.startsWith(root)) {
1302                         String rel = path.substring(root.length());
1303                         Set<File> files = sourceMapping.getTargetFiles(getOutputDirectory(), rel);
1304                         for (File file : files) {
1305                             if (!file.exists()) {
1306                                 File parentFile = file.getParentFile();
1307 
1308                                 if (!parentFile.exists()) {
1309                                     Files.createDirectories(parentFile.toPath());
1310                                 }
1311 
1312                                 byte[] bytes = generatePackage(compilerConfiguration, rel);
1313                                 Files.write(file.toPath(), bytes);
1314                             }
1315                         }
1316                     }
1317                 }
1318             }
1319         }
1320     }
1321 
1322     private byte[] generatePackage(CompilerConfiguration compilerConfiguration, String javaFile) {
1323         int version = getOpcode(compilerConfiguration);
1324         String internalPackageName = javaFile.substring(0, javaFile.length() - ".java".length());
1325         if (File.separatorChar != '/') {
1326             internalPackageName = internalPackageName.replace(File.separatorChar, '/');
1327         }
1328         ClassWriter cw = new ClassWriter(0);
1329         cw.visit(
1330                 version,
1331                 Opcodes.ACC_SYNTHETIC | Opcodes.ACC_ABSTRACT | Opcodes.ACC_INTERFACE,
1332                 internalPackageName,
1333                 null,
1334                 "java/lang/Object",
1335                 null);
1336         cw.visitSource("package-info.java", null);
1337         return cw.toByteArray();
1338     }
1339 
1340     private int getOpcode(CompilerConfiguration compilerConfiguration) {
1341         String version = compilerConfiguration.getReleaseVersion();
1342         if (version == null) {
1343             version = compilerConfiguration.getTargetVersion();
1344             if (version == null) {
1345                 version = "1.5";
1346             }
1347         }
1348         if (version.startsWith("1.")) {
1349             version = version.substring(2);
1350         }
1351         int iVersion = Integer.parseInt(version);
1352         if (iVersion < 2) {
1353             throw new IllegalArgumentException("Unsupported java version '" + version + "'");
1354         }
1355         return iVersion - 2 + Opcodes.V1_2;
1356     }
1357 
1358     protected boolean isTestCompile() {
1359         return false;
1360     }
1361 
1362     /**
1363      * @return all source files for the compiler
1364      */
1365     private Set<File> getCompileSources(Compiler compiler, CompilerConfiguration compilerConfiguration)
1366             throws MojoExecutionException, CompilerException {
1367         String inputFileEnding = compiler.getInputFileEnding(compilerConfiguration);
1368         if (inputFileEnding == null || inputFileEnding.isEmpty()) {
1369             // see MCOMPILER-199 GroovyEclipseCompiler doesn't set inputFileEnding
1370             // so we can presume it's all files from the source directory
1371             inputFileEnding = ".*";
1372         }
1373         SourceInclusionScanner scanner = getSourceInclusionScanner(inputFileEnding);
1374 
1375         SourceMapping mapping = getSourceMapping(compilerConfiguration, compiler);
1376 
1377         scanner.addSourceMapping(mapping);
1378 
1379         Set<File> compileSources = new HashSet<>();
1380 
1381         for (String sourceRoot : getCompileSourceRoots()) {
1382             File rootFile = new File(sourceRoot);
1383 
1384             if (!rootFile.isDirectory()
1385                     || rootFile.getAbsoluteFile().equals(compilerConfiguration.getGeneratedSourcesDirectory())) {
1386                 continue;
1387             }
1388 
1389             try {
1390                 compileSources.addAll(scanner.getIncludedSources(rootFile, null));
1391             } catch (InclusionScanException e) {
1392                 throw new MojoExecutionException(
1393                         "Error scanning source root: '" + sourceRoot + "' for stale files to recompile.", e);
1394             }
1395         }
1396 
1397         return compileSources;
1398     }
1399 
1400     protected abstract Set<String> getIncludes();
1401 
1402     protected abstract Set<String> getExcludes();
1403 
1404     /**
1405      * @param compilerConfiguration
1406      * @param compiler
1407      * @return {@code true} if at least a single source file is newer than it's class file
1408      */
1409     private boolean isSourceChanged(CompilerConfiguration compilerConfiguration, Compiler compiler) {
1410         Set<File> staleSources = Collections.emptySet();
1411         try {
1412             staleSources = computeStaleSources(compilerConfiguration, compiler, getSourceInclusionScanner(staleMillis));
1413         } catch (MojoExecutionException | CompilerException ex) {
1414             // we cannot detect Stale Sources, so don't do anything beside logging
1415             getLog().warn("Cannot detect stale sources.");
1416             return false;
1417         }
1418 
1419         if (getLog().isDebugEnabled() || showCompilationChanges) {
1420             for (File f : staleSources) {
1421                 getLog().info("\tStale source detected: " + f.getAbsolutePath());
1422             }
1423         }
1424         return !staleSources.isEmpty();
1425     }
1426 
1427     /**
1428      * try to get thread count if a Maven 3 build, using reflection as the plugin must not be maven3 api dependent
1429      *
1430      * @return number of thread for this build or 1 if not multi-thread build
1431      */
1432     protected int getRequestThreadCount() {
1433         return session.getRequest().getDegreeOfConcurrency();
1434     }
1435 
1436     protected Date getBuildStartTime() {
1437         return getBuildStartTimeInstant().map(Date::from).orElseGet(Date::new);
1438     }
1439 
1440     private Optional<Instant> getBuildStartTimeInstant() {
1441         return Optional.ofNullable(session.getRequest())
1442                 .map(MavenExecutionRequest::getStartTime)
1443                 .map(Date::toInstant)
1444                 .map(i -> i.truncatedTo(ChronoUnit.MILLIS));
1445     }
1446 
1447     private String getMemoryValue(String setting) {
1448         String value = null;
1449 
1450         // Allow '128' or '128m'
1451         if (isDigits(setting)) {
1452             value = setting + "m";
1453         } else if ((isDigits(setting.substring(0, setting.length() - 1)))
1454                 && (setting.toLowerCase().endsWith("m"))) {
1455             value = setting;
1456         }
1457         return value;
1458     }
1459 
1460     // TODO remove the part with ToolchainManager lookup once we depend on
1461     // 3.0.9 (have it as prerequisite). Define as regular component field then.
1462     protected final Toolchain getToolchain() {
1463         Toolchain tc = null;
1464 
1465         if (jdkToolchain != null) {
1466             // Maven 3.3.1 has plugin execution scoped Toolchain Support
1467             try {
1468                 Method getToolchainsMethod = toolchainManager
1469                         .getClass()
1470                         .getMethod("getToolchains", MavenSession.class, String.class, Map.class);
1471 
1472                 @SuppressWarnings("unchecked")
1473                 List<Toolchain> tcs =
1474                         (List<Toolchain>) getToolchainsMethod.invoke(toolchainManager, session, "jdk", jdkToolchain);
1475 
1476                 if (tcs != null && !tcs.isEmpty()) {
1477                     tc = tcs.get(0);
1478                 }
1479             } catch (NoSuchMethodException
1480                     | SecurityException
1481                     | IllegalAccessException
1482                     | IllegalArgumentException
1483                     | InvocationTargetException e) {
1484                 // ignore
1485             }
1486         }
1487 
1488         if (tc == null) {
1489             tc = toolchainManager.getToolchainFromBuildContext("jdk", session);
1490         }
1491 
1492         return tc;
1493     }
1494 
1495     private boolean isDigits(String string) {
1496         for (int i = 0; i < string.length(); i++) {
1497             if (!Character.isDigit(string.charAt(i))) {
1498                 return false;
1499             }
1500         }
1501         return true;
1502     }
1503 
1504     private Set<File> computeStaleSources(
1505             CompilerConfiguration compilerConfiguration, Compiler compiler, SourceInclusionScanner scanner)
1506             throws MojoExecutionException, CompilerException {
1507         SourceMapping mapping = getSourceMapping(compilerConfiguration, compiler);
1508 
1509         File outputDirectory;
1510         CompilerOutputStyle outputStyle = compiler.getCompilerOutputStyle();
1511         if (outputStyle == CompilerOutputStyle.ONE_OUTPUT_FILE_FOR_ALL_INPUT_FILES) {
1512             outputDirectory = buildDirectory;
1513         } else {
1514             outputDirectory = getOutputDirectory();
1515         }
1516 
1517         scanner.addSourceMapping(mapping);
1518 
1519         Set<File> staleSources = new HashSet<>();
1520 
1521         for (String sourceRoot : getCompileSourceRoots()) {
1522             File rootFile = new File(sourceRoot);
1523 
1524             if (!rootFile.isDirectory()) {
1525                 continue;
1526             }
1527 
1528             try {
1529                 staleSources.addAll(scanner.getIncludedSources(rootFile, outputDirectory));
1530             } catch (InclusionScanException e) {
1531                 throw new MojoExecutionException(
1532                         "Error scanning source root: \'" + sourceRoot + "\' for stale files to recompile.", e);
1533             }
1534         }
1535 
1536         return staleSources;
1537     }
1538 
1539     private SourceMapping getSourceMapping(CompilerConfiguration compilerConfiguration, Compiler compiler)
1540             throws CompilerException, MojoExecutionException {
1541         CompilerOutputStyle outputStyle = compiler.getCompilerOutputStyle();
1542 
1543         SourceMapping mapping;
1544         if (outputStyle == CompilerOutputStyle.ONE_OUTPUT_FILE_PER_INPUT_FILE) {
1545             mapping = new SuffixMapping(
1546                     compiler.getInputFileEnding(compilerConfiguration),
1547                     compiler.getOutputFileEnding(compilerConfiguration));
1548         } else if (outputStyle == CompilerOutputStyle.ONE_OUTPUT_FILE_FOR_ALL_INPUT_FILES) {
1549             mapping = new SingleTargetSourceMapping(
1550                     compiler.getInputFileEnding(compilerConfiguration), compiler.getOutputFile(compilerConfiguration));
1551 
1552         } else {
1553             throw new MojoExecutionException("Unknown compiler output style: '" + outputStyle + "'.");
1554         }
1555         return mapping;
1556     }
1557 
1558     /**
1559      * @todo also in ant plugin. This should be resolved at some point so that it does not need to
1560      * be calculated continuously - or should the plugins accept empty source roots as is?
1561      */
1562     private static List<String> removeEmptyCompileSourceRoots(List<String> compileSourceRootsList) {
1563         List<String> newCompileSourceRootsList = new ArrayList<>();
1564         if (compileSourceRootsList != null) {
1565             // copy as I may be modifying it
1566             for (String srcDir : compileSourceRootsList) {
1567                 if (!newCompileSourceRootsList.contains(srcDir) && new File(srcDir).exists()) {
1568                     newCompileSourceRootsList.add(srcDir);
1569                 }
1570             }
1571         }
1572         return newCompileSourceRootsList;
1573     }
1574 
1575     /**
1576      * We just compare the timestamps of all local dependency files (inter-module dependency classpath) and the own
1577      * generated classes and if we got a file which is &gt;= the build-started timestamp, then we caught a file which
1578      * got changed during this build.
1579      *
1580      * @return {@code true} if at least one single dependency has changed.
1581      */
1582     protected boolean isDependencyChanged() {
1583         final Instant buildStartTime = getBuildStartTimeInstant().orElse(null);
1584         if (buildStartTime == null) {
1585             // we just cannot determine it, so don't do anything beside logging
1586             getLog().debug("Cannot determine build start time, skipping incremental build detection.");
1587             return false;
1588         }
1589 
1590         if (fileExtensions == null || fileExtensions.isEmpty()) {
1591             fileExtensions = new HashSet<>(Arrays.asList("class", "jar"));
1592         }
1593 
1594         List<String> pathElements = new ArrayList<>();
1595         pathElements.addAll(getClasspathElements());
1596         pathElements.addAll(getModulepathElements());
1597 
1598         for (String pathElement : pathElements) {
1599             Path artifactPath = Paths.get(pathElement);
1600 
1601             // Search files only on dependencies (other modules), not on the current project,
1602             if (Files.isDirectory(artifactPath)
1603                     && !artifactPath.equals(getOutputDirectory().toPath())) {
1604                 try (Stream<Path> walk = Files.walk(artifactPath)) {
1605                     if (walk.anyMatch(p -> hasNewFile(p, buildStartTime))) {
1606                         return true;
1607                     }
1608                 } catch (IOException ex) {
1609                     // we just cannot determine it, so don't do anything beside logging
1610                     getLog().warn("I/O error walking the path: " + ex.getMessage());
1611                     return false;
1612                 }
1613             } else if (hasNewFile(artifactPath, buildStartTime)) {
1614                 return true;
1615             }
1616         }
1617 
1618         // obviously there was no new file detected.
1619         return false;
1620     }
1621 
1622     /**
1623      * @param file entry to check
1624      * @param buildStartTime time build start
1625      * @return if any changes occurred
1626      */
1627     private boolean hasNewFile(Path file, Instant buildStartTime) {
1628         if (Files.isRegularFile(file)
1629                 && fileExtensions.contains(
1630                         FileUtils.extension(file.getFileName().toString()))) {
1631             try {
1632                 Instant lastModifiedTime = Files.getLastModifiedTime(file)
1633                         .toInstant()
1634                         .minusMillis(staleMillis)
1635                         .truncatedTo(ChronoUnit.MILLIS);
1636                 boolean hasChanged = lastModifiedTime.isAfter(buildStartTime);
1637                 if (hasChanged && (getLog().isDebugEnabled() || showCompilationChanges)) {
1638                     getLog().info("\tNew dependency detected: " + file.toAbsolutePath());
1639                 }
1640                 return hasChanged;
1641             } catch (IOException ex) {
1642                 // we just cannot determine it, so don't do anything beside logging
1643                 getLog().warn("I/O error reading the lastModifiedTime: " + ex.getMessage());
1644             }
1645         }
1646 
1647         return false;
1648     }
1649 
1650     private List<String> resolveProcessorPathEntries() throws MojoExecutionException {
1651         if (annotationProcessorPaths == null || annotationProcessorPaths.isEmpty()) {
1652             return null;
1653         }
1654 
1655         try {
1656             List<org.eclipse.aether.graph.Dependency> dependencies = convertToDependencies(annotationProcessorPaths);
1657             List<org.eclipse.aether.graph.Dependency> managedDependencies =
1658                     getManagedDependenciesForAnnotationProcessorPaths();
1659             CollectRequest collectRequest =
1660                     new CollectRequest(dependencies, managedDependencies, project.getRemoteProjectRepositories());
1661             DependencyRequest dependencyRequest = new DependencyRequest();
1662             dependencyRequest.setCollectRequest(collectRequest);
1663             DependencyResult dependencyResult =
1664                     repositorySystem.resolveDependencies(session.getRepositorySession(), dependencyRequest);
1665 
1666             return dependencyResult.getArtifactResults().stream()
1667                     .map(resolved -> resolved.getArtifact().getFile().getAbsolutePath())
1668                     .collect(Collectors.toList());
1669         } catch (Exception e) {
1670             throw new MojoExecutionException(
1671                     "Resolution of annotationProcessorPath dependencies failed: " + e.getLocalizedMessage(), e);
1672         }
1673     }
1674 
1675     private List<org.eclipse.aether.graph.Dependency> convertToDependencies(
1676             List<DependencyCoordinate> annotationProcessorPaths) throws MojoExecutionException {
1677         List<org.eclipse.aether.graph.Dependency> dependencies = new ArrayList<>();
1678         for (DependencyCoordinate annotationProcessorPath : annotationProcessorPaths) {
1679             ArtifactHandler handler = artifactHandlerManager.getArtifactHandler(annotationProcessorPath.getType());
1680             String version = getAnnotationProcessorPathVersion(annotationProcessorPath);
1681             Artifact artifact = new DefaultArtifact(
1682                     annotationProcessorPath.getGroupId(),
1683                     annotationProcessorPath.getArtifactId(),
1684                     annotationProcessorPath.getClassifier(),
1685                     handler.getExtension(),
1686                     version);
1687             Set<Exclusion> exclusions = convertToAetherExclusions(annotationProcessorPath.getExclusions());
1688             dependencies.add(new org.eclipse.aether.graph.Dependency(artifact, JavaScopes.RUNTIME, false, exclusions));
1689         }
1690         return dependencies;
1691     }
1692 
1693     private String getAnnotationProcessorPathVersion(DependencyCoordinate annotationProcessorPath)
1694             throws MojoExecutionException {
1695         String configuredVersion = annotationProcessorPath.getVersion();
1696         if (configuredVersion != null) {
1697             return configuredVersion;
1698         } else {
1699             List<Dependency> managedDependencies = getProjectManagedDependencies();
1700             return findManagedVersion(annotationProcessorPath, managedDependencies)
1701                     .orElseThrow(() -> new MojoExecutionException(String.format(
1702                             "Cannot find version for annotation processor path '%s'. The version needs to be either"
1703                                     + " provided directly in the plugin configuration or via dependency management.",
1704                             annotationProcessorPath)));
1705         }
1706     }
1707 
1708     private Optional<String> findManagedVersion(
1709             DependencyCoordinate dependencyCoordinate, List<Dependency> managedDependencies) {
1710         return managedDependencies.stream()
1711                 .filter(dep -> Objects.equals(dep.getGroupId(), dependencyCoordinate.getGroupId())
1712                         && Objects.equals(dep.getArtifactId(), dependencyCoordinate.getArtifactId())
1713                         && Objects.equals(dep.getClassifier(), dependencyCoordinate.getClassifier())
1714                         && Objects.equals(dep.getType(), dependencyCoordinate.getType()))
1715                 .findAny()
1716                 .map(org.apache.maven.model.Dependency::getVersion);
1717     }
1718 
1719     private List<org.eclipse.aether.graph.Dependency> getManagedDependenciesForAnnotationProcessorPaths() {
1720         if (!annotationProcessorPathsUseDepMgmt) {
1721             return Collections.emptyList();
1722         }
1723         List<Dependency> projectManagedDependencies = getProjectManagedDependencies();
1724         ArtifactTypeRegistry artifactTypeRegistry =
1725                 session.getRepositorySession().getArtifactTypeRegistry();
1726 
1727         return projectManagedDependencies.stream()
1728                 .map(dep -> RepositoryUtils.toDependency(dep, artifactTypeRegistry))
1729                 .collect(Collectors.toList());
1730     }
1731 
1732     private List<Dependency> getProjectManagedDependencies() {
1733         DependencyManagement dependencyManagement = project.getDependencyManagement();
1734         if (dependencyManagement == null || dependencyManagement.getDependencies() == null) {
1735             return Collections.emptyList();
1736         }
1737         return dependencyManagement.getDependencies();
1738     }
1739 
1740     private Set<Exclusion> convertToAetherExclusions(Set<DependencyExclusion> exclusions) {
1741         if (exclusions == null || exclusions.isEmpty()) {
1742             return Collections.emptySet();
1743         }
1744         Set<Exclusion> aetherExclusions = new HashSet<>();
1745         for (DependencyExclusion exclusion : exclusions) {
1746             Exclusion aetherExclusion = new Exclusion(
1747                     exclusion.getGroupId(),
1748                     exclusion.getArtifactId(),
1749                     exclusion.getClassifier(),
1750                     exclusion.getExtension());
1751             aetherExclusions.add(aetherExclusion);
1752         }
1753         return aetherExclusions;
1754     }
1755 
1756     private void writePlugin(MessageBuilder mb) {
1757         mb.a("    <plugin>").newline();
1758         mb.a("      <groupId>org.apache.maven.plugins</groupId>").newline();
1759         mb.a("      <artifactId>maven-compiler-plugin</artifactId>").newline();
1760 
1761         String version = getMavenCompilerPluginVersion();
1762         if (version != null) {
1763             mb.a("      <version>").a(version).a("</version>").newline();
1764         }
1765         writeConfig(mb);
1766 
1767         mb.a("    </plugin>").newline();
1768     }
1769 
1770     private void writeConfig(MessageBuilder mb) {
1771         mb.a("      <configuration>").newline();
1772 
1773         if (release != null) {
1774             mb.a("        <release>").a(release).a("</release>").newline();
1775         } else if (JavaVersion.JAVA_VERSION.isAtLeast("9")) {
1776             String rls = target.replaceAll(".\\.", "");
1777             // when using Java9+, motivate to use release instead of source/target
1778             mb.a("        <release>").a(rls).a("</release>").newline();
1779         } else {
1780             mb.a("        <source>").a(source).a("</source>").newline();
1781             mb.a("        <target>").a(target).a("</target>").newline();
1782         }
1783         mb.a("      </configuration>").newline();
1784     }
1785 
1786     private String getMavenCompilerPluginVersion() {
1787         Properties pomProperties = new Properties();
1788 
1789         try (InputStream is = AbstractCompilerMojo.class.getResourceAsStream(
1790                 "/META-INF/maven/org.apache.maven.plugins/maven-compiler-plugin/pom.properties")) {
1791             if (is != null) {
1792                 pomProperties.load(is);
1793             }
1794         } catch (IOException e) {
1795             // noop
1796         }
1797 
1798         return pomProperties.getProperty("version");
1799     }
1800 
1801     private boolean hasInputFileTreeChanged(IncrementalBuildHelper ibh, Set<File> inputFiles) {
1802         Path mojoConfigBase;
1803         try {
1804             mojoConfigBase = ibh.getMojoStatusDirectory().toPath();
1805         } catch (MojoExecutionException e) {
1806             // we cannot get the mojo status dir, so don't do anything beside logging
1807             getLog().warn("Error reading mojo status directory.");
1808             return false;
1809         }
1810         Path mojoConfigFile = mojoConfigBase.resolve(INPUT_FILES_LST_FILENAME);
1811 
1812         List<String> oldInputFiles = Collections.emptyList();
1813         if (Files.isRegularFile(mojoConfigFile)) {
1814             try {
1815                 oldInputFiles = Files.readAllLines(mojoConfigFile);
1816             } catch (IOException e) {
1817                 // we cannot read the mojo config file, so don't do anything beside logging
1818                 getLog().warn("Error while reading old mojo status: " + mojoConfigFile);
1819                 return false;
1820             }
1821         }
1822 
1823         List<String> newInputFiles =
1824                 inputFiles.stream().sorted().map(File::getAbsolutePath).collect(Collectors.toList());
1825 
1826         try {
1827             Files.write(mojoConfigFile, newInputFiles);
1828         } catch (IOException e) {
1829             // we cannot write the mojo config file, so don't do anything beside logging
1830             getLog().warn("Error while writing new mojo status: " + mojoConfigFile);
1831             return false;
1832         }
1833 
1834         DeltaList<String> inputTreeChanges = new DeltaList<>(oldInputFiles, newInputFiles);
1835         if (getLog().isDebugEnabled() || showCompilationChanges) {
1836             for (String fileAdded : inputTreeChanges.getAdded()) {
1837                 getLog().info("\tInput tree files (+): " + fileAdded);
1838             }
1839             for (String fileRemoved : inputTreeChanges.getRemoved()) {
1840                 getLog().info("\tInput tree files (-): " + fileRemoved);
1841             }
1842         }
1843 
1844         return inputTreeChanges.hasChanged();
1845     }
1846 
1847     public void setTarget(String target) {
1848         this.target = target;
1849         targetOrReleaseSet = true;
1850     }
1851 
1852     public void setRelease(String release) {
1853         this.release = release;
1854         targetOrReleaseSet = true;
1855     }
1856 
1857     final String getImplicit() {
1858         return implicit;
1859     }
1860 
1861     /**
1862      * JDK-8318913 workaround: Patch module-info.class to set the java release version for java/jdk modules.
1863      *
1864      * @param compilerResult should succeed.
1865      * @param sources the list of the source files to check for the "module-info.java"
1866      *
1867      * @see <a href="https://issues.apache.org/jira/browse/MCOMPILER-542">MCOMPILER-542</a>
1868      * @see <a href="https://bugs.openjdk.org/browse/JDK-8318913">JDK-8318913</a>
1869      */
1870     private void patchJdkModuleVersion(CompilerResult compilerResult, Set<File> sources) throws MojoExecutionException {
1871         if (compilerResult.isSuccess() && getModuleDeclaration(sources).isPresent()) {
1872             Path moduleDescriptor = getOutputDirectory().toPath().resolve("module-info.class");
1873             if (Files.isRegularFile(moduleDescriptor)) {
1874                 try {
1875                     final byte[] descriptorOriginal = Files.readAllBytes(moduleDescriptor);
1876                     final byte[] descriptorMod =
1877                             ModuleInfoTransformer.transform(descriptorOriginal, getRelease(), getLog());
1878                     if (descriptorMod != null) {
1879                         Files.write(moduleDescriptor, descriptorMod);
1880                     }
1881                 } catch (IOException ex) {
1882                     throw new MojoExecutionException("Error reading or writing module-info.class", ex);
1883                 }
1884             }
1885         }
1886     }
1887 }