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