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
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.6";
102
103 static final String DEFAULT_TARGET = "1.6";
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 @Parameter( property = "maven.compiler.source", defaultValue = DEFAULT_SOURCE )
173 protected String source;
174
175
176
177
178
179
180 @Parameter( property = "maven.compiler.target", defaultValue = DEFAULT_TARGET )
181 protected String target;
182
183
184
185
186
187
188 @Parameter( property = "maven.compiler.release" )
189 protected String release;
190
191
192
193
194
195
196 @Parameter( property = "encoding", defaultValue = "${project.build.sourceEncoding}" )
197 private String encoding;
198
199
200
201
202
203 @Parameter( property = "lastModGranularityMs", defaultValue = "0" )
204 private int staleMillis;
205
206
207
208
209
210 @Parameter( property = "maven.compiler.compilerId", defaultValue = "javac" )
211 private String compilerId;
212
213
214
215
216 @Parameter( property = "maven.compiler.compilerVersion" )
217 private String compilerVersion;
218
219
220
221
222
223 @Parameter( property = "maven.compiler.fork", defaultValue = "false" )
224 private boolean fork;
225
226
227
228
229
230
231
232 @Parameter( property = "maven.compiler.meminitial" )
233 private String meminitial;
234
235
236
237
238
239
240
241 @Parameter( property = "maven.compiler.maxmem" )
242 private String maxmem;
243
244
245
246
247 @Parameter( property = "maven.compiler.executable" )
248 private String executable;
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263 @Parameter
264 private String proc;
265
266
267
268
269
270
271
272
273
274 @Parameter
275 private String[] annotationProcessors;
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
303 @Parameter
304 private List<DependencyCoordinate> annotationProcessorPaths;
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329 @Parameter
330 @Deprecated
331 protected Map<String, String> compilerArguments;
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 String generatedSourcesPath = generatedSourcesDirectory.getAbsolutePath();
659
660 compileSourceRoots.add( generatedSourcesPath );
661
662 if ( isTestCompile() )
663 {
664 getLog().debug( "Adding " + generatedSourcesPath + " to test-compile source roots:\n "
665 + StringUtils.join( project.getTestCompileSourceRoots()
666 .iterator(), "\n " ) );
667
668 project.addTestCompileSourceRoot( generatedSourcesPath );
669
670 getLog().debug( "New test-compile source roots:\n "
671 + StringUtils.join( project.getTestCompileSourceRoots()
672 .iterator(), "\n " ) );
673 }
674 else
675 {
676 getLog().debug( "Adding " + generatedSourcesPath + " to compile source roots:\n "
677 + StringUtils.join( project.getCompileSourceRoots()
678 .iterator(), "\n " ) );
679
680 project.addCompileSourceRoot( generatedSourcesPath );
681
682 getLog().debug( "New compile source roots:\n " + StringUtils.join( project.getCompileSourceRoots()
683 .iterator(), "\n " ) );
684 }
685 }
686
687 compilerConfiguration.setSourceLocations( compileSourceRoots );
688
689 compilerConfiguration.setAnnotationProcessors( annotationProcessors );
690
691 compilerConfiguration.setProcessorPathEntries( resolveProcessorPathEntries() );
692
693 compilerConfiguration.setSourceEncoding( encoding );
694
695 compilerConfiguration.setFork( fork );
696
697 if ( fork )
698 {
699 if ( !StringUtils.isEmpty( meminitial ) )
700 {
701 String value = getMemoryValue( meminitial );
702
703 if ( value != null )
704 {
705 compilerConfiguration.setMeminitial( value );
706 }
707 else
708 {
709 getLog().info( "Invalid value for meminitial '" + meminitial + "'. Ignoring this option." );
710 }
711 }
712
713 if ( !StringUtils.isEmpty( maxmem ) )
714 {
715 String value = getMemoryValue( maxmem );
716
717 if ( value != null )
718 {
719 compilerConfiguration.setMaxmem( value );
720 }
721 else
722 {
723 getLog().info( "Invalid value for maxmem '" + maxmem + "'. Ignoring this option." );
724 }
725 }
726 }
727
728 compilerConfiguration.setExecutable( executable );
729
730 compilerConfiguration.setWorkingDirectory( basedir );
731
732 compilerConfiguration.setCompilerVersion( compilerVersion );
733
734 compilerConfiguration.setBuildDirectory( buildDirectory );
735
736 compilerConfiguration.setOutputFileName( outputFileName );
737
738 if ( CompilerConfiguration.CompilerReuseStrategy.AlwaysNew.getStrategy().equals( this.compilerReuseStrategy ) )
739 {
740 compilerConfiguration.setCompilerReuseStrategy( CompilerConfiguration.CompilerReuseStrategy.AlwaysNew );
741 }
742 else if ( CompilerConfiguration.CompilerReuseStrategy.ReuseSame.getStrategy().equals(
743 this.compilerReuseStrategy ) )
744 {
745 if ( getRequestThreadCount() > 1 )
746 {
747 if ( !skipMultiThreadWarning )
748 {
749 getLog().warn( "You are in a multi-thread build and compilerReuseStrategy is set to reuseSame."
750 + " This can cause issues in some environments (os/jdk)!"
751 + " Consider using reuseCreated strategy."
752 + System.getProperty( "line.separator" )
753 + "If your env is fine with reuseSame, you can skip this warning with the "
754 + "configuration field skipMultiThreadWarning "
755 + "or -Dmaven.compiler.skipMultiThreadWarning=true" );
756 }
757 }
758 compilerConfiguration.setCompilerReuseStrategy( CompilerConfiguration.CompilerReuseStrategy.ReuseSame );
759 }
760 else
761 {
762
763 compilerConfiguration.setCompilerReuseStrategy( CompilerConfiguration.CompilerReuseStrategy.ReuseCreated );
764 }
765
766 getLog().debug( "CompilerReuseStrategy: " + compilerConfiguration.getCompilerReuseStrategy().getStrategy() );
767
768 compilerConfiguration.setForceJavacCompilerUse( forceJavacCompilerUse );
769
770 boolean canUpdateTarget;
771
772 IncrementalBuildHelper incrementalBuildHelper = new IncrementalBuildHelper( mojoExecution, session );
773
774 final Set<File> sources;
775
776 IncrementalBuildHelperRequest incrementalBuildHelperRequest = null;
777
778 if ( useIncrementalCompilation )
779 {
780 getLog().debug( "useIncrementalCompilation enabled" );
781 try
782 {
783 canUpdateTarget = compiler.canUpdateTarget( compilerConfiguration );
784
785 sources = getCompileSources( compiler, compilerConfiguration );
786
787 preparePaths( sources );
788
789 incrementalBuildHelperRequest = new IncrementalBuildHelperRequest().inputFiles( sources );
790
791
792 if ( ( compiler.getCompilerOutputStyle().equals( CompilerOutputStyle.ONE_OUTPUT_FILE_FOR_ALL_INPUT_FILES ) && !canUpdateTarget )
793 || isDependencyChanged()
794 || isSourceChanged( compilerConfiguration, compiler )
795 || incrementalBuildHelper.inputFileTreeChanged( incrementalBuildHelperRequest ) )
796
797 {
798 getLog().info( "Changes detected - recompiling the module!" );
799
800 compilerConfiguration.setSourceFiles( sources );
801 }
802 else
803 {
804 getLog().info( "Nothing to compile - all classes are up to date" );
805
806 return;
807 }
808 }
809 catch ( CompilerException e )
810 {
811 throw new MojoExecutionException( "Error while computing stale sources.", e );
812 }
813 }
814 else
815 {
816 getLog().debug( "useIncrementalCompilation disabled" );
817
818 Set<File> staleSources;
819 try
820 {
821 staleSources =
822 computeStaleSources( compilerConfiguration, compiler, getSourceInclusionScanner( staleMillis ) );
823
824 canUpdateTarget = compiler.canUpdateTarget( compilerConfiguration );
825
826 if ( compiler.getCompilerOutputStyle().equals( CompilerOutputStyle.ONE_OUTPUT_FILE_FOR_ALL_INPUT_FILES )
827 && !canUpdateTarget )
828 {
829 getLog().info( "RESCANNING!" );
830
831 String inputFileEnding = compiler.getInputFileEnding( compilerConfiguration );
832
833 staleSources = computeStaleSources( compilerConfiguration, compiler,
834 getSourceInclusionScanner( inputFileEnding ) );
835 }
836
837 }
838 catch ( CompilerException e )
839 {
840 throw new MojoExecutionException( "Error while computing stale sources.", e );
841 }
842
843 if ( staleSources.isEmpty() )
844 {
845 getLog().info( "Nothing to compile - all classes are up to date" );
846
847 return;
848 }
849
850 compilerConfiguration.setSourceFiles( staleSources );
851
852 try
853 {
854
855 sources = getCompileSources( compiler, compilerConfiguration );
856
857 getLog().debug( "#sources: " + sources.size() );
858 for ( File file : sources )
859 {
860 getLog().debug( file.getPath() );
861 }
862
863 preparePaths( sources );
864 }
865 catch ( CompilerException e )
866 {
867 throw new MojoExecutionException( "Error while computing stale sources.", e );
868 }
869 }
870
871
872 compilerConfiguration.setClasspathEntries( getClasspathElements() );
873
874 compilerConfiguration.setModulepathEntries( getModulepathElements() );
875
876 Map<String, String> effectiveCompilerArguments = getCompilerArguments();
877
878 String effectiveCompilerArgument = getCompilerArgument();
879
880 if ( ( effectiveCompilerArguments != null ) || ( effectiveCompilerArgument != null )
881 || ( compilerArgs != null ) )
882 {
883 if ( effectiveCompilerArguments != null )
884 {
885 for ( Map.Entry<String, String> me : effectiveCompilerArguments.entrySet() )
886 {
887 String key = me.getKey();
888 String value = me.getValue();
889 if ( !key.startsWith( "-" ) )
890 {
891 key = "-" + key;
892 }
893
894 if ( key.startsWith( "-A" ) && StringUtils.isNotEmpty( value ) )
895 {
896 compilerConfiguration.addCompilerCustomArgument( key + "=" + value, null );
897 }
898 else
899 {
900 compilerConfiguration.addCompilerCustomArgument( key, value );
901 }
902 }
903 }
904 if ( !StringUtils.isEmpty( effectiveCompilerArgument ) )
905 {
906 compilerConfiguration.addCompilerCustomArgument( effectiveCompilerArgument, null );
907 }
908 if ( compilerArgs != null )
909 {
910 for ( String arg : compilerArgs )
911 {
912 compilerConfiguration.addCompilerCustomArgument( arg, null );
913 }
914 }
915 }
916
917
918
919
920 if ( getLog().isDebugEnabled() )
921 {
922 getLog().debug( "Classpath:" );
923
924 for ( String s : getClasspathElements() )
925 {
926 getLog().debug( " " + s );
927 }
928
929 if ( !getModulepathElements().isEmpty() )
930 {
931 getLog().debug( "Modulepath:" );
932 for ( String s : getModulepathElements() )
933 {
934 getLog().debug( " " + s );
935 }
936 }
937
938 getLog().debug( "Source roots:" );
939
940 for ( String root : getCompileSourceRoots() )
941 {
942 getLog().debug( " " + root );
943 }
944
945 try
946 {
947 if ( fork )
948 {
949 if ( compilerConfiguration.getExecutable() != null )
950 {
951 getLog().debug( "Excutable: " );
952 getLog().debug( " " + compilerConfiguration.getExecutable() );
953 }
954 }
955
956 String[] cl = compiler.createCommandLine( compilerConfiguration );
957 if ( getLog().isDebugEnabled() && cl != null && cl.length > 0 )
958 {
959 StringBuilder sb = new StringBuilder();
960 sb.append( cl[0] );
961 for ( int i = 1; i < cl.length; i++ )
962 {
963 sb.append( " " );
964 sb.append( cl[i] );
965 }
966 getLog().debug( "Command line options:" );
967 getLog().debug( sb );
968 }
969 }
970 catch ( CompilerException ce )
971 {
972 getLog().debug( ce );
973 }
974 }
975
976
977 List<String> jpmsLines = new ArrayList<String>();
978
979
980 final List<String> runtimeArgs = Arrays.asList( "--upgrade-module-path",
981 "--add-exports",
982 "--add-reads",
983 "--add-modules",
984 "--limit-modules" );
985
986
987 Iterator<Map.Entry<String, String>> entryIter =
988 compilerConfiguration.getCustomCompilerArgumentsEntries().iterator();
989 while ( entryIter.hasNext() )
990 {
991 Map.Entry<String, String> entry = entryIter.next();
992
993 if ( runtimeArgs.contains( entry.getKey() ) )
994 {
995 jpmsLines.add( entry.getKey() );
996
997 String value = entry.getValue();
998 if ( value == null )
999 {
1000 entry = entryIter.next();
1001 value = entry.getKey();
1002 }
1003 jpmsLines.add( value );
1004 }
1005 else if ( "--patch-module".equals( entry.getKey() ) )
1006 {
1007 String value = entry.getValue();
1008 if ( value == null )
1009 {
1010 entry = entryIter.next();
1011 value = entry.getKey();
1012 }
1013
1014 String[] values = value.split( "=" );
1015
1016 StringBuilder patchModule = new StringBuilder( values[0] );
1017 patchModule.append( '=' );
1018
1019 Set<String> patchModules = new LinkedHashSet<>();
1020 Set<Path> sourceRoots = new HashSet<>( getCompileSourceRoots().size() );
1021 for ( String sourceRoot : getCompileSourceRoots() )
1022 {
1023 sourceRoots.add( Paths.get( sourceRoot ) );
1024 }
1025
1026 String[] files = values[1].split( PS );
1027
1028 for ( String file : files )
1029 {
1030 Path filePath = Paths.get( file );
1031 if ( getOutputDirectory().toPath().equals( filePath ) )
1032 {
1033 patchModules.add( "_" );
1034 }
1035 else if ( getOutputDirectory().toPath().startsWith( filePath ) )
1036 {
1037
1038 continue;
1039 }
1040 else if ( sourceRoots.contains( filePath ) )
1041 {
1042 patchModules.add( "_" );
1043 }
1044 else
1045 {
1046 JavaModuleDescriptor descriptor = getPathElements().get( file );
1047
1048 if ( descriptor == null )
1049 {
1050 if ( Files.isDirectory( filePath ) )
1051 {
1052 patchModules.add( file );
1053 }
1054 else
1055 {
1056 getLog().warn( "Can't locate " + file );
1057 }
1058 }
1059 else if ( !values[0].equals( descriptor.name() ) )
1060 {
1061 patchModules.add( descriptor.name() );
1062 }
1063 }
1064 }
1065
1066 StringBuilder sb = new StringBuilder();
1067
1068 if ( patchModules.size() > 0 )
1069 {
1070 for ( String mod : patchModules )
1071 {
1072 if ( sb.length() > 0 )
1073 {
1074 sb.append( ", " );
1075 }
1076
1077 sb.append( mod );
1078 }
1079
1080 jpmsLines.add( "--patch-module" );
1081 jpmsLines.add( patchModule + sb.toString() );
1082 }
1083
1084 }
1085 }
1086
1087 if ( !jpmsLines.isEmpty() )
1088 {
1089 Path jpmsArgs = Paths.get( getOutputDirectory().getAbsolutePath(), "META-INF/jpms.args" );
1090 try
1091 {
1092 Files.createDirectories( jpmsArgs.getParent() );
1093
1094 Files.write( jpmsArgs, jpmsLines, Charset.defaultCharset() );
1095 }
1096 catch ( IOException e )
1097 {
1098 getLog().warn( e.getMessage() );
1099 }
1100 }
1101
1102
1103
1104
1105
1106
1107 if ( StringUtils.isEmpty( compilerConfiguration.getSourceEncoding() ) )
1108 {
1109 getLog().warn( "File encoding has not been set, using platform encoding " + ReaderFactory.FILE_ENCODING
1110 + ", i.e. build is platform dependent!" );
1111 }
1112
1113 CompilerResult compilerResult;
1114
1115
1116 if ( useIncrementalCompilation )
1117 {
1118 incrementalBuildHelperRequest.outputDirectory( getOutputDirectory() );
1119
1120 incrementalBuildHelper.beforeRebuildExecution( incrementalBuildHelperRequest );
1121
1122 getLog().debug( "incrementalBuildHelper#beforeRebuildExecution" );
1123 }
1124
1125 try
1126 {
1127 try
1128 {
1129 compilerResult = compiler.performCompile( compilerConfiguration );
1130 }
1131 catch ( CompilerNotImplementedException cnie )
1132 {
1133 List<CompilerError> messages = compiler.compile( compilerConfiguration );
1134 compilerResult = convertToCompilerResult( messages );
1135 }
1136 }
1137 catch ( Exception e )
1138 {
1139
1140 throw new MojoExecutionException( "Fatal error compiling", e );
1141 }
1142
1143 if ( useIncrementalCompilation )
1144 {
1145 if ( incrementalBuildHelperRequest.getOutputDirectory().exists() )
1146 {
1147 getLog().debug( "incrementalBuildHelper#afterRebuildExecution" );
1148
1149 incrementalBuildHelper.afterRebuildExecution( incrementalBuildHelperRequest );
1150 }
1151 else
1152 {
1153 getLog().debug(
1154 "skip incrementalBuildHelper#afterRebuildExecution as the output directory doesn't exist" );
1155 }
1156 }
1157
1158 List<CompilerMessage> warnings = new ArrayList<CompilerMessage>();
1159 List<CompilerMessage> errors = new ArrayList<CompilerMessage>();
1160 List<CompilerMessage> others = new ArrayList<CompilerMessage>();
1161 for ( CompilerMessage message : compilerResult.getCompilerMessages() )
1162 {
1163 if ( message.getKind() == CompilerMessage.Kind.ERROR )
1164 {
1165 errors.add( message );
1166 }
1167 else if ( message.getKind() == CompilerMessage.Kind.WARNING
1168 || message.getKind() == CompilerMessage.Kind.MANDATORY_WARNING )
1169 {
1170 warnings.add( message );
1171 }
1172 else
1173 {
1174 others.add( message );
1175 }
1176 }
1177
1178 if ( failOnError && !compilerResult.isSuccess() )
1179 {
1180 for ( CompilerMessage message : others )
1181 {
1182 assert message.getKind() != CompilerMessage.Kind.ERROR
1183 && message.getKind() != CompilerMessage.Kind.WARNING
1184 && message.getKind() != CompilerMessage.Kind.MANDATORY_WARNING;
1185 getLog().info( message.toString() );
1186 }
1187 if ( !warnings.isEmpty() )
1188 {
1189 getLog().info( "-------------------------------------------------------------" );
1190 getLog().warn( "COMPILATION WARNING : " );
1191 getLog().info( "-------------------------------------------------------------" );
1192 for ( CompilerMessage warning : warnings )
1193 {
1194 getLog().warn( warning.toString() );
1195 }
1196 getLog().info( warnings.size() + ( ( warnings.size() > 1 ) ? " warnings " : " warning" ) );
1197 getLog().info( "-------------------------------------------------------------" );
1198 }
1199
1200 if ( !errors.isEmpty() )
1201 {
1202 getLog().info( "-------------------------------------------------------------" );
1203 getLog().error( "COMPILATION ERROR : " );
1204 getLog().info( "-------------------------------------------------------------" );
1205 for ( CompilerMessage error : errors )
1206 {
1207 getLog().error( error.toString() );
1208 }
1209 getLog().info( errors.size() + ( ( errors.size() > 1 ) ? " errors " : " error" ) );
1210 getLog().info( "-------------------------------------------------------------" );
1211 }
1212
1213 if ( !errors.isEmpty() )
1214 {
1215 throw new CompilationFailureException( errors );
1216 }
1217 else
1218 {
1219 throw new CompilationFailureException( warnings );
1220 }
1221 }
1222 else
1223 {
1224 for ( CompilerMessage message : compilerResult.getCompilerMessages() )
1225 {
1226 switch ( message.getKind() )
1227 {
1228 case NOTE:
1229 case OTHER:
1230 getLog().info( message.toString() );
1231 break;
1232
1233 case ERROR:
1234 getLog().error( message.toString() );
1235 break;
1236
1237 case MANDATORY_WARNING:
1238 case WARNING:
1239 default:
1240 getLog().warn( message.toString() );
1241 break;
1242 }
1243 }
1244 }
1245 }
1246
1247 protected boolean isTestCompile()
1248 {
1249 return false;
1250 }
1251
1252 protected CompilerResult convertToCompilerResult( List<CompilerError> compilerErrors )
1253 {
1254 if ( compilerErrors == null )
1255 {
1256 return new CompilerResult();
1257 }
1258 List<CompilerMessage> messages = new ArrayList<CompilerMessage>( compilerErrors.size() );
1259 boolean success = true;
1260 for ( CompilerError compilerError : compilerErrors )
1261 {
1262 messages.add(
1263 new CompilerMessage( compilerError.getFile(), compilerError.getKind(), compilerError.getStartLine(),
1264 compilerError.getStartColumn(), compilerError.getEndLine(),
1265 compilerError.getEndColumn(), compilerError.getMessage() ) );
1266 if ( compilerError.isError() )
1267 {
1268 success = false;
1269 }
1270 }
1271
1272 return new CompilerResult( success, messages );
1273 }
1274
1275
1276
1277
1278 private Set<File> getCompileSources( Compiler compiler, CompilerConfiguration compilerConfiguration )
1279 throws MojoExecutionException, CompilerException
1280 {
1281 String inputFileEnding = compiler.getInputFileEnding( compilerConfiguration );
1282 if ( StringUtils.isEmpty( inputFileEnding ) )
1283 {
1284
1285
1286 inputFileEnding = ".*";
1287 }
1288 SourceInclusionScanner scanner = getSourceInclusionScanner( inputFileEnding );
1289
1290 SourceMapping mapping = getSourceMapping( compilerConfiguration, compiler );
1291
1292 scanner.addSourceMapping( mapping );
1293
1294 Set<File> compileSources = new HashSet<File>();
1295
1296 for ( String sourceRoot : getCompileSourceRoots() )
1297 {
1298 File rootFile = new File( sourceRoot );
1299
1300 if ( !rootFile.isDirectory()
1301 || rootFile.getAbsoluteFile().equals( compilerConfiguration.getGeneratedSourcesDirectory() ) )
1302 {
1303 continue;
1304 }
1305
1306 try
1307 {
1308 compileSources.addAll( scanner.getIncludedSources( rootFile, null ) );
1309 }
1310 catch ( InclusionScanException e )
1311 {
1312 throw new MojoExecutionException(
1313 "Error scanning source root: \'" + sourceRoot + "\' for stale files to recompile.", e );
1314 }
1315 }
1316
1317 return compileSources;
1318 }
1319
1320
1321
1322
1323
1324
1325 private boolean isSourceChanged( CompilerConfiguration compilerConfiguration, Compiler compiler )
1326 throws CompilerException, MojoExecutionException
1327 {
1328 Set<File> staleSources =
1329 computeStaleSources( compilerConfiguration, compiler, getSourceInclusionScanner( staleMillis ) );
1330
1331 if ( getLog().isDebugEnabled() )
1332 {
1333 for ( File f : staleSources )
1334 {
1335 getLog().debug( "Stale source detected: " + f.getAbsolutePath() );
1336 }
1337 }
1338 return staleSources != null && staleSources.size() > 0;
1339 }
1340
1341
1342
1343
1344
1345
1346
1347 protected int getRequestThreadCount()
1348 {
1349 try
1350 {
1351 Method getRequestMethod = session.getClass().getMethod( "getRequest" );
1352 Object mavenExecutionRequest = getRequestMethod.invoke( this.session );
1353 Method getThreadCountMethod = mavenExecutionRequest.getClass().getMethod( "getThreadCount" );
1354 String threadCount = (String) getThreadCountMethod.invoke( mavenExecutionRequest );
1355 return Integer.valueOf( threadCount );
1356 }
1357 catch ( Exception e )
1358 {
1359 getLog().debug( "unable to get threadCount for the current build: " + e.getMessage() );
1360 }
1361 return 1;
1362 }
1363
1364 protected Date getBuildStartTime()
1365 {
1366 Date buildStartTime = null;
1367 try
1368 {
1369 Method getRequestMethod = session.getClass().getMethod( "getRequest" );
1370 Object mavenExecutionRequest = getRequestMethod.invoke( session );
1371 Method getStartTimeMethod = mavenExecutionRequest.getClass().getMethod( "getStartTime" );
1372 buildStartTime = (Date) getStartTimeMethod.invoke( mavenExecutionRequest );
1373 }
1374 catch ( Exception e )
1375 {
1376 getLog().debug( "unable to get start time for the current build: " + e.getMessage() );
1377 }
1378
1379 if ( buildStartTime == null )
1380 {
1381 return new Date();
1382 }
1383
1384 return buildStartTime;
1385 }
1386
1387
1388 private String getMemoryValue( String setting )
1389 {
1390 String value = null;
1391
1392
1393 if ( isDigits( setting ) )
1394 {
1395 value = setting + "m";
1396 }
1397 else if ( ( isDigits( setting.substring( 0, setting.length() - 1 ) ) )
1398 && ( setting.toLowerCase().endsWith( "m" ) ) )
1399 {
1400 value = setting;
1401 }
1402 return value;
1403 }
1404
1405
1406
1407 protected final Toolchain getToolchain()
1408 {
1409 Toolchain tc = null;
1410
1411 if ( jdkToolchain != null )
1412 {
1413
1414 try
1415 {
1416 Method getToolchainsMethod =
1417 toolchainManager.getClass().getMethod( "getToolchains", MavenSession.class, String.class,
1418 Map.class );
1419
1420 @SuppressWarnings( "unchecked" )
1421 List<Toolchain> tcs =
1422 (List<Toolchain>) getToolchainsMethod.invoke( toolchainManager, session, "jdk",
1423 jdkToolchain );
1424
1425 if ( tcs != null && tcs.size() > 0 )
1426 {
1427 tc = tcs.get( 0 );
1428 }
1429 }
1430 catch ( NoSuchMethodException e )
1431 {
1432
1433 }
1434 catch ( SecurityException e )
1435 {
1436
1437 }
1438 catch ( IllegalAccessException e )
1439 {
1440
1441 }
1442 catch ( IllegalArgumentException e )
1443 {
1444
1445 }
1446 catch ( InvocationTargetException e )
1447 {
1448
1449 }
1450 }
1451
1452 if ( tc == null )
1453 {
1454 tc = toolchainManager.getToolchainFromBuildContext( "jdk", session );
1455 }
1456
1457 return tc;
1458 }
1459
1460 private boolean isDigits( String string )
1461 {
1462 for ( int i = 0; i < string.length(); i++ )
1463 {
1464 if ( !Character.isDigit( string.charAt( i ) ) )
1465 {
1466 return false;
1467 }
1468 }
1469 return true;
1470 }
1471
1472 private Set<File> computeStaleSources( CompilerConfiguration compilerConfiguration, Compiler compiler,
1473 SourceInclusionScanner scanner )
1474 throws MojoExecutionException, CompilerException
1475 {
1476 SourceMapping mapping = getSourceMapping( compilerConfiguration, compiler );
1477
1478 File outputDirectory;
1479 CompilerOutputStyle outputStyle = compiler.getCompilerOutputStyle();
1480 if ( outputStyle == CompilerOutputStyle.ONE_OUTPUT_FILE_FOR_ALL_INPUT_FILES )
1481 {
1482 outputDirectory = buildDirectory;
1483 }
1484 else
1485 {
1486 outputDirectory = getOutputDirectory();
1487 }
1488
1489 scanner.addSourceMapping( mapping );
1490
1491 Set<File> staleSources = new HashSet<File>();
1492
1493 for ( String sourceRoot : getCompileSourceRoots() )
1494 {
1495 File rootFile = new File( sourceRoot );
1496
1497 if ( !rootFile.isDirectory() )
1498 {
1499 continue;
1500 }
1501
1502 try
1503 {
1504 staleSources.addAll( scanner.getIncludedSources( rootFile, outputDirectory ) );
1505 }
1506 catch ( InclusionScanException e )
1507 {
1508 throw new MojoExecutionException(
1509 "Error scanning source root: \'" + sourceRoot + "\' for stale files to recompile.", e );
1510 }
1511 }
1512
1513 return staleSources;
1514 }
1515
1516 private SourceMapping getSourceMapping( CompilerConfiguration compilerConfiguration, Compiler compiler )
1517 throws CompilerException, MojoExecutionException
1518 {
1519 CompilerOutputStyle outputStyle = compiler.getCompilerOutputStyle();
1520
1521 SourceMapping mapping;
1522 if ( outputStyle == CompilerOutputStyle.ONE_OUTPUT_FILE_PER_INPUT_FILE )
1523 {
1524 mapping = new SuffixMapping( compiler.getInputFileEnding( compilerConfiguration ),
1525 compiler.getOutputFileEnding( compilerConfiguration ) );
1526 }
1527 else if ( outputStyle == CompilerOutputStyle.ONE_OUTPUT_FILE_FOR_ALL_INPUT_FILES )
1528 {
1529 mapping = new SingleTargetSourceMapping( compiler.getInputFileEnding( compilerConfiguration ),
1530 compiler.getOutputFile( compilerConfiguration ) );
1531
1532 }
1533 else
1534 {
1535 throw new MojoExecutionException( "Unknown compiler output style: '" + outputStyle + "'." );
1536 }
1537 return mapping;
1538 }
1539
1540
1541
1542
1543
1544 private static List<String> removeEmptyCompileSourceRoots( List<String> compileSourceRootsList )
1545 {
1546 List<String> newCompileSourceRootsList = new ArrayList<String>();
1547 if ( compileSourceRootsList != null )
1548 {
1549
1550 for ( String srcDir : compileSourceRootsList )
1551 {
1552 if ( !newCompileSourceRootsList.contains( srcDir ) && new File( srcDir ).exists() )
1553 {
1554 newCompileSourceRootsList.add( srcDir );
1555 }
1556 }
1557 }
1558 return newCompileSourceRootsList;
1559 }
1560
1561
1562
1563
1564
1565
1566
1567
1568 protected boolean isDependencyChanged()
1569 {
1570 if ( session == null )
1571 {
1572
1573 getLog().info( "Cannot determine build start date, skipping incremental build detection." );
1574 return false;
1575 }
1576
1577 if ( fileExtensions == null || fileExtensions.isEmpty() )
1578 {
1579 fileExtensions = new ArrayList<String>();
1580 fileExtensions.add( ".class" );
1581 }
1582
1583 Date buildStartTime = getBuildStartTime();
1584
1585 List<String> pathElements = new ArrayList<String>();
1586 pathElements.addAll( getClasspathElements() );
1587 pathElements.addAll( getModulepathElements() );
1588
1589 for ( String pathElement : pathElements )
1590 {
1591
1592
1593 File artifactPath = new File( pathElement );
1594 if ( artifactPath.isDirectory() )
1595 {
1596 if ( hasNewFile( artifactPath, buildStartTime ) )
1597 {
1598 getLog().debug( "New dependency detected: " + artifactPath.getAbsolutePath() );
1599 return true;
1600 }
1601 }
1602 }
1603
1604
1605 return false;
1606 }
1607
1608
1609
1610
1611
1612
1613 private boolean hasNewFile( File classPathEntry, Date buildStartTime )
1614 {
1615 if ( !classPathEntry.exists() )
1616 {
1617 return false;
1618 }
1619
1620 if ( classPathEntry.isFile() )
1621 {
1622 return classPathEntry.lastModified() >= buildStartTime.getTime()
1623 && fileExtensions.contains( FileUtils.getExtension( classPathEntry.getName() ) );
1624 }
1625
1626 File[] children = classPathEntry.listFiles();
1627
1628 for ( File child : children )
1629 {
1630 if ( hasNewFile( child, buildStartTime ) )
1631 {
1632 return true;
1633 }
1634 }
1635
1636 return false;
1637 }
1638
1639 private List<String> resolveProcessorPathEntries()
1640 throws MojoExecutionException
1641 {
1642 if ( annotationProcessorPaths == null || annotationProcessorPaths.isEmpty() )
1643 {
1644 return null;
1645 }
1646
1647 try
1648 {
1649 Set<Artifact> requiredArtifacts = new LinkedHashSet<Artifact>();
1650
1651 for ( DependencyCoordinate coord : annotationProcessorPaths )
1652 {
1653 ArtifactHandler handler = artifactHandlerManager.getArtifactHandler( coord.getType() );
1654
1655 Artifact artifact = new DefaultArtifact(
1656 coord.getGroupId(),
1657 coord.getArtifactId(),
1658 VersionRange.createFromVersionSpec( coord.getVersion() ),
1659 Artifact.SCOPE_RUNTIME,
1660 coord.getType(),
1661 coord.getClassifier(),
1662 handler,
1663 false );
1664
1665 requiredArtifacts.add( artifact );
1666 }
1667
1668 ArtifactResolutionRequest request = new ArtifactResolutionRequest()
1669 .setArtifact( requiredArtifacts.iterator().next() )
1670 .setResolveRoot( true )
1671 .setResolveTransitively( true )
1672 .setArtifactDependencies( requiredArtifacts )
1673 .setLocalRepository( session.getLocalRepository() )
1674 .setRemoteRepositories( project.getRemoteArtifactRepositories() );
1675
1676 ArtifactResolutionResult resolutionResult = repositorySystem.resolve( request );
1677
1678 resolutionErrorHandler.throwErrors( request, resolutionResult );
1679
1680 List<String> elements = new ArrayList<String>( resolutionResult.getArtifacts().size() );
1681
1682 for ( Object resolved : resolutionResult.getArtifacts() )
1683 {
1684 elements.add( ( (Artifact) resolved ).getFile().getAbsolutePath() );
1685 }
1686
1687 return elements;
1688 }
1689 catch ( Exception e )
1690 {
1691 throw new MojoExecutionException( "Resolution of annotationProcessorPath dependencies failed: "
1692 + e.getLocalizedMessage(), e );
1693 }
1694 }
1695
1696 private void writePlugin( MessageBuilder mb )
1697 {
1698 mb.a( " <plugin>" ).newline();
1699 mb.a( " <groupId>org.apache.maven.plugins</groupId>" ).newline();
1700 mb.a( " <artifactId>maven-compiler-plugin</artifactId>" ).newline();
1701
1702 String version = getMavenCompilerPluginVersion();
1703 if ( version != null )
1704 {
1705 mb.a( " <version>" ).a( version ).a( "</version>" ).newline();
1706 }
1707 writeConfig( mb );
1708
1709 mb.a( " </plugin>" ).newline();
1710 }
1711
1712 private void writeConfig( MessageBuilder mb )
1713 {
1714 mb.a( " <configuration>" ).newline();
1715
1716 if ( release != null )
1717 {
1718 mb.a( " <release>" ).a( release ).a( "</release>" ).newline();
1719 }
1720 else if ( JavaVersion.JAVA_VERSION.isAtLeast( "9" ) )
1721 {
1722 String rls = target.replaceAll( ".\\.", "" );
1723
1724 mb.a( " <release>" ).a( rls ).a( "</release>" ).newline();
1725 }
1726 else
1727 {
1728 mb.a( " <source>" ).a( source ).a( "</source>" ).newline();
1729 mb.a( " <target>" ).a( target ).a( "</target>" ).newline();
1730 }
1731 mb.a( " </configuration>" ).newline();
1732 }
1733
1734 private String getMavenCompilerPluginVersion()
1735 {
1736 Properties pomProperties = new Properties();
1737
1738 try ( InputStream is = AbstractCompilerMojo.class
1739 .getResourceAsStream( "/META-INF/maven/org.apache.maven.plugins/maven-compiler-plugin/pom.properties" ) )
1740 {
1741 if ( is != null )
1742 {
1743 pomProperties.load( is );
1744 }
1745 }
1746 catch ( IOException e )
1747 {
1748
1749 }
1750
1751 return pomProperties.getProperty( "version" );
1752 }
1753
1754 public void setTarget( String target )
1755 {
1756 this.target = target;
1757 targetOrReleaseSet = true;
1758 }
1759
1760 public void setRelease( String release )
1761 {
1762 this.release = release;
1763 targetOrReleaseSet = true;
1764 }
1765 }