View Javadoc

1   package org.apache.maven.plugin;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import org.apache.maven.execution.MavenSession;
23  import org.apache.maven.toolchain.Toolchain;
24  import org.apache.maven.toolchain.ToolchainManager;
25  import org.codehaus.plexus.compiler.Compiler;
26  import org.codehaus.plexus.compiler.CompilerConfiguration;
27  import org.codehaus.plexus.compiler.CompilerError;
28  import org.codehaus.plexus.compiler.CompilerException;
29  import org.codehaus.plexus.compiler.CompilerOutputStyle;
30  import org.codehaus.plexus.compiler.manager.CompilerManager;
31  import org.codehaus.plexus.compiler.manager.NoSuchCompilerException;
32  import org.codehaus.plexus.compiler.util.scan.InclusionScanException;
33  import org.codehaus.plexus.compiler.util.scan.SourceInclusionScanner;
34  import org.codehaus.plexus.compiler.util.scan.mapping.SingleTargetSourceMapping;
35  import org.codehaus.plexus.compiler.util.scan.mapping.SourceMapping;
36  import org.codehaus.plexus.compiler.util.scan.mapping.SuffixMapping;
37  import org.codehaus.plexus.util.ReaderFactory;
38  import org.codehaus.plexus.util.StringUtils;
39  
40  import java.io.File;
41  import java.lang.reflect.Method;
42  import java.util.ArrayList;
43  import java.util.HashSet;
44  import java.util.LinkedHashMap;
45  import java.util.List;
46  import java.util.Map;
47  import java.util.Set;
48  
49  /**
50   * TODO: At least one step could be optimized, currently the plugin will do two
51   * scans of all the source code if the compiler has to have the entire set of
52   * sources. This is currently the case for at least the C# compiler and most
53   * likely all the other .NET compilers too.
54   *
55   * @author others
56   * @author <a href="mailto:trygvis@inamo.no">Trygve Laugst&oslash;l</a>
57   * @version $Id: AbstractCompilerMojo.java 1336851 2012-05-10 19:39:09Z hboutemy $
58   * @since 2.0
59   */
60  public abstract class AbstractCompilerMojo
61      extends AbstractMojo
62  {
63      // ----------------------------------------------------------------------
64      // Configurables
65      // ----------------------------------------------------------------------
66  
67      /**
68       * Indicates whether the build will continue even if there are compilation errors.
69       *
70       * @parameter expression="${maven.compiler.failOnError}" default-value="true"
71       * @since 2.0.2
72       */
73      private boolean failOnError = true;
74  
75      /**
76       * Set to <code>true</code> to include debugging information in the compiled class files.
77       *
78       * @parameter expression="${maven.compiler.debug}" default-value="true"
79       */
80      private boolean debug = true;
81  
82      /**
83       * Set to <code>true</code> to show messages about what the compiler is doing.
84       *
85       * @parameter expression="${maven.compiler.verbose}" default-value="false"
86       */
87      private boolean verbose;
88  
89      /**
90       * Sets whether to show source locations where deprecated APIs are used.
91       *
92       * @parameter expression="${maven.compiler.showDeprecation}" default-value="false"
93       */
94      private boolean showDeprecation;
95  
96      /**
97       * Set to <code>true</code> to optimize the compiled code using the compiler's optimization methods.
98       *
99       * @parameter expression="${maven.compiler.optimize}" default-value="false"
100      */
101     private boolean optimize;
102 
103     /**
104      * Set to <code>true</code> to show compilation warnings.
105      *
106      * @parameter expression="${maven.compiler.showWarnings}" default-value="false"
107      */
108     private boolean showWarnings;
109 
110     /**
111      * The -source argument for the Java compiler.
112      *
113      * @parameter expression="${maven.compiler.source}" default-value="1.5"
114      */
115     protected String source;
116 
117     /**
118      * The -target argument for the Java compiler.
119      *
120      * @parameter expression="${maven.compiler.target}" default-value="1.5"
121      */
122     protected String target;
123 
124     /**
125      * The -encoding argument for the Java compiler.
126      *
127      * @parameter expression="${encoding}" default-value="${project.build.sourceEncoding}"
128      * @since 2.1
129      */
130     private String encoding;
131 
132     /**
133      * Sets the granularity in milliseconds of the last modification
134      * date for testing whether a source needs recompilation.
135      *
136      * @parameter expression="${lastModGranularityMs}" default-value="0"
137      */
138     private int staleMillis;
139 
140     /**
141      * The compiler id of the compiler to use. See this
142      * <a href="non-javac-compilers.html">guide</a> for more information.
143      *
144      * @parameter expression="${maven.compiler.compilerId}" default-value="javac"
145      */
146     private String compilerId;
147 
148     /**
149      * Version of the compiler to use, ex. "1.3", "1.5", if {@link #fork} is set to <code>true</code>.
150      *
151      * @parameter expression="${maven.compiler.compilerVersion}"
152      */
153     private String compilerVersion;
154 
155     /**
156      * Allows running the compiler in a separate process.
157      * If <code>false</code> it uses the built in compiler, while if <code>true</code> it will use an executable.
158      *
159      * @parameter expression="${maven.compiler.fork}" default-value="false"
160      */
161     private boolean fork;
162 
163     /**
164      * Initial size, in megabytes, of the memory allocation pool, ex. "64", "64m"
165      * if {@link #fork} is set to <code>true</code>.
166      *
167      * @parameter expression="${maven.compiler.meminitial}"
168      * @since 2.0.1
169      */
170     private String meminitial;
171 
172     /**
173      * Sets the maximum size, in megabytes, of the memory allocation pool, ex. "128", "128m"
174      * if {@link #fork} is set to <code>true</code>.
175      *
176      * @parameter expression="${maven.compiler.maxmem}"
177      * @since 2.0.1
178      */
179     private String maxmem;
180 
181     /**
182      * Sets the executable of the compiler to use when {@link #fork} is <code>true</code>.
183      *
184      * @parameter expression="${maven.compiler.executable}"
185      */
186     private String executable;
187 
188     /**
189      * <p>
190      * Sets whether annotation processing is performed or not. Only applies to JDK 1.6+
191      * If not set, both compilation and annotation processing are performed at the same time.
192      * </p>
193      * <p>Allowed values are:</p>
194      * <ul>
195      * <li><code>none</code> - no annotation processing is performed.</li>
196      * <li><code>only</code> - only annotation processing is done, no compilation.</li>
197      * </ul>
198      *
199      * @parameter
200      * @since 2.2
201      */
202     private String proc;
203 
204     /**
205      * <p>
206      * Names of annotation processors to run. Only applies to JDK 1.6+
207      * If not set, the default annotation processors discovery process applies.
208      * </p>
209      *
210      * @parameter
211      * @since 2.2
212      */
213     private String[] annotationProcessors;
214 
215     /**
216      * <p>
217      * Sets the arguments to be passed to the compiler (prepending a dash) if {@link #fork} is set to <code>true</code>.
218      * </p>
219      * <p>
220      * This is because the list of valid arguments passed to a Java compiler
221      * varies based on the compiler version.
222      * </p>
223      * <p>
224      * To pass <code>-Xmaxerrs 1000 -Xlint -Xlint:-path -Averbose=true</code> you should include the following:
225      * </p>
226      * <pre>
227      * &lt;compilerArguments&gt;
228      *   &lt;Xmaxerrs&gt;1000&lt;/Xmaxerrs&gt;
229      *   &lt;Xlint/&gt;
230      *   &lt;Xlint:-path/&gt;
231      *   &lt;Averbose&gt;true&lt;/Averbose&gt;
232      * &lt;/compilerArguments&gt;
233      * </pre>
234      *
235      * @parameter
236      * @since 2.0.1
237      */
238     protected Map<String, String> compilerArguments;
239 
240     /**
241      * <p>
242      * Sets the unformatted single argument string to be passed to the compiler if {@link #fork} is set to <code>true</code>.
243      * To pass multiple arguments such as <code>-Xmaxerrs 1000</code> (which are actually two arguments) you have to use {@link #compilerArguments}.
244      * </p>
245      * <p>
246      * This is because the list of valid arguments passed to a Java compiler
247      * varies based on the compiler version.
248      * </p>
249      *
250      * @parameter
251      */
252     protected String compilerArgument;
253 
254     /**
255      * Sets the name of the output file when compiling a set of
256      * sources to a single file.
257      *
258      * @parameter expression="${project.build.finalName}"
259      */
260     private String outputFileName;
261 
262     /**
263      * Keyword list to be appended to the <code>-g</code> command-line switch. Legal values are none or a 
264      * comma-separated list of the following keywords: <code>lines</code>, <code>vars</code>, and <code>source</code>.
265      * If debug level is not specified, by default, nothing will be appended to <code>-g</code>.
266      * If debug is not turned on, this attribute will be ignored.
267      *
268      * @parameter expression="${maven.compiler.debuglevel}"
269      * @since 2.1
270      */
271     private String debuglevel;
272 
273     /**
274      * @component
275      */
276     private ToolchainManager toolchainManager;
277 
278     // ----------------------------------------------------------------------
279     // Read-only parameters
280     // ----------------------------------------------------------------------
281 
282     /**
283      * The directory to run the compiler from if fork is true.
284      *
285      * @parameter default-value="${basedir}"
286      * @required
287      * @readonly
288      */
289     private File basedir;
290 
291     /**
292      * The target directory of the compiler if fork is true.
293      *
294      * @parameter default-value="${project.build.directory}"
295      * @required
296      * @readonly
297      */
298     private File buildDirectory;
299 
300     /**
301      * Plexus compiler manager.
302      *
303      * @component
304      */
305     private CompilerManager compilerManager;
306 
307     /**
308      * The current build session instance. This is used for toolchain manager API calls.
309      *
310      * @parameter default-value="${session}"
311      * @required
312      * @readonly
313      */
314     private MavenSession session;
315 
316     /**
317      * Strategy to re use javacc class created:
318      * <ul>
319      *   <li><code>reuseCreated</code> (default): will reuse already created but in case of multi-threaded builds,
320      *   each thread will have its own instance</li>
321      *   <li><code>reuseSame</code>: the same Javacc class will be used for each compilation even for multi-threaded build</li>
322      *   <li><code>alwaysNew</code>: a new Javacc class will be created for each compilation</li>
323      * </ul>
324      * Note this parameter value depends on the os/jdk you are using, but the default value should work on most of env.
325      *
326      * @parameter default-value="${reuseCreated}" expression="${maven.compiler.compilerReuseStrategy}"
327      * @since 2.5
328      */
329     private String compilerReuseStrategy = "reuseCreated";
330 
331     /**
332      * @parameter default-value="${false}" expression="${maven.compiler.skipMultiThreadWarning}"
333      * @since 2.5
334      */
335     private boolean skipMultiThreadWarning;
336 
337     protected abstract SourceInclusionScanner getSourceInclusionScanner( int staleMillis );
338 
339     protected abstract SourceInclusionScanner getSourceInclusionScanner( String inputFileEnding );
340 
341     protected abstract List<String> getClasspathElements();
342 
343     protected abstract List<String> getCompileSourceRoots();
344 
345     protected abstract File getOutputDirectory();
346 
347     protected abstract String getSource();
348 
349     protected abstract String getTarget();
350 
351     protected abstract String getCompilerArgument();
352 
353     protected abstract Map<String, String> getCompilerArguments();
354 
355     protected abstract File getGeneratedSourcesDirectory();
356 
357     public void execute()
358         throws MojoExecutionException, CompilationFailureException
359     {
360         // ----------------------------------------------------------------------
361         // Look up the compiler. This is done before other code than can
362         // cause the mojo to return before the lookup is done possibly resulting
363         // in misconfigured POMs still building.
364         // ----------------------------------------------------------------------
365 
366         Compiler compiler;
367 
368         getLog().debug( "Using compiler '" + compilerId + "'." );
369 
370         try
371         {
372             compiler = compilerManager.getCompiler( compilerId );
373         }
374         catch ( NoSuchCompilerException e )
375         {
376             throw new MojoExecutionException( "No such compiler '" + e.getCompilerId() + "'." );
377         }
378 
379         //-----------toolchains start here ----------------------------------
380         //use the compilerId as identifier for toolchains as well.
381         Toolchain tc = getToolchain();
382         if ( tc != null )
383         {
384             getLog().info( "Toolchain in compiler-plugin: " + tc );
385             if ( executable != null )
386             {
387                 getLog().warn( "Toolchains are ignored, 'executable' parameter is set to " + executable );
388             }
389             else
390             {
391                 fork = true;
392                 //TODO somehow shaky dependency between compilerId and tool executable.
393                 executable = tc.findTool( compilerId );
394             }
395         }
396         // ----------------------------------------------------------------------
397         //
398         // ----------------------------------------------------------------------
399 
400         List<String> compileSourceRoots = removeEmptyCompileSourceRoots( getCompileSourceRoots() );
401 
402         if ( compileSourceRoots.isEmpty() )
403         {
404             getLog().info( "No sources to compile" );
405 
406             return;
407         }
408 
409         if ( getLog().isDebugEnabled() )
410         {
411             getLog().debug( "Source directories: " + compileSourceRoots.toString().replace( ',', '\n' ) );
412             getLog().debug( "Classpath: " + getClasspathElements().toString().replace( ',', '\n' ) );
413             getLog().debug( "Output directory: " + getOutputDirectory() );
414         }
415 
416         // ----------------------------------------------------------------------
417         // Create the compiler configuration
418         // ----------------------------------------------------------------------
419 
420         CompilerConfiguration compilerConfiguration = new CompilerConfiguration();
421 
422         compilerConfiguration.setOutputLocation( getOutputDirectory().getAbsolutePath() );
423 
424         compilerConfiguration.setClasspathEntries( getClasspathElements() );
425 
426         compilerConfiguration.setSourceLocations( compileSourceRoots );
427 
428         compilerConfiguration.setOptimize( optimize );
429 
430         compilerConfiguration.setDebug( debug );
431 
432         if ( debug && StringUtils.isNotEmpty( debuglevel ) )
433         {
434             String[] split = StringUtils.split( debuglevel, "," );
435             for ( int i = 0; i < split.length; i++ )
436             {
437                 if ( !( split[i].equalsIgnoreCase( "none" ) || split[i].equalsIgnoreCase( "lines" )
438                     || split[i].equalsIgnoreCase( "vars" ) || split[i].equalsIgnoreCase( "source" ) ) )
439                 {
440                     throw new IllegalArgumentException( "The specified debug level: '" + split[i] + "' is unsupported. "
441                                                             + "Legal values are 'none', 'lines', 'vars', and 'source'." );
442                 }
443             }
444             compilerConfiguration.setDebugLevel( debuglevel );
445         }
446 
447         compilerConfiguration.setVerbose( verbose );
448 
449         compilerConfiguration.setShowWarnings( showWarnings );
450 
451         compilerConfiguration.setShowDeprecation( showDeprecation );
452 
453         compilerConfiguration.setSourceVersion( getSource() );
454 
455         compilerConfiguration.setTargetVersion( getTarget() );
456 
457         compilerConfiguration.setProc( proc );
458 
459         compilerConfiguration.setGeneratedSourcesDirectory( getGeneratedSourcesDirectory() );
460 
461         compilerConfiguration.setAnnotationProcessors( annotationProcessors );
462 
463         compilerConfiguration.setSourceEncoding( encoding );
464 
465         Map<String, String> effectiveCompilerArguments = getCompilerArguments();
466 
467         String effectiveCompilerArgument = getCompilerArgument();
468 
469         if ( ( effectiveCompilerArguments != null ) || ( effectiveCompilerArgument != null ) )
470         {
471             LinkedHashMap<String, String> cplrArgsCopy = new LinkedHashMap<String, String>();
472             if ( effectiveCompilerArguments != null )
473             {
474                 for ( Map.Entry<String, String> me : effectiveCompilerArguments.entrySet() )
475                 {
476                     String key = me.getKey();
477                     String value = me.getValue();
478                     if ( !key.startsWith( "-" ) )
479                     {
480                         key = "-" + key;
481                     }
482 
483                     if ( key.startsWith( "-A" ) && StringUtils.isNotEmpty( value ) )
484                     {
485                         cplrArgsCopy.put( key + "=" + value, null );
486                     }
487                     else
488                     {
489                         cplrArgsCopy.put( key, value );
490                     }
491                 }
492             }
493             if ( !StringUtils.isEmpty( effectiveCompilerArgument ) )
494             {
495                 cplrArgsCopy.put( effectiveCompilerArgument, null );
496             }
497             compilerConfiguration.setCustomCompilerArguments( cplrArgsCopy );
498         }
499 
500         compilerConfiguration.setFork( fork );
501 
502         if ( fork )
503         {
504             if ( !StringUtils.isEmpty( meminitial ) )
505             {
506                 String value = getMemoryValue( meminitial );
507 
508                 if ( value != null )
509                 {
510                     compilerConfiguration.setMeminitial( value );
511                 }
512                 else
513                 {
514                     getLog().info( "Invalid value for meminitial '" + meminitial + "'. Ignoring this option." );
515                 }
516             }
517 
518             if ( !StringUtils.isEmpty( maxmem ) )
519             {
520                 String value = getMemoryValue( maxmem );
521 
522                 if ( value != null )
523                 {
524                     compilerConfiguration.setMaxmem( value );
525                 }
526                 else
527                 {
528                     getLog().info( "Invalid value for maxmem '" + maxmem + "'. Ignoring this option." );
529                 }
530             }
531         }
532 
533         compilerConfiguration.setExecutable( executable );
534 
535         compilerConfiguration.setWorkingDirectory( basedir );
536 
537         compilerConfiguration.setCompilerVersion( compilerVersion );
538 
539         compilerConfiguration.setBuildDirectory( buildDirectory );
540 
541         compilerConfiguration.setOutputFileName( outputFileName );
542 
543         if ( CompilerConfiguration.CompilerReuseStrategy.AlwaysNew.getStrategy().equals( this.compilerReuseStrategy ) )
544         {
545             compilerConfiguration.setCompilerReuseStrategy( CompilerConfiguration.CompilerReuseStrategy.AlwaysNew );
546         }
547         else if ( CompilerConfiguration.CompilerReuseStrategy.ReuseSame.getStrategy().equals(
548             this.compilerReuseStrategy ) )
549         {
550             if ( getRequestThreadCount() > 1 )
551             {
552                 if ( !skipMultiThreadWarning )
553                 {
554                     StringBuilder sb = new StringBuilder(
555                         "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." );
556                     sb.append( System.getProperty( "line.separator" ) );
557                     sb.append(
558                         "If your env is fine with reuseSame, you can skip this warning with the configuration field skipMultiThreadWarning or -Dmaven.compiler.skipMultiThreadWarning=true" );
559                     getLog().warn( sb.toString() );
560                 }
561             }
562             compilerConfiguration.setCompilerReuseStrategy( CompilerConfiguration.CompilerReuseStrategy.ReuseSame );
563         }
564         else
565         {
566 
567             compilerConfiguration.setCompilerReuseStrategy( CompilerConfiguration.CompilerReuseStrategy.ReuseCreated );
568         }
569 
570         getLog().debug( "CompilerReuseStrategy: " + compilerConfiguration.getCompilerReuseStrategy().getStrategy() );
571 
572         // TODO: have an option to always compile (without need to clean)
573         Set<File> staleSources;
574 
575         boolean canUpdateTarget;
576 
577         try
578         {
579             staleSources =
580                 computeStaleSources( compilerConfiguration, compiler, getSourceInclusionScanner( staleMillis ) );
581 
582             canUpdateTarget = compiler.canUpdateTarget( compilerConfiguration );
583 
584             if ( compiler.getCompilerOutputStyle().equals( CompilerOutputStyle.ONE_OUTPUT_FILE_FOR_ALL_INPUT_FILES )
585                 && !canUpdateTarget )
586             {
587                 getLog().info( "RESCANNING!" );
588                 // TODO: This second scan for source files is sub-optimal
589                 String inputFileEnding = compiler.getInputFileEnding( compilerConfiguration );
590 
591                 Set<File> sources = computeStaleSources( compilerConfiguration, compiler,
592                                                          getSourceInclusionScanner( inputFileEnding ) );
593 
594                 compilerConfiguration.setSourceFiles( sources );
595             }
596             else
597             {
598                 compilerConfiguration.setSourceFiles( staleSources );
599             }
600         }
601         catch ( CompilerException e )
602         {
603             throw new MojoExecutionException( "Error while computing stale sources.", e );
604         }
605 
606         if ( staleSources.isEmpty() )
607         {
608             getLog().info( "Nothing to compile - all classes are up to date" );
609 
610             return;
611         }
612 
613         // ----------------------------------------------------------------------
614         // Dump configuration
615         // ----------------------------------------------------------------------
616 
617         if ( getLog().isDebugEnabled() )
618         {
619             getLog().debug( "Classpath:" );
620 
621             for ( String s : getClasspathElements() )
622             {
623                 getLog().debug( " " + s );
624             }
625 
626             getLog().debug( "Source roots:" );
627 
628             for ( String root : getCompileSourceRoots() )
629             {
630                 getLog().debug( " " + root );
631             }
632 
633             try
634             {
635                 if ( fork )
636                 {
637                     if ( compilerConfiguration.getExecutable() != null )
638                     {
639                         getLog().debug( "Excutable: " );
640                         getLog().debug( " " + compilerConfiguration.getExecutable() );
641                     }
642                 }
643 
644                 String[] cl = compiler.createCommandLine( compilerConfiguration );
645                 if ( cl != null && cl.length > 0 )
646                 {
647                     StringBuilder sb = new StringBuilder();
648                     sb.append( cl[0] );
649                     for ( int i = 1; i < cl.length; i++ )
650                     {
651                         sb.append( " " );
652                         sb.append( cl[i] );
653                     }
654                     getLog().debug( "Command line options:" );
655                     getLog().debug( sb );
656                 }
657             }
658             catch ( CompilerException ce )
659             {
660                 getLog().debug( ce );
661             }
662         }
663 
664         // ----------------------------------------------------------------------
665         // Compile!
666         // ----------------------------------------------------------------------
667 
668         if ( StringUtils.isEmpty( compilerConfiguration.getSourceEncoding() ) )
669         {
670             getLog().warn( "File encoding has not been set, using platform encoding " + ReaderFactory.FILE_ENCODING
671                                + ", i.e. build is platform dependent!" );
672         }
673 
674         List<CompilerError> messages;
675 
676         try
677         {
678             messages = compiler.compile( compilerConfiguration );
679         }
680         catch ( Exception e )
681         {
682             // TODO: don't catch Exception
683             throw new MojoExecutionException( "Fatal error compiling", e );
684         }
685 
686         List<CompilerError> warnings = new ArrayList<CompilerError>();
687         List<CompilerError> errors = new ArrayList<CompilerError>();
688         if ( messages != null )
689         {
690             for ( CompilerError message : messages )
691             {
692                 if ( message.isError() )
693                 {
694                     errors.add( message );
695                 }
696                 else
697                 {
698                     warnings.add( message );
699                 }
700             }
701         }
702 
703         if ( failOnError && !errors.isEmpty() )
704         {
705             if ( !warnings.isEmpty() )
706             {
707                 getLog().info( "-------------------------------------------------------------" );
708                 getLog().warn( "COMPILATION WARNING : " );
709                 getLog().info( "-------------------------------------------------------------" );
710                 for ( CompilerError warning : warnings )
711                 {
712                     getLog().warn( warning.toString() );
713                 }
714                 getLog().info( warnings.size() + ( ( warnings.size() > 1 ) ? " warnings " : " warning" ) );
715                 getLog().info( "-------------------------------------------------------------" );
716             }
717 
718             getLog().info( "-------------------------------------------------------------" );
719             getLog().error( "COMPILATION ERROR : " );
720             getLog().info( "-------------------------------------------------------------" );
721 
722             for ( CompilerError error : errors )
723             {
724                 getLog().error( error.toString() );
725             }
726             getLog().info( errors.size() + ( ( errors.size() > 1 ) ? " errors " : " error" ) );
727             getLog().info( "-------------------------------------------------------------" );
728 
729             throw new CompilationFailureException( errors );
730         }
731         else
732         {
733             for ( CompilerError message : messages )
734             {
735                 getLog().warn( message.toString() );
736             }
737         }
738     }
739 
740     /**
741      * try to get thread count if a Maven 3 build, using reflection as the plugin must not be maven3 api dependant
742      *
743      * @return number of thread for this build or 1 if not multi-thread build
744      */
745     protected int getRequestThreadCount()
746     {
747         try
748         {
749             Method getRequestMethod = this.session.getClass().getMethod( "getRequest" );
750             Object mavenExecutionRequest = getRequestMethod.invoke( this.session );
751             Method getThreadCountMethod = mavenExecutionRequest.getClass().getMethod( "getThreadCount" );
752             String threadCount = (String) getThreadCountMethod.invoke( mavenExecutionRequest );
753             return Integer.valueOf( threadCount );
754         }
755         catch ( Exception e )
756         {
757             getLog().debug( "unable to get threadCount for the current build: " + e.getMessage() );
758         }
759         return 1;
760     }
761 
762     private String getMemoryValue( String setting )
763     {
764         String value = null;
765 
766         // Allow '128' or '128m'
767         if ( isDigits( setting ) )
768         {
769             value = setting + "m";
770         }
771         else
772         {
773             if ( ( isDigits( setting.substring( 0, setting.length() - 1 ) ) ) && ( setting.toLowerCase().endsWith(
774                 "m" ) ) )
775             {
776                 value = setting;
777             }
778         }
779         return value;
780     }
781 
782     //TODO remove the part with ToolchainManager lookup once we depend on
783     //3.0.9 (have it as prerequisite). Define as regular component field then.
784     private Toolchain getToolchain()
785     {
786         Toolchain tc = null;
787         if ( toolchainManager != null )
788         {
789             tc = toolchainManager.getToolchainFromBuildContext( "jdk", session );
790         }
791         return tc;
792     }
793 
794     private boolean isDigits( String string )
795     {
796         for ( int i = 0; i < string.length(); i++ )
797         {
798             if ( !Character.isDigit( string.charAt( i ) ) )
799             {
800                 return false;
801             }
802         }
803         return true;
804     }
805 
806     private Set<File> computeStaleSources( CompilerConfiguration compilerConfiguration, Compiler compiler,
807                                            SourceInclusionScanner scanner )
808         throws MojoExecutionException, CompilerException
809     {
810         CompilerOutputStyle outputStyle = compiler.getCompilerOutputStyle();
811 
812         SourceMapping mapping;
813 
814         File outputDirectory;
815 
816         if ( outputStyle == CompilerOutputStyle.ONE_OUTPUT_FILE_PER_INPUT_FILE )
817         {
818             mapping = new SuffixMapping( compiler.getInputFileEnding( compilerConfiguration ),
819                                          compiler.getOutputFileEnding( compilerConfiguration ) );
820 
821             outputDirectory = getOutputDirectory();
822         }
823         else if ( outputStyle == CompilerOutputStyle.ONE_OUTPUT_FILE_FOR_ALL_INPUT_FILES )
824         {
825             mapping = new SingleTargetSourceMapping( compiler.getInputFileEnding( compilerConfiguration ),
826                                                      compiler.getOutputFile( compilerConfiguration ) );
827 
828             outputDirectory = buildDirectory;
829         }
830         else
831         {
832             throw new MojoExecutionException( "Unknown compiler output style: '" + outputStyle + "'." );
833         }
834 
835         scanner.addSourceMapping( mapping );
836 
837         Set<File> staleSources = new HashSet<File>();
838 
839         for ( String sourceRoot : getCompileSourceRoots() )
840         {
841             File rootFile = new File( sourceRoot );
842 
843             if ( !rootFile.isDirectory() )
844             {
845                 continue;
846             }
847 
848             try
849             {
850                 staleSources.addAll( scanner.getIncludedSources( rootFile, outputDirectory ) );
851             }
852             catch ( InclusionScanException e )
853             {
854                 throw new MojoExecutionException(
855                     "Error scanning source root: \'" + sourceRoot + "\' for stale files to recompile.", e );
856             }
857         }
858 
859         return staleSources;
860     }
861 
862     /**
863      * @todo also in ant plugin. This should be resolved at some point so that it does not need to
864      * be calculated continuously - or should the plugins accept empty source roots as is?
865      */
866     private static List<String> removeEmptyCompileSourceRoots( List<String> compileSourceRootsList )
867     {
868         List<String> newCompileSourceRootsList = new ArrayList<String>();
869         if ( compileSourceRootsList != null )
870         {
871             // copy as I may be modifying it
872             for ( String srcDir : compileSourceRootsList )
873             {
874                 if ( !newCompileSourceRootsList.contains( srcDir ) && new File( srcDir ).exists() )
875                 {
876                     newCompileSourceRootsList.add( srcDir );
877                 }
878             }
879         }
880         return newCompileSourceRootsList;
881     }
882 }