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 compilerConfiguration.setSourceFiles(staleSources);
1004
1005 try {
1006
1007 sources = getCompileSources(compiler, compilerConfiguration);
1008
1009 if (getLog().isDebugEnabled()) {
1010 getLog().debug("#sources: " + sources.size());
1011 for (File file : sources) {
1012 getLog().debug(file.getPath());
1013 }
1014 }
1015
1016 preparePaths(sources);
1017 } catch (CompilerException e) {
1018 throw new MojoExecutionException("Error while computing stale sources.", e);
1019 }
1020 }
1021
1022
1023 compilerConfiguration.setClasspathEntries(getClasspathElements());
1024
1025 compilerConfiguration.setModulepathEntries(getModulepathElements());
1026
1027 compilerConfiguration.setIncludes(getIncludes());
1028
1029 compilerConfiguration.setExcludes(getExcludes());
1030
1031 Map<String, String> effectiveCompilerArguments = getCompilerArguments();
1032
1033 String effectiveCompilerArgument = getCompilerArgument();
1034
1035 if ((effectiveCompilerArguments != null) || (effectiveCompilerArgument != null) || (compilerArgs != null)) {
1036 if (effectiveCompilerArguments != null) {
1037 for (Map.Entry<String, String> me : effectiveCompilerArguments.entrySet()) {
1038 String key = me.getKey();
1039 String value = me.getValue();
1040 if (!key.startsWith("-")) {
1041 key = "-" + key;
1042 }
1043
1044 if (key.startsWith("-A") && (value != null && !value.isEmpty())) {
1045 compilerConfiguration.addCompilerCustomArgument(key + "=" + value, null);
1046 } else {
1047 compilerConfiguration.addCompilerCustomArgument(key, value);
1048 }
1049 }
1050 }
1051 if (!(effectiveCompilerArgument == null || effectiveCompilerArgument.isEmpty())) {
1052 compilerConfiguration.addCompilerCustomArgument(effectiveCompilerArgument, null);
1053 }
1054 if (compilerArgs != null) {
1055 for (String arg : compilerArgs) {
1056 compilerConfiguration.addCompilerCustomArgument(arg, null);
1057 }
1058 }
1059 }
1060
1061
1062
1063
1064 if (getLog().isDebugEnabled()) {
1065 getLog().debug("Classpath:");
1066
1067 for (String s : getClasspathElements()) {
1068 getLog().debug(" " + s);
1069 }
1070
1071 if (!getModulepathElements().isEmpty()) {
1072 getLog().debug("Modulepath:");
1073 for (String s : getModulepathElements()) {
1074 getLog().debug(" " + s);
1075 }
1076 }
1077
1078 getLog().debug("Source roots:");
1079
1080 for (String root : getCompileSourceRoots()) {
1081 getLog().debug(" " + root);
1082 }
1083
1084 try {
1085 if (fork) {
1086 if (compilerConfiguration.getExecutable() != null) {
1087 getLog().debug("Executable: ");
1088 getLog().debug(" " + compilerConfiguration.getExecutable());
1089 }
1090 }
1091
1092 String[] cl = compiler.createCommandLine(compilerConfiguration);
1093 if (cl != null && cl.length > 0) {
1094 StringBuilder sb = new StringBuilder();
1095 sb.append(cl[0]);
1096 for (int i = 1; i < cl.length; i++) {
1097 sb.append(" ");
1098 sb.append(cl[i]);
1099 }
1100 getLog().debug("Command line options:");
1101 getLog().debug(sb);
1102 }
1103 } catch (CompilerException ce) {
1104 getLog().debug(ce);
1105 }
1106 }
1107
1108 List<String> jpmsLines = new ArrayList<>();
1109
1110
1111 final List<String> runtimeArgs = Arrays.asList(
1112 "--upgrade-module-path", "--add-exports", "--add-reads", "--add-modules", "--limit-modules");
1113
1114
1115 Iterator<Map.Entry<String, String>> entryIter =
1116 compilerConfiguration.getCustomCompilerArgumentsEntries().iterator();
1117 while (entryIter.hasNext()) {
1118 Map.Entry<String, String> entry = entryIter.next();
1119
1120 if (runtimeArgs.contains(entry.getKey())) {
1121 jpmsLines.add(entry.getKey());
1122
1123 String value = entry.getValue();
1124 if (value == null) {
1125 entry = entryIter.next();
1126 value = entry.getKey();
1127 }
1128 jpmsLines.add(value);
1129 } else if ("--patch-module".equals(entry.getKey())) {
1130 String value = entry.getValue();
1131 if (value == null) {
1132 entry = entryIter.next();
1133 value = entry.getKey();
1134 }
1135
1136 String[] values = value.split("=");
1137
1138 StringBuilder patchModule = new StringBuilder(values[0]);
1139 patchModule.append('=');
1140
1141 Set<String> patchModules = new LinkedHashSet<>();
1142 Set<Path> sourceRoots = new HashSet<>(getCompileSourceRoots().size());
1143 for (String sourceRoot : getCompileSourceRoots()) {
1144 sourceRoots.add(Paths.get(sourceRoot));
1145 }
1146
1147 String[] files = values[1].split(PS);
1148
1149 for (String file : files) {
1150 Path filePath = Paths.get(file);
1151 if (getOutputDirectory().toPath().equals(filePath)) {
1152 patchModules.add("_");
1153 } else if (getOutputDirectory().toPath().startsWith(filePath)) {
1154
1155 continue;
1156 } else if (sourceRoots.contains(filePath)) {
1157 patchModules.add("_");
1158 } else {
1159 JavaModuleDescriptor descriptor = getPathElements().get(file);
1160
1161 if (descriptor == null) {
1162 if (Files.isDirectory(filePath)) {
1163 patchModules.add(file);
1164 } else {
1165 getLog().warn("Can't locate " + file);
1166 }
1167 } else if (!values[0].equals(descriptor.name())) {
1168 patchModules.add(descriptor.name());
1169 }
1170 }
1171 }
1172
1173 StringBuilder sb = new StringBuilder();
1174
1175 if (!patchModules.isEmpty()) {
1176 for (String mod : patchModules) {
1177 if (sb.length() > 0) {
1178 sb.append(", ");
1179 }
1180
1181 sb.append(mod);
1182 }
1183
1184 jpmsLines.add("--patch-module");
1185 jpmsLines.add(patchModule + sb.toString());
1186 }
1187 }
1188 }
1189
1190 if (!jpmsLines.isEmpty()) {
1191 Path jpmsArgs = Paths.get(getOutputDirectory().getAbsolutePath(), "META-INF/jpms.args");
1192 try {
1193 Files.createDirectories(jpmsArgs.getParent());
1194
1195 Files.write(jpmsArgs, jpmsLines, Charset.defaultCharset());
1196 } catch (IOException e) {
1197 getLog().warn(e.getMessage());
1198 }
1199 }
1200
1201
1202
1203
1204
1205 if (StringUtils.isEmpty(compilerConfiguration.getSourceEncoding())) {
1206 getLog().warn("File encoding has not been set, using platform encoding "
1207 + MessageUtils.buffer().strong(Charset.defaultCharset())
1208 + ", i.e. build is platform dependent!");
1209 }
1210
1211 CompilerResult compilerResult;
1212
1213 if (useIncrementalCompilation) {
1214 incrementalBuildHelperRequest.outputDirectory(getOutputDirectory());
1215
1216
1217
1218 if (getGeneratedSourcesDirectory() != null) {
1219 try (Stream<Path> walk =
1220 Files.walk(getGeneratedSourcesDirectory().toPath())) {
1221 walk.sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete);
1222
1223 Files.createDirectories(getGeneratedSourcesDirectory().toPath());
1224 } catch (IOException ex) {
1225 getLog().warn("I/O error deleting the annotation processing generated files: " + ex.getMessage());
1226 }
1227 }
1228
1229 incrementalBuildHelper.beforeRebuildExecution(incrementalBuildHelperRequest);
1230
1231 getLog().debug("incrementalBuildHelper#beforeRebuildExecution");
1232 }
1233
1234 try {
1235 compilerResult = compiler.performCompile(compilerConfiguration);
1236 } catch (Exception e) {
1237
1238 throw new MojoExecutionException("Fatal error compiling", e);
1239 }
1240
1241 if (createMissingPackageInfoClass
1242 && compilerResult.isSuccess()
1243 && compiler.getCompilerOutputStyle() == CompilerOutputStyle.ONE_OUTPUT_FILE_PER_INPUT_FILE) {
1244 try {
1245 SourceMapping sourceMapping = getSourceMapping(compilerConfiguration, compiler);
1246 createMissingPackageInfoClasses(compilerConfiguration, sourceMapping, sources);
1247 } catch (Exception e) {
1248 getLog().warn("Error creating missing package info classes", e);
1249 }
1250 }
1251
1252 if (outputTimestamp != null
1253 && !outputTimestamp.isEmpty()
1254 && (outputTimestamp.length() > 1 || Character.isDigit(outputTimestamp.charAt(0)))) {
1255
1256 patchJdkModuleVersion(compilerResult, sources);
1257 }
1258
1259 if (useIncrementalCompilation) {
1260 if (incrementalBuildHelperRequest.getOutputDirectory().exists()) {
1261 getLog().debug("incrementalBuildHelper#afterRebuildExecution");
1262
1263 incrementalBuildHelper.afterRebuildExecution(incrementalBuildHelperRequest);
1264 } else {
1265 getLog().debug(
1266 "skip incrementalBuildHelper#afterRebuildExecution as the output directory doesn't exist");
1267 }
1268 }
1269
1270 List<CompilerMessage> warnings = new ArrayList<>();
1271 List<CompilerMessage> errors = new ArrayList<>();
1272 List<CompilerMessage> others = new ArrayList<>();
1273 for (CompilerMessage message : compilerResult.getCompilerMessages()) {
1274 switch (message.getKind()) {
1275 case ERROR:
1276 errors.add(message);
1277 break;
1278 case WARNING:
1279 case MANDATORY_WARNING:
1280 warnings.add(message);
1281 break;
1282 default:
1283 others.add(message);
1284 break;
1285 }
1286 }
1287
1288 if (failOnError && !compilerResult.isSuccess()) {
1289 for (CompilerMessage message : others) {
1290 assert message.getKind() != CompilerMessage.Kind.ERROR
1291 && message.getKind() != CompilerMessage.Kind.WARNING
1292 && message.getKind() != CompilerMessage.Kind.MANDATORY_WARNING;
1293 getLog().info(message.toString());
1294 }
1295 if (!warnings.isEmpty()) {
1296 getLog().info("-------------------------------------------------------------");
1297 getLog().warn("COMPILATION WARNING : ");
1298 getLog().info("-------------------------------------------------------------");
1299 for (CompilerMessage warning : warnings) {
1300 getLog().warn(warning.toString());
1301 }
1302 getLog().info(warnings.size() + ((warnings.size() > 1) ? " warnings " : " warning"));
1303 getLog().info("-------------------------------------------------------------");
1304 }
1305
1306 if (!errors.isEmpty()) {
1307 getLog().info("-------------------------------------------------------------");
1308 getLog().error("COMPILATION ERROR : ");
1309 getLog().info("-------------------------------------------------------------");
1310 for (CompilerMessage error : errors) {
1311 getLog().error(error.toString());
1312 }
1313 getLog().info(errors.size() + ((errors.size() > 1) ? " errors " : " error"));
1314 getLog().info("-------------------------------------------------------------");
1315 }
1316
1317 if (!errors.isEmpty()) {
1318 throw new CompilationFailureException(errors);
1319 } else {
1320 throw new CompilationFailureException(warnings);
1321 }
1322 } else {
1323 for (CompilerMessage message : compilerResult.getCompilerMessages()) {
1324 switch (message.getKind()) {
1325 case NOTE:
1326 case OTHER:
1327 getLog().info(message.toString());
1328 break;
1329 case ERROR:
1330 getLog().error(message.toString());
1331 break;
1332 case MANDATORY_WARNING:
1333 case WARNING:
1334 default:
1335 getLog().warn(message.toString());
1336 break;
1337 }
1338 }
1339 }
1340 }
1341
1342 private void createMissingPackageInfoClasses(
1343 CompilerConfiguration compilerConfiguration, SourceMapping sourceMapping, Set<File> sources)
1344 throws InclusionScanException, IOException {
1345 for (File source : sources) {
1346 String path = source.toString();
1347 if (path.endsWith(File.separator + "package-info.java")) {
1348 for (String root : getCompileSourceRoots()) {
1349 root = root + File.separator;
1350 if (path.startsWith(root)) {
1351 String rel = path.substring(root.length());
1352 Set<File> files = sourceMapping.getTargetFiles(getOutputDirectory(), rel);
1353 for (File file : files) {
1354 if (!file.exists()) {
1355 File parentFile = file.getParentFile();
1356
1357 if (!parentFile.exists()) {
1358 Files.createDirectories(parentFile.toPath());
1359 }
1360
1361 byte[] bytes = generatePackage(compilerConfiguration, rel);
1362 Files.write(file.toPath(), bytes);
1363 }
1364 }
1365 }
1366 }
1367 }
1368 }
1369 }
1370
1371 private byte[] generatePackage(CompilerConfiguration compilerConfiguration, String javaFile) {
1372 int version = getOpcode(compilerConfiguration);
1373 String internalPackageName = javaFile.substring(0, javaFile.length() - ".java".length());
1374 if (File.separatorChar != '/') {
1375 internalPackageName = internalPackageName.replace(File.separatorChar, '/');
1376 }
1377 ClassWriter cw = new ClassWriter(0);
1378 cw.visit(
1379 version,
1380 Opcodes.ACC_SYNTHETIC | Opcodes.ACC_ABSTRACT | Opcodes.ACC_INTERFACE,
1381 internalPackageName,
1382 null,
1383 "java/lang/Object",
1384 null);
1385 cw.visitSource("package-info.java", null);
1386 return cw.toByteArray();
1387 }
1388
1389 private int getOpcode(CompilerConfiguration compilerConfiguration) {
1390 String version = compilerConfiguration.getReleaseVersion();
1391 if (version == null) {
1392 version = compilerConfiguration.getTargetVersion();
1393 if (version == null) {
1394 version = "1.5";
1395 }
1396 }
1397 if (version.startsWith("1.")) {
1398 version = version.substring(2);
1399 }
1400 int iVersion = Integer.parseInt(version);
1401 if (iVersion < 2) {
1402 throw new IllegalArgumentException("Unsupported java version '" + version + "'");
1403 }
1404 return iVersion - 2 + Opcodes.V1_2;
1405 }
1406
1407 protected boolean isTestCompile() {
1408 return false;
1409 }
1410
1411
1412
1413
1414 private Set<File> getCompileSources(Compiler compiler, CompilerConfiguration compilerConfiguration)
1415 throws MojoExecutionException, CompilerException {
1416 String inputFileEnding = compiler.getInputFileEnding(compilerConfiguration);
1417 if (inputFileEnding == null || inputFileEnding.isEmpty()) {
1418
1419
1420 inputFileEnding = ".*";
1421 }
1422 SourceInclusionScanner scanner = getSourceInclusionScanner(inputFileEnding);
1423
1424 SourceMapping mapping = getSourceMapping(compilerConfiguration, compiler);
1425
1426 scanner.addSourceMapping(mapping);
1427
1428 Set<File> compileSources = new HashSet<>();
1429
1430 for (String sourceRoot : getCompileSourceRoots()) {
1431 File rootFile = new File(sourceRoot);
1432
1433 if (!rootFile.isDirectory()
1434 || rootFile.getAbsoluteFile().equals(compilerConfiguration.getGeneratedSourcesDirectory())) {
1435 continue;
1436 }
1437
1438 try {
1439 compileSources.addAll(scanner.getIncludedSources(rootFile, null));
1440 } catch (InclusionScanException e) {
1441 throw new MojoExecutionException(
1442 "Error scanning source root: '" + sourceRoot + "' for stale files to recompile.", e);
1443 }
1444 }
1445
1446 return compileSources;
1447 }
1448
1449 protected abstract Set<String> getIncludes();
1450
1451 protected abstract Set<String> getExcludes();
1452
1453
1454
1455
1456
1457
1458 private boolean isSourceChanged(CompilerConfiguration compilerConfiguration, Compiler compiler) {
1459 Set<File> staleSources = Collections.emptySet();
1460 try {
1461 staleSources = computeStaleSources(compilerConfiguration, compiler, getSourceInclusionScanner(staleMillis));
1462 } catch (MojoExecutionException | CompilerException ex) {
1463
1464 getLog().warn("Cannot detect stale sources.");
1465 return false;
1466 }
1467
1468 if (getLog().isDebugEnabled() || showCompilationChanges) {
1469 for (File f : staleSources) {
1470 getLog().info("\tStale source detected: " + f.getAbsolutePath());
1471 }
1472 }
1473 return !staleSources.isEmpty();
1474 }
1475
1476
1477
1478
1479
1480
1481 protected int getRequestThreadCount() {
1482 return session.getRequest().getDegreeOfConcurrency();
1483 }
1484
1485 protected Date getBuildStartTime() {
1486 return getBuildStartTimeInstant().map(Date::from).orElseGet(Date::new);
1487 }
1488
1489 private Optional<Instant> getBuildStartTimeInstant() {
1490 return Optional.ofNullable(session.getRequest())
1491 .map(MavenExecutionRequest::getStartTime)
1492 .map(Date::toInstant)
1493 .map(i -> i.truncatedTo(ChronoUnit.MILLIS));
1494 }
1495
1496 private String getMemoryValue(String setting) {
1497 String value = null;
1498
1499
1500 if (isDigits(setting)) {
1501 value = setting + "m";
1502 } else if ((isDigits(setting.substring(0, setting.length() - 1)))
1503 && (setting.toLowerCase().endsWith("m"))) {
1504 value = setting;
1505 }
1506 return value;
1507 }
1508
1509 protected final Toolchain getToolchain() {
1510 Toolchain tc = null;
1511
1512 if (jdkToolchain != null) {
1513 List<Toolchain> tcs = toolchainManager.getToolchains(session, "jdk", jdkToolchain);
1514 if (tcs != null && !tcs.isEmpty()) {
1515 tc = tcs.get(0);
1516 }
1517 }
1518
1519 if (tc == null) {
1520 tc = toolchainManager.getToolchainFromBuildContext("jdk", session);
1521 }
1522
1523 return tc;
1524 }
1525
1526 private boolean isDigits(String string) {
1527 for (int i = 0; i < string.length(); i++) {
1528 if (!Character.isDigit(string.charAt(i))) {
1529 return false;
1530 }
1531 }
1532 return true;
1533 }
1534
1535 private Set<File> computeStaleSources(
1536 CompilerConfiguration compilerConfiguration, Compiler compiler, SourceInclusionScanner scanner)
1537 throws MojoExecutionException, CompilerException {
1538 SourceMapping mapping = getSourceMapping(compilerConfiguration, compiler);
1539
1540 File outputDirectory;
1541 CompilerOutputStyle outputStyle = compiler.getCompilerOutputStyle();
1542 if (outputStyle == CompilerOutputStyle.ONE_OUTPUT_FILE_FOR_ALL_INPUT_FILES) {
1543 outputDirectory = buildDirectory;
1544 } else {
1545 outputDirectory = getOutputDirectory();
1546 }
1547
1548 scanner.addSourceMapping(mapping);
1549
1550 Set<File> staleSources = new HashSet<>();
1551
1552 for (String sourceRoot : getCompileSourceRoots()) {
1553 File rootFile = new File(sourceRoot);
1554
1555 if (!rootFile.isDirectory()) {
1556 continue;
1557 }
1558
1559 try {
1560 staleSources.addAll(scanner.getIncludedSources(rootFile, outputDirectory));
1561 } catch (InclusionScanException e) {
1562 throw new MojoExecutionException(
1563 "Error scanning source root: \'" + sourceRoot + "\' for stale files to recompile.", e);
1564 }
1565 }
1566
1567 return staleSources;
1568 }
1569
1570 private SourceMapping getSourceMapping(CompilerConfiguration compilerConfiguration, Compiler compiler)
1571 throws CompilerException, MojoExecutionException {
1572 CompilerOutputStyle outputStyle = compiler.getCompilerOutputStyle();
1573
1574 SourceMapping mapping;
1575 if (outputStyle == CompilerOutputStyle.ONE_OUTPUT_FILE_PER_INPUT_FILE) {
1576 mapping = new SuffixMapping(
1577 compiler.getInputFileEnding(compilerConfiguration),
1578 compiler.getOutputFileEnding(compilerConfiguration));
1579 } else if (outputStyle == CompilerOutputStyle.ONE_OUTPUT_FILE_FOR_ALL_INPUT_FILES) {
1580 mapping = new SingleTargetSourceMapping(
1581 compiler.getInputFileEnding(compilerConfiguration), compiler.getOutputFile(compilerConfiguration));
1582
1583 } else {
1584 throw new MojoExecutionException("Unknown compiler output style: '" + outputStyle + "'.");
1585 }
1586 return mapping;
1587 }
1588
1589
1590
1591
1592
1593 private static List<String> removeEmptyCompileSourceRoots(List<String> compileSourceRootsList) {
1594 List<String> newCompileSourceRootsList = new ArrayList<>();
1595 if (compileSourceRootsList != null) {
1596
1597 for (String srcDir : compileSourceRootsList) {
1598 if (!newCompileSourceRootsList.contains(srcDir) && new File(srcDir).exists()) {
1599 newCompileSourceRootsList.add(srcDir);
1600 }
1601 }
1602 }
1603 return newCompileSourceRootsList;
1604 }
1605
1606
1607
1608
1609
1610
1611
1612
1613 protected boolean isDependencyChanged() {
1614 final Instant buildStartTime = getBuildStartTimeInstant().orElse(null);
1615 if (buildStartTime == null) {
1616
1617 getLog().debug("Cannot determine build start time, skipping incremental build detection.");
1618 return false;
1619 }
1620
1621 if (fileExtensions == null || fileExtensions.isEmpty()) {
1622 fileExtensions = new HashSet<>(Arrays.asList("class", "jar"));
1623 }
1624
1625 List<String> pathElements = new ArrayList<>();
1626 pathElements.addAll(getClasspathElements());
1627 pathElements.addAll(getModulepathElements());
1628
1629 for (String pathElement : pathElements) {
1630 Path artifactPath = Paths.get(pathElement);
1631
1632
1633 if (Files.isDirectory(artifactPath)
1634 && !artifactPath.equals(getOutputDirectory().toPath())) {
1635 try (Stream<Path> walk = Files.walk(artifactPath)) {
1636 if (walk.anyMatch(p -> hasNewFile(p, buildStartTime))) {
1637 return true;
1638 }
1639 } catch (IOException ex) {
1640
1641 getLog().warn("I/O error walking the path: " + ex.getMessage());
1642 return false;
1643 }
1644 } else if (hasNewFile(artifactPath, buildStartTime)) {
1645 return true;
1646 }
1647 }
1648
1649
1650 return false;
1651 }
1652
1653
1654
1655
1656
1657
1658 private boolean hasNewFile(Path file, Instant buildStartTime) {
1659 if (Files.isRegularFile(file)
1660 && fileExtensions.contains(
1661 FileUtils.extension(file.getFileName().toString()))) {
1662 try {
1663 Instant lastModifiedTime = Files.getLastModifiedTime(file)
1664 .toInstant()
1665 .minusMillis(staleMillis)
1666 .truncatedTo(ChronoUnit.MILLIS);
1667 boolean hasChanged = lastModifiedTime.isAfter(buildStartTime);
1668 if (hasChanged && (getLog().isDebugEnabled() || showCompilationChanges)) {
1669 getLog().info("\tNew dependency detected: " + file.toAbsolutePath());
1670 }
1671 return hasChanged;
1672 } catch (IOException ex) {
1673
1674 getLog().warn("I/O error reading the lastModifiedTime: " + ex.getMessage());
1675 }
1676 }
1677
1678 return false;
1679 }
1680
1681 private List<String> resolveProcessorPathEntries() throws MojoExecutionException {
1682 if (annotationProcessorPaths == null || annotationProcessorPaths.isEmpty()) {
1683 return null;
1684 }
1685
1686 try {
1687 List<org.eclipse.aether.graph.Dependency> dependencies = convertToDependencies(annotationProcessorPaths);
1688 List<org.eclipse.aether.graph.Dependency> managedDependencies =
1689 getManagedDependenciesForAnnotationProcessorPaths();
1690 CollectRequest collectRequest =
1691 new CollectRequest(dependencies, managedDependencies, project.getRemoteProjectRepositories());
1692 DependencyRequest dependencyRequest = new DependencyRequest();
1693 dependencyRequest.setCollectRequest(collectRequest);
1694 DependencyResult dependencyResult =
1695 repositorySystem.resolveDependencies(session.getRepositorySession(), dependencyRequest);
1696
1697 return dependencyResult.getArtifactResults().stream()
1698 .map(resolved -> resolved.getArtifact().getFile().getAbsolutePath())
1699 .collect(Collectors.toList());
1700 } catch (Exception e) {
1701 throw new MojoExecutionException(
1702 "Resolution of annotationProcessorPath dependencies failed: " + e.getLocalizedMessage(), e);
1703 }
1704 }
1705
1706 private List<org.eclipse.aether.graph.Dependency> convertToDependencies(
1707 List<DependencyCoordinate> annotationProcessorPaths) throws MojoExecutionException {
1708 List<org.eclipse.aether.graph.Dependency> dependencies = new ArrayList<>();
1709 for (DependencyCoordinate annotationProcessorPath : annotationProcessorPaths) {
1710 ArtifactHandler handler = artifactHandlerManager.getArtifactHandler(annotationProcessorPath.getType());
1711 String version = getAnnotationProcessorPathVersion(annotationProcessorPath);
1712 Artifact artifact = new DefaultArtifact(
1713 annotationProcessorPath.getGroupId(),
1714 annotationProcessorPath.getArtifactId(),
1715 annotationProcessorPath.getClassifier(),
1716 handler.getExtension(),
1717 version);
1718 Set<Exclusion> exclusions = convertToAetherExclusions(annotationProcessorPath.getExclusions());
1719 dependencies.add(new org.eclipse.aether.graph.Dependency(artifact, JavaScopes.RUNTIME, false, exclusions));
1720 }
1721 return dependencies;
1722 }
1723
1724 private String getAnnotationProcessorPathVersion(DependencyCoordinate annotationProcessorPath)
1725 throws MojoExecutionException {
1726 String configuredVersion = annotationProcessorPath.getVersion();
1727 if (configuredVersion != null) {
1728 return configuredVersion;
1729 } else {
1730 List<Dependency> managedDependencies = getProjectManagedDependencies();
1731 return findManagedVersion(annotationProcessorPath, managedDependencies)
1732 .orElseThrow(() -> new MojoExecutionException(String.format(
1733 "Cannot find version for annotation processor path '%s'. The version needs to be either"
1734 + " provided directly in the plugin configuration or via dependency management.",
1735 annotationProcessorPath)));
1736 }
1737 }
1738
1739 private Optional<String> findManagedVersion(
1740 DependencyCoordinate dependencyCoordinate, List<Dependency> managedDependencies) {
1741 return managedDependencies.stream()
1742 .filter(dep -> Objects.equals(dep.getGroupId(), dependencyCoordinate.getGroupId())
1743 && Objects.equals(dep.getArtifactId(), dependencyCoordinate.getArtifactId())
1744 && Objects.equals(dep.getClassifier(), dependencyCoordinate.getClassifier())
1745 && Objects.equals(dep.getType(), dependencyCoordinate.getType()))
1746 .findAny()
1747 .map(org.apache.maven.model.Dependency::getVersion);
1748 }
1749
1750 private List<org.eclipse.aether.graph.Dependency> getManagedDependenciesForAnnotationProcessorPaths() {
1751 if (!annotationProcessorPathsUseDepMgmt) {
1752 return Collections.emptyList();
1753 }
1754 List<Dependency> projectManagedDependencies = getProjectManagedDependencies();
1755 ArtifactTypeRegistry artifactTypeRegistry =
1756 session.getRepositorySession().getArtifactTypeRegistry();
1757
1758 return projectManagedDependencies.stream()
1759 .map(dep -> RepositoryUtils.toDependency(dep, artifactTypeRegistry))
1760 .collect(Collectors.toList());
1761 }
1762
1763 private List<Dependency> getProjectManagedDependencies() {
1764 DependencyManagement dependencyManagement = project.getDependencyManagement();
1765 if (dependencyManagement == null || dependencyManagement.getDependencies() == null) {
1766 return Collections.emptyList();
1767 }
1768 return dependencyManagement.getDependencies();
1769 }
1770
1771 private Set<Exclusion> convertToAetherExclusions(Set<DependencyExclusion> exclusions) {
1772 if (exclusions == null || exclusions.isEmpty()) {
1773 return Collections.emptySet();
1774 }
1775 Set<Exclusion> aetherExclusions = new HashSet<>();
1776 for (DependencyExclusion exclusion : exclusions) {
1777 Exclusion aetherExclusion = new Exclusion(
1778 exclusion.getGroupId(),
1779 exclusion.getArtifactId(),
1780 exclusion.getClassifier(),
1781 exclusion.getExtension());
1782 aetherExclusions.add(aetherExclusion);
1783 }
1784 return aetherExclusions;
1785 }
1786
1787 private void writePlugin(MessageBuilder mb) {
1788 mb.a(" <plugin>").newline();
1789 mb.a(" <groupId>org.apache.maven.plugins</groupId>").newline();
1790 mb.a(" <artifactId>maven-compiler-plugin</artifactId>").newline();
1791
1792 String version = getMavenCompilerPluginVersion();
1793 if (version != null) {
1794 mb.a(" <version>").a(version).a("</version>").newline();
1795 }
1796 writeConfig(mb);
1797
1798 mb.a(" </plugin>").newline();
1799 }
1800
1801 private void writeConfig(MessageBuilder mb) {
1802 mb.a(" <configuration>").newline();
1803
1804 if (release != null) {
1805 mb.a(" <release>").a(release).a("</release>").newline();
1806 } else if (JavaVersion.JAVA_VERSION.isAtLeast("9")) {
1807 String rls = target.replaceAll(".\\.", "");
1808
1809 mb.a(" <release>").a(rls).a("</release>").newline();
1810 } else {
1811 mb.a(" <source>").a(source).a("</source>").newline();
1812 mb.a(" <target>").a(target).a("</target>").newline();
1813 }
1814 mb.a(" </configuration>").newline();
1815 }
1816
1817 private String getMavenCompilerPluginVersion() {
1818 Properties pomProperties = new Properties();
1819
1820 try (InputStream is = AbstractCompilerMojo.class.getResourceAsStream(
1821 "/META-INF/maven/org.apache.maven.plugins/maven-compiler-plugin/pom.properties")) {
1822 if (is != null) {
1823 pomProperties.load(is);
1824 }
1825 } catch (IOException e) {
1826
1827 }
1828
1829 return pomProperties.getProperty("version");
1830 }
1831
1832 private boolean hasInputFileTreeChanged(IncrementalBuildHelper ibh, Set<File> inputFiles) {
1833 Path mojoConfigBase;
1834 try {
1835 mojoConfigBase = ibh.getMojoStatusDirectory().toPath();
1836 } catch (MojoExecutionException e) {
1837
1838 getLog().warn("Error reading mojo status directory.");
1839 return false;
1840 }
1841 Path mojoConfigFile = mojoConfigBase.resolve(INPUT_FILES_LST_FILENAME);
1842
1843 Set<String> oldInputFiles = Collections.emptySet();
1844 if (Files.isRegularFile(mojoConfigFile)) {
1845 try {
1846 oldInputFiles = new HashSet<>(Files.readAllLines(mojoConfigFile));
1847 } catch (IOException e) {
1848
1849 getLog().warn("Error while reading old mojo status: " + mojoConfigFile);
1850 return false;
1851 }
1852 }
1853
1854 Set<String> newInputFiles = inputFiles.stream()
1855 .sorted()
1856 .map(File::getAbsolutePath)
1857 .collect(Collectors.toCollection(LinkedHashSet::new));
1858
1859 try {
1860 Files.write(mojoConfigFile, newInputFiles);
1861 } catch (IOException e) {
1862
1863 getLog().warn("Error while writing new mojo status: " + mojoConfigFile);
1864 return false;
1865 }
1866
1867 DeltaList<String> inputTreeChanges = new DeltaList<>(oldInputFiles, newInputFiles);
1868 if (getLog().isDebugEnabled() || showCompilationChanges) {
1869 for (String fileAdded : inputTreeChanges.getAdded()) {
1870 getLog().info("\tInput tree files (+): " + fileAdded);
1871 }
1872 for (String fileRemoved : inputTreeChanges.getRemoved()) {
1873 getLog().info("\tInput tree files (-): " + fileRemoved);
1874 }
1875 }
1876
1877 return inputTreeChanges.hasChanged();
1878 }
1879
1880 public void setTarget(String target) {
1881 this.target = target;
1882 targetOrReleaseSet = true;
1883 }
1884
1885 public void setRelease(String release) {
1886 this.release = release;
1887 targetOrReleaseSet = true;
1888 }
1889
1890 final String getImplicit() {
1891 return implicit;
1892 }
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903 private void patchJdkModuleVersion(CompilerResult compilerResult, Set<File> sources) throws MojoExecutionException {
1904 if (compilerResult.isSuccess() && getModuleDeclaration(sources).isPresent()) {
1905 Path moduleDescriptor = getOutputDirectory().toPath().resolve("module-info.class");
1906 if (Files.isRegularFile(moduleDescriptor)) {
1907 try {
1908 final byte[] descriptorOriginal = Files.readAllBytes(moduleDescriptor);
1909 final byte[] descriptorMod =
1910 ModuleInfoTransformer.transform(descriptorOriginal, getRelease(), getLog());
1911 if (descriptorMod != null) {
1912 Files.write(moduleDescriptor, descriptorMod);
1913 }
1914 } catch (IOException ex) {
1915 throw new MojoExecutionException("Error reading or writing module-info.class", ex);
1916 }
1917 }
1918 }
1919 }
1920 }