Coverage Report - org.apache.maven.plugin.AbstractCompilerMojo
 
Classes in this File Line Coverage Branch Coverage Complexity
AbstractCompilerMojo
71 %
151/210
55 %
72/130
5,824
 
 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  15
 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  15
     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  15
     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  15
     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  15
         getLog().debug( "Using compiler '" + compilerId + "'." );
 369  
 
 370  
         try
 371  
         {
 372  15
             compiler = compilerManager.getCompiler( compilerId );
 373  
         }
 374  0
         catch ( NoSuchCompilerException e )
 375  
         {
 376  0
             throw new MojoExecutionException( "No such compiler '" + e.getCompilerId() + "'." );
 377  15
         }
 378  
 
 379  
         //-----------toolchains start here ----------------------------------
 380  
         //use the compilerId as identifier for toolchains as well.
 381  15
         Toolchain tc = getToolchain();
 382  15
         if ( tc != null )
 383  
         {
 384  0
             getLog().info( "Toolchain in compiler-plugin: " + tc );
 385  0
             if ( executable != null )
 386  
             {
 387  0
                 getLog().warn( "Toolchains are ignored, 'executable' parameter is set to " + executable );
 388  
             }
 389  
             else
 390  
             {
 391  0
                 fork = true;
 392  
                 //TODO somehow shaky dependency between compilerId and tool executable.
 393  0
                 executable = tc.findTool( compilerId );
 394  
             }
 395  
         }
 396  
         // ----------------------------------------------------------------------
 397  
         //
 398  
         // ----------------------------------------------------------------------
 399  
 
 400  15
         List<String> compileSourceRoots = removeEmptyCompileSourceRoots( getCompileSourceRoots() );
 401  
 
 402  15
         if ( compileSourceRoots.isEmpty() )
 403  
         {
 404  2
             getLog().info( "No sources to compile" );
 405  
 
 406  2
             return;
 407  
         }
 408  
 
 409  13
         if ( getLog().isDebugEnabled() )
 410  
         {
 411  13
             getLog().debug( "Source directories: " + compileSourceRoots.toString().replace( ',', '\n' ) );
 412  13
             getLog().debug( "Classpath: " + getClasspathElements().toString().replace( ',', '\n' ) );
 413  13
             getLog().debug( "Output directory: " + getOutputDirectory() );
 414  
         }
 415  
 
 416  
         // ----------------------------------------------------------------------
 417  
         // Create the compiler configuration
 418  
         // ----------------------------------------------------------------------
 419  
 
 420  13
         CompilerConfiguration compilerConfiguration = new CompilerConfiguration();
 421  
 
 422  13
         compilerConfiguration.setOutputLocation( getOutputDirectory().getAbsolutePath() );
 423  
 
 424  13
         compilerConfiguration.setClasspathEntries( getClasspathElements() );
 425  
 
 426  13
         compilerConfiguration.setSourceLocations( compileSourceRoots );
 427  
 
 428  13
         compilerConfiguration.setOptimize( optimize );
 429  
 
 430  13
         compilerConfiguration.setDebug( debug );
 431  
 
 432  13
         if ( debug && StringUtils.isNotEmpty( debuglevel ) )
 433  
         {
 434  0
             String[] split = StringUtils.split( debuglevel, "," );
 435  0
             for ( int i = 0; i < split.length; i++ )
 436  
             {
 437  0
                 if ( !( split[i].equalsIgnoreCase( "none" ) || split[i].equalsIgnoreCase( "lines" )
 438  
                     || split[i].equalsIgnoreCase( "vars" ) || split[i].equalsIgnoreCase( "source" ) ) )
 439  
                 {
 440  0
                     throw new IllegalArgumentException( "The specified debug level: '" + split[i] + "' is unsupported. "
 441  
                                                             + "Legal values are 'none', 'lines', 'vars', and 'source'." );
 442  
                 }
 443  
             }
 444  0
             compilerConfiguration.setDebugLevel( debuglevel );
 445  
         }
 446  
 
 447  13
         compilerConfiguration.setVerbose( verbose );
 448  
 
 449  13
         compilerConfiguration.setShowWarnings( showWarnings );
 450  
 
 451  13
         compilerConfiguration.setShowDeprecation( showDeprecation );
 452  
 
 453  13
         compilerConfiguration.setSourceVersion( getSource() );
 454  
 
 455  13
         compilerConfiguration.setTargetVersion( getTarget() );
 456  
 
 457  13
         compilerConfiguration.setProc( proc );
 458  
 
 459  13
         compilerConfiguration.setGeneratedSourcesDirectory( getGeneratedSourcesDirectory() );
 460  
 
 461  13
         compilerConfiguration.setAnnotationProcessors( annotationProcessors );
 462  
 
 463  13
         compilerConfiguration.setSourceEncoding( encoding );
 464  
 
 465  13
         Map<String, String> effectiveCompilerArguments = getCompilerArguments();
 466  
 
 467  13
         String effectiveCompilerArgument = getCompilerArgument();
 468  
 
 469  13
         if ( ( effectiveCompilerArguments != null ) || ( effectiveCompilerArgument != null ) )
 470  
         {
 471  1
             LinkedHashMap<String, String> cplrArgsCopy = new LinkedHashMap<String, String>();
 472  1
             if ( effectiveCompilerArguments != null )
 473  
             {
 474  1
                 for ( Map.Entry<String, String> me : effectiveCompilerArguments.entrySet() )
 475  
                 {
 476  1
                     String key = me.getKey();
 477  1
                     String value = me.getValue();
 478  1
                     if ( !key.startsWith( "-" ) )
 479  
                     {
 480  1
                         key = "-" + key;
 481  
                     }
 482  
 
 483  1
                     if ( key.startsWith( "-A" ) && StringUtils.isNotEmpty( value ) )
 484  
                     {
 485  0
                         cplrArgsCopy.put( key + "=" + value, null );
 486  
                     }
 487  
                     else
 488  
                     {
 489  1
                         cplrArgsCopy.put( key, value );
 490  
                     }
 491  1
                 }
 492  
             }
 493  1
             if ( !StringUtils.isEmpty( effectiveCompilerArgument ) )
 494  
             {
 495  1
                 cplrArgsCopy.put( effectiveCompilerArgument, null );
 496  
             }
 497  1
             compilerConfiguration.setCustomCompilerArguments( cplrArgsCopy );
 498  
         }
 499  
 
 500  13
         compilerConfiguration.setFork( fork );
 501  
 
 502  13
         if ( fork )
 503  
         {
 504  2
             if ( !StringUtils.isEmpty( meminitial ) )
 505  
             {
 506  2
                 String value = getMemoryValue( meminitial );
 507  
 
 508  2
                 if ( value != null )
 509  
                 {
 510  2
                     compilerConfiguration.setMeminitial( value );
 511  
                 }
 512  
                 else
 513  
                 {
 514  0
                     getLog().info( "Invalid value for meminitial '" + meminitial + "'. Ignoring this option." );
 515  
                 }
 516  
             }
 517  
 
 518  2
             if ( !StringUtils.isEmpty( maxmem ) )
 519  
             {
 520  2
                 String value = getMemoryValue( maxmem );
 521  
 
 522  2
                 if ( value != null )
 523  
                 {
 524  2
                     compilerConfiguration.setMaxmem( value );
 525  
                 }
 526  
                 else
 527  
                 {
 528  0
                     getLog().info( "Invalid value for maxmem '" + maxmem + "'. Ignoring this option." );
 529  
                 }
 530  
             }
 531  
         }
 532  
 
 533  13
         compilerConfiguration.setExecutable( executable );
 534  
 
 535  13
         compilerConfiguration.setWorkingDirectory( basedir );
 536  
 
 537  13
         compilerConfiguration.setCompilerVersion( compilerVersion );
 538  
 
 539  13
         compilerConfiguration.setBuildDirectory( buildDirectory );
 540  
 
 541  13
         compilerConfiguration.setOutputFileName( outputFileName );
 542  
 
 543  13
         if ( CompilerConfiguration.CompilerReuseStrategy.AlwaysNew.getStrategy().equals( this.compilerReuseStrategy ) )
 544  
         {
 545  0
             compilerConfiguration.setCompilerReuseStrategy( CompilerConfiguration.CompilerReuseStrategy.AlwaysNew );
 546  
         }
 547  13
         else if ( CompilerConfiguration.CompilerReuseStrategy.ReuseSame.getStrategy().equals(
 548  
             this.compilerReuseStrategy ) )
 549  
         {
 550  0
             if ( getRequestThreadCount() > 1 )
 551  
             {
 552  0
                 if ( !skipMultiThreadWarning )
 553  
                 {
 554  0
                     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  0
                     sb.append( System.getProperty( "line.separator" ) );
 557  0
                     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  0
                     getLog().warn( sb.toString() );
 560  
                 }
 561  
             }
 562  0
             compilerConfiguration.setCompilerReuseStrategy( CompilerConfiguration.CompilerReuseStrategy.ReuseSame );
 563  
         }
 564  
         else
 565  
         {
 566  
 
 567  13
             compilerConfiguration.setCompilerReuseStrategy( CompilerConfiguration.CompilerReuseStrategy.ReuseCreated );
 568  
         }
 569  
 
 570  13
         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  13
             staleSources =
 580  
                 computeStaleSources( compilerConfiguration, compiler, getSourceInclusionScanner( staleMillis ) );
 581  
 
 582  13
             canUpdateTarget = compiler.canUpdateTarget( compilerConfiguration );
 583  
 
 584  13
             if ( compiler.getCompilerOutputStyle().equals( CompilerOutputStyle.ONE_OUTPUT_FILE_FOR_ALL_INPUT_FILES )
 585  
                 && !canUpdateTarget )
 586  
             {
 587  7
                 getLog().info( "RESCANNING!" );
 588  
                 // TODO: This second scan for source files is sub-optimal
 589  7
                 String inputFileEnding = compiler.getInputFileEnding( compilerConfiguration );
 590  
 
 591  7
                 Set<File> sources = computeStaleSources( compilerConfiguration, compiler,
 592  
                                                          getSourceInclusionScanner( inputFileEnding ) );
 593  
 
 594  7
                 compilerConfiguration.setSourceFiles( sources );
 595  7
             }
 596  
             else
 597  
             {
 598  6
                 compilerConfiguration.setSourceFiles( staleSources );
 599  
             }
 600  
         }
 601  0
         catch ( CompilerException e )
 602  
         {
 603  0
             throw new MojoExecutionException( "Error while computing stale sources.", e );
 604  13
         }
 605  
 
 606  13
         if ( staleSources.isEmpty() )
 607  
         {
 608  6
             getLog().info( "Nothing to compile - all classes are up to date" );
 609  
 
 610  6
             return;
 611  
         }
 612  
 
 613  
         // ----------------------------------------------------------------------
 614  
         // Dump configuration
 615  
         // ----------------------------------------------------------------------
 616  
 
 617  7
         if ( getLog().isDebugEnabled() )
 618  
         {
 619  7
             getLog().debug( "Classpath:" );
 620  
 
 621  7
             for ( String s : getClasspathElements() )
 622  
             {
 623  4
                 getLog().debug( " " + s );
 624  
             }
 625  
 
 626  7
             getLog().debug( "Source roots:" );
 627  
 
 628  7
             for ( String root : getCompileSourceRoots() )
 629  
             {
 630  7
                 getLog().debug( " " + root );
 631  
             }
 632  
 
 633  
             try
 634  
             {
 635  7
                 if ( fork )
 636  
                 {
 637  0
                     if ( compilerConfiguration.getExecutable() != null )
 638  
                     {
 639  0
                         getLog().debug( "Excutable: " );
 640  0
                         getLog().debug( " " + compilerConfiguration.getExecutable() );
 641  
                     }
 642  
                 }
 643  
 
 644  7
                 String[] cl = compiler.createCommandLine( compilerConfiguration );
 645  7
                 if ( cl != null && cl.length > 0 )
 646  
                 {
 647  0
                     StringBuilder sb = new StringBuilder();
 648  0
                     sb.append( cl[0] );
 649  0
                     for ( int i = 1; i < cl.length; i++ )
 650  
                     {
 651  0
                         sb.append( " " );
 652  0
                         sb.append( cl[i] );
 653  
                     }
 654  0
                     getLog().debug( "Command line options:" );
 655  0
                     getLog().debug( sb );
 656  
                 }
 657  
             }
 658  0
             catch ( CompilerException ce )
 659  
             {
 660  0
                 getLog().debug( ce );
 661  7
             }
 662  
         }
 663  
 
 664  
         // ----------------------------------------------------------------------
 665  
         // Compile!
 666  
         // ----------------------------------------------------------------------
 667  
 
 668  7
         if ( StringUtils.isEmpty( compilerConfiguration.getSourceEncoding() ) )
 669  
         {
 670  7
             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  7
             messages = compiler.compile( compilerConfiguration );
 679  
         }
 680  0
         catch ( Exception e )
 681  
         {
 682  
             // TODO: don't catch Exception
 683  0
             throw new MojoExecutionException( "Fatal error compiling", e );
 684  7
         }
 685  
 
 686  7
         List<CompilerError> warnings = new ArrayList<CompilerError>();
 687  7
         List<CompilerError> errors = new ArrayList<CompilerError>();
 688  7
         if ( messages != null )
 689  
         {
 690  7
             for ( CompilerError message : messages )
 691  
             {
 692  7
                 if ( message.isError() )
 693  
                 {
 694  2
                     errors.add( message );
 695  
                 }
 696  
                 else
 697  
                 {
 698  5
                     warnings.add( message );
 699  
                 }
 700  
             }
 701  
         }
 702  
 
 703  7
         if ( failOnError && !errors.isEmpty() )
 704  
         {
 705  1
             if ( !warnings.isEmpty() )
 706  
             {
 707  0
                 getLog().info( "-------------------------------------------------------------" );
 708  0
                 getLog().warn( "COMPILATION WARNING : " );
 709  0
                 getLog().info( "-------------------------------------------------------------" );
 710  0
                 for ( CompilerError warning : warnings )
 711  
                 {
 712  0
                     getLog().warn( warning.toString() );
 713  
                 }
 714  0
                 getLog().info( warnings.size() + ( ( warnings.size() > 1 ) ? " warnings " : " warning" ) );
 715  0
                 getLog().info( "-------------------------------------------------------------" );
 716  
             }
 717  
 
 718  1
             getLog().info( "-------------------------------------------------------------" );
 719  1
             getLog().error( "COMPILATION ERROR : " );
 720  1
             getLog().info( "-------------------------------------------------------------" );
 721  
 
 722  1
             for ( CompilerError error : errors )
 723  
             {
 724  1
                 getLog().error( error.toString() );
 725  
             }
 726  1
             getLog().info( errors.size() + ( ( errors.size() > 1 ) ? " errors " : " error" ) );
 727  1
             getLog().info( "-------------------------------------------------------------" );
 728  
 
 729  1
             throw new CompilationFailureException( errors );
 730  
         }
 731  
         else
 732  
         {
 733  6
             for ( CompilerError message : messages )
 734  
             {
 735  6
                 getLog().warn( message.toString() );
 736  
             }
 737  
         }
 738  6
     }
 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  0
             Method getRequestMethod = this.session.getClass().getMethod( "getRequest" );
 750  0
             Object mavenExecutionRequest = getRequestMethod.invoke( this.session );
 751  0
             Method getThreadCountMethod = mavenExecutionRequest.getClass().getMethod( "getThreadCount" );
 752  0
             String threadCount = (String) getThreadCountMethod.invoke( mavenExecutionRequest );
 753  0
             return Integer.valueOf( threadCount );
 754  
         }
 755  0
         catch ( Exception e )
 756  
         {
 757  0
             getLog().debug( "unable to get threadCount for the current build: " + e.getMessage() );
 758  
         }
 759  0
         return 1;
 760  
     }
 761  
 
 762  
     private String getMemoryValue( String setting )
 763  
     {
 764  4
         String value = null;
 765  
 
 766  
         // Allow '128' or '128m'
 767  4
         if ( isDigits( setting ) )
 768  
         {
 769  0
             value = setting + "m";
 770  
         }
 771  
         else
 772  
         {
 773  4
             if ( ( isDigits( setting.substring( 0, setting.length() - 1 ) ) ) && ( setting.toLowerCase().endsWith(
 774  
                 "m" ) ) )
 775  
             {
 776  4
                 value = setting;
 777  
             }
 778  
         }
 779  4
         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  15
         Toolchain tc = null;
 787  15
         if ( toolchainManager != null )
 788  
         {
 789  15
             tc = toolchainManager.getToolchainFromBuildContext( "jdk", session );
 790  
         }
 791  15
         return tc;
 792  
     }
 793  
 
 794  
     private boolean isDigits( String string )
 795  
     {
 796  28
         for ( int i = 0; i < string.length(); i++ )
 797  
         {
 798  24
             if ( !Character.isDigit( string.charAt( i ) ) )
 799  
             {
 800  4
                 return false;
 801  
             }
 802  
         }
 803  4
         return true;
 804  
     }
 805  
 
 806  
     private Set<File> computeStaleSources( CompilerConfiguration compilerConfiguration, Compiler compiler,
 807  
                                            SourceInclusionScanner scanner )
 808  
         throws MojoExecutionException, CompilerException
 809  
     {
 810  20
         CompilerOutputStyle outputStyle = compiler.getCompilerOutputStyle();
 811  
 
 812  
         SourceMapping mapping;
 813  
 
 814  
         File outputDirectory;
 815  
 
 816  20
         if ( outputStyle == CompilerOutputStyle.ONE_OUTPUT_FILE_PER_INPUT_FILE )
 817  
         {
 818  6
             mapping = new SuffixMapping( compiler.getInputFileEnding( compilerConfiguration ),
 819  
                                          compiler.getOutputFileEnding( compilerConfiguration ) );
 820  
 
 821  6
             outputDirectory = getOutputDirectory();
 822  
         }
 823  14
         else if ( outputStyle == CompilerOutputStyle.ONE_OUTPUT_FILE_FOR_ALL_INPUT_FILES )
 824  
         {
 825  14
             mapping = new SingleTargetSourceMapping( compiler.getInputFileEnding( compilerConfiguration ),
 826  
                                                      compiler.getOutputFile( compilerConfiguration ) );
 827  
 
 828  14
             outputDirectory = buildDirectory;
 829  
         }
 830  
         else
 831  
         {
 832  0
             throw new MojoExecutionException( "Unknown compiler output style: '" + outputStyle + "'." );
 833  
         }
 834  
 
 835  20
         scanner.addSourceMapping( mapping );
 836  
 
 837  20
         Set<File> staleSources = new HashSet<File>();
 838  
 
 839  20
         for ( String sourceRoot : getCompileSourceRoots() )
 840  
         {
 841  20
             File rootFile = new File( sourceRoot );
 842  
 
 843  20
             if ( !rootFile.isDirectory() )
 844  
             {
 845  0
                 continue;
 846  
             }
 847  
 
 848  
             try
 849  
             {
 850  20
                 staleSources.addAll( scanner.getIncludedSources( rootFile, outputDirectory ) );
 851  
             }
 852  0
             catch ( InclusionScanException e )
 853  
             {
 854  0
                 throw new MojoExecutionException(
 855  
                     "Error scanning source root: \'" + sourceRoot + "\' for stale files to recompile.", e );
 856  20
             }
 857  20
         }
 858  
 
 859  20
         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  15
         List<String> newCompileSourceRootsList = new ArrayList<String>();
 869  15
         if ( compileSourceRootsList != null )
 870  
         {
 871  
             // copy as I may be modifying it
 872  15
             for ( String srcDir : compileSourceRootsList )
 873  
             {
 874  15
                 if ( !newCompileSourceRootsList.contains( srcDir ) && new File( srcDir ).exists() )
 875  
                 {
 876  13
                     newCompileSourceRootsList.add( srcDir );
 877  
                 }
 878  
             }
 879  
         }
 880  15
         return newCompileSourceRootsList;
 881  
     }
 882  
 }