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