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