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 public void execute() throws MojoExecutionException, CompilationFailureException {
707 try {
708 executeReal();
709 } finally {
710 addGeneratedSourcesToProject();
711 }
712 }
713
714 private void addGeneratedSourcesToProject() {
715 File generatedSourcesDirectory = getGeneratedSourcesDirectory();
716 if (generatedSourcesDirectory == null) {
717 return;
718 }
719
720 String generatedSourcesPath = generatedSourcesDirectory.getAbsolutePath();
721
722 if (isTestCompile()) {
723 getLog().debug("Adding " + generatedSourcesPath
724 + " to the project test-compile source roots but NOT the actual test-compile source roots:\n "
725 + StringUtils.join(project.getTestCompileSourceRoots().iterator(), "\n "));
726
727 project.addTestCompileSourceRoot(generatedSourcesPath);
728 } else {
729 getLog().debug("Adding " + generatedSourcesPath
730 + " to the project compile source roots but NOT the actual compile source roots:\n "
731 + StringUtils.join(project.getCompileSourceRoots().iterator(), "\n "));
732
733 project.addCompileSourceRoot(generatedSourcesPath);
734 }
735 }
736
737 @SuppressWarnings("checkstyle:MethodLength")
738 private void executeReal() throws MojoExecutionException, CompilationFailureException {
739
740
741
742
743
744
745 Compiler compiler;
746
747 getLog().debug("Using compiler '" + compilerId + "'.");
748
749 try {
750 compiler = compilerManager.getCompiler(compilerId);
751 } catch (NoSuchCompilerException e) {
752 throw new MojoExecutionException("No such compiler '" + e.getCompilerId() + "'.", e);
753 }
754
755
756
757 Toolchain tc = getToolchain();
758 if (tc != null) {
759 getLog().info("Toolchain in maven-compiler-plugin: " + tc);
760 if (executable != null) {
761 getLog().warn("Toolchains are ignored, 'executable' parameter is set to " + executable);
762 } else {
763 fork = true;
764
765 executable = tc.findTool(compilerId);
766 }
767 }
768
769
770
771
772 List<String> compileSourceRoots = removeEmptyCompileSourceRoots(getCompileSourceRoots());
773
774 if (compileSourceRoots.isEmpty()) {
775 getLog().info("No sources to compile");
776
777 return;
778 }
779
780
781 if (!targetOrReleaseSet) {
782 MessageBuilder mb = MessageUtils.buffer()
783 .a("No explicit value set for target or release! ")
784 .a("To ensure the same result even after upgrading this plugin, please add ")
785 .newline()
786 .newline();
787
788 writePlugin(mb);
789
790 getLog().warn(mb.toString());
791 }
792
793
794
795
796
797 CompilerConfiguration compilerConfiguration = new CompilerConfiguration();
798
799 compilerConfiguration.setOutputLocation(getOutputDirectory().getAbsolutePath());
800
801 compilerConfiguration.setOptimize(optimize);
802
803 compilerConfiguration.setDebug(debug);
804
805 compilerConfiguration.setDebugFileName(getDebugFileName());
806
807 compilerConfiguration.setImplicitOption(implicit);
808
809 if (debug && (debuglevel != null && !debuglevel.isEmpty())) {
810 String[] split = StringUtils.split(debuglevel, ",");
811 for (String aSplit : split) {
812 if (!(aSplit.equalsIgnoreCase("none")
813 || aSplit.equalsIgnoreCase("lines")
814 || aSplit.equalsIgnoreCase("vars")
815 || aSplit.equalsIgnoreCase("source"))) {
816 throw new IllegalArgumentException("The specified debug level: '" + aSplit + "' is unsupported. "
817 + "Legal values are 'none', 'lines', 'vars', and 'source'.");
818 }
819 }
820 compilerConfiguration.setDebugLevel(debuglevel);
821 }
822
823 compilerConfiguration.setParameters(parameters);
824
825 compilerConfiguration.setEnablePreview(enablePreview);
826
827 compilerConfiguration.setVerbose(verbose);
828
829 compilerConfiguration.setShowWarnings(showWarnings);
830
831 compilerConfiguration.setFailOnWarning(failOnWarning);
832
833 if (failOnWarning && !showWarnings) {
834 getLog().warn("The property failOnWarning is set to true, but showWarnings is set to false.");
835 getLog().warn("With compiler's warnings silenced the failOnWarning has no effect.");
836 }
837
838 compilerConfiguration.setShowDeprecation(showDeprecation);
839
840 compilerConfiguration.setSourceVersion(getSource());
841
842 compilerConfiguration.setTargetVersion(getTarget());
843
844 compilerConfiguration.setReleaseVersion(getRelease());
845
846 compilerConfiguration.setProc(proc);
847
848 File generatedSourcesDirectory = getGeneratedSourcesDirectory();
849 compilerConfiguration.setGeneratedSourcesDirectory(
850 generatedSourcesDirectory != null ? generatedSourcesDirectory.getAbsoluteFile() : null);
851
852 if (generatedSourcesDirectory != null) {
853 if (!generatedSourcesDirectory.exists()) {
854 generatedSourcesDirectory.mkdirs();
855 }
856 }
857
858 compilerConfiguration.setSourceLocations(compileSourceRoots);
859
860 compilerConfiguration.setAnnotationProcessors(annotationProcessors);
861
862 compilerConfiguration.setProcessorPathEntries(resolveProcessorPathEntries());
863
864 compilerConfiguration.setSourceEncoding(encoding);
865
866 compilerConfiguration.setFork(fork);
867
868 if (fork) {
869 if (!(meminitial == null || meminitial.isEmpty())) {
870 String value = getMemoryValue(meminitial);
871
872 if (value != null) {
873 compilerConfiguration.setMeminitial(value);
874 } else {
875 getLog().info("Invalid value for meminitial '" + meminitial + "'. Ignoring this option.");
876 }
877 }
878
879 if (!(maxmem == null || maxmem.isEmpty())) {
880 String value = getMemoryValue(maxmem);
881
882 if (value != null) {
883 compilerConfiguration.setMaxmem(value);
884 } else {
885 getLog().info("Invalid value for maxmem '" + maxmem + "'. Ignoring this option.");
886 }
887 }
888 }
889
890 compilerConfiguration.setExecutable(executable);
891
892 compilerConfiguration.setWorkingDirectory(basedir);
893
894 compilerConfiguration.setCompilerVersion(compilerVersion);
895
896 compilerConfiguration.setBuildDirectory(buildDirectory);
897
898 compilerConfiguration.setOutputFileName(outputFileName);
899
900 if (CompilerConfiguration.CompilerReuseStrategy.AlwaysNew.getStrategy().equals(this.compilerReuseStrategy)) {
901 compilerConfiguration.setCompilerReuseStrategy(CompilerConfiguration.CompilerReuseStrategy.AlwaysNew);
902 } else if (CompilerConfiguration.CompilerReuseStrategy.ReuseSame.getStrategy()
903 .equals(this.compilerReuseStrategy)) {
904 if (getRequestThreadCount() > 1) {
905 if (!skipMultiThreadWarning) {
906 getLog().warn("You are in a multi-thread build and compilerReuseStrategy is set to reuseSame."
907 + " This can cause issues in some environments (os/jdk)!"
908 + " Consider using reuseCreated strategy."
909 + System.getProperty("line.separator")
910 + "If your env is fine with reuseSame, you can skip this warning with the "
911 + "configuration field skipMultiThreadWarning "
912 + "or -Dmaven.compiler.skipMultiThreadWarning=true");
913 }
914 }
915 compilerConfiguration.setCompilerReuseStrategy(CompilerConfiguration.CompilerReuseStrategy.ReuseSame);
916 } else {
917
918 compilerConfiguration.setCompilerReuseStrategy(CompilerConfiguration.CompilerReuseStrategy.ReuseCreated);
919 }
920
921 getLog().debug("CompilerReuseStrategy: "
922 + compilerConfiguration.getCompilerReuseStrategy().getStrategy());
923
924 compilerConfiguration.setForceJavacCompilerUse(forceLegacyJavacApi || forceJavacCompilerUse);
925
926 boolean canUpdateTarget;
927
928 IncrementalBuildHelper incrementalBuildHelper = new IncrementalBuildHelper(mojoExecution, session);
929
930 final Set<File> sources;
931
932 IncrementalBuildHelperRequest incrementalBuildHelperRequest = null;
933
934 if (useIncrementalCompilation) {
935 getLog().debug("useIncrementalCompilation enabled");
936 try {
937 canUpdateTarget = compiler.canUpdateTarget(compilerConfiguration);
938
939 sources = getCompileSources(compiler, compilerConfiguration);
940
941 preparePaths(sources);
942
943 incrementalBuildHelperRequest = new IncrementalBuildHelperRequest().inputFiles(sources);
944
945
946 String immutableOutputFile = (compiler.getCompilerOutputStyle()
947 .equals(CompilerOutputStyle.ONE_OUTPUT_FILE_FOR_ALL_INPUT_FILES)
948 && !canUpdateTarget)
949 ? "immutable single output file"
950 : null;
951 String dependencyChanged = isDependencyChanged() ? "changed dependency" : null;
952 String sourceChanged = isSourceChanged(compilerConfiguration, compiler) ? "changed source code" : null;
953 String inputFileTreeChanged = hasInputFileTreeChanged(incrementalBuildHelper, sources)
954 ? "added or removed source files"
955 : null;
956
957
958 String cause = Stream.of(immutableOutputFile, dependencyChanged, sourceChanged, inputFileTreeChanged)
959 .filter(Objects::nonNull)
960 .findFirst()
961 .orElse(null);
962
963 if (cause != null) {
964 getLog().info("Recompiling the module because of "
965 + MessageUtils.buffer().strong(cause) + ".");
966 compilerConfiguration.setSourceFiles(sources);
967 } else {
968 getLog().info("Nothing to compile - all classes are up to date.");
969 return;
970 }
971 } catch (CompilerException e) {
972 throw new MojoExecutionException("Error while computing stale sources.", e);
973 }
974 } else {
975 getLog().debug("useIncrementalCompilation disabled");
976
977 Set<File> staleSources;
978 try {
979 staleSources =
980 computeStaleSources(compilerConfiguration, compiler, getSourceInclusionScanner(staleMillis));
981
982 canUpdateTarget = compiler.canUpdateTarget(compilerConfiguration);
983
984 if (compiler.getCompilerOutputStyle().equals(CompilerOutputStyle.ONE_OUTPUT_FILE_FOR_ALL_INPUT_FILES)
985 && !canUpdateTarget) {
986 getLog().info("RESCANNING!");
987
988 String inputFileEnding = compiler.getInputFileEnding(compilerConfiguration);
989
990 staleSources = computeStaleSources(
991 compilerConfiguration, compiler, getSourceInclusionScanner(inputFileEnding));
992 }
993
994 } catch (CompilerException e) {
995 throw new MojoExecutionException("Error while computing stale sources.", e);
996 }
997
998 if (staleSources.isEmpty()) {
999 getLog().info("Nothing to compile - all classes are up to date.");
1000 return;
1001 }
1002
1003 staleSources = filterSourceFiles(staleSources);
1004 compilerConfiguration.setSourceFiles(staleSources);
1005
1006 try {
1007
1008 sources = getCompileSources(compiler, compilerConfiguration);
1009
1010 if (getLog().isDebugEnabled()) {
1011 getLog().debug("#sources: " + sources.size());
1012 for (File file : sources) {
1013 getLog().debug(file.getPath());
1014 }
1015 }
1016
1017 preparePaths(sources);
1018 } catch (CompilerException e) {
1019 throw new MojoExecutionException("Error while computing stale sources.", e);
1020 }
1021 }
1022
1023
1024 compilerConfiguration.setClasspathEntries(getClasspathElements());
1025
1026 compilerConfiguration.setModulepathEntries(getModulepathElements());
1027
1028 compilerConfiguration.setIncludes(getIncludes());
1029
1030 compilerConfiguration.setExcludes(getExcludes());
1031
1032 Map<String, String> effectiveCompilerArguments = getCompilerArguments();
1033
1034 String effectiveCompilerArgument = getCompilerArgument();
1035
1036 if ((effectiveCompilerArguments != null) || (effectiveCompilerArgument != null) || (compilerArgs != null)) {
1037 if (effectiveCompilerArguments != null) {
1038 for (Map.Entry<String, String> me : effectiveCompilerArguments.entrySet()) {
1039 String key = me.getKey();
1040 String value = me.getValue();
1041 if (!key.startsWith("-")) {
1042 key = "-" + key;
1043 }
1044
1045 if (key.startsWith("-A") && (value != null && !value.isEmpty())) {
1046 compilerConfiguration.addCompilerCustomArgument(key + "=" + value, null);
1047 } else {
1048 compilerConfiguration.addCompilerCustomArgument(key, value);
1049 }
1050 }
1051 }
1052 if (!(effectiveCompilerArgument == null || effectiveCompilerArgument.isEmpty())) {
1053 compilerConfiguration.addCompilerCustomArgument(effectiveCompilerArgument, null);
1054 }
1055 if (compilerArgs != null) {
1056 for (String arg : compilerArgs) {
1057 compilerConfiguration.addCompilerCustomArgument(arg, null);
1058 }
1059 }
1060 }
1061
1062
1063
1064
1065 if (getLog().isDebugEnabled()) {
1066 getLog().debug("Classpath:");
1067
1068 for (String s : getClasspathElements()) {
1069 getLog().debug(" " + s);
1070 }
1071
1072 if (!getModulepathElements().isEmpty()) {
1073 getLog().debug("Modulepath:");
1074 for (String s : getModulepathElements()) {
1075 getLog().debug(" " + s);
1076 }
1077 }
1078
1079 getLog().debug("Source roots:");
1080
1081 for (String root : getCompileSourceRoots()) {
1082 getLog().debug(" " + root);
1083 }
1084
1085 try {
1086 if (fork) {
1087 if (compilerConfiguration.getExecutable() != null) {
1088 getLog().debug("Executable: ");
1089 getLog().debug(" " + compilerConfiguration.getExecutable());
1090 }
1091 }
1092
1093 String[] cl = compiler.createCommandLine(compilerConfiguration);
1094 if (cl != null && cl.length > 0) {
1095 StringBuilder sb = new StringBuilder();
1096 sb.append(cl[0]);
1097 for (int i = 1; i < cl.length; i++) {
1098 sb.append(" ");
1099 sb.append(cl[i]);
1100 }
1101 getLog().debug("Command line options:");
1102 getLog().debug(sb);
1103 }
1104 } catch (CompilerException ce) {
1105 getLog().debug(ce);
1106 }
1107 }
1108
1109 List<String> jpmsLines = new ArrayList<>();
1110
1111
1112 final List<String> runtimeArgs = Arrays.asList(
1113 "--upgrade-module-path", "--add-exports", "--add-reads", "--add-modules", "--limit-modules");
1114
1115
1116 Iterator<Map.Entry<String, String>> entryIter =
1117 compilerConfiguration.getCustomCompilerArgumentsEntries().iterator();
1118 while (entryIter.hasNext()) {
1119 Map.Entry<String, String> entry = entryIter.next();
1120
1121 if (runtimeArgs.contains(entry.getKey())) {
1122 jpmsLines.add(entry.getKey());
1123
1124 String value = entry.getValue();
1125 if (value == null) {
1126 entry = entryIter.next();
1127 value = entry.getKey();
1128 }
1129 jpmsLines.add(value);
1130 } else if ("--patch-module".equals(entry.getKey())) {
1131 String value = entry.getValue();
1132 if (value == null) {
1133 entry = entryIter.next();
1134 value = entry.getKey();
1135 }
1136
1137 String[] values = value.split("=");
1138
1139 StringBuilder patchModule = new StringBuilder(values[0]);
1140 patchModule.append('=');
1141
1142 Set<String> patchModules = new LinkedHashSet<>();
1143 Set<Path> sourceRoots = new HashSet<>(getCompileSourceRoots().size());
1144 for (String sourceRoot : getCompileSourceRoots()) {
1145 sourceRoots.add(Paths.get(sourceRoot));
1146 }
1147
1148 String[] files = values[1].split(PS);
1149
1150 for (String file : files) {
1151 Path filePath = Paths.get(file);
1152 if (getOutputDirectory().toPath().equals(filePath)) {
1153 patchModules.add("_");
1154 } else if (getOutputDirectory().toPath().startsWith(filePath)) {
1155
1156 continue;
1157 } else if (sourceRoots.contains(filePath)) {
1158 patchModules.add("_");
1159 } else {
1160 JavaModuleDescriptor descriptor = getPathElements().get(file);
1161
1162 if (descriptor == null) {
1163 if (Files.isDirectory(filePath)) {
1164 patchModules.add(file);
1165 } else {
1166 getLog().warn("Can't locate " + file);
1167 }
1168 } else if (!values[0].equals(descriptor.name())) {
1169 patchModules.add(descriptor.name());
1170 }
1171 }
1172 }
1173
1174 StringBuilder sb = new StringBuilder();
1175
1176 if (!patchModules.isEmpty()) {
1177 for (String mod : patchModules) {
1178 if (sb.length() > 0) {
1179 sb.append(", ");
1180 }
1181
1182 sb.append(mod);
1183 }
1184
1185 jpmsLines.add("--patch-module");
1186 jpmsLines.add(patchModule + sb.toString());
1187 }
1188 }
1189 }
1190
1191 if (!jpmsLines.isEmpty()) {
1192 Path jpmsArgs = Paths.get(getOutputDirectory().getAbsolutePath(), "META-INF/jpms.args");
1193 try {
1194 Files.createDirectories(jpmsArgs.getParent());
1195
1196 Files.write(jpmsArgs, jpmsLines, Charset.defaultCharset());
1197 } catch (IOException e) {
1198 getLog().warn(e.getMessage());
1199 }
1200 }
1201
1202
1203
1204
1205
1206 if (StringUtils.isEmpty(compilerConfiguration.getSourceEncoding())) {
1207 getLog().warn("File encoding has not been set, using platform encoding "
1208 + MessageUtils.buffer().strong(Charset.defaultCharset())
1209 + ", i.e. build is platform dependent!");
1210 }
1211
1212 CompilerResult compilerResult;
1213
1214 if (useIncrementalCompilation) {
1215 incrementalBuildHelperRequest.outputDirectory(getOutputDirectory());
1216
1217
1218
1219 if (getGeneratedSourcesDirectory() != null) {
1220 try (Stream<Path> walk =
1221 Files.walk(getGeneratedSourcesDirectory().toPath())) {
1222 walk.sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete);
1223
1224 Files.createDirectories(getGeneratedSourcesDirectory().toPath());
1225 } catch (IOException ex) {
1226 getLog().warn("I/O error deleting the annotation processing generated files: " + ex.getMessage());
1227 }
1228 }
1229
1230 incrementalBuildHelper.beforeRebuildExecution(incrementalBuildHelperRequest);
1231
1232 getLog().debug("incrementalBuildHelper#beforeRebuildExecution");
1233 }
1234
1235 try {
1236 compilerResult = compiler.performCompile(compilerConfiguration);
1237 } catch (Exception e) {
1238
1239 throw new MojoExecutionException("Fatal error compiling", e);
1240 }
1241
1242 if (createMissingPackageInfoClass
1243 && compilerResult.isSuccess()
1244 && compiler.getCompilerOutputStyle() == CompilerOutputStyle.ONE_OUTPUT_FILE_PER_INPUT_FILE) {
1245 try {
1246 SourceMapping sourceMapping = getSourceMapping(compilerConfiguration, compiler);
1247 createMissingPackageInfoClasses(compilerConfiguration, sourceMapping, sources);
1248 } catch (Exception e) {
1249 getLog().warn("Error creating missing package info classes", e);
1250 }
1251 }
1252
1253 if (outputTimestamp != null
1254 && !outputTimestamp.isEmpty()
1255 && (outputTimestamp.length() > 1 || Character.isDigit(outputTimestamp.charAt(0)))) {
1256
1257 patchJdkModuleVersion(compilerResult, sources);
1258 }
1259
1260 if (useIncrementalCompilation) {
1261 if (incrementalBuildHelperRequest.getOutputDirectory().exists()) {
1262 getLog().debug("incrementalBuildHelper#afterRebuildExecution");
1263
1264 incrementalBuildHelper.afterRebuildExecution(incrementalBuildHelperRequest);
1265 } else {
1266 getLog().debug(
1267 "skip incrementalBuildHelper#afterRebuildExecution as the output directory doesn't exist");
1268 }
1269 }
1270
1271 List<CompilerMessage> warnings = new ArrayList<>();
1272 List<CompilerMessage> errors = new ArrayList<>();
1273 List<CompilerMessage> others = new ArrayList<>();
1274 for (CompilerMessage message : compilerResult.getCompilerMessages()) {
1275 switch (message.getKind()) {
1276 case ERROR:
1277 errors.add(message);
1278 break;
1279 case WARNING:
1280 case MANDATORY_WARNING:
1281 warnings.add(message);
1282 break;
1283 default:
1284 others.add(message);
1285 break;
1286 }
1287 }
1288
1289 if (failOnError && !compilerResult.isSuccess()) {
1290 for (CompilerMessage message : others) {
1291 assert message.getKind() != CompilerMessage.Kind.ERROR
1292 && message.getKind() != CompilerMessage.Kind.WARNING
1293 && message.getKind() != CompilerMessage.Kind.MANDATORY_WARNING;
1294 getLog().info(message.toString());
1295 }
1296 if (!warnings.isEmpty()) {
1297 getLog().info("-------------------------------------------------------------");
1298 getLog().warn("COMPILATION WARNING : ");
1299 getLog().info("-------------------------------------------------------------");
1300 for (CompilerMessage warning : warnings) {
1301 getLog().warn(warning.toString());
1302 }
1303 getLog().info(warnings.size() + ((warnings.size() > 1) ? " warnings " : " warning"));
1304 getLog().info("-------------------------------------------------------------");
1305 }
1306
1307 if (!errors.isEmpty()) {
1308 getLog().info("-------------------------------------------------------------");
1309 getLog().error("COMPILATION ERROR : ");
1310 getLog().info("-------------------------------------------------------------");
1311 for (CompilerMessage error : errors) {
1312 getLog().error(error.toString());
1313 }
1314 getLog().info(errors.size() + ((errors.size() > 1) ? " errors " : " error"));
1315 getLog().info("-------------------------------------------------------------");
1316 }
1317
1318 if (!errors.isEmpty()) {
1319 throw new CompilationFailureException(errors);
1320 } else {
1321 throw new CompilationFailureException(warnings);
1322 }
1323 } else {
1324 for (CompilerMessage message : compilerResult.getCompilerMessages()) {
1325 switch (message.getKind()) {
1326 case NOTE:
1327 case OTHER:
1328 getLog().info(message.toString());
1329 break;
1330 case ERROR:
1331 getLog().error(message.toString());
1332 break;
1333 case MANDATORY_WARNING:
1334 case WARNING:
1335 default:
1336 getLog().warn(message.toString());
1337 break;
1338 }
1339 }
1340 }
1341 }
1342
1343 private void createMissingPackageInfoClasses(
1344 CompilerConfiguration compilerConfiguration, SourceMapping sourceMapping, Set<File> sources)
1345 throws InclusionScanException, IOException {
1346 for (File source : sources) {
1347 String path = source.toString();
1348 if (path.endsWith(File.separator + "package-info.java")) {
1349 for (String root : getCompileSourceRoots()) {
1350 root = root + File.separator;
1351 if (path.startsWith(root)) {
1352 String rel = path.substring(root.length());
1353 Set<File> files = sourceMapping.getTargetFiles(getOutputDirectory(), rel);
1354 for (File file : files) {
1355 if (!file.exists()) {
1356 File parentFile = file.getParentFile();
1357
1358 if (!parentFile.exists()) {
1359 Files.createDirectories(parentFile.toPath());
1360 }
1361
1362 byte[] bytes = generatePackage(compilerConfiguration, rel);
1363 Files.write(file.toPath(), bytes);
1364 }
1365 }
1366 }
1367 }
1368 }
1369 }
1370 }
1371
1372 private byte[] generatePackage(CompilerConfiguration compilerConfiguration, String javaFile) {
1373 int version = getOpcode(compilerConfiguration);
1374 String internalPackageName = javaFile.substring(0, javaFile.length() - ".java".length());
1375 if (File.separatorChar != '/') {
1376 internalPackageName = internalPackageName.replace(File.separatorChar, '/');
1377 }
1378 ClassWriter cw = new ClassWriter(0);
1379 cw.visit(
1380 version,
1381 Opcodes.ACC_SYNTHETIC | Opcodes.ACC_ABSTRACT | Opcodes.ACC_INTERFACE,
1382 internalPackageName,
1383 null,
1384 "java/lang/Object",
1385 null);
1386 cw.visitSource("package-info.java", null);
1387 return cw.toByteArray();
1388 }
1389
1390 private int getOpcode(CompilerConfiguration compilerConfiguration) {
1391 String version = compilerConfiguration.getReleaseVersion();
1392 if (version == null) {
1393 version = compilerConfiguration.getTargetVersion();
1394 if (version == null) {
1395 version = "1.5";
1396 }
1397 }
1398 if (version.startsWith("1.")) {
1399 version = version.substring(2);
1400 }
1401 int iVersion = Integer.parseInt(version);
1402 if (iVersion < 2) {
1403 throw new IllegalArgumentException("Unsupported java version '" + version + "'");
1404 }
1405 return iVersion - 2 + Opcodes.V1_2;
1406 }
1407
1408 protected boolean isTestCompile() {
1409 return false;
1410 }
1411
1412
1413
1414
1415 private Set<File> getCompileSources(Compiler compiler, CompilerConfiguration compilerConfiguration)
1416 throws MojoExecutionException, CompilerException {
1417 String inputFileEnding = compiler.getInputFileEnding(compilerConfiguration);
1418 if (inputFileEnding == null || inputFileEnding.isEmpty()) {
1419
1420
1421 inputFileEnding = ".*";
1422 }
1423 SourceInclusionScanner scanner = getSourceInclusionScanner(inputFileEnding);
1424
1425 SourceMapping mapping = getSourceMapping(compilerConfiguration, compiler);
1426
1427 scanner.addSourceMapping(mapping);
1428
1429 Set<File> compileSources = new HashSet<>();
1430
1431 for (String sourceRoot : getCompileSourceRoots()) {
1432 File rootFile = new File(sourceRoot);
1433
1434 if (!rootFile.isDirectory()
1435 || rootFile.getAbsoluteFile().equals(compilerConfiguration.getGeneratedSourcesDirectory())) {
1436 continue;
1437 }
1438
1439 try {
1440 compileSources.addAll(scanner.getIncludedSources(rootFile, null));
1441 } catch (InclusionScanException e) {
1442 throw new MojoExecutionException(
1443 "Error scanning source root: '" + sourceRoot + "' for stale files to recompile.", e);
1444 }
1445 }
1446
1447 return compileSources;
1448 }
1449
1450 protected abstract Set<String> getIncludes();
1451
1452 protected abstract Set<String> getExcludes();
1453
1454
1455
1456
1457
1458
1459 private boolean isSourceChanged(CompilerConfiguration compilerConfiguration, Compiler compiler) {
1460 Set<File> staleSources = Collections.emptySet();
1461 try {
1462 staleSources = computeStaleSources(compilerConfiguration, compiler, getSourceInclusionScanner(staleMillis));
1463 } catch (MojoExecutionException | CompilerException ex) {
1464
1465 getLog().warn("Cannot detect stale sources.");
1466 return false;
1467 }
1468
1469 if (getLog().isDebugEnabled() || showCompilationChanges) {
1470 for (File f : staleSources) {
1471 getLog().info("\tStale source detected: " + f.getAbsolutePath());
1472 }
1473 }
1474 return !staleSources.isEmpty();
1475 }
1476
1477
1478
1479
1480
1481
1482 protected int getRequestThreadCount() {
1483 return session.getRequest().getDegreeOfConcurrency();
1484 }
1485
1486 protected Date getBuildStartTime() {
1487 return getBuildStartTimeInstant().map(Date::from).orElseGet(Date::new);
1488 }
1489
1490 private Optional<Instant> getBuildStartTimeInstant() {
1491 return Optional.ofNullable(session.getRequest())
1492 .map(MavenExecutionRequest::getStartTime)
1493 .map(Date::toInstant)
1494 .map(i -> i.truncatedTo(ChronoUnit.MILLIS));
1495 }
1496
1497 private String getMemoryValue(String setting) {
1498 String value = null;
1499
1500
1501 if (isDigits(setting)) {
1502 value = setting + "m";
1503 } else if ((isDigits(setting.substring(0, setting.length() - 1)))
1504 && (setting.toLowerCase().endsWith("m"))) {
1505 value = setting;
1506 }
1507 return value;
1508 }
1509
1510 protected final Toolchain getToolchain() {
1511 Toolchain tc = null;
1512
1513 if (jdkToolchain != null) {
1514 List<Toolchain> tcs = toolchainManager.getToolchains(session, "jdk", jdkToolchain);
1515 if (tcs != null && !tcs.isEmpty()) {
1516 tc = tcs.get(0);
1517 }
1518 }
1519
1520 if (tc == null) {
1521 tc = toolchainManager.getToolchainFromBuildContext("jdk", session);
1522 }
1523
1524 return tc;
1525 }
1526
1527 private boolean isDigits(String string) {
1528 for (int i = 0; i < string.length(); i++) {
1529 if (!Character.isDigit(string.charAt(i))) {
1530 return false;
1531 }
1532 }
1533 return true;
1534 }
1535
1536 private Set<File> computeStaleSources(
1537 CompilerConfiguration compilerConfiguration, Compiler compiler, SourceInclusionScanner scanner)
1538 throws MojoExecutionException, CompilerException {
1539 SourceMapping mapping = getSourceMapping(compilerConfiguration, compiler);
1540
1541 File outputDirectory;
1542 CompilerOutputStyle outputStyle = compiler.getCompilerOutputStyle();
1543 if (outputStyle == CompilerOutputStyle.ONE_OUTPUT_FILE_FOR_ALL_INPUT_FILES) {
1544 outputDirectory = buildDirectory;
1545 } else {
1546 outputDirectory = getOutputDirectory();
1547 }
1548
1549 scanner.addSourceMapping(mapping);
1550
1551 Set<File> staleSources = new HashSet<>();
1552
1553 for (String sourceRoot : getCompileSourceRoots()) {
1554 File rootFile = new File(sourceRoot);
1555
1556 if (!rootFile.isDirectory()) {
1557 continue;
1558 }
1559
1560 try {
1561 staleSources.addAll(scanner.getIncludedSources(rootFile, outputDirectory));
1562 } catch (InclusionScanException e) {
1563 throw new MojoExecutionException(
1564 "Error scanning source root: \'" + sourceRoot + "\' for stale files to recompile.", e);
1565 }
1566 }
1567
1568 return staleSources;
1569 }
1570
1571 private SourceMapping getSourceMapping(CompilerConfiguration compilerConfiguration, Compiler compiler)
1572 throws CompilerException, MojoExecutionException {
1573 CompilerOutputStyle outputStyle = compiler.getCompilerOutputStyle();
1574
1575 SourceMapping mapping;
1576 if (outputStyle == CompilerOutputStyle.ONE_OUTPUT_FILE_PER_INPUT_FILE) {
1577 mapping = new SuffixMapping(
1578 compiler.getInputFileEnding(compilerConfiguration),
1579 compiler.getOutputFileEnding(compilerConfiguration));
1580 } else if (outputStyle == CompilerOutputStyle.ONE_OUTPUT_FILE_FOR_ALL_INPUT_FILES) {
1581 mapping = new SingleTargetSourceMapping(
1582 compiler.getInputFileEnding(compilerConfiguration), compiler.getOutputFile(compilerConfiguration));
1583
1584 } else {
1585 throw new MojoExecutionException("Unknown compiler output style: '" + outputStyle + "'.");
1586 }
1587 return mapping;
1588 }
1589
1590
1591
1592
1593
1594 private static List<String> removeEmptyCompileSourceRoots(List<String> compileSourceRootsList) {
1595 List<String> newCompileSourceRootsList = new ArrayList<>();
1596 if (compileSourceRootsList != null) {
1597
1598 for (String srcDir : compileSourceRootsList) {
1599 if (!newCompileSourceRootsList.contains(srcDir) && new File(srcDir).exists()) {
1600 newCompileSourceRootsList.add(srcDir);
1601 }
1602 }
1603 }
1604 return newCompileSourceRootsList;
1605 }
1606
1607
1608
1609
1610
1611
1612
1613
1614 protected boolean isDependencyChanged() {
1615 final Instant buildStartTime = getBuildStartTimeInstant().orElse(null);
1616 if (buildStartTime == null) {
1617
1618 getLog().debug("Cannot determine build start time, skipping incremental build detection.");
1619 return false;
1620 }
1621
1622 if (fileExtensions == null || fileExtensions.isEmpty()) {
1623 fileExtensions = new HashSet<>(Arrays.asList("class", "jar"));
1624 }
1625
1626 List<String> pathElements = new ArrayList<>();
1627 pathElements.addAll(getClasspathElements());
1628 pathElements.addAll(getModulepathElements());
1629
1630 for (String pathElement : pathElements) {
1631 Path artifactPath = Paths.get(pathElement);
1632
1633
1634 if (Files.isDirectory(artifactPath)
1635 && !artifactPath.equals(getOutputDirectory().toPath())) {
1636 try (Stream<Path> walk = Files.walk(artifactPath)) {
1637 if (walk.anyMatch(p -> hasNewFile(p, buildStartTime))) {
1638 return true;
1639 }
1640 } catch (IOException ex) {
1641
1642 getLog().warn("I/O error walking the path: " + ex.getMessage());
1643 return false;
1644 }
1645 } else if (hasNewFile(artifactPath, buildStartTime)) {
1646 return true;
1647 }
1648 }
1649
1650
1651 return false;
1652 }
1653
1654
1655
1656
1657
1658
1659 private boolean hasNewFile(Path file, Instant buildStartTime) {
1660 if (Files.isRegularFile(file)
1661 && fileExtensions.contains(
1662 FileUtils.extension(file.getFileName().toString()))) {
1663 try {
1664 Instant lastModifiedTime = Files.getLastModifiedTime(file)
1665 .toInstant()
1666 .minusMillis(staleMillis)
1667 .truncatedTo(ChronoUnit.MILLIS);
1668 boolean hasChanged = lastModifiedTime.isAfter(buildStartTime);
1669 if (hasChanged && (getLog().isDebugEnabled() || showCompilationChanges)) {
1670 getLog().info("\tNew dependency detected: " + file.toAbsolutePath());
1671 }
1672 return hasChanged;
1673 } catch (IOException ex) {
1674
1675 getLog().warn("I/O error reading the lastModifiedTime: " + ex.getMessage());
1676 }
1677 }
1678
1679 return false;
1680 }
1681
1682 private List<String> resolveProcessorPathEntries() throws MojoExecutionException {
1683 if (annotationProcessorPaths == null || annotationProcessorPaths.isEmpty()) {
1684 return null;
1685 }
1686
1687 try {
1688 List<org.eclipse.aether.graph.Dependency> dependencies = convertToDependencies(annotationProcessorPaths);
1689 List<org.eclipse.aether.graph.Dependency> managedDependencies =
1690 getManagedDependenciesForAnnotationProcessorPaths();
1691 CollectRequest collectRequest =
1692 new CollectRequest(dependencies, managedDependencies, project.getRemoteProjectRepositories());
1693 DependencyRequest dependencyRequest = new DependencyRequest();
1694 dependencyRequest.setCollectRequest(collectRequest);
1695 DependencyResult dependencyResult =
1696 repositorySystem.resolveDependencies(session.getRepositorySession(), dependencyRequest);
1697
1698 return dependencyResult.getArtifactResults().stream()
1699 .map(resolved -> resolved.getArtifact().getFile().getAbsolutePath())
1700 .collect(Collectors.toList());
1701 } catch (Exception e) {
1702 throw new MojoExecutionException(
1703 "Resolution of annotationProcessorPath dependencies failed: " + e.getLocalizedMessage(), e);
1704 }
1705 }
1706
1707 private List<org.eclipse.aether.graph.Dependency> convertToDependencies(
1708 List<DependencyCoordinate> annotationProcessorPaths) throws MojoExecutionException {
1709 List<org.eclipse.aether.graph.Dependency> dependencies = new ArrayList<>();
1710 for (DependencyCoordinate annotationProcessorPath : annotationProcessorPaths) {
1711 ArtifactHandler handler = artifactHandlerManager.getArtifactHandler(annotationProcessorPath.getType());
1712 String version = getAnnotationProcessorPathVersion(annotationProcessorPath);
1713 Artifact artifact = new DefaultArtifact(
1714 annotationProcessorPath.getGroupId(),
1715 annotationProcessorPath.getArtifactId(),
1716 annotationProcessorPath.getClassifier(),
1717 handler.getExtension(),
1718 version);
1719 Set<Exclusion> exclusions = convertToAetherExclusions(annotationProcessorPath.getExclusions());
1720 dependencies.add(new org.eclipse.aether.graph.Dependency(artifact, JavaScopes.RUNTIME, false, exclusions));
1721 }
1722 return dependencies;
1723 }
1724
1725 private String getAnnotationProcessorPathVersion(DependencyCoordinate annotationProcessorPath)
1726 throws MojoExecutionException {
1727 String configuredVersion = annotationProcessorPath.getVersion();
1728 if (configuredVersion != null) {
1729 return configuredVersion;
1730 } else {
1731 List<Dependency> managedDependencies = getProjectManagedDependencies();
1732 return findManagedVersion(annotationProcessorPath, managedDependencies)
1733 .orElseThrow(() -> new MojoExecutionException(String.format(
1734 "Cannot find version for annotation processor path '%s'. The version needs to be either"
1735 + " provided directly in the plugin configuration or via dependency management.",
1736 annotationProcessorPath)));
1737 }
1738 }
1739
1740 private Optional<String> findManagedVersion(
1741 DependencyCoordinate dependencyCoordinate, List<Dependency> managedDependencies) {
1742 return managedDependencies.stream()
1743 .filter(dep -> Objects.equals(dep.getGroupId(), dependencyCoordinate.getGroupId())
1744 && Objects.equals(dep.getArtifactId(), dependencyCoordinate.getArtifactId())
1745 && Objects.equals(dep.getClassifier(), dependencyCoordinate.getClassifier())
1746 && Objects.equals(dep.getType(), dependencyCoordinate.getType()))
1747 .findAny()
1748 .map(org.apache.maven.model.Dependency::getVersion);
1749 }
1750
1751 private List<org.eclipse.aether.graph.Dependency> getManagedDependenciesForAnnotationProcessorPaths() {
1752 if (!annotationProcessorPathsUseDepMgmt) {
1753 return Collections.emptyList();
1754 }
1755 List<Dependency> projectManagedDependencies = getProjectManagedDependencies();
1756 ArtifactTypeRegistry artifactTypeRegistry =
1757 session.getRepositorySession().getArtifactTypeRegistry();
1758
1759 return projectManagedDependencies.stream()
1760 .map(dep -> RepositoryUtils.toDependency(dep, artifactTypeRegistry))
1761 .collect(Collectors.toList());
1762 }
1763
1764 private List<Dependency> getProjectManagedDependencies() {
1765 DependencyManagement dependencyManagement = project.getDependencyManagement();
1766 if (dependencyManagement == null || dependencyManagement.getDependencies() == null) {
1767 return Collections.emptyList();
1768 }
1769 return dependencyManagement.getDependencies();
1770 }
1771
1772 private Set<Exclusion> convertToAetherExclusions(Set<DependencyExclusion> exclusions) {
1773 if (exclusions == null || exclusions.isEmpty()) {
1774 return Collections.emptySet();
1775 }
1776 Set<Exclusion> aetherExclusions = new HashSet<>();
1777 for (DependencyExclusion exclusion : exclusions) {
1778 Exclusion aetherExclusion = new Exclusion(
1779 exclusion.getGroupId(),
1780 exclusion.getArtifactId(),
1781 exclusion.getClassifier(),
1782 exclusion.getExtension());
1783 aetherExclusions.add(aetherExclusion);
1784 }
1785 return aetherExclusions;
1786 }
1787
1788 private void writePlugin(MessageBuilder mb) {
1789 mb.a(" <plugin>").newline();
1790 mb.a(" <groupId>org.apache.maven.plugins</groupId>").newline();
1791 mb.a(" <artifactId>maven-compiler-plugin</artifactId>").newline();
1792
1793 String version = getMavenCompilerPluginVersion();
1794 if (version != null) {
1795 mb.a(" <version>").a(version).a("</version>").newline();
1796 }
1797 writeConfig(mb);
1798
1799 mb.a(" </plugin>").newline();
1800 }
1801
1802 private void writeConfig(MessageBuilder mb) {
1803 mb.a(" <configuration>").newline();
1804
1805 if (release != null) {
1806 mb.a(" <release>").a(release).a("</release>").newline();
1807 } else if (JavaVersion.JAVA_VERSION.isAtLeast("9")) {
1808 String rls = target.replaceAll(".\\.", "");
1809
1810 mb.a(" <release>").a(rls).a("</release>").newline();
1811 } else {
1812 mb.a(" <source>").a(source).a("</source>").newline();
1813 mb.a(" <target>").a(target).a("</target>").newline();
1814 }
1815 mb.a(" </configuration>").newline();
1816 }
1817
1818 private String getMavenCompilerPluginVersion() {
1819 Properties pomProperties = new Properties();
1820
1821 try (InputStream is = AbstractCompilerMojo.class.getResourceAsStream(
1822 "/META-INF/maven/org.apache.maven.plugins/maven-compiler-plugin/pom.properties")) {
1823 if (is != null) {
1824 pomProperties.load(is);
1825 }
1826 } catch (IOException e) {
1827
1828 }
1829
1830 return pomProperties.getProperty("version");
1831 }
1832
1833 private boolean hasInputFileTreeChanged(IncrementalBuildHelper ibh, Set<File> inputFiles) {
1834 Path mojoConfigBase;
1835 try {
1836 mojoConfigBase = ibh.getMojoStatusDirectory().toPath();
1837 } catch (MojoExecutionException e) {
1838
1839 getLog().warn("Error reading mojo status directory.");
1840 return false;
1841 }
1842 Path mojoConfigFile = mojoConfigBase.resolve(INPUT_FILES_LST_FILENAME);
1843
1844 Set<String> oldInputFiles = Collections.emptySet();
1845 if (Files.isRegularFile(mojoConfigFile)) {
1846 try {
1847 oldInputFiles = new HashSet<>(Files.readAllLines(mojoConfigFile));
1848 } catch (IOException e) {
1849
1850 getLog().warn("Error while reading old mojo status: " + mojoConfigFile);
1851 return false;
1852 }
1853 }
1854
1855 Set<String> newInputFiles = inputFiles.stream()
1856 .sorted()
1857 .map(File::getAbsolutePath)
1858 .collect(Collectors.toCollection(LinkedHashSet::new));
1859
1860 try {
1861 Files.write(mojoConfigFile, newInputFiles);
1862 } catch (IOException e) {
1863
1864 getLog().warn("Error while writing new mojo status: " + mojoConfigFile);
1865 return false;
1866 }
1867
1868 DeltaList<String> inputTreeChanges = new DeltaList<>(oldInputFiles, newInputFiles);
1869 if (getLog().isDebugEnabled() || showCompilationChanges) {
1870 for (String fileAdded : inputTreeChanges.getAdded()) {
1871 getLog().info("\tInput tree files (+): " + fileAdded);
1872 }
1873 for (String fileRemoved : inputTreeChanges.getRemoved()) {
1874 getLog().info("\tInput tree files (-): " + fileRemoved);
1875 }
1876 }
1877
1878 return inputTreeChanges.hasChanged();
1879 }
1880
1881 public void setTarget(String target) {
1882 this.target = target;
1883 targetOrReleaseSet = true;
1884 }
1885
1886 public void setRelease(String release) {
1887 this.release = release;
1888 targetOrReleaseSet = true;
1889 }
1890
1891 private Set<File> filterSourceFiles(Set<File> sourceFiles) {
1892 final File generatedSources = getGeneratedSourcesDirectory();
1893 if (generatedSources == null) {
1894 return sourceFiles;
1895 }
1896 final String generatedSourcesPath = generatedSources.getAbsolutePath();
1897 return sourceFiles.stream()
1898 .filter(x -> !x.getAbsolutePath().startsWith(generatedSourcesPath))
1899 .collect(Collectors.toSet());
1900 }
1901
1902 final String getImplicit() {
1903 return implicit;
1904 }
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915 private void patchJdkModuleVersion(CompilerResult compilerResult, Set<File> sources) throws MojoExecutionException {
1916 if (compilerResult.isSuccess() && getModuleDeclaration(sources).isPresent()) {
1917 Path moduleDescriptor = getOutputDirectory().toPath().resolve("module-info.class");
1918 if (Files.isRegularFile(moduleDescriptor)) {
1919 try {
1920 final byte[] descriptorOriginal = Files.readAllBytes(moduleDescriptor);
1921 final byte[] descriptorMod =
1922 ModuleInfoTransformer.transform(descriptorOriginal, getRelease(), getLog());
1923 if (descriptorMod != null) {
1924 Files.write(moduleDescriptor, descriptorMod);
1925 }
1926 } catch (IOException ex) {
1927 throw new MojoExecutionException("Error reading or writing module-info.class", ex);
1928 }
1929 }
1930 }
1931 }
1932 }