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