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