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