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