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