Coverage Report - org.apache.maven.plugin.compiler.AbstractCompilerMojo
 
Classes in this File Line Coverage Branch Coverage Complexity
AbstractCompilerMojo
71 %
195/273
57 %
97/170
5,667
 
 1  
 package org.apache.maven.plugin.compiler;
 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.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  
  * TODO: At least one step could be optimized, currently the plugin will do two
 61  
  * scans of all the source code if the compiler has to have the entire set of
 62  
  * sources. This is currently the case for at least the C# compiler and most
 63  
  * likely all the other .NET compilers too.
 64  
  *
 65  
  * @author others
 66  
  * @author <a href="mailto:trygvis@inamo.no">Trygve Laugst&oslash;l</a>
 67  
  * @version $Id: AbstractCompilerMojo.java 1410034 2012-11-15 21:40:58Z olamy $
 68  
  * @since 2.0
 69  
  */
 70  15
 public abstract class AbstractCompilerMojo
 71  
     extends AbstractMojo
 72  
 {
 73  
     // ----------------------------------------------------------------------
 74  
     // Configurables
 75  
     // ----------------------------------------------------------------------
 76  
 
 77  
     /**
 78  
      * Indicates whether the build will continue even if there are compilation errors.
 79  
      *
 80  
      * @since 2.0.2
 81  
      */
 82  15
     @Parameter ( property = "maven.compiler.failOnError", defaultValue = "true" )
 83  
     private boolean failOnError = true;
 84  
 
 85  
     /**
 86  
      * Set to <code>true</code> to include debugging information in the compiled class files.
 87  
      */
 88  15
     @Parameter ( property = "maven.compiler.debug", defaultValue = "true" )
 89  
     private boolean debug = true;
 90  
 
 91  
     /**
 92  
      * Set to <code>true</code> to show messages about what the compiler is doing.
 93  
      */
 94  
     @Parameter ( property = "maven.compiler.verbose", defaultValue = "false" )
 95  
     private boolean verbose;
 96  
 
 97  
     /**
 98  
      * Sets whether to show source locations where deprecated APIs are used.
 99  
      */
 100  
     @Parameter ( property = "maven.compiler.showDeprecation", defaultValue = "false" )
 101  
     private boolean showDeprecation;
 102  
 
 103  
     /**
 104  
      * Set to <code>true</code> to optimize the compiled code using the compiler's optimization methods.
 105  
      */
 106  
     @Parameter ( property = "maven.compiler.optimize", defaultValue = "false" )
 107  
     private boolean optimize;
 108  
 
 109  
     /**
 110  
      * Set to <code>true</code> to show compilation warnings.
 111  
      */
 112  
     @Parameter ( property = "maven.compiler.showWarnings", defaultValue = "false" )
 113  
     private boolean showWarnings;
 114  
 
 115  
     /**
 116  
      * The -source argument for the Java compiler.
 117  
      */
 118  
     @Parameter ( property = "maven.compiler.source", defaultValue = "1.5" )
 119  
     protected String source;
 120  
 
 121  
     /**
 122  
      * The -target argument for the Java compiler.
 123  
      */
 124  
     @Parameter ( property = "maven.compiler.target", defaultValue = "1.5" )
 125  
     protected String target;
 126  
 
 127  
     /**
 128  
      * The -encoding argument for the Java compiler.
 129  
      *
 130  
      * @since 2.1
 131  
      */
 132  
     @Parameter ( property = "encoding", defaultValue = "${project.build.sourceEncoding}" )
 133  
     private String encoding;
 134  
 
 135  
     /**
 136  
      * Sets the granularity in milliseconds of the last modification
 137  
      * date for testing whether a source needs recompilation.
 138  
      */
 139  
     @Parameter ( property = "lastModGranularityMs", defaultValue = "0" )
 140  
     private int staleMillis;
 141  
 
 142  
     /**
 143  
      * The compiler id of the compiler to use. See this
 144  
      * <a href="non-javac-compilers.html">guide</a> for more information.
 145  
      */
 146  
     @Parameter ( property = "maven.compiler.compilerId", defaultValue = "javac" )
 147  
     private String compilerId;
 148  
 
 149  
     /**
 150  
      * Version of the compiler to use, ex. "1.3", "1.5", if {@link #fork} is set to <code>true</code>.
 151  
      */
 152  
     @Parameter ( property = "maven.compiler.compilerVersion" )
 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 ( property = "maven.compiler.fork", defaultValue = "false" )
 160  
     private boolean fork;
 161  
 
 162  
     /**
 163  
      * Initial size, in megabytes, of the memory allocation pool, ex. "64", "64m"
 164  
      * if {@link #fork} is set to <code>true</code>.
 165  
      *
 166  
      * @since 2.0.1
 167  
      */
 168  
     @Parameter ( property = "maven.compiler.meminitial" )
 169  
     private String meminitial;
 170  
 
 171  
     /**
 172  
      * Sets the maximum size, in megabytes, of the memory allocation pool, ex. "128", "128m"
 173  
      * if {@link #fork} is set to <code>true</code>.
 174  
      *
 175  
      * @since 2.0.1
 176  
      */
 177  
     @Parameter ( property = "maven.compiler.maxmem" )
 178  
     private String maxmem;
 179  
 
 180  
     /**
 181  
      * Sets the executable of the compiler to use when {@link #fork} is <code>true</code>.
 182  
      */
 183  
     @Parameter ( property = "maven.compiler.executable" )
 184  
     private String executable;
 185  
 
 186  
     /**
 187  
      * <p>
 188  
      * Sets whether annotation processing is performed or not. Only applies to JDK 1.6+
 189  
      * If not set, both compilation and annotation processing are performed at the same time.
 190  
      * </p>
 191  
      * <p>Allowed values are:</p>
 192  
      * <ul>
 193  
      * <li><code>none</code> - no annotation processing is performed.</li>
 194  
      * <li><code>only</code> - only annotation processing is done, no compilation.</li>
 195  
      * </ul>
 196  
      *
 197  
      * @since 2.2
 198  
      */
 199  
     @Parameter
 200  
     private String proc;
 201  
 
 202  
     /**
 203  
      * <p>
 204  
      * Names of annotation processors to run. Only applies to JDK 1.6+
 205  
      * If not set, the default annotation processors discovery process applies.
 206  
      * </p>
 207  
      *
 208  
      * @since 2.2
 209  
      */
 210  
     @Parameter
 211  
     private String[] annotationProcessors;
 212  
 
 213  
     /**
 214  
      * <p>
 215  
      * Sets the arguments to be passed to the compiler (prepending a dash) if {@link #fork} is set to <code>true</code>.
 216  
      * </p>
 217  
      * <p>
 218  
      * This is because the list of valid arguments passed to a Java compiler
 219  
      * varies based on the compiler version.
 220  
      * </p>
 221  
      * <p>
 222  
      * To pass <code>-Xmaxerrs 1000 -Xlint -Xlint:-path -Averbose=true</code> you should include the following:
 223  
      * </p>
 224  
      * <pre>
 225  
      * &lt;compilerArguments&gt;
 226  
      *   &lt;Xmaxerrs&gt;1000&lt;/Xmaxerrs&gt;
 227  
      *   &lt;Xlint/&gt;
 228  
      *   &lt;Xlint:-path/&gt;
 229  
      *   &lt;Averbose&gt;true&lt;/Averbose&gt;
 230  
      * &lt;/compilerArguments&gt;
 231  
      * </pre>
 232  
      *
 233  
      * @since 2.0.1
 234  
      */
 235  
     @Parameter
 236  
     protected Map<String, String> compilerArguments;
 237  
 
 238  
     /**
 239  
      * <p>
 240  
      * Sets the unformatted single argument string to be passed to the compiler if {@link #fork} is set to <code>true</code>.
 241  
      * To pass multiple arguments such as <code>-Xmaxerrs 1000</code> (which are actually two arguments) you have to use {@link #compilerArguments}.
 242  
      * </p>
 243  
      * <p>
 244  
      * This is because the list of valid arguments passed to a Java compiler
 245  
      * varies based on the compiler version.
 246  
      * </p>
 247  
      */
 248  
     @Parameter
 249  
     protected String compilerArgument;
 250  
 
 251  
     /**
 252  
      * Sets the name of the output file when compiling a set of
 253  
      * sources to a single file.
 254  
      * <p/>
 255  
      * expression="${project.build.finalName}"
 256  
      */
 257  
     @Parameter
 258  
     private String outputFileName;
 259  
 
 260  
     /**
 261  
      * Keyword list to be appended to the <code>-g</code> command-line switch. Legal values are none or a
 262  
      * comma-separated list of the following keywords: <code>lines</code>, <code>vars</code>, and <code>source</code>.
 263  
      * If debug level is not specified, by default, nothing will be appended to <code>-g</code>.
 264  
      * If debug is not turned on, this attribute will be ignored.
 265  
      *
 266  
      * @since 2.1
 267  
      */
 268  
     @Parameter ( property = "maven.compiler.debuglevel" )
 269  
     private String debuglevel;
 270  
 
 271  
     /**
 272  
      *
 273  
      */
 274  
     @Component
 275  
     private ToolchainManager toolchainManager;
 276  
 
 277  
     // ----------------------------------------------------------------------
 278  
     // Read-only parameters
 279  
     // ----------------------------------------------------------------------
 280  
 
 281  
     /**
 282  
      * The directory to run the compiler from if fork is true.
 283  
      */
 284  
     @Parameter ( defaultValue = "${basedir}", required = true, readonly = true )
 285  
     private File basedir;
 286  
 
 287  
     /**
 288  
      * The target directory of the compiler if fork is true.
 289  
      */
 290  
     @Parameter ( defaultValue = "${project.build.directory}", required = true, readonly = true )
 291  
     private File buildDirectory;
 292  
 
 293  
     /**
 294  
      * Plexus compiler manager.
 295  
      */
 296  
     @Component
 297  
     private CompilerManager compilerManager;
 298  
 
 299  
     /**
 300  
      * The current build session instance. This is used for toolchain manager API calls.
 301  
      */
 302  
     @Component
 303  
     private MavenSession session;
 304  
 
 305  
     /**
 306  
      * Strategy to re use javacc class created:
 307  
      * <ul>
 308  
      * <li><code>reuseCreated</code> (default): will reuse already created but in case of multi-threaded builds,
 309  
      * each thread will have its own instance</li>
 310  
      * <li><code>reuseSame</code>: the same Javacc class will be used for each compilation even for multi-threaded build</li>
 311  
      * <li><code>alwaysNew</code>: a new Javacc class will be created for each compilation</li>
 312  
      * </ul>
 313  
      * Note this parameter value depends on the os/jdk you are using, but the default value should work on most of env.
 314  
      *
 315  
      * @since 2.5
 316  
      */
 317  15
     @Parameter ( defaultValue = "${reuseCreated}", property = "maven.compiler.compilerReuseStrategy" )
 318  
     private String compilerReuseStrategy = "reuseCreated";
 319  
 
 320  
     /**
 321  
      * @since 2.5
 322  
      */
 323  
     @Parameter ( defaultValue = "false", property = "maven.compiler.skipMultiThreadWarning" )
 324  
     private boolean skipMultiThreadWarning;
 325  
 
 326  
     /**
 327  
      * compiler can now use javax.tools if available in your current jdk, you can disable this feature
 328  
      * using -Dmaven.compiler.forceJavacCompilerUse=true or in the plugin configuration
 329  
      *
 330  
      * @since 2.6
 331  
      */
 332  
     @Parameter ( defaultValue = "false", property = "maven.compiler.forceJavacCompilerUse" )
 333  
     private boolean forceJavacCompilerUse;
 334  
 
 335  
     /**
 336  
      * @since 2.6 needed for storing the status for the incremental build support.
 337  
      */
 338  
     @Parameter ( property = "mojoExecution" )
 339  
     private MojoExecution mojoExecution;
 340  
 
 341  
     /**
 342  
      * We need this to determine the start timestamp of the build.
 343  
      *
 344  
      * @since 2.6
 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  
         // Look up the compiler. This is done before other code than can
 374  
         // cause the mojo to return before the lookup is done possibly resulting
 375  
         // in misconfigured POMs still building.
 376  
         // ----------------------------------------------------------------------
 377  
 
 378  
         Compiler compiler;
 379  
 
 380  15
         getLog().debug( "Using compiler '" + compilerId + "'." );
 381  
 
 382  
         try
 383  
         {
 384  15
             compiler = compilerManager.getCompiler( compilerId );
 385  
         }
 386  0
         catch ( NoSuchCompilerException e )
 387  
         {
 388  0
             throw new MojoExecutionException( "No such compiler '" + e.getCompilerId() + "'." );
 389  15
         }
 390  
 
 391  
         //-----------toolchains start here ----------------------------------
 392  
         //use the compilerId as identifier for toolchains as well.
 393  15
         Toolchain tc = getToolchain();
 394  15
         if ( tc != null )
 395  
         {
 396  0
             getLog().info( "Toolchain in compiler-plugin: " + tc );
 397  0
             if ( executable != null )
 398  
             {
 399  0
                 getLog().warn( "Toolchains are ignored, 'executable' parameter is set to " + executable );
 400  
             }
 401  
             else
 402  
             {
 403  0
                 fork = true;
 404  
                 //TODO somehow shaky dependency between compilerId and tool executable.
 405  0
                 executable = tc.findTool( compilerId );
 406  
             }
 407  
         }
 408  
         // ----------------------------------------------------------------------
 409  
         //
 410  
         // ----------------------------------------------------------------------
 411  
 
 412  15
         List<String> compileSourceRoots = removeEmptyCompileSourceRoots( getCompileSourceRoots() );
 413  
 
 414  15
         if ( compileSourceRoots.isEmpty() )
 415  
         {
 416  2
             getLog().info( "No sources to compile" );
 417  
 
 418  2
             return;
 419  
         }
 420  
 
 421  13
         if ( getLog().isDebugEnabled() )
 422  
         {
 423  13
             getLog().debug( "Source directories: " + compileSourceRoots.toString().replace( ',', '\n' ) );
 424  13
             getLog().debug( "Classpath: " + getClasspathElements().toString().replace( ',', '\n' ) );
 425  13
             getLog().debug( "Output directory: " + getOutputDirectory() );
 426  
         }
 427  
 
 428  
         // ----------------------------------------------------------------------
 429  
         // Create the compiler configuration
 430  
         // ----------------------------------------------------------------------
 431  
 
 432  13
         CompilerConfiguration compilerConfiguration = new CompilerConfiguration();
 433  
 
 434  13
         compilerConfiguration.setOutputLocation( getOutputDirectory().getAbsolutePath() );
 435  
 
 436  13
         compilerConfiguration.setClasspathEntries( getClasspathElements() );
 437  
 
 438  13
         compilerConfiguration.setSourceLocations( compileSourceRoots );
 439  
 
 440  13
         compilerConfiguration.setOptimize( optimize );
 441  
 
 442  13
         compilerConfiguration.setDebug( debug );
 443  
 
 444  13
         if ( debug && StringUtils.isNotEmpty( debuglevel ) )
 445  
         {
 446  0
             String[] split = StringUtils.split( debuglevel, "," );
 447  0
             for ( int i = 0; i < split.length; i++ )
 448  
             {
 449  0
                 if ( !( split[i].equalsIgnoreCase( "none" ) || split[i].equalsIgnoreCase( "lines" )
 450  
                     || split[i].equalsIgnoreCase( "vars" ) || split[i].equalsIgnoreCase( "source" ) ) )
 451  
                 {
 452  0
                     throw new IllegalArgumentException( "The specified debug level: '" + split[i] + "' is unsupported. "
 453  
                                                             + "Legal values are 'none', 'lines', 'vars', and 'source'." );
 454  
                 }
 455  
             }
 456  0
             compilerConfiguration.setDebugLevel( debuglevel );
 457  
         }
 458  
 
 459  13
         compilerConfiguration.setVerbose( verbose );
 460  
 
 461  13
         compilerConfiguration.setShowWarnings( showWarnings );
 462  
 
 463  13
         compilerConfiguration.setShowDeprecation( showDeprecation );
 464  
 
 465  13
         compilerConfiguration.setSourceVersion( getSource() );
 466  
 
 467  13
         compilerConfiguration.setTargetVersion( getTarget() );
 468  
 
 469  13
         compilerConfiguration.setProc( proc );
 470  
 
 471  13
         compilerConfiguration.setGeneratedSourcesDirectory( getGeneratedSourcesDirectory() );
 472  
 
 473  13
         compilerConfiguration.setAnnotationProcessors( annotationProcessors );
 474  
 
 475  13
         compilerConfiguration.setSourceEncoding( encoding );
 476  
 
 477  13
         Map<String, String> effectiveCompilerArguments = getCompilerArguments();
 478  
 
 479  13
         String effectiveCompilerArgument = getCompilerArgument();
 480  
 
 481  13
         if ( ( effectiveCompilerArguments != null ) || ( effectiveCompilerArgument != null ) )
 482  
         {
 483  1
             LinkedHashMap<String, String> cplrArgsCopy = new LinkedHashMap<String, String>();
 484  1
             if ( effectiveCompilerArguments != null )
 485  
             {
 486  1
                 for ( Map.Entry<String, String> me : effectiveCompilerArguments.entrySet() )
 487  
                 {
 488  1
                     String key = me.getKey();
 489  1
                     String value = me.getValue();
 490  1
                     if ( !key.startsWith( "-" ) )
 491  
                     {
 492  1
                         key = "-" + key;
 493  
                     }
 494  
 
 495  1
                     if ( key.startsWith( "-A" ) && StringUtils.isNotEmpty( value ) )
 496  
                     {
 497  0
                         cplrArgsCopy.put( key + "=" + value, null );
 498  
                     }
 499  
                     else
 500  
                     {
 501  1
                         cplrArgsCopy.put( key, value );
 502  
                     }
 503  1
                 }
 504  
             }
 505  1
             if ( !StringUtils.isEmpty( effectiveCompilerArgument ) )
 506  
             {
 507  1
                 cplrArgsCopy.put( effectiveCompilerArgument, null );
 508  
             }
 509  1
             compilerConfiguration.setCustomCompilerArguments( cplrArgsCopy );
 510  
         }
 511  
 
 512  13
         compilerConfiguration.setFork( fork );
 513  
 
 514  13
         if ( fork )
 515  
         {
 516  2
             if ( !StringUtils.isEmpty( meminitial ) )
 517  
             {
 518  2
                 String value = getMemoryValue( meminitial );
 519  
 
 520  2
                 if ( value != null )
 521  
                 {
 522  2
                     compilerConfiguration.setMeminitial( value );
 523  
                 }
 524  
                 else
 525  
                 {
 526  0
                     getLog().info( "Invalid value for meminitial '" + meminitial + "'. Ignoring this option." );
 527  
                 }
 528  
             }
 529  
 
 530  2
             if ( !StringUtils.isEmpty( maxmem ) )
 531  
             {
 532  2
                 String value = getMemoryValue( maxmem );
 533  
 
 534  2
                 if ( value != null )
 535  
                 {
 536  2
                     compilerConfiguration.setMaxmem( value );
 537  
                 }
 538  
                 else
 539  
                 {
 540  0
                     getLog().info( "Invalid value for maxmem '" + maxmem + "'. Ignoring this option." );
 541  
                 }
 542  
             }
 543  
         }
 544  
 
 545  13
         compilerConfiguration.setExecutable( executable );
 546  
 
 547  13
         compilerConfiguration.setWorkingDirectory( basedir );
 548  
 
 549  13
         compilerConfiguration.setCompilerVersion( compilerVersion );
 550  
 
 551  13
         compilerConfiguration.setBuildDirectory( buildDirectory );
 552  
 
 553  13
         compilerConfiguration.setOutputFileName( outputFileName );
 554  
 
 555  13
         if ( CompilerConfiguration.CompilerReuseStrategy.AlwaysNew.getStrategy().equals( this.compilerReuseStrategy ) )
 556  
         {
 557  0
             compilerConfiguration.setCompilerReuseStrategy( CompilerConfiguration.CompilerReuseStrategy.AlwaysNew );
 558  
         }
 559  13
         else if ( CompilerConfiguration.CompilerReuseStrategy.ReuseSame.getStrategy().equals(
 560  
             this.compilerReuseStrategy ) )
 561  
         {
 562  0
             if ( getRequestThreadCount() > 1 )
 563  
             {
 564  0
                 if ( !skipMultiThreadWarning )
 565  
                 {
 566  0
                     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  0
                     sb.append( System.getProperty( "line.separator" ) );
 569  0
                     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  0
                     getLog().warn( sb.toString() );
 572  
                 }
 573  
             }
 574  0
             compilerConfiguration.setCompilerReuseStrategy( CompilerConfiguration.CompilerReuseStrategy.ReuseSame );
 575  
         }
 576  
         else
 577  
         {
 578  
 
 579  13
             compilerConfiguration.setCompilerReuseStrategy( CompilerConfiguration.CompilerReuseStrategy.ReuseCreated );
 580  
         }
 581  
 
 582  13
         getLog().debug( "CompilerReuseStrategy: " + compilerConfiguration.getCompilerReuseStrategy().getStrategy() );
 583  
 
 584  13
         compilerConfiguration.setForceJavacCompilerUse( forceJavacCompilerUse );
 585  
 
 586  
         boolean canUpdateTarget;
 587  
 
 588  13
         IncrementalBuildHelper incrementalBuildHelper = new IncrementalBuildHelper( mojoExecution, mavenSession );
 589  
 
 590  
         try
 591  
         {
 592  13
             canUpdateTarget = compiler.canUpdateTarget( compilerConfiguration );
 593  13
             Set<File> sources = getCompileSources( compiler, compilerConfiguration );
 594  
 
 595  13
             if ( ( compiler.getCompilerOutputStyle().equals( CompilerOutputStyle.ONE_OUTPUT_FILE_FOR_ALL_INPUT_FILES )
 596  
                 && !canUpdateTarget ) || isDependencyChanged() || isSourceChanged( compilerConfiguration, compiler )
 597  
                 || incrementalBuildHelper.inputFileTreeChanged( sources ) )
 598  
             {
 599  13
                 getLog().info( "Changes detected - recompiling the module!" );
 600  
 
 601  13
                 compilerConfiguration.setSourceFiles( sources );
 602  
             }
 603  
             else
 604  
             {
 605  0
                 getLog().info( "Nothing to compile - all classes are up to date" );
 606  
 
 607  0
                 return;
 608  
             }
 609  
         }
 610  0
         catch ( CompilerException e )
 611  
         {
 612  0
             throw new MojoExecutionException( "Error while computing stale sources.", e );
 613  13
         }
 614  
 
 615  
         // ----------------------------------------------------------------------
 616  
         // Dump configuration
 617  
         // ----------------------------------------------------------------------
 618  
 
 619  13
         if ( getLog().isDebugEnabled() )
 620  
         {
 621  13
             getLog().debug( "Classpath:" );
 622  
 
 623  13
             for ( String s : getClasspathElements() )
 624  
             {
 625  10
                 getLog().debug( " " + s );
 626  
             }
 627  
 
 628  13
             getLog().debug( "Source roots:" );
 629  
 
 630  13
             for ( String root : getCompileSourceRoots() )
 631  
             {
 632  13
                 getLog().debug( " " + root );
 633  
             }
 634  
 
 635  
             try
 636  
             {
 637  13
                 if ( fork )
 638  
                 {
 639  2
                     if ( compilerConfiguration.getExecutable() != null )
 640  
                     {
 641  2
                         getLog().debug( "Excutable: " );
 642  2
                         getLog().debug( " " + compilerConfiguration.getExecutable() );
 643  
                     }
 644  
                 }
 645  
 
 646  13
                 String[] cl = compiler.createCommandLine( compilerConfiguration );
 647  13
                 if ( cl != null && cl.length > 0 )
 648  
                 {
 649  6
                     StringBuilder sb = new StringBuilder();
 650  6
                     sb.append( cl[0] );
 651  68
                     for ( int i = 1; i < cl.length; i++ )
 652  
                     {
 653  62
                         sb.append( " " );
 654  62
                         sb.append( cl[i] );
 655  
                     }
 656  6
                     getLog().debug( "Command line options:" );
 657  6
                     getLog().debug( sb );
 658  
                 }
 659  
             }
 660  0
             catch ( CompilerException ce )
 661  
             {
 662  0
                 getLog().debug( ce );
 663  13
             }
 664  
         }
 665  
 
 666  
         // ----------------------------------------------------------------------
 667  
         // Compile!
 668  
         // ----------------------------------------------------------------------
 669  
 
 670  13
         if ( StringUtils.isEmpty( compilerConfiguration.getSourceEncoding() ) )
 671  
         {
 672  13
             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  13
         incrementalBuildHelper.beforeRebuildExecution( getOutputDirectory() );
 679  
 
 680  
         try
 681  
         {
 682  
             try
 683  
             {
 684  13
                 compilerResult = compiler.performCompile( compilerConfiguration );
 685  
             }
 686  0
             catch ( CompilerNotImplementedException cnie )
 687  
             {
 688  0
                 List<CompilerError> messages = compiler.compile( compilerConfiguration );
 689  0
                 compilerResult = convertToCompilerResult( messages );
 690  13
             }
 691  
         }
 692  0
         catch ( Exception e )
 693  
         {
 694  
             // TODO: don't catch Exception
 695  0
             throw new MojoExecutionException( "Fatal error compiling", e );
 696  13
         }
 697  
 
 698  
         // now scan the same directory again and create a diff
 699  13
         incrementalBuildHelper.afterRebuildExecution();
 700  
 
 701  13
         List<CompilerMessage> warnings = new ArrayList<CompilerMessage>();
 702  13
         List<CompilerMessage> errors = new ArrayList<CompilerMessage>();
 703  13
         for ( CompilerMessage message : compilerResult.getCompilerMessages() )
 704  
         {
 705  7
             if ( message.isError() )
 706  
             {
 707  0
                 errors.add( message );
 708  
             }
 709  
             else
 710  
             {
 711  7
                 warnings.add( message );
 712  
             }
 713  
         }
 714  
 
 715  13
         if ( failOnError && !compilerResult.isSuccess() )
 716  
         {
 717  1
             if ( !warnings.isEmpty() )
 718  
             {
 719  1
                 getLog().info( "-------------------------------------------------------------" );
 720  1
                 getLog().warn( "COMPILATION WARNING : " );
 721  1
                 getLog().info( "-------------------------------------------------------------" );
 722  1
                 for ( CompilerMessage warning : warnings )
 723  
                 {
 724  1
                     getLog().warn( warning.toString() );
 725  
                 }
 726  1
                 getLog().info( warnings.size() + ( ( warnings.size() > 1 ) ? " warnings " : " warning" ) );
 727  1
                 getLog().info( "-------------------------------------------------------------" );
 728  
             }
 729  
 
 730  1
             if ( !errors.isEmpty() )
 731  
             {
 732  0
                 getLog().info( "-------------------------------------------------------------" );
 733  0
                 getLog().error( "COMPILATION ERROR : " );
 734  0
                 getLog().info( "-------------------------------------------------------------" );
 735  0
                 for ( CompilerMessage error : errors )
 736  
                 {
 737  0
                     getLog().error( error.toString() );
 738  
                 }
 739  0
                 getLog().info( errors.size() + ( ( errors.size() > 1 ) ? " errors " : " error" ) );
 740  0
                 getLog().info( "-------------------------------------------------------------" );
 741  
             }
 742  
 
 743  1
             if ( !errors.isEmpty() )
 744  
             {
 745  0
                 throw new CompilationFailureException( errors );
 746  
             }
 747  
             else
 748  
             {
 749  1
                 throw new CompilationFailureException( warnings );
 750  
             }
 751  
         }
 752  
         else
 753  
         {
 754  12
             for ( CompilerMessage message : compilerResult.getCompilerMessages() )
 755  
             {
 756  6
                 getLog().warn( message.toString() );
 757  
             }
 758  
         }
 759  12
     }
 760  
 
 761  
     protected CompilerResult convertToCompilerResult( List<CompilerError> compilerErrors )
 762  
     {
 763  0
         if ( compilerErrors == null )
 764  
         {
 765  0
             return new CompilerResult();
 766  
         }
 767  0
         List<CompilerMessage> messages = new ArrayList<CompilerMessage>( compilerErrors.size() );
 768  0
         boolean success = true;
 769  0
         for ( CompilerError compilerError : compilerErrors )
 770  
         {
 771  0
             messages.add(
 772  
                 new CompilerMessage( compilerError.getFile(), compilerError.getKind(), compilerError.getStartLine(),
 773  
                                      compilerError.getStartColumn(), compilerError.getEndLine(),
 774  
                                      compilerError.getEndColumn(), compilerError.getMessage() ) );
 775  0
             if ( compilerError.isError() )
 776  
             {
 777  0
                 success = false;
 778  
             }
 779  
         }
 780  
 
 781  0
         return new CompilerResult( success, messages );
 782  
     }
 783  
 
 784  
     /**
 785  
      * @return all source files for the compiler
 786  
      */
 787  
     private Set<File> getCompileSources( Compiler compiler, CompilerConfiguration compilerConfiguration )
 788  
         throws MojoExecutionException, CompilerException
 789  
     {
 790  13
         String inputFileEnding = compiler.getInputFileEnding( compilerConfiguration );
 791  13
         SourceInclusionScanner scanner = getSourceInclusionScanner( inputFileEnding );
 792  
 
 793  13
         SourceMapping mapping = getSourceMapping( compilerConfiguration, compiler );
 794  
 
 795  13
         scanner.addSourceMapping( mapping );
 796  
 
 797  13
         Set<File> compileSources = new HashSet<File>();
 798  
 
 799  13
         for ( String sourceRoot : getCompileSourceRoots() )
 800  
         {
 801  13
             File rootFile = new File( sourceRoot );
 802  
 
 803  13
             if ( !rootFile.isDirectory() )
 804  
             {
 805  0
                 continue;
 806  
             }
 807  
 
 808  
             try
 809  
             {
 810  13
                 compileSources.addAll( scanner.getIncludedSources( rootFile, null ) );
 811  
             }
 812  0
             catch ( InclusionScanException e )
 813  
             {
 814  0
                 throw new MojoExecutionException(
 815  
                     "Error scanning source root: \'" + sourceRoot + "\' for stale files to recompile.", e );
 816  13
             }
 817  13
         }
 818  
 
 819  13
         return compileSources;
 820  
     }
 821  
 
 822  
     /**
 823  
      * @param compilerConfiguration
 824  
      * @param compiler
 825  
      * @return <code>true</code> if at least a single source file is newer than it's class file
 826  
      */
 827  
     private boolean isSourceChanged( CompilerConfiguration compilerConfiguration, Compiler compiler )
 828  
         throws CompilerException, MojoExecutionException
 829  
     {
 830  6
         Set<File> staleSources =
 831  
             computeStaleSources( compilerConfiguration, compiler, getSourceInclusionScanner( staleMillis ) );
 832  
 
 833  6
         return staleSources != null && staleSources.size() > 0;
 834  
     }
 835  
 
 836  
 
 837  
     /**
 838  
      * try to get thread count if a Maven 3 build, using reflection as the plugin must not be maven3 api dependant
 839  
      *
 840  
      * @return number of thread for this build or 1 if not multi-thread build
 841  
      */
 842  
     protected int getRequestThreadCount()
 843  
     {
 844  
         try
 845  
         {
 846  0
             Method getRequestMethod = session.getClass().getMethod( "getRequest" );
 847  0
             Object mavenExecutionRequest = getRequestMethod.invoke( this.session );
 848  0
             Method getThreadCountMethod = mavenExecutionRequest.getClass().getMethod( "getThreadCount" );
 849  0
             String threadCount = (String) getThreadCountMethod.invoke( mavenExecutionRequest );
 850  0
             return Integer.valueOf( threadCount );
 851  
         }
 852  0
         catch ( Exception e )
 853  
         {
 854  0
             getLog().debug( "unable to get threadCount for the current build: " + e.getMessage() );
 855  
         }
 856  0
         return 1;
 857  
     }
 858  
 
 859  
     protected Date getBuildStartTime()
 860  
     {
 861  
         try
 862  
         {
 863  6
             Method getRequestMethod = session.getClass().getMethod( "getRequest" );
 864  0
             Object mavenExecutionRequest = getRequestMethod.invoke( session );
 865  0
             Method getStartTimeMethod = mavenExecutionRequest.getClass().getMethod( "getStartTime" );
 866  0
             Date buildStartTime = (Date) getStartTimeMethod.invoke( mavenExecutionRequest );
 867  0
             return buildStartTime;
 868  
         }
 869  6
         catch ( Exception e )
 870  
         {
 871  6
             getLog().debug( "unable to get start time for the current build: " + e.getMessage() );
 872  
         }
 873  
 
 874  6
         return new Date();
 875  
     }
 876  
 
 877  
 
 878  
     private String getMemoryValue( String setting )
 879  
     {
 880  4
         String value = null;
 881  
 
 882  
         // Allow '128' or '128m'
 883  4
         if ( isDigits( setting ) )
 884  
         {
 885  0
             value = setting + "m";
 886  
         }
 887  
         else
 888  
         {
 889  4
             if ( ( isDigits( setting.substring( 0, setting.length() - 1 ) ) ) && ( setting.toLowerCase().endsWith(
 890  
                 "m" ) ) )
 891  
             {
 892  4
                 value = setting;
 893  
             }
 894  
         }
 895  4
         return value;
 896  
     }
 897  
 
 898  
     //TODO remove the part with ToolchainManager lookup once we depend on
 899  
     //3.0.9 (have it as prerequisite). Define as regular component field then.
 900  
     private Toolchain getToolchain()
 901  
     {
 902  15
         Toolchain tc = null;
 903  15
         if ( toolchainManager != null )
 904  
         {
 905  15
             tc = toolchainManager.getToolchainFromBuildContext( "jdk", session );
 906  
         }
 907  15
         return tc;
 908  
     }
 909  
 
 910  
     private boolean isDigits( String string )
 911  
     {
 912  28
         for ( int i = 0; i < string.length(); i++ )
 913  
         {
 914  24
             if ( !Character.isDigit( string.charAt( i ) ) )
 915  
             {
 916  4
                 return false;
 917  
             }
 918  
         }
 919  4
         return true;
 920  
     }
 921  
 
 922  
     private Set<File> computeStaleSources( CompilerConfiguration compilerConfiguration, Compiler compiler,
 923  
                                            SourceInclusionScanner scanner )
 924  
         throws MojoExecutionException, CompilerException
 925  
     {
 926  6
         SourceMapping mapping = getSourceMapping( compilerConfiguration, compiler );
 927  
 
 928  
         File outputDirectory;
 929  6
         CompilerOutputStyle outputStyle = compiler.getCompilerOutputStyle();
 930  6
         if ( outputStyle == CompilerOutputStyle.ONE_OUTPUT_FILE_FOR_ALL_INPUT_FILES )
 931  
         {
 932  0
             outputDirectory = buildDirectory;
 933  
         }
 934  
         else
 935  
         {
 936  6
             outputDirectory = getOutputDirectory();
 937  
         }
 938  
 
 939  6
         scanner.addSourceMapping( mapping );
 940  
 
 941  6
         Set<File> staleSources = new HashSet<File>();
 942  
 
 943  6
         for ( String sourceRoot : getCompileSourceRoots() )
 944  
         {
 945  6
             File rootFile = new File( sourceRoot );
 946  
 
 947  6
             if ( !rootFile.isDirectory() )
 948  
             {
 949  0
                 continue;
 950  
             }
 951  
 
 952  
             try
 953  
             {
 954  6
                 staleSources.addAll( scanner.getIncludedSources( rootFile, outputDirectory ) );
 955  
             }
 956  0
             catch ( InclusionScanException e )
 957  
             {
 958  0
                 throw new MojoExecutionException(
 959  
                     "Error scanning source root: \'" + sourceRoot + "\' for stale files to recompile.", e );
 960  6
             }
 961  6
         }
 962  
 
 963  6
         return staleSources;
 964  
     }
 965  
 
 966  
     private SourceMapping getSourceMapping( CompilerConfiguration compilerConfiguration, Compiler compiler )
 967  
         throws CompilerException, MojoExecutionException
 968  
     {
 969  19
         CompilerOutputStyle outputStyle = compiler.getCompilerOutputStyle();
 970  
 
 971  
         SourceMapping mapping;
 972  19
         if ( outputStyle == CompilerOutputStyle.ONE_OUTPUT_FILE_PER_INPUT_FILE )
 973  
         {
 974  12
             mapping = new SuffixMapping( compiler.getInputFileEnding( compilerConfiguration ),
 975  
                                          compiler.getOutputFileEnding( compilerConfiguration ) );
 976  
         }
 977  7
         else if ( outputStyle == CompilerOutputStyle.ONE_OUTPUT_FILE_FOR_ALL_INPUT_FILES )
 978  
         {
 979  7
             mapping = new SingleTargetSourceMapping( compiler.getInputFileEnding( compilerConfiguration ),
 980  
                                                      compiler.getOutputFile( compilerConfiguration ) );
 981  
 
 982  
         }
 983  
         else
 984  
         {
 985  0
             throw new MojoExecutionException( "Unknown compiler output style: '" + outputStyle + "'." );
 986  
         }
 987  19
         return mapping;
 988  
     }
 989  
 
 990  
     /**
 991  
      * @todo also in ant plugin. This should be resolved at some point so that it does not need to
 992  
      * be calculated continuously - or should the plugins accept empty source roots as is?
 993  
      */
 994  
     private static List<String> removeEmptyCompileSourceRoots( List<String> compileSourceRootsList )
 995  
     {
 996  15
         List<String> newCompileSourceRootsList = new ArrayList<String>();
 997  15
         if ( compileSourceRootsList != null )
 998  
         {
 999  
             // copy as I may be modifying it
 1000  15
             for ( String srcDir : compileSourceRootsList )
 1001  
             {
 1002  15
                 if ( !newCompileSourceRootsList.contains( srcDir ) && new File( srcDir ).exists() )
 1003  
                 {
 1004  13
                     newCompileSourceRootsList.add( srcDir );
 1005  
                 }
 1006  
             }
 1007  
         }
 1008  15
         return newCompileSourceRootsList;
 1009  
     }
 1010  
 
 1011  
     /**
 1012  
      * We just compare the timestamps of all local dependency files (inter-module dependency classpath)
 1013  
      * and the own generated classes
 1014  
      * and if we got a file which is >= the buid-started timestamp, then we catched a file which got
 1015  
      * changed during this build.
 1016  
      *
 1017  
      * @return <code>true</code> if at least one single dependency has changed.
 1018  
      */
 1019  
     protected boolean isDependencyChanged()
 1020  
     {
 1021  6
         if ( mavenSession == null )
 1022  
         {
 1023  
             // we just cannot determine it, so don't do anything beside logging
 1024  0
             getLog().info( "Cannot determine build start date, skipping incremental build detection." );
 1025  0
             return false;
 1026  
         }
 1027  
 
 1028  6
         Date buildStartTime = getBuildStartTime();
 1029  
 
 1030  6
         for ( String classPathElement : getClasspathElements() )
 1031  
         {
 1032  
             // ProjectArtifacts are artifacts which are available in the local project
 1033  
             // that's the only ones we are interested in now.
 1034  6
             File artifactPath = new File( classPathElement );
 1035  6
             if ( artifactPath.isDirectory() )
 1036  
             {
 1037  3
                 if ( hasNewFile( artifactPath, buildStartTime ) )
 1038  
                 {
 1039  0
                     return true;
 1040  
                 }
 1041  
             }
 1042  6
         }
 1043  
 
 1044  
         // obviously there was no new file detected.
 1045  6
         return false;
 1046  
     }
 1047  
 
 1048  
     private boolean hasNewFile( File classPathEntry, Date buildStartTime )
 1049  
     {
 1050  6
         if ( !classPathEntry.exists() )
 1051  
         {
 1052  0
             return false;
 1053  
         }
 1054  
 
 1055  6
         if ( classPathEntry.isFile() )
 1056  
         {
 1057  3
             return classPathEntry.lastModified() >= buildStartTime.getTime();
 1058  
         }
 1059  
 
 1060  3
         File[] children = classPathEntry.listFiles();
 1061  
 
 1062  6
         for ( File child : children )
 1063  
         {
 1064  3
             if ( hasNewFile( child, buildStartTime ) )
 1065  
             {
 1066  0
                 return true;
 1067  
             }
 1068  
         }
 1069  
 
 1070  3
         return false;
 1071  
     }
 1072  
 }