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