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