You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by st...@apache.org on 2012/08/27 14:56:09 UTC

svn commit: r1377661 - in /maven/plugins/trunk/maven-compiler-plugin: ./ src/main/java/org/apache/maven/plugin/

Author: struberg
Date: Mon Aug 27 12:56:09 2012
New Revision: 1377661

URL: http://svn.apache.org/viewvc?rev=1377661&view=rev
Log:
MCOMPILER-21 improve incremental build support.

* detect dependency files which got changed during the build.
In that case a re-compile is needed for all sources.

* This also fixes the detection of the 'staleSources'. We now actually
re-compile all sources as it's almost impossible to detect cross-refs
properly.


Modified:
    maven/plugins/trunk/maven-compiler-plugin/pom.xml
    maven/plugins/trunk/maven-compiler-plugin/src/main/java/org/apache/maven/plugin/AbstractCompilerMojo.java
    maven/plugins/trunk/maven-compiler-plugin/src/main/java/org/apache/maven/plugin/CompilerMojo.java
    maven/plugins/trunk/maven-compiler-plugin/src/main/java/org/apache/maven/plugin/TestCompilerMojo.java

Modified: maven/plugins/trunk/maven-compiler-plugin/pom.xml
URL: http://svn.apache.org/viewvc/maven/plugins/trunk/maven-compiler-plugin/pom.xml?rev=1377661&r1=1377660&r2=1377661&view=diff
==============================================================================
--- maven/plugins/trunk/maven-compiler-plugin/pom.xml (original)
+++ maven/plugins/trunk/maven-compiler-plugin/pom.xml Mon Aug 27 12:56:09 2012
@@ -168,13 +168,13 @@ under the License.
     <dependency>
       <groupId>org.codehaus.plexus</groupId>
       <artifactId>plexus-container-default</artifactId>
-      <version>1.0-alpha-9-stable-1</version>
+      <version>1.5.5</version>
     </dependency>
 
     <dependency>
-      <groupId>org.apache.maven.shared</groupId>
+      <groupId>org.apache.maven.plugin-testing</groupId>
       <artifactId>maven-plugin-testing-harness</artifactId>
-      <version>1.1</version>
+      <version>2.0-alpha-1</version>
       <scope>test</scope>
     </dependency>
   </dependencies>

Modified: maven/plugins/trunk/maven-compiler-plugin/src/main/java/org/apache/maven/plugin/AbstractCompilerMojo.java
URL: http://svn.apache.org/viewvc/maven/plugins/trunk/maven-compiler-plugin/src/main/java/org/apache/maven/plugin/AbstractCompilerMojo.java?rev=1377661&r1=1377660&r2=1377661&view=diff
==============================================================================
--- maven/plugins/trunk/maven-compiler-plugin/src/main/java/org/apache/maven/plugin/AbstractCompilerMojo.java (original)
+++ maven/plugins/trunk/maven-compiler-plugin/src/main/java/org/apache/maven/plugin/AbstractCompilerMojo.java Mon Aug 27 12:56:09 2012
@@ -19,6 +19,7 @@ package org.apache.maven.plugin;
  * under the License.
  */
 
+import org.apache.maven.artifact.Artifact;
 import org.apache.maven.execution.MavenSession;
 import org.apache.maven.plugins.annotations.Component;
 import org.apache.maven.plugins.annotations.Parameter;
@@ -42,6 +43,8 @@ import org.codehaus.plexus.util.StringUt
 import java.io.File;
 import java.lang.reflect.Method;
 import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.List;
@@ -315,6 +318,13 @@ public abstract class AbstractCompilerMo
     @Parameter( defaultValue = "false", property = "maven.compiler.skipMultiThreadWarning" )
     private boolean skipMultiThreadWarning;
 
+    /**
+     * We need this to determine the start timestamp of the build.
+     * @since 2.6
+     */
+    @Component
+    protected MavenSession mavenSession;
+
     protected abstract SourceInclusionScanner getSourceInclusionScanner( int staleMillis );
 
     protected abstract SourceInclusionScanner getSourceInclusionScanner( String inputFileEnding );
@@ -550,33 +560,27 @@ public abstract class AbstractCompilerMo
 
         getLog().debug( "CompilerReuseStrategy: " + compilerConfiguration.getCompilerReuseStrategy().getStrategy() );
 
-        // TODO: have an option to always compile (without need to clean)
-        Set<File> staleSources;
-
         boolean canUpdateTarget;
 
         try
         {
-            staleSources =
-                computeStaleSources( compilerConfiguration, compiler, getSourceInclusionScanner( staleMillis ) );
-
             canUpdateTarget = compiler.canUpdateTarget( compilerConfiguration );
 
-            if ( compiler.getCompilerOutputStyle().equals( CompilerOutputStyle.ONE_OUTPUT_FILE_FOR_ALL_INPUT_FILES )
-                && !canUpdateTarget )
+            if ( ( compiler.getCompilerOutputStyle().equals( CompilerOutputStyle.ONE_OUTPUT_FILE_FOR_ALL_INPUT_FILES )
+                   && !canUpdateTarget )
+                 || isDependencyChanged(getArtifacts())
+                 || isSourceChanged(compilerConfiguration, compiler) )
             {
-                getLog().info( "RESCANNING!" );
-                // TODO: This second scan for source files is sub-optimal
-                String inputFileEnding = compiler.getInputFileEnding( compilerConfiguration );
-
-                Set<File> sources = computeStaleSources( compilerConfiguration, compiler,
-                                                         getSourceInclusionScanner( inputFileEnding ) );
+                getLog().info( "Recompiling the module!" );
+                Set<File> sources = getCompileSources( compiler, compilerConfiguration );
 
                 compilerConfiguration.setSourceFiles( sources );
             }
             else
             {
-                compilerConfiguration.setSourceFiles( staleSources );
+                getLog().info( "Nothing to compile - all classes are up to date" );
+
+                return;
             }
         }
         catch ( CompilerException e )
@@ -584,12 +588,6 @@ public abstract class AbstractCompilerMo
             throw new MojoExecutionException( "Error while computing stale sources.", e );
         }
 
-        if ( staleSources.isEmpty() )
-        {
-            getLog().info( "Nothing to compile - all classes are up to date" );
-
-            return;
-        }
 
         // ----------------------------------------------------------------------
         // Dump configuration
@@ -719,6 +717,64 @@ public abstract class AbstractCompilerMo
     }
 
     /**
+     * @return all source files for the compiler
+     */
+    private Set<File> getCompileSources( Compiler compiler, CompilerConfiguration compilerConfiguration ) throws MojoExecutionException, CompilerException
+    {
+        String inputFileEnding = compiler.getInputFileEnding( compilerConfiguration );
+        SourceInclusionScanner scanner = getSourceInclusionScanner( inputFileEnding );
+
+        SourceMapping mapping = getSourceMapping( compilerConfiguration, compiler );
+
+        scanner.addSourceMapping( mapping );
+
+        Set<File> compileSources = new HashSet<File>();
+
+        for ( String sourceRoot : getCompileSourceRoots() )
+        {
+            File rootFile = new File( sourceRoot );
+
+            if ( !rootFile.isDirectory() )
+            {
+                continue;
+            }
+
+            try
+            {
+                compileSources.addAll( scanner.getIncludedSources( rootFile, null ) );
+            }
+            catch ( InclusionScanException e )
+            {
+                throw new MojoExecutionException(
+                        "Error scanning source root: \'" + sourceRoot + "\' for stale files to recompile.", e );
+            }
+        }
+
+        return compileSources;
+    }
+
+    /**
+     *
+     * @return <code>true</code> if at least a single source file is newer than it's class file
+     * @param compilerConfiguration
+     * @param compiler
+     */
+    private boolean isSourceChanged( CompilerConfiguration compilerConfiguration, Compiler compiler )
+            throws CompilerException, MojoExecutionException
+    {
+        Set<File> staleSources =
+                computeStaleSources( compilerConfiguration, compiler, getSourceInclusionScanner( staleMillis ) );
+
+        return staleSources != null && staleSources.size() > 0;
+    }
+
+
+    /**
+     * Get all the project artifacts for the current scope
+     */
+    protected abstract Collection<Artifact> getArtifacts();
+
+    /**
      * try to get thread count if a Maven 3 build, using reflection as the plugin must not be maven3 api dependant
      *
      * @return number of thread for this build or 1 if not multi-thread build
@@ -727,7 +783,7 @@ public abstract class AbstractCompilerMo
     {
         try
         {
-            Method getRequestMethod = this.session.getClass().getMethod( "getRequest" );
+            Method getRequestMethod = session.getClass().getMethod( "getRequest" );
             Object mavenExecutionRequest = getRequestMethod.invoke( this.session );
             Method getThreadCountMethod = mavenExecutionRequest.getClass().getMethod( "getThreadCount" );
             String threadCount = (String) getThreadCountMethod.invoke( mavenExecutionRequest );
@@ -740,6 +796,26 @@ public abstract class AbstractCompilerMo
         return 1;
     }
 
+    protected Date getBuildStartTime()
+    {
+        try
+        {
+            Method getRequestMethod = session.getClass().getMethod( "getRequest" );
+            Object mavenExecutionRequest = getRequestMethod.invoke( session );
+            Method getStartTimeMethod = mavenExecutionRequest.getClass().getMethod( "getStartTime" );
+            Date buildStartTime = (Date) getStartTimeMethod.invoke( mavenExecutionRequest );
+            return buildStartTime;
+        }
+        catch ( Exception e )
+        {
+            getLog().debug( "unable to get start time for the current build: " + e.getMessage() );
+        }
+
+        return new Date();
+    }
+
+
+
     private String getMemoryValue( String setting )
     {
         String value = null;
@@ -788,29 +864,18 @@ public abstract class AbstractCompilerMo
                                            SourceInclusionScanner scanner )
         throws MojoExecutionException, CompilerException
     {
-        CompilerOutputStyle outputStyle = compiler.getCompilerOutputStyle();
+        SourceMapping mapping = getSourceMapping( compilerConfiguration, compiler );
 
-        SourceMapping mapping;
 
         File outputDirectory;
-
-        if ( outputStyle == CompilerOutputStyle.ONE_OUTPUT_FILE_PER_INPUT_FILE )
-        {
-            mapping = new SuffixMapping( compiler.getInputFileEnding( compilerConfiguration ),
-                                         compiler.getOutputFileEnding( compilerConfiguration ) );
-
-            outputDirectory = getOutputDirectory();
-        }
-        else if ( outputStyle == CompilerOutputStyle.ONE_OUTPUT_FILE_FOR_ALL_INPUT_FILES )
+        CompilerOutputStyle outputStyle = compiler.getCompilerOutputStyle();
+        if ( outputStyle == CompilerOutputStyle.ONE_OUTPUT_FILE_FOR_ALL_INPUT_FILES )
         {
-            mapping = new SingleTargetSourceMapping( compiler.getInputFileEnding( compilerConfiguration ),
-                                                     compiler.getOutputFile( compilerConfiguration ) );
-
             outputDirectory = buildDirectory;
         }
         else
         {
-            throw new MojoExecutionException( "Unknown compiler output style: '" + outputStyle + "'." );
+            outputDirectory = getOutputDirectory();
         }
 
         scanner.addSourceMapping( mapping );
@@ -840,6 +905,30 @@ public abstract class AbstractCompilerMo
         return staleSources;
     }
 
+    private SourceMapping getSourceMapping( CompilerConfiguration compilerConfiguration, Compiler compiler )
+            throws CompilerException, MojoExecutionException
+    {
+        CompilerOutputStyle outputStyle = compiler.getCompilerOutputStyle();
+
+        SourceMapping mapping;
+        if ( outputStyle == CompilerOutputStyle.ONE_OUTPUT_FILE_PER_INPUT_FILE )
+        {
+            mapping = new SuffixMapping( compiler.getInputFileEnding( compilerConfiguration ),
+                                         compiler.getOutputFileEnding( compilerConfiguration ) );
+        }
+        else if ( outputStyle == CompilerOutputStyle.ONE_OUTPUT_FILE_FOR_ALL_INPUT_FILES )
+        {
+            mapping = new SingleTargetSourceMapping( compiler.getInputFileEnding( compilerConfiguration ),
+                                                     compiler.getOutputFile( compilerConfiguration ) );
+
+        }
+        else
+        {
+            throw new MojoExecutionException( "Unknown compiler output style: '" + outputStyle + "'." );
+        }
+        return mapping;
+    }
+
     /**
      * @todo also in ant plugin. This should be resolved at some point so that it does not need to
      * be calculated continuously - or should the plugins accept empty source roots as is?
@@ -860,4 +949,65 @@ public abstract class AbstractCompilerMo
         }
         return newCompileSourceRootsList;
     }
+
+    /**
+     * We just compare the timestamps of all local dependency files (inter-module dependency classpath)
+     * and if we got a file which is >= the buid-started timestamp, then we catched a file which got
+     * changed during this build.
+     *
+     * @return <code>true</code> if at least one single dependency has changed.
+     */
+    protected boolean isDependencyChanged(Collection<Artifact> artifacts)
+    {
+        if ( mavenSession == null )
+        {
+            // we just cannot determine it, so don't do anything beside logging
+            getLog().info( "Cannot determine build start date, skipping incremental build detection." );
+            return false;
+        }
+
+        Date buildStartTime = getBuildStartTime();
+
+        for ( Artifact artifact : artifacts )
+        {
+            // ProjectArtifacts are artifacts which are available in the local project
+            // that's the only ones we are interested in now.
+            File artifactPath = artifact.getFile();
+            if ( artifactPath != null && artifactPath.isDirectory() )
+            {
+                if ( hasNewFile( artifactPath, buildStartTime ) )
+                {
+                    return true;
+                }
+            }
+        }
+
+        // obviously there was no new file detected.
+        return false;
+    }
+
+    private boolean hasNewFile( File classPathEntry, Date buildStartTime )
+    {
+        if ( ! classPathEntry.exists() )
+        {
+            return false;
+        }
+
+        if ( classPathEntry.isFile() )
+        {
+            return classPathEntry.lastModified() >= buildStartTime.getTime();
+        }
+
+        File[] children = classPathEntry.listFiles();
+
+        for ( File child : children )
+        {
+            if ( hasNewFile( child, buildStartTime ) )
+            {
+                return true;
+            }
+        }
+
+        return false;
+    }
 }

Modified: maven/plugins/trunk/maven-compiler-plugin/src/main/java/org/apache/maven/plugin/CompilerMojo.java
URL: http://svn.apache.org/viewvc/maven/plugins/trunk/maven-compiler-plugin/src/main/java/org/apache/maven/plugin/CompilerMojo.java?rev=1377661&r1=1377660&r2=1377661&view=diff
==============================================================================
--- maven/plugins/trunk/maven-compiler-plugin/src/main/java/org/apache/maven/plugin/CompilerMojo.java (original)
+++ maven/plugins/trunk/maven-compiler-plugin/src/main/java/org/apache/maven/plugin/CompilerMojo.java Mon Aug 27 12:56:09 2012
@@ -28,6 +28,7 @@ import org.codehaus.plexus.compiler.util
 import org.codehaus.plexus.compiler.util.scan.StaleSourceScanner;
 
 import java.io.File;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
@@ -65,7 +66,7 @@ public class CompilerMojo
     private File outputDirectory;
 
     /**
-     * Project artifacts.
+     * Projects main artifact.
      *
      * @todo this is an export variable, really
      */
@@ -73,6 +74,12 @@ public class CompilerMojo
     private Artifact projectArtifact;
 
     /**
+     * We need all the projects artifacts to determine whether we shall force a re-compile.
+     */
+    @Parameter( defaultValue = "${project.artifacts}", readonly = true, required = true )
+    private Set<Artifact> projectArtifacts;
+
+    /**
      * A list of inclusion filters for the compiler.
      */
     @Parameter
@@ -142,13 +149,20 @@ public class CompilerMojo
         return scanner;
     }
 
+    @Override
+    protected Collection<Artifact> getArtifacts()
+    {
+        return projectArtifacts;
+    }
+
     protected SourceInclusionScanner getSourceInclusionScanner( String inputFileEnding )
     {
         SourceInclusionScanner scanner = null;
 
         if ( includes.isEmpty() && excludes.isEmpty() )
         {
-            includes = Collections.singleton( "**/*." + inputFileEnding );
+            String includePattern = "**/*" + ( inputFileEnding.startsWith( "." ) ? "" : "." ) + inputFileEnding;
+            includes = Collections.singleton( includePattern );
             scanner = new SimpleSourceInclusionScanner( includes, Collections.EMPTY_SET );
         }
         else
@@ -188,4 +202,4 @@ public class CompilerMojo
         return generatedSourcesDirectory;
     }
 
-}
\ No newline at end of file
+}

Modified: maven/plugins/trunk/maven-compiler-plugin/src/main/java/org/apache/maven/plugin/TestCompilerMojo.java
URL: http://svn.apache.org/viewvc/maven/plugins/trunk/maven-compiler-plugin/src/main/java/org/apache/maven/plugin/TestCompilerMojo.java?rev=1377661&r1=1377660&r2=1377661&view=diff
==============================================================================
--- maven/plugins/trunk/maven-compiler-plugin/src/main/java/org/apache/maven/plugin/TestCompilerMojo.java (original)
+++ maven/plugins/trunk/maven-compiler-plugin/src/main/java/org/apache/maven/plugin/TestCompilerMojo.java Mon Aug 27 12:56:09 2012
@@ -19,6 +19,7 @@ package org.apache.maven.plugin;
  * under the License.
  */
 
+import org.apache.maven.artifact.Artifact;
 import org.apache.maven.plugins.annotations.ResolutionScope;
 import org.apache.maven.plugins.annotations.LifecyclePhase;
 import org.apache.maven.plugins.annotations.Parameter;
@@ -27,6 +28,7 @@ import org.codehaus.plexus.compiler.util
 import org.codehaus.plexus.compiler.util.scan.StaleSourceScanner;
 
 import java.io.File;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
@@ -139,6 +141,13 @@ public class TestCompilerMojo
     private File generatedTestSourcesDirectory;
 
 
+    /**
+     * We need all the projects test artifacts to determine whether we shall force a re-compile.
+     */
+    @Parameter( defaultValue = "${project.testArtifacts}", readonly = true, required = true )
+    private List<Artifact> testArtifacts;
+
+
     public void execute()
         throws MojoExecutionException, CompilationFailureException
     {
@@ -193,7 +202,8 @@ public class TestCompilerMojo
 
         if ( testIncludes.isEmpty() && testExcludes.isEmpty() )
         {
-            testIncludes = Collections.singleton( "**/*." + inputFileEnding );
+            String includePattern = "**/*" + ( inputFileEnding.startsWith( "." ) ? "" : "." ) + inputFileEnding;
+            testIncludes = Collections.singleton( includePattern );
             scanner = new SimpleSourceInclusionScanner( testIncludes, Collections.EMPTY_SET );
         }
         else
@@ -208,6 +218,11 @@ public class TestCompilerMojo
         return scanner;
     }
 
+    @Override
+    protected Collection<Artifact> getArtifacts() {
+        return testArtifacts;
+    }
+
     protected String getSource()
     {
         return testSource == null ? source : testSource;