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 } catch (IOException ex) {
1176 getLog().warn("I/O error deleting the annotation processing generated files: " + ex.getMessage());
1177 }
1178 }
1179
1180 incrementalBuildHelper.beforeRebuildExecution(incrementalBuildHelperRequest);
1181
1182 getLog().debug("incrementalBuildHelper#beforeRebuildExecution");
1183 }
1184
1185 try {
1186 compilerResult = compiler.performCompile(compilerConfiguration);
1187 } catch (Exception e) {
1188
1189 throw new MojoExecutionException("Fatal error compiling", e);
1190 }
1191
1192 if (createMissingPackageInfoClass
1193 && compilerResult.isSuccess()
1194 && compiler.getCompilerOutputStyle() == CompilerOutputStyle.ONE_OUTPUT_FILE_PER_INPUT_FILE) {
1195 try {
1196 SourceMapping sourceMapping = getSourceMapping(compilerConfiguration, compiler);
1197 createMissingPackageInfoClasses(compilerConfiguration, sourceMapping, sources);
1198 } catch (Exception e) {
1199 getLog().warn("Error creating missing package info classes", e);
1200 }
1201 }
1202
1203 if (outputTimestamp != null && (outputTimestamp.length() > 1 || Character.isDigit(outputTimestamp.charAt(0)))) {
1204
1205 patchJdkModuleVersion(compilerResult, sources);
1206 }
1207
1208 if (useIncrementalCompilation) {
1209 if (incrementalBuildHelperRequest.getOutputDirectory().exists()) {
1210 getLog().debug("incrementalBuildHelper#afterRebuildExecution");
1211
1212 incrementalBuildHelper.afterRebuildExecution(incrementalBuildHelperRequest);
1213 } else {
1214 getLog().debug(
1215 "skip incrementalBuildHelper#afterRebuildExecution as the output directory doesn't exist");
1216 }
1217 }
1218
1219 List<CompilerMessage> warnings = new ArrayList<>();
1220 List<CompilerMessage> errors = new ArrayList<>();
1221 List<CompilerMessage> others = new ArrayList<>();
1222 for (CompilerMessage message : compilerResult.getCompilerMessages()) {
1223 switch (message.getKind()) {
1224 case ERROR:
1225 errors.add(message);
1226 break;
1227 case WARNING:
1228 case MANDATORY_WARNING:
1229 warnings.add(message);
1230 break;
1231 default:
1232 others.add(message);
1233 break;
1234 }
1235 }
1236
1237 if (failOnError && !compilerResult.isSuccess()) {
1238 for (CompilerMessage message : others) {
1239 assert message.getKind() != CompilerMessage.Kind.ERROR
1240 && message.getKind() != CompilerMessage.Kind.WARNING
1241 && message.getKind() != CompilerMessage.Kind.MANDATORY_WARNING;
1242 getLog().info(message.toString());
1243 }
1244 if (!warnings.isEmpty()) {
1245 getLog().info("-------------------------------------------------------------");
1246 getLog().warn("COMPILATION WARNING : ");
1247 getLog().info("-------------------------------------------------------------");
1248 for (CompilerMessage warning : warnings) {
1249 getLog().warn(warning.toString());
1250 }
1251 getLog().info(warnings.size() + ((warnings.size() > 1) ? " warnings " : " warning"));
1252 getLog().info("-------------------------------------------------------------");
1253 }
1254
1255 if (!errors.isEmpty()) {
1256 getLog().info("-------------------------------------------------------------");
1257 getLog().error("COMPILATION ERROR : ");
1258 getLog().info("-------------------------------------------------------------");
1259 for (CompilerMessage error : errors) {
1260 getLog().error(error.toString());
1261 }
1262 getLog().info(errors.size() + ((errors.size() > 1) ? " errors " : " error"));
1263 getLog().info("-------------------------------------------------------------");
1264 }
1265
1266 if (!errors.isEmpty()) {
1267 throw new CompilationFailureException(errors);
1268 } else {
1269 throw new CompilationFailureException(warnings);
1270 }
1271 } else {
1272 for (CompilerMessage message : compilerResult.getCompilerMessages()) {
1273 switch (message.getKind()) {
1274 case NOTE:
1275 case OTHER:
1276 getLog().info(message.toString());
1277 break;
1278 case ERROR:
1279 getLog().error(message.toString());
1280 break;
1281 case MANDATORY_WARNING:
1282 case WARNING:
1283 default:
1284 getLog().warn(message.toString());
1285 break;
1286 }
1287 }
1288 }
1289 }
1290
1291 private void createMissingPackageInfoClasses(
1292 CompilerConfiguration compilerConfiguration, SourceMapping sourceMapping, Set<File> sources)
1293 throws InclusionScanException, IOException {
1294 for (File source : sources) {
1295 String path = source.toString();
1296 if (path.endsWith(File.separator + "package-info.java")) {
1297 for (String root : getCompileSourceRoots()) {
1298 root = root + File.separator;
1299 if (path.startsWith(root)) {
1300 String rel = path.substring(root.length());
1301 Set<File> files = sourceMapping.getTargetFiles(getOutputDirectory(), rel);
1302 for (File file : files) {
1303 if (!file.exists()) {
1304 File parentFile = file.getParentFile();
1305
1306 if (!parentFile.exists()) {
1307 Files.createDirectories(parentFile.toPath());
1308 }
1309
1310 byte[] bytes = generatePackage(compilerConfiguration, rel);
1311 Files.write(file.toPath(), bytes);
1312 }
1313 }
1314 }
1315 }
1316 }
1317 }
1318 }
1319
1320 private byte[] generatePackage(CompilerConfiguration compilerConfiguration, String javaFile) {
1321 int version = getOpcode(compilerConfiguration);
1322 String internalPackageName = javaFile.substring(0, javaFile.length() - ".java".length());
1323 if (File.separatorChar != '/') {
1324 internalPackageName = internalPackageName.replace(File.separatorChar, '/');
1325 }
1326 ClassWriter cw = new ClassWriter(0);
1327 cw.visit(
1328 version,
1329 Opcodes.ACC_SYNTHETIC | Opcodes.ACC_ABSTRACT | Opcodes.ACC_INTERFACE,
1330 internalPackageName,
1331 null,
1332 "java/lang/Object",
1333 null);
1334 cw.visitSource("package-info.java", null);
1335 return cw.toByteArray();
1336 }
1337
1338 private int getOpcode(CompilerConfiguration compilerConfiguration) {
1339 String version = compilerConfiguration.getReleaseVersion();
1340 if (version == null) {
1341 version = compilerConfiguration.getTargetVersion();
1342 if (version == null) {
1343 version = "1.5";
1344 }
1345 }
1346 if (version.startsWith("1.")) {
1347 version = version.substring(2);
1348 }
1349 int iVersion = Integer.parseInt(version);
1350 if (iVersion < 2) {
1351 throw new IllegalArgumentException("Unsupported java version '" + version + "'");
1352 }
1353 return iVersion - 2 + Opcodes.V1_2;
1354 }
1355
1356 protected boolean isTestCompile() {
1357 return false;
1358 }
1359
1360
1361
1362
1363 private Set<File> getCompileSources(Compiler compiler, CompilerConfiguration compilerConfiguration)
1364 throws MojoExecutionException, CompilerException {
1365 String inputFileEnding = compiler.getInputFileEnding(compilerConfiguration);
1366 if (inputFileEnding == null || inputFileEnding.isEmpty()) {
1367
1368
1369 inputFileEnding = ".*";
1370 }
1371 SourceInclusionScanner scanner = getSourceInclusionScanner(inputFileEnding);
1372
1373 SourceMapping mapping = getSourceMapping(compilerConfiguration, compiler);
1374
1375 scanner.addSourceMapping(mapping);
1376
1377 Set<File> compileSources = new HashSet<>();
1378
1379 for (String sourceRoot : getCompileSourceRoots()) {
1380 File rootFile = new File(sourceRoot);
1381
1382 if (!rootFile.isDirectory()
1383 || rootFile.getAbsoluteFile().equals(compilerConfiguration.getGeneratedSourcesDirectory())) {
1384 continue;
1385 }
1386
1387 try {
1388 compileSources.addAll(scanner.getIncludedSources(rootFile, null));
1389 } catch (InclusionScanException e) {
1390 throw new MojoExecutionException(
1391 "Error scanning source root: '" + sourceRoot + "' for stale files to recompile.", e);
1392 }
1393 }
1394
1395 return compileSources;
1396 }
1397
1398 protected abstract Set<String> getIncludes();
1399
1400 protected abstract Set<String> getExcludes();
1401
1402
1403
1404
1405
1406
1407 private boolean isSourceChanged(CompilerConfiguration compilerConfiguration, Compiler compiler) {
1408 Set<File> staleSources = Collections.emptySet();
1409 try {
1410 staleSources = computeStaleSources(compilerConfiguration, compiler, getSourceInclusionScanner(staleMillis));
1411 } catch (MojoExecutionException | CompilerException ex) {
1412
1413 getLog().warn("Cannot detect stale sources.");
1414 return false;
1415 }
1416
1417 if (getLog().isDebugEnabled() || showCompilationChanges) {
1418 for (File f : staleSources) {
1419 getLog().info("\tStale source detected: " + f.getAbsolutePath());
1420 }
1421 }
1422 return !staleSources.isEmpty();
1423 }
1424
1425
1426
1427
1428
1429
1430 protected int getRequestThreadCount() {
1431 return session.getRequest().getDegreeOfConcurrency();
1432 }
1433
1434 protected Date getBuildStartTime() {
1435 return getBuildStartTimeInstant().map(Date::from).orElseGet(Date::new);
1436 }
1437
1438 private Optional<Instant> getBuildStartTimeInstant() {
1439 return Optional.ofNullable(session.getRequest())
1440 .map(MavenExecutionRequest::getStartTime)
1441 .map(Date::toInstant)
1442 .map(i -> i.truncatedTo(ChronoUnit.MILLIS));
1443 }
1444
1445 private String getMemoryValue(String setting) {
1446 String value = null;
1447
1448
1449 if (isDigits(setting)) {
1450 value = setting + "m";
1451 } else if ((isDigits(setting.substring(0, setting.length() - 1)))
1452 && (setting.toLowerCase().endsWith("m"))) {
1453 value = setting;
1454 }
1455 return value;
1456 }
1457
1458
1459
1460 protected final Toolchain getToolchain() {
1461 Toolchain tc = null;
1462
1463 if (jdkToolchain != null) {
1464
1465 try {
1466 Method getToolchainsMethod = toolchainManager
1467 .getClass()
1468 .getMethod("getToolchains", MavenSession.class, String.class, Map.class);
1469
1470 @SuppressWarnings("unchecked")
1471 List<Toolchain> tcs =
1472 (List<Toolchain>) getToolchainsMethod.invoke(toolchainManager, session, "jdk", jdkToolchain);
1473
1474 if (tcs != null && !tcs.isEmpty()) {
1475 tc = tcs.get(0);
1476 }
1477 } catch (NoSuchMethodException
1478 | SecurityException
1479 | IllegalAccessException
1480 | IllegalArgumentException
1481 | InvocationTargetException e) {
1482
1483 }
1484 }
1485
1486 if (tc == null) {
1487 tc = toolchainManager.getToolchainFromBuildContext("jdk", session);
1488 }
1489
1490 return tc;
1491 }
1492
1493 private boolean isDigits(String string) {
1494 for (int i = 0; i < string.length(); i++) {
1495 if (!Character.isDigit(string.charAt(i))) {
1496 return false;
1497 }
1498 }
1499 return true;
1500 }
1501
1502 private Set<File> computeStaleSources(
1503 CompilerConfiguration compilerConfiguration, Compiler compiler, SourceInclusionScanner scanner)
1504 throws MojoExecutionException, CompilerException {
1505 SourceMapping mapping = getSourceMapping(compilerConfiguration, compiler);
1506
1507 File outputDirectory;
1508 CompilerOutputStyle outputStyle = compiler.getCompilerOutputStyle();
1509 if (outputStyle == CompilerOutputStyle.ONE_OUTPUT_FILE_FOR_ALL_INPUT_FILES) {
1510 outputDirectory = buildDirectory;
1511 } else {
1512 outputDirectory = getOutputDirectory();
1513 }
1514
1515 scanner.addSourceMapping(mapping);
1516
1517 Set<File> staleSources = new HashSet<>();
1518
1519 for (String sourceRoot : getCompileSourceRoots()) {
1520 File rootFile = new File(sourceRoot);
1521
1522 if (!rootFile.isDirectory()) {
1523 continue;
1524 }
1525
1526 try {
1527 staleSources.addAll(scanner.getIncludedSources(rootFile, outputDirectory));
1528 } catch (InclusionScanException e) {
1529 throw new MojoExecutionException(
1530 "Error scanning source root: \'" + sourceRoot + "\' for stale files to recompile.", e);
1531 }
1532 }
1533
1534 return staleSources;
1535 }
1536
1537 private SourceMapping getSourceMapping(CompilerConfiguration compilerConfiguration, Compiler compiler)
1538 throws CompilerException, MojoExecutionException {
1539 CompilerOutputStyle outputStyle = compiler.getCompilerOutputStyle();
1540
1541 SourceMapping mapping;
1542 if (outputStyle == CompilerOutputStyle.ONE_OUTPUT_FILE_PER_INPUT_FILE) {
1543 mapping = new SuffixMapping(
1544 compiler.getInputFileEnding(compilerConfiguration),
1545 compiler.getOutputFileEnding(compilerConfiguration));
1546 } else if (outputStyle == CompilerOutputStyle.ONE_OUTPUT_FILE_FOR_ALL_INPUT_FILES) {
1547 mapping = new SingleTargetSourceMapping(
1548 compiler.getInputFileEnding(compilerConfiguration), compiler.getOutputFile(compilerConfiguration));
1549
1550 } else {
1551 throw new MojoExecutionException("Unknown compiler output style: '" + outputStyle + "'.");
1552 }
1553 return mapping;
1554 }
1555
1556
1557
1558
1559
1560 private static List<String> removeEmptyCompileSourceRoots(List<String> compileSourceRootsList) {
1561 List<String> newCompileSourceRootsList = new ArrayList<>();
1562 if (compileSourceRootsList != null) {
1563
1564 for (String srcDir : compileSourceRootsList) {
1565 if (!newCompileSourceRootsList.contains(srcDir) && new File(srcDir).exists()) {
1566 newCompileSourceRootsList.add(srcDir);
1567 }
1568 }
1569 }
1570 return newCompileSourceRootsList;
1571 }
1572
1573
1574
1575
1576
1577
1578
1579
1580 protected boolean isDependencyChanged() {
1581 final Instant buildStartTime = getBuildStartTimeInstant().orElse(null);
1582 if (buildStartTime == null) {
1583
1584 getLog().debug("Cannot determine build start time, skipping incremental build detection.");
1585 return false;
1586 }
1587
1588 if (fileExtensions == null || fileExtensions.isEmpty()) {
1589 fileExtensions = new HashSet<>(Arrays.asList("class", "jar"));
1590 }
1591
1592 List<String> pathElements = new ArrayList<>();
1593 pathElements.addAll(getClasspathElements());
1594 pathElements.addAll(getModulepathElements());
1595
1596 for (String pathElement : pathElements) {
1597 Path artifactPath = Paths.get(pathElement);
1598
1599
1600 if (Files.isDirectory(artifactPath)
1601 && !artifactPath.equals(getOutputDirectory().toPath())) {
1602 try (Stream<Path> walk = Files.walk(artifactPath)) {
1603 if (walk.anyMatch(p -> hasNewFile(p, buildStartTime))) {
1604 return true;
1605 }
1606 } catch (IOException ex) {
1607
1608 getLog().warn("I/O error walking the path: " + ex.getMessage());
1609 return false;
1610 }
1611 } else if (hasNewFile(artifactPath, buildStartTime)) {
1612 return true;
1613 }
1614 }
1615
1616
1617 return false;
1618 }
1619
1620
1621
1622
1623
1624
1625 private boolean hasNewFile(Path file, Instant buildStartTime) {
1626 if (Files.isRegularFile(file)
1627 && fileExtensions.contains(
1628 FileUtils.extension(file.getFileName().toString()))) {
1629 try {
1630 Instant lastModifiedTime = Files.getLastModifiedTime(file)
1631 .toInstant()
1632 .minusMillis(staleMillis)
1633 .truncatedTo(ChronoUnit.MILLIS);
1634 boolean hasChanged = lastModifiedTime.isAfter(buildStartTime);
1635 if (hasChanged && (getLog().isDebugEnabled() || showCompilationChanges)) {
1636 getLog().info("\tNew dependency detected: " + file.toAbsolutePath());
1637 }
1638 return hasChanged;
1639 } catch (IOException ex) {
1640
1641 getLog().warn("I/O error reading the lastModifiedTime: " + ex.getMessage());
1642 }
1643 }
1644
1645 return false;
1646 }
1647
1648 private List<String> resolveProcessorPathEntries() throws MojoExecutionException {
1649 if (annotationProcessorPaths == null || annotationProcessorPaths.isEmpty()) {
1650 return null;
1651 }
1652
1653 try {
1654 List<org.eclipse.aether.graph.Dependency> dependencies = convertToDependencies(annotationProcessorPaths);
1655 List<org.eclipse.aether.graph.Dependency> managedDependencies =
1656 getManagedDependenciesForAnnotationProcessorPaths();
1657 CollectRequest collectRequest =
1658 new CollectRequest(dependencies, managedDependencies, project.getRemoteProjectRepositories());
1659 DependencyRequest dependencyRequest = new DependencyRequest();
1660 dependencyRequest.setCollectRequest(collectRequest);
1661 DependencyResult dependencyResult =
1662 repositorySystem.resolveDependencies(session.getRepositorySession(), dependencyRequest);
1663
1664 return dependencyResult.getArtifactResults().stream()
1665 .map(resolved -> resolved.getArtifact().getFile().getAbsolutePath())
1666 .collect(Collectors.toList());
1667 } catch (Exception e) {
1668 throw new MojoExecutionException(
1669 "Resolution of annotationProcessorPath dependencies failed: " + e.getLocalizedMessage(), e);
1670 }
1671 }
1672
1673 private List<org.eclipse.aether.graph.Dependency> convertToDependencies(
1674 List<DependencyCoordinate> annotationProcessorPaths) throws MojoExecutionException {
1675 List<org.eclipse.aether.graph.Dependency> dependencies = new ArrayList<>();
1676 for (DependencyCoordinate annotationProcessorPath : annotationProcessorPaths) {
1677 ArtifactHandler handler = artifactHandlerManager.getArtifactHandler(annotationProcessorPath.getType());
1678 String version = getAnnotationProcessorPathVersion(annotationProcessorPath);
1679 Artifact artifact = new DefaultArtifact(
1680 annotationProcessorPath.getGroupId(),
1681 annotationProcessorPath.getArtifactId(),
1682 annotationProcessorPath.getClassifier(),
1683 handler.getExtension(),
1684 version);
1685 Set<Exclusion> exclusions = convertToAetherExclusions(annotationProcessorPath.getExclusions());
1686 dependencies.add(new org.eclipse.aether.graph.Dependency(artifact, JavaScopes.RUNTIME, false, exclusions));
1687 }
1688 return dependencies;
1689 }
1690
1691 private String getAnnotationProcessorPathVersion(DependencyCoordinate annotationProcessorPath)
1692 throws MojoExecutionException {
1693 String configuredVersion = annotationProcessorPath.getVersion();
1694 if (configuredVersion != null) {
1695 return configuredVersion;
1696 } else {
1697 List<Dependency> managedDependencies = getProjectManagedDependencies();
1698 return findManagedVersion(annotationProcessorPath, managedDependencies)
1699 .orElseThrow(() -> new MojoExecutionException(String.format(
1700 "Cannot find version for annotation processor path '%s'. The version needs to be either"
1701 + " provided directly in the plugin configuration or via dependency management.",
1702 annotationProcessorPath)));
1703 }
1704 }
1705
1706 private Optional<String> findManagedVersion(
1707 DependencyCoordinate dependencyCoordinate, List<Dependency> managedDependencies) {
1708 return managedDependencies.stream()
1709 .filter(dep -> Objects.equals(dep.getGroupId(), dependencyCoordinate.getGroupId())
1710 && Objects.equals(dep.getArtifactId(), dependencyCoordinate.getArtifactId())
1711 && Objects.equals(dep.getClassifier(), dependencyCoordinate.getClassifier())
1712 && Objects.equals(dep.getType(), dependencyCoordinate.getType()))
1713 .findAny()
1714 .map(org.apache.maven.model.Dependency::getVersion);
1715 }
1716
1717 private List<org.eclipse.aether.graph.Dependency> getManagedDependenciesForAnnotationProcessorPaths() {
1718 if (!annotationProcessorPathsUseDepMgmt) {
1719 return Collections.emptyList();
1720 }
1721 List<Dependency> projectManagedDependencies = getProjectManagedDependencies();
1722 ArtifactTypeRegistry artifactTypeRegistry =
1723 session.getRepositorySession().getArtifactTypeRegistry();
1724
1725 return projectManagedDependencies.stream()
1726 .map(dep -> RepositoryUtils.toDependency(dep, artifactTypeRegistry))
1727 .collect(Collectors.toList());
1728 }
1729
1730 private List<Dependency> getProjectManagedDependencies() {
1731 DependencyManagement dependencyManagement = project.getDependencyManagement();
1732 if (dependencyManagement == null || dependencyManagement.getDependencies() == null) {
1733 return Collections.emptyList();
1734 }
1735 return dependencyManagement.getDependencies();
1736 }
1737
1738 private Set<Exclusion> convertToAetherExclusions(Set<DependencyExclusion> exclusions) {
1739 if (exclusions == null || exclusions.isEmpty()) {
1740 return Collections.emptySet();
1741 }
1742 Set<Exclusion> aetherExclusions = new HashSet<>();
1743 for (DependencyExclusion exclusion : exclusions) {
1744 Exclusion aetherExclusion = new Exclusion(
1745 exclusion.getGroupId(),
1746 exclusion.getArtifactId(),
1747 exclusion.getClassifier(),
1748 exclusion.getExtension());
1749 aetherExclusions.add(aetherExclusion);
1750 }
1751 return aetherExclusions;
1752 }
1753
1754 private void writePlugin(MessageBuilder mb) {
1755 mb.a(" <plugin>").newline();
1756 mb.a(" <groupId>org.apache.maven.plugins</groupId>").newline();
1757 mb.a(" <artifactId>maven-compiler-plugin</artifactId>").newline();
1758
1759 String version = getMavenCompilerPluginVersion();
1760 if (version != null) {
1761 mb.a(" <version>").a(version).a("</version>").newline();
1762 }
1763 writeConfig(mb);
1764
1765 mb.a(" </plugin>").newline();
1766 }
1767
1768 private void writeConfig(MessageBuilder mb) {
1769 mb.a(" <configuration>").newline();
1770
1771 if (release != null) {
1772 mb.a(" <release>").a(release).a("</release>").newline();
1773 } else if (JavaVersion.JAVA_VERSION.isAtLeast("9")) {
1774 String rls = target.replaceAll(".\\.", "");
1775
1776 mb.a(" <release>").a(rls).a("</release>").newline();
1777 } else {
1778 mb.a(" <source>").a(source).a("</source>").newline();
1779 mb.a(" <target>").a(target).a("</target>").newline();
1780 }
1781 mb.a(" </configuration>").newline();
1782 }
1783
1784 private String getMavenCompilerPluginVersion() {
1785 Properties pomProperties = new Properties();
1786
1787 try (InputStream is = AbstractCompilerMojo.class.getResourceAsStream(
1788 "/META-INF/maven/org.apache.maven.plugins/maven-compiler-plugin/pom.properties")) {
1789 if (is != null) {
1790 pomProperties.load(is);
1791 }
1792 } catch (IOException e) {
1793
1794 }
1795
1796 return pomProperties.getProperty("version");
1797 }
1798
1799 private boolean hasInputFileTreeChanged(IncrementalBuildHelper ibh, Set<File> inputFiles) {
1800 Path mojoConfigBase;
1801 try {
1802 mojoConfigBase = ibh.getMojoStatusDirectory().toPath();
1803 } catch (MojoExecutionException e) {
1804
1805 getLog().warn("Error reading mojo status directory.");
1806 return false;
1807 }
1808 Path mojoConfigFile = mojoConfigBase.resolve(INPUT_FILES_LST_FILENAME);
1809
1810 List<String> oldInputFiles = Collections.emptyList();
1811 if (Files.isRegularFile(mojoConfigFile)) {
1812 try {
1813 oldInputFiles = Files.readAllLines(mojoConfigFile);
1814 } catch (IOException e) {
1815
1816 getLog().warn("Error while reading old mojo status: " + mojoConfigFile);
1817 return false;
1818 }
1819 }
1820
1821 List<String> newInputFiles =
1822 inputFiles.stream().sorted().map(File::getAbsolutePath).collect(Collectors.toList());
1823
1824 try {
1825 Files.write(mojoConfigFile, newInputFiles);
1826 } catch (IOException e) {
1827
1828 getLog().warn("Error while writing new mojo status: " + mojoConfigFile);
1829 return false;
1830 }
1831
1832 DeltaList<String> inputTreeChanges = new DeltaList<>(oldInputFiles, newInputFiles);
1833 if (getLog().isDebugEnabled() || showCompilationChanges) {
1834 for (String fileAdded : inputTreeChanges.getAdded()) {
1835 getLog().info("\tInput tree files (+): " + fileAdded);
1836 }
1837 for (String fileRemoved : inputTreeChanges.getRemoved()) {
1838 getLog().info("\tInput tree files (-): " + fileRemoved);
1839 }
1840 }
1841
1842 return inputTreeChanges.hasChanged();
1843 }
1844
1845 public void setTarget(String target) {
1846 this.target = target;
1847 targetOrReleaseSet = true;
1848 }
1849
1850 public void setRelease(String release) {
1851 this.release = release;
1852 targetOrReleaseSet = true;
1853 }
1854
1855 final String getImplicit() {
1856 return implicit;
1857 }
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868 private void patchJdkModuleVersion(CompilerResult compilerResult, Set<File> sources) throws MojoExecutionException {
1869 if (compilerResult.isSuccess() && getModuleDeclaration(sources).isPresent()) {
1870 Path moduleDescriptor = getOutputDirectory().toPath().resolve("module-info.class");
1871 if (Files.isRegularFile(moduleDescriptor)) {
1872 try {
1873 final byte[] descriptorOriginal = Files.readAllBytes(moduleDescriptor);
1874 final byte[] descriptorMod =
1875 ModuleInfoTransformer.transform(descriptorOriginal, getRelease(), getLog());
1876 if (descriptorMod != null) {
1877 Files.write(moduleDescriptor, descriptorMod);
1878 }
1879 } catch (IOException ex) {
1880 throw new MojoExecutionException("Error reading or writing module-info.class", ex);
1881 }
1882 }
1883 }
1884 }
1885 }