[ https://issues.apache.org/jira/browse/MCOMPILER-278?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=15510220#comment-15510220 ]
Michael Zav'yalov commented on MCOMPILER-278: --------------------------------------------- I do not see full good fix, but can we have a partial fix where jar checksum can be used to detect modifications? Here is a patch for this: Index: src/main/java/org/apache/maven/plugin/compiler/AbstractCompilerMojo.java =================================================================== --- src/main/java/org/apache/maven/plugin/compiler/AbstractCompilerMojo.java (revision 1761542) +++ src/main/java/org/apache/maven/plugin/compiler/AbstractCompilerMojo.java (working copy) @@ -18,7 +18,6 @@ * specific language governing permissions and limitations * under the License. */ - import org.apache.maven.execution.MavenSession; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecution; @@ -49,20 +48,28 @@ import org.codehaus.plexus.compiler.util.scan.mapping.SuffixMapping; import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; import java.lang.reflect.Method; import java.util.ArrayList; +import java.util.Arrays; import java.util.Date; +import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; /** - * TODO: At least one step could be optimized, currently the plugin will do two - * scans of all the source code if the compiler has to have the entire set of - * sources. This is currently the case for at least the C# compiler and most - * likely all the other .NET compilers too. + * TODO: At least one step could be optimized, currently the plugin will do two scans of all the source code if the compiler has to have the + * entire set of sources. This is currently the case for at least the C# compiler and most likely all the other .NET compilers too. * * @author others * @author <a href="mailto:tryg...@inamo.no">Trygve Laugstøl</a> @@ -70,1139 +77,1251 @@ * @since 2.0 */ public abstract class AbstractCompilerMojo - extends AbstractMojo + extends AbstractMojo { - // ---------------------------------------------------------------------- - // Configurables - // ---------------------------------------------------------------------- - /** - * Indicates whether the build will continue even if there are compilation errors. - * - * @since 2.0.2 - */ - @Parameter(property = "maven.compiler.failOnError", defaultValue = "true") - private boolean failOnError = true; + private static final String DEPENDENCY_INFO_FILENAME = "dependencies.info"; - /** - * Set to <code>true</code> to include debugging information in the compiled class files. - */ - @Parameter(property = "maven.compiler.debug", defaultValue = "true") - private boolean debug = true; + // ---------------------------------------------------------------------- + // Configurables + // ---------------------------------------------------------------------- + /** + * Indicates whether the build will continue even if there are compilation errors. + * + * @since 2.0.2 + */ + @Parameter(property = "maven.compiler.failOnError", defaultValue = "true") + private boolean failOnError = true; - /** - * Set to <code>true</code> to show messages about what the compiler is doing. - */ - @Parameter(property = "maven.compiler.verbose", defaultValue = "false") - private boolean verbose; + /** + * Set to <code>true</code> to include debugging information in the compiled class files. + */ + @Parameter(property = "maven.compiler.debug", defaultValue = "true") + private boolean debug = true; - /** - * Sets whether to show source locations where deprecated APIs are used. - */ - @Parameter(property = "maven.compiler.showDeprecation", defaultValue = "false") - private boolean showDeprecation; + /** + * Set to <code>true</code> to show messages about what the compiler is doing. + */ + @Parameter(property = "maven.compiler.verbose", defaultValue = "false") + private boolean verbose; - /** - * Set to <code>true</code> to optimize the compiled code using the compiler's optimization methods. - */ - @Parameter(property = "maven.compiler.optimize", defaultValue = "false") - private boolean optimize; + /** + * Sets whether to show source locations where deprecated APIs are used. + */ + @Parameter(property = "maven.compiler.showDeprecation", defaultValue = "false") + private boolean showDeprecation; - /** - * Set to <code>true</code> to show compilation warnings. - */ - @Parameter(property = "maven.compiler.showWarnings", defaultValue = "false") - private boolean showWarnings; + /** + * Set to <code>true</code> to optimize the compiled code using the compiler's optimization methods. + */ + @Parameter(property = "maven.compiler.optimize", defaultValue = "false") + private boolean optimize; - /** - * The -source argument for the Java compiler. - */ - @Parameter(property = "maven.compiler.source", defaultValue = "1.5") - protected String source; + /** + * Set to <code>true</code> to show compilation warnings. + */ + @Parameter(property = "maven.compiler.showWarnings", defaultValue = "false") + private boolean showWarnings; - /** - * The -target argument for the Java compiler. - */ - @Parameter(property = "maven.compiler.target", defaultValue = "1.5") - protected String target; + /** + * The -source argument for the Java compiler. + */ + @Parameter(property = "maven.compiler.source", defaultValue = "1.5") + protected String source; - /** - * The -encoding argument for the Java compiler. - * - * @since 2.1 - */ - @Parameter(property = "encoding", defaultValue = "${project.build.sourceEncoding}") - private String encoding; + /** + * The -target argument for the Java compiler. + */ + @Parameter(property = "maven.compiler.target", defaultValue = "1.5") + protected String target; - /** - * Sets the granularity in milliseconds of the last modification - * date for testing whether a source needs recompilation. - */ - @Parameter(property = "lastModGranularityMs", defaultValue = "0") - private int staleMillis; + /** + * The -encoding argument for the Java compiler. + * + * @since 2.1 + */ + @Parameter(property = "encoding", defaultValue = "${project.build.sourceEncoding}") + private String encoding; - /** - * The compiler id of the compiler to use. See this - * <a href="non-javac-compilers.html">guide</a> for more information. - */ - @Parameter(property = "maven.compiler.compilerId", defaultValue = "javac") - private String compilerId; + /** + * Sets the granularity in milliseconds of the last modification date for testing whether a source needs recompilation. + */ + @Parameter(property = "lastModGranularityMs", defaultValue = "0") + private int staleMillis; - /** - * Version of the compiler to use, ex. "1.3", "1.5", if {@link #fork} is set to <code>true</code>. - */ - @Parameter(property = "maven.compiler.compilerVersion") - private String compilerVersion; + /** + * The compiler id of the compiler to use. See this + * <a href="non-javac-compilers.html">guide</a> for more information. + */ + @Parameter(property = "maven.compiler.compilerId", defaultValue = "javac") + private String compilerId; - /** - * Allows running the compiler in a separate process. - * If <code>false</code> it uses the built in compiler, while if <code>true</code> it will use an executable. - */ - @Parameter(property = "maven.compiler.fork", defaultValue = "false") - private boolean fork; + /** + * Version of the compiler to use, ex. "1.3", "1.5", if {@link #fork} is set to <code>true</code>. + */ + @Parameter(property = "maven.compiler.compilerVersion") + private String compilerVersion; - /** - * Initial size, in megabytes, of the memory allocation pool, ex. "64", "64m" - * if {@link #fork} is set to <code>true</code>. - * - * @since 2.0.1 - */ - @Parameter(property = "maven.compiler.meminitial") - private String meminitial; + /** + * Allows running the compiler in a separate process. If <code>false</code> it uses the built in compiler, while if <code>true</code> it + * will use an executable. + */ + @Parameter(property = "maven.compiler.fork", defaultValue = "false") + private boolean fork; - /** - * Sets the maximum size, in megabytes, of the memory allocation pool, ex. "128", "128m" - * if {@link #fork} is set to <code>true</code>. - * - * @since 2.0.1 - */ - @Parameter(property = "maven.compiler.maxmem") - private String maxmem; + /** + * Initial size, in megabytes, of the memory allocation pool, ex. "64", "64m" if {@link #fork} is set to <code>true</code>. + * + * @since 2.0.1 + */ + @Parameter(property = "maven.compiler.meminitial") + private String meminitial; - /** - * Sets the executable of the compiler to use when {@link #fork} is <code>true</code>. - */ - @Parameter(property = "maven.compiler.executable") - private String executable; + /** + * Sets the maximum size, in megabytes, of the memory allocation pool, ex. "128", "128m" if {@link #fork} is set to <code>true</code>. + * + * @since 2.0.1 + */ + @Parameter(property = "maven.compiler.maxmem") + private String maxmem; - /** - * <p> - * Sets whether annotation processing is performed or not. Only applies to JDK 1.6+ - * If not set, both compilation and annotation processing are performed at the same time. - * </p> - * <p>Allowed values are:</p> - * <ul> - * <li><code>none</code> - no annotation processing is performed.</li> - * <li><code>only</code> - only annotation processing is done, no compilation.</li> - * </ul> - * - * @since 2.2 - */ - @Parameter - private String proc; + /** + * Sets the executable of the compiler to use when {@link #fork} is <code>true</code>. + */ + @Parameter(property = "maven.compiler.executable") + private String executable; - /** - * <p> - * Names of annotation processors to run. Only applies to JDK 1.6+ - * If not set, the default annotation processors discovery process applies. - * </p> - * - * @since 2.2 - */ - @Parameter - private String[] annotationProcessors; + /** + * <p> + * Sets whether annotation processing is performed or not. Only applies to JDK 1.6+ If not set, both compilation and annotation + * processing are performed at the same time. + * </p> + * <p> + * Allowed values are:</p> + * <ul> + * <li><code>none</code> - no annotation processing is performed.</li> + * <li><code>only</code> - only annotation processing is done, no compilation.</li> + * </ul> + * + * @since 2.2 + */ + @Parameter + private String proc; - /** - * <p> - * Sets the arguments to be passed to the compiler (prepending a dash) if {@link #fork} is set to <code>true</code>. - * </p> - * <p> - * This is because the list of valid arguments passed to a Java compiler - * varies based on the compiler version. - * </p> - * <p> - * To pass <code>-Xmaxerrs 1000 -Xlint -Xlint:-path -Averbose=true</code> you should include the following: - * </p> - * <pre> - * <compilerArguments> - * <Xmaxerrs>1000</Xmaxerrs> - * <Xlint/> - * <Xlint:-path/> - * <Averbose>true</Averbose> - * </compilerArguments> - * </pre> - * - * @since 2.0.1 - * @deprecated use {@link #compilerArgs} instead. - */ - @Parameter - @Deprecated - protected Map<String, String> compilerArguments; + /** + * <p> + * Names of annotation processors to run. Only applies to JDK 1.6+ If not set, the default annotation processors discovery process + * applies. + * </p> + * + * @since 2.2 + */ + @Parameter + private String[] annotationProcessors; - /** - * <p> - * Sets the arguments to be passed to the compiler if {@link #fork} is set to <code>true</code>. - * Example: - * <pre> - * <compilerArgs> - * <arg>-Xmaxerrs=1000</arg> - * <arg>-Xlint</arg> - * </compilerArgs> - * </pre> - * - * @since 3.1 - */ - @Parameter - protected List<String> compilerArgs; - - /** - * <p> - * Sets the unformatted single argument string to be passed to the compiler if {@link #fork} is set to <code>true</code>. - * To pass multiple arguments such as <code>-Xmaxerrs 1000</code> (which are actually two arguments) you have to use {@link #compilerArguments}. - * </p> - * <p> - * This is because the list of valid arguments passed to a Java compiler - * varies based on the compiler version. - * </p> - */ - @Parameter - protected String compilerArgument; + /** + * <p> + * Sets the arguments to be passed to the compiler (prepending a dash) if {@link #fork} is set to <code>true</code>. + * </p> + * <p> + * This is because the list of valid arguments passed to a Java compiler varies based on the compiler version. + * </p> + * <p> + * To pass <code>-Xmaxerrs 1000 -Xlint -Xlint:-path -Averbose=true</code> you should include the following: + * </p> + * <pre> + * <compilerArguments> + * <Xmaxerrs>1000</Xmaxerrs> + * <Xlint/> + * <Xlint:-path/> + * <Averbose>true</Averbose> + * </compilerArguments> + * </pre> + * + * @since 2.0.1 + * @deprecated use {@link #compilerArgs} instead. + */ + @Parameter + @Deprecated + protected Map<String, String> compilerArguments; - /** - * Sets the name of the output file when compiling a set of - * sources to a single file. - * <p/> - * expression="${project.build.finalName}" - */ - @Parameter - private String outputFileName; + /** + * <p> + * Sets the arguments to be passed to the compiler if {@link #fork} is set to <code>true</code>. Example: + * <pre> + * <compilerArgs> + * <arg>-Xmaxerrs=1000</arg> + * <arg>-Xlint</arg> + * </compilerArgs> + * </pre> + * + * @since 3.1 + */ + @Parameter + protected List<String> compilerArgs; - /** - * Keyword list to be appended to the <code>-g</code> command-line switch. Legal values are none or a - * comma-separated list of the following keywords: <code>lines</code>, <code>vars</code>, and <code>source</code>. - * If debug level is not specified, by default, nothing will be appended to <code>-g</code>. - * If debug is not turned on, this attribute will be ignored. - * - * @since 2.1 - */ - @Parameter(property = "maven.compiler.debuglevel") - private String debuglevel; + /** + * <p> + * Sets the unformatted single argument string to be passed to the compiler if {@link #fork} is set to <code>true</code>. To pass + * multiple arguments such as <code>-Xmaxerrs 1000</code> (which are actually two arguments) you have to use {@link #compilerArguments}. + * </p> + * <p> + * This is because the list of valid arguments passed to a Java compiler varies based on the compiler version. + * </p> + */ + @Parameter + protected String compilerArgument; - /** - * - */ - @Component - private ToolchainManager toolchainManager; + /** + * Sets the name of the output file when compiling a set of sources to a single file. + * <p/> + * expression="${project.build.finalName}" + */ + @Parameter + private String outputFileName; - // ---------------------------------------------------------------------- - // Read-only parameters - // ---------------------------------------------------------------------- + /** + * Keyword list to be appended to the <code>-g</code> command-line switch. Legal values are none or a comma-separated list of the + * following keywords: <code>lines</code>, <code>vars</code>, and <code>source</code>. If debug level is not specified, by default, + * nothing will be appended to <code>-g</code>. If debug is not turned on, this attribute will be ignored. + * + * @since 2.1 + */ + @Parameter(property = "maven.compiler.debuglevel") + private String debuglevel; - /** - * The directory to run the compiler from if fork is true. - */ - @Parameter(defaultValue = "${basedir}", required = true, readonly = true) - private File basedir; + /** + * + */ + @Component + private ToolchainManager toolchainManager; - /** - * The target directory of the compiler if fork is true. - */ - @Parameter(defaultValue = "${project.build.directory}", required = true, readonly = true) - private File buildDirectory; + // ---------------------------------------------------------------------- + // Read-only parameters + // ---------------------------------------------------------------------- + /** + * The directory to run the compiler from if fork is true. + */ + @Parameter(defaultValue = "${basedir}", required = true, readonly = true) + private File basedir; - /** - * Plexus compiler manager. - */ - @Component - private CompilerManager compilerManager; + /** + * The target directory of the compiler if fork is true. + */ + @Parameter(defaultValue = "${project.build.directory}", required = true, readonly = true) + private File buildDirectory; - /** - * The current build session instance. This is used for toolchain manager API calls. - */ - @Component - private MavenSession session; + /** + * Plexus compiler manager. + */ + @Component + private CompilerManager compilerManager; - /** - * Strategy to re use javacc class created: - * <ul> - * <li><code>reuseCreated</code> (default): will reuse already created but in case of multi-threaded builds, - * each thread will have its own instance</li> - * <li><code>reuseSame</code>: the same Javacc class will be used for each compilation even for multi-threaded build</li> - * <li><code>alwaysNew</code>: a new Javacc class will be created for each compilation</li> - * </ul> - * Note this parameter value depends on the os/jdk you are using, but the default value should work on most of env. - * - * @since 2.5 - */ - @Parameter(defaultValue = "${reuseCreated}", property = "maven.compiler.compilerReuseStrategy") - private String compilerReuseStrategy = "reuseCreated"; + /** + * The current build session instance. This is used for toolchain manager API calls. + */ + @Component + private MavenSession session; - /** - * @since 2.5 - */ - @Parameter(defaultValue = "false", property = "maven.compiler.skipMultiThreadWarning") - private boolean skipMultiThreadWarning; + /** + * Strategy to re use javacc class created: + * <ul> + * <li><code>reuseCreated</code> (default): will reuse already created but in case of multi-threaded builds, each thread will have its + * own instance</li> + * <li><code>reuseSame</code>: the same Javacc class will be used for each compilation even for multi-threaded build</li> + * <li><code>alwaysNew</code>: a new Javacc class will be created for each compilation</li> + * </ul> + * Note this parameter value depends on the os/jdk you are using, but the default value should work on most of env. + * + * @since 2.5 + */ + @Parameter(defaultValue = "${reuseCreated}", property = "maven.compiler.compilerReuseStrategy") + private String compilerReuseStrategy = "reuseCreated"; - /** - * compiler can now use javax.tools if available in your current jdk, you can disable this feature - * using -Dmaven.compiler.forceJavacCompilerUse=true or in the plugin configuration - * - * @since 3.0 - */ - @Parameter(defaultValue = "false", property = "maven.compiler.forceJavacCompilerUse") - private boolean forceJavacCompilerUse; + /** + * @since 2.5 + */ + @Parameter(defaultValue = "false", property = "maven.compiler.skipMultiThreadWarning") + private boolean skipMultiThreadWarning; - /** - * @since 3.0 needed for storing the status for the incremental build support. - */ - @Parameter(property = "mojoExecution") - private MojoExecution mojoExecution; + /** + * compiler can now use javax.tools if available in your current jdk, you can disable this feature using + * -Dmaven.compiler.forceJavacCompilerUse=true or in the plugin configuration + * + * @since 3.0 + */ + @Parameter(defaultValue = "false", property = "maven.compiler.forceJavacCompilerUse") + private boolean forceJavacCompilerUse; - /** - * We need this to determine the start timestamp of the build. - * - * @since 3.0 - */ - @Component - protected MavenSession mavenSession; + /** + * @since 3.0 needed for storing the status for the incremental build support. + */ + @Parameter(property = "mojoExecution") + private MojoExecution mojoExecution; - /** - * file extensions to check timestamp for incremental build - * <b>default contains only <code>.class</code></b> - * - * @since 3.1 - */ - @Parameter - private List<String> fileExtensions; + /** + * We need this to determine the start timestamp of the build. + * + * @since 3.0 + */ + @Component + protected MavenSession mavenSession; - /** - * to enable/disable incrementation compilation feature - * @since 3.1 - */ - @Parameter(defaultValue = "true", property = "maven.compiler.useIncrementalCompilation") - private boolean useIncrementalCompilation = true; + /** + * file extensions to check timestamp for incremental build + * <b>default contains only <code>.class</code></b> + * + * @since 3.1 + */ + @Parameter + private List<String> fileExtensions; - protected abstract SourceInclusionScanner getSourceInclusionScanner( int staleMillis ); + /** + * to enable/disable incrementation compilation feature + * + * @since 3.1 + */ + @Parameter(defaultValue = "true", property = "maven.compiler.useIncrementalCompilation") + private boolean useIncrementalCompilation = true; - protected abstract SourceInclusionScanner getSourceInclusionScanner( String inputFileEnding ); + protected abstract SourceInclusionScanner getSourceInclusionScanner(int staleMillis); - protected abstract List<String> getClasspathElements(); + protected abstract SourceInclusionScanner getSourceInclusionScanner(String inputFileEnding); - protected abstract List<String> getCompileSourceRoots(); + protected abstract List<String> getClasspathElements(); - protected abstract File getOutputDirectory(); + protected abstract List<String> getCompileSourceRoots(); - protected abstract String getSource(); + protected abstract File getOutputDirectory(); - protected abstract String getTarget(); + protected abstract String getSource(); - protected abstract String getCompilerArgument(); + protected abstract String getTarget(); - protected abstract Map<String, String> getCompilerArguments(); + protected abstract String getCompilerArgument(); - protected abstract File getGeneratedSourcesDirectory(); + protected abstract Map<String, String> getCompilerArguments(); - public void execute() - throws MojoExecutionException, CompilationFailureException - { - // ---------------------------------------------------------------------- - // Look up the compiler. This is done before other code than can - // cause the mojo to return before the lookup is done possibly resulting - // in misconfigured POMs still building. - // ---------------------------------------------------------------------- + protected abstract File getGeneratedSourcesDirectory(); - Compiler compiler; + public void execute() + throws MojoExecutionException, CompilationFailureException + { + // ---------------------------------------------------------------------- + // Look up the compiler. This is done before other code than can + // cause the mojo to return before the lookup is done possibly resulting + // in misconfigured POMs still building. + // ---------------------------------------------------------------------- - getLog().debug( "Using compiler '" + compilerId + "'." ); + Compiler compiler; - try - { - compiler = compilerManager.getCompiler( compilerId ); - } - catch ( NoSuchCompilerException e ) - { - throw new MojoExecutionException( "No such compiler '" + e.getCompilerId() + "'." ); - } + getLog().debug("Using compiler '" + compilerId + "'."); - //-----------toolchains start here ---------------------------------- - //use the compilerId as identifier for toolchains as well. - Toolchain tc = getToolchain(); - if ( tc != null ) - { - getLog().info( "Toolchain in compiler-plugin: " + tc ); - if ( executable != null ) - { - getLog().warn( "Toolchains are ignored, 'executable' parameter is set to " + executable ); - } - else - { - fork = true; - //TODO somehow shaky dependency between compilerId and tool executable. - executable = tc.findTool( compilerId ); - } - } - // ---------------------------------------------------------------------- - // - // ---------------------------------------------------------------------- + try + { + compiler = compilerManager.getCompiler(compilerId); + } + catch (NoSuchCompilerException e) + { + throw new MojoExecutionException("No such compiler '" + e.getCompilerId() + "'."); + } - List<String> compileSourceRoots = removeEmptyCompileSourceRoots( getCompileSourceRoots() ); + //-----------toolchains start here ---------------------------------- + //use the compilerId as identifier for toolchains as well. + Toolchain tc = getToolchain(); + if (tc != null) + { + getLog().info("Toolchain in compiler-plugin: " + tc); + if (executable != null) + { + getLog().warn("Toolchains are ignored, 'executable' parameter is set to " + executable); + } + else + { + fork = true; + //TODO somehow shaky dependency between compilerId and tool executable. + executable = tc.findTool(compilerId); + } + } + // ---------------------------------------------------------------------- + // + // ---------------------------------------------------------------------- - if ( compileSourceRoots.isEmpty() ) - { - getLog().info( "No sources to compile" ); + List<String> compileSourceRoots = removeEmptyCompileSourceRoots(getCompileSourceRoots()); - return; - } + if (compileSourceRoots.isEmpty()) + { + getLog().info("No sources to compile"); - if ( getLog().isDebugEnabled() ) - { - getLog().debug( "Source directories: " + compileSourceRoots.toString().replace( ',', '\n' ) ); - getLog().debug( "Classpath: " + getClasspathElements().toString().replace( ',', '\n' ) ); - getLog().debug( "Output directory: " + getOutputDirectory() ); - } + return; + } - // ---------------------------------------------------------------------- - // Create the compiler configuration - // ---------------------------------------------------------------------- + if (getLog().isDebugEnabled()) + { + getLog().debug("Source directories: " + compileSourceRoots.toString().replace(',', '\n')); + getLog().debug("Classpath: " + getClasspathElements().toString().replace(',', '\n')); + getLog().debug("Output directory: " + getOutputDirectory()); + } - CompilerConfiguration compilerConfiguration = new CompilerConfiguration(); + // ---------------------------------------------------------------------- + // Create the compiler configuration + // ---------------------------------------------------------------------- + CompilerConfiguration compilerConfiguration = new CompilerConfiguration(); - compilerConfiguration.setOutputLocation( getOutputDirectory().getAbsolutePath() ); + compilerConfiguration.setOutputLocation(getOutputDirectory().getAbsolutePath()); - compilerConfiguration.setClasspathEntries( getClasspathElements() ); + compilerConfiguration.setClasspathEntries(getClasspathElements()); - compilerConfiguration.setSourceLocations( compileSourceRoots ); + compilerConfiguration.setSourceLocations(compileSourceRoots); - compilerConfiguration.setOptimize( optimize ); + compilerConfiguration.setOptimize(optimize); - compilerConfiguration.setDebug( debug ); + compilerConfiguration.setDebug(debug); - if ( debug && StringUtils.isNotEmpty( debuglevel ) ) - { - String[] split = StringUtils.split( debuglevel, "," ); - for ( int i = 0; i < split.length; i++ ) + if (debug && StringUtils.isNotEmpty(debuglevel)) + { + String[] split = StringUtils.split(debuglevel, ","); + for (int i = 0; i < split.length; i++) + { + if (!(split[i].equalsIgnoreCase("none") || split[i].equalsIgnoreCase("lines") + || split[i].equalsIgnoreCase("vars") || split[i].equalsIgnoreCase("source"))) { - if ( !( split[i].equalsIgnoreCase( "none" ) || split[i].equalsIgnoreCase( "lines" ) - || split[i].equalsIgnoreCase( "vars" ) || split[i].equalsIgnoreCase( "source" ) ) ) - { - throw new IllegalArgumentException( "The specified debug level: '" + split[i] + "' is unsupported. " - + "Legal values are 'none', 'lines', 'vars', and 'source'." ); - } + throw new IllegalArgumentException("The specified debug level: '" + split[i] + "' is unsupported. " + + "Legal values are 'none', 'lines', 'vars', and 'source'."); } - compilerConfiguration.setDebugLevel( debuglevel ); - } + } + compilerConfiguration.setDebugLevel(debuglevel); + } - compilerConfiguration.setVerbose( verbose ); + compilerConfiguration.setVerbose(verbose); - compilerConfiguration.setShowWarnings( showWarnings ); + compilerConfiguration.setShowWarnings(showWarnings); - compilerConfiguration.setShowDeprecation( showDeprecation ); + compilerConfiguration.setShowDeprecation(showDeprecation); - compilerConfiguration.setSourceVersion( getSource() ); + compilerConfiguration.setSourceVersion(getSource()); - compilerConfiguration.setTargetVersion( getTarget() ); + compilerConfiguration.setTargetVersion(getTarget()); - compilerConfiguration.setProc( proc ); + compilerConfiguration.setProc(proc); - compilerConfiguration.setGeneratedSourcesDirectory( getGeneratedSourcesDirectory() ); + compilerConfiguration.setGeneratedSourcesDirectory(getGeneratedSourcesDirectory()); - compilerConfiguration.setAnnotationProcessors( annotationProcessors ); + compilerConfiguration.setAnnotationProcessors(annotationProcessors); - compilerConfiguration.setSourceEncoding( encoding ); + compilerConfiguration.setSourceEncoding(encoding); - Map<String, String> effectiveCompilerArguments = getCompilerArguments(); + Map<String, String> effectiveCompilerArguments = getCompilerArguments(); - String effectiveCompilerArgument = getCompilerArgument(); + String effectiveCompilerArgument = getCompilerArgument(); - if ( ( effectiveCompilerArguments != null ) || ( effectiveCompilerArgument != null ) || ( compilerArgs != null ) ) - { - LinkedHashMap<String, String> cplrArgsCopy = new LinkedHashMap<String, String>(); - if ( effectiveCompilerArguments != null ) + if ((effectiveCompilerArguments != null) || (effectiveCompilerArgument != null) || (compilerArgs != null)) + { + LinkedHashMap<String, String> cplrArgsCopy = new LinkedHashMap<String, String>(); + if (effectiveCompilerArguments != null) + { + for (Map.Entry<String, String> me : effectiveCompilerArguments.entrySet()) { - for ( Map.Entry<String, String> me : effectiveCompilerArguments.entrySet() ) - { - String key = me.getKey(); - String value = me.getValue(); - if ( !key.startsWith( "-" ) ) - { - key = "-" + key; - } + String key = me.getKey(); + String value = me.getValue(); + if (!key.startsWith("-")) + { + key = "-" + key; + } - if ( key.startsWith( "-A" ) && StringUtils.isNotEmpty( value ) ) - { - cplrArgsCopy.put( key + "=" + value, null ); - } - else - { - cplrArgsCopy.put( key, value ); - } - } + if (key.startsWith("-A") && StringUtils.isNotEmpty(value)) + { + cplrArgsCopy.put(key + "=" + value, null); + } + else + { + cplrArgsCopy.put(key, value); + } } - if ( !StringUtils.isEmpty( effectiveCompilerArgument ) ) + } + if (!StringUtils.isEmpty(effectiveCompilerArgument)) + { + cplrArgsCopy.put(effectiveCompilerArgument, null); + } + if (compilerArgs != null) + { + for (String arg : compilerArgs) { - cplrArgsCopy.put( effectiveCompilerArgument, null ); + cplrArgsCopy.put(arg, null); } - if ( compilerArgs != null ) + } + compilerConfiguration.setCustomCompilerArguments(cplrArgsCopy); + } + + compilerConfiguration.setFork(fork); + + if (fork) + { + if (!StringUtils.isEmpty(meminitial)) + { + String value = getMemoryValue(meminitial); + + if (value != null) { - for ( String arg : compilerArgs ) - { - cplrArgsCopy.put( arg, null ); - } + compilerConfiguration.setMeminitial(value); } - compilerConfiguration.setCustomCompilerArguments( cplrArgsCopy ); - } + else + { + getLog().info("Invalid value for meminitial '" + meminitial + "'. Ignoring this option."); + } + } - compilerConfiguration.setFork( fork ); + if (!StringUtils.isEmpty(maxmem)) + { + String value = getMemoryValue(maxmem); - if ( fork ) - { - if ( !StringUtils.isEmpty( meminitial ) ) + if (value != null) { - String value = getMemoryValue( meminitial ); - - if ( value != null ) - { - compilerConfiguration.setMeminitial( value ); - } - else - { - getLog().info( "Invalid value for meminitial '" + meminitial + "'. Ignoring this option." ); - } + compilerConfiguration.setMaxmem(value); } - - if ( !StringUtils.isEmpty( maxmem ) ) + else { - String value = getMemoryValue( maxmem ); - - if ( value != null ) - { - compilerConfiguration.setMaxmem( value ); - } - else - { - getLog().info( "Invalid value for maxmem '" + maxmem + "'. Ignoring this option." ); - } + getLog().info("Invalid value for maxmem '" + maxmem + "'. Ignoring this option."); } - } + } + } - compilerConfiguration.setExecutable( executable ); + compilerConfiguration.setExecutable(executable); - compilerConfiguration.setWorkingDirectory( basedir ); + compilerConfiguration.setWorkingDirectory(basedir); - compilerConfiguration.setCompilerVersion( compilerVersion ); + compilerConfiguration.setCompilerVersion(compilerVersion); - compilerConfiguration.setBuildDirectory( buildDirectory ); + compilerConfiguration.setBuildDirectory(buildDirectory); - compilerConfiguration.setOutputFileName( outputFileName ); + compilerConfiguration.setOutputFileName(outputFileName); - if ( CompilerConfiguration.CompilerReuseStrategy.AlwaysNew.getStrategy().equals( this.compilerReuseStrategy ) ) - { - compilerConfiguration.setCompilerReuseStrategy( CompilerConfiguration.CompilerReuseStrategy.AlwaysNew ); - } - else if ( CompilerConfiguration.CompilerReuseStrategy.ReuseSame.getStrategy().equals( - this.compilerReuseStrategy ) ) - { - if ( getRequestThreadCount() > 1 ) + if (CompilerConfiguration.CompilerReuseStrategy.AlwaysNew.getStrategy().equals(this.compilerReuseStrategy)) + { + compilerConfiguration.setCompilerReuseStrategy(CompilerConfiguration.CompilerReuseStrategy.AlwaysNew); + } + else if (CompilerConfiguration.CompilerReuseStrategy.ReuseSame.getStrategy().equals( + this.compilerReuseStrategy)) + { + if (getRequestThreadCount() > 1) + { + if (!skipMultiThreadWarning) { - if ( !skipMultiThreadWarning ) - { - StringBuilder sb = new StringBuilder( - "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." ); - sb.append( System.getProperty( "line.separator" ) ); - sb.append( - "If your env is fine with reuseSame, you can skip this warning with the configuration field skipMultiThreadWarning or -Dmaven.compiler.skipMultiThreadWarning=true" ); - getLog().warn( sb.toString() ); - } + StringBuilder sb = new StringBuilder( + "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."); + sb.append(System.getProperty("line.separator")); + sb.append( + "If your env is fine with reuseSame, you can skip this warning with the configuration field skipMultiThreadWarning or -Dmaven.compiler.skipMultiThreadWarning=true"); + getLog().warn(sb.toString()); } - compilerConfiguration.setCompilerReuseStrategy( CompilerConfiguration.CompilerReuseStrategy.ReuseSame ); - } - else - { + } + compilerConfiguration.setCompilerReuseStrategy(CompilerConfiguration.CompilerReuseStrategy.ReuseSame); + } + else + { - compilerConfiguration.setCompilerReuseStrategy( CompilerConfiguration.CompilerReuseStrategy.ReuseCreated ); - } + compilerConfiguration.setCompilerReuseStrategy(CompilerConfiguration.CompilerReuseStrategy.ReuseCreated); + } - getLog().debug( "CompilerReuseStrategy: " + compilerConfiguration.getCompilerReuseStrategy().getStrategy() ); + getLog().debug("CompilerReuseStrategy: " + compilerConfiguration.getCompilerReuseStrategy().getStrategy()); - compilerConfiguration.setForceJavacCompilerUse( forceJavacCompilerUse ); + compilerConfiguration.setForceJavacCompilerUse(forceJavacCompilerUse); - boolean canUpdateTarget; + boolean canUpdateTarget; - IncrementalBuildHelper incrementalBuildHelper = new IncrementalBuildHelper( mojoExecution, mavenSession ); + IncrementalBuildHelper incrementalBuildHelper = new IncrementalBuildHelper(mojoExecution, mavenSession); - Set<File> sources = null; + Set<File> sources = null; - IncrementalBuildHelperRequest incrementalBuildHelperRequest = null; + IncrementalBuildHelperRequest incrementalBuildHelperRequest = null; + Map<File, byte[]> dependencyInfo = null; - if ( useIncrementalCompilation ) - { - getLog().debug( "useIncrementalCompilation enabled" ); - try - { - canUpdateTarget = compiler.canUpdateTarget( compilerConfiguration ); + if (useIncrementalCompilation) + { + getLog().debug("useIncrementalCompilation enabled"); + try + { + canUpdateTarget = compiler.canUpdateTarget(compilerConfiguration); - sources = getCompileSources( compiler, compilerConfiguration ); + sources = getCompileSources(compiler, compilerConfiguration); - incrementalBuildHelperRequest = new IncrementalBuildHelperRequest().inputFiles( sources ); + incrementalBuildHelperRequest = new IncrementalBuildHelperRequest().inputFiles(sources); + dependencyInfo = createDependencyInfo(); - if ( ( compiler.getCompilerOutputStyle().equals( CompilerOutputStyle.ONE_OUTPUT_FILE_FOR_ALL_INPUT_FILES ) - && !canUpdateTarget ) || isDependencyChanged() || isSourceChanged( compilerConfiguration, compiler ) - || incrementalBuildHelper.inputFileTreeChanged( incrementalBuildHelperRequest ) ) - { - getLog().info( "Changes detected - recompiling the module!" ); + if ((compiler.getCompilerOutputStyle().equals(CompilerOutputStyle.ONE_OUTPUT_FILE_FOR_ALL_INPUT_FILES) + && !canUpdateTarget) || isDependencyChanged(incrementalBuildHelper, dependencyInfo) + || isSourceChanged(compilerConfiguration, compiler) + || incrementalBuildHelper.inputFileTreeChanged(incrementalBuildHelperRequest)) + { + getLog().info("Changes detected - recompiling the module!"); - compilerConfiguration.setSourceFiles( sources ); - } - else - { - getLog().info( "Nothing to compile - all classes are up to date" ); - - return; - } + compilerConfiguration.setSourceFiles(sources); } - catch ( CompilerException e ) + else { - throw new MojoExecutionException( "Error while computing stale sources.", e ); + getLog().info("Nothing to compile - all classes are up to date"); + + return; } - } - else - { - getLog().debug( "useIncrementalCompilation disabled" ); - Set<File> staleSources; - try - { - staleSources = - computeStaleSources( compilerConfiguration, compiler, getSourceInclusionScanner( staleMillis ) ); + } + catch (CompilerException e) + { + throw new MojoExecutionException("Error while computing stale sources.", e); + } + } + else + { + getLog().debug("useIncrementalCompilation disabled"); + Set<File> staleSources; + try + { + staleSources + = computeStaleSources(compilerConfiguration, compiler, getSourceInclusionScanner(staleMillis)); - canUpdateTarget = compiler.canUpdateTarget( compilerConfiguration ); + canUpdateTarget = compiler.canUpdateTarget(compilerConfiguration); - if ( compiler.getCompilerOutputStyle().equals( CompilerOutputStyle.ONE_OUTPUT_FILE_FOR_ALL_INPUT_FILES ) - && !canUpdateTarget ) - { - getLog().info( "RESCANNING!" ); - // TODO: This second scan for source files is sub-optimal - String inputFileEnding = compiler.getInputFileEnding( compilerConfiguration ); + if (compiler.getCompilerOutputStyle().equals(CompilerOutputStyle.ONE_OUTPUT_FILE_FOR_ALL_INPUT_FILES) + && !canUpdateTarget) + { + getLog().info("RESCANNING!"); + // TODO: This second scan for source files is sub-optimal + String inputFileEnding = compiler.getInputFileEnding(compilerConfiguration); - sources = computeStaleSources( compilerConfiguration, compiler, - getSourceInclusionScanner( inputFileEnding ) ); + sources = computeStaleSources(compilerConfiguration, compiler, + getSourceInclusionScanner(inputFileEnding)); - compilerConfiguration.setSourceFiles( sources ); - } - else - { - compilerConfiguration.setSourceFiles( staleSources ); - } + compilerConfiguration.setSourceFiles(sources); } - catch ( CompilerException e ) + else { - throw new MojoExecutionException( "Error while computing stale sources.", e ); + compilerConfiguration.setSourceFiles(staleSources); } + } + catch (CompilerException e) + { + throw new MojoExecutionException("Error while computing stale sources.", e); + } - if ( staleSources.isEmpty() ) - { - getLog().info( "Nothing to compile - all classes are up to date" ); + if (staleSources.isEmpty()) + { + getLog().info("Nothing to compile - all classes are up to date"); - return; - } - } - // ---------------------------------------------------------------------- - // Dump configuration - // ---------------------------------------------------------------------- + return; + } + } + // ---------------------------------------------------------------------- + // Dump configuration + // ---------------------------------------------------------------------- - if ( getLog().isDebugEnabled() ) - { - getLog().debug( "Classpath:" ); + if (getLog().isDebugEnabled()) + { + getLog().debug("Classpath:"); - for ( String s : getClasspathElements() ) - { - getLog().debug( " " + s ); - } + for (String s : getClasspathElements()) + { + getLog().debug(" " + s); + } - getLog().debug( "Source roots:" ); + getLog().debug("Source roots:"); - for ( String root : getCompileSourceRoots() ) + for (String root : getCompileSourceRoots()) + { + getLog().debug(" " + root); + } + + try + { + if (fork) { - getLog().debug( " " + root ); + if (compilerConfiguration.getExecutable() != null) + { + getLog().debug("Excutable: "); + getLog().debug(" " + compilerConfiguration.getExecutable()); + } } - try + String[] cl = compiler.createCommandLine(compilerConfiguration); + if (cl != null && cl.length > 0) { - if ( fork ) - { - if ( compilerConfiguration.getExecutable() != null ) - { - getLog().debug( "Excutable: " ); - getLog().debug( " " + compilerConfiguration.getExecutable() ); - } - } - - String[] cl = compiler.createCommandLine( compilerConfiguration ); - if ( cl != null && cl.length > 0 ) - { - StringBuilder sb = new StringBuilder(); - sb.append( cl[0] ); - for ( int i = 1; i < cl.length; i++ ) - { - sb.append( " " ); - sb.append( cl[i] ); - } - getLog().debug( "Command line options:" ); - getLog().debug( sb ); - } + StringBuilder sb = new StringBuilder(); + sb.append(cl[0]); + for (int i = 1; i < cl.length; i++) + { + sb.append(" "); + sb.append(cl[i]); + } + getLog().debug("Command line options:"); + getLog().debug(sb); } - catch ( CompilerException ce ) - { - getLog().debug( ce ); - } - } + } + catch (CompilerException ce) + { + getLog().debug(ce); + } + } - // ---------------------------------------------------------------------- - // Compile! - // ---------------------------------------------------------------------- + // ---------------------------------------------------------------------- + // Compile! + // ---------------------------------------------------------------------- + if (StringUtils.isEmpty(compilerConfiguration.getSourceEncoding())) + { + getLog().warn("File encoding has not been set, using platform encoding " + ReaderFactory.FILE_ENCODING + + ", i.e. build is platform dependent!"); + } - if ( StringUtils.isEmpty( compilerConfiguration.getSourceEncoding() ) ) - { - getLog().warn( "File encoding has not been set, using platform encoding " + ReaderFactory.FILE_ENCODING - + ", i.e. build is platform dependent!" ); - } + CompilerResult compilerResult; - CompilerResult compilerResult; + if (useIncrementalCompilation) + { + incrementalBuildHelperRequest.outputDirectory(getOutputDirectory()); + incrementalBuildHelper.beforeRebuildExecution(incrementalBuildHelperRequest); - if ( useIncrementalCompilation ) - { - incrementalBuildHelperRequest.outputDirectory( getOutputDirectory() ); + getLog().debug("incrementalBuildHelper#beforeRebuildExecution"); + } - incrementalBuildHelper.beforeRebuildExecution( incrementalBuildHelperRequest ); + try + { + try + { + compilerResult = compiler.performCompile(compilerConfiguration); + } + catch (CompilerNotImplementedException cnie) + { + List<CompilerError> messages = compiler.compile(compilerConfiguration); + compilerResult = convertToCompilerResult(messages); + } + } + catch (Exception e) + { + // TODO: don't catch Exception + throw new MojoExecutionException("Fatal error compiling", e); + } - getLog().debug( "incrementalBuildHelper#beforeRebuildExecution" ); - } + if (useIncrementalCompilation) + { + if (incrementalBuildHelperRequest.getOutputDirectory().exists()) + { + getLog().debug("incrementalBuildHelper#afterRebuildExecution"); + // now scan the same directory again and create a diff + incrementalBuildHelper.afterRebuildExecution(incrementalBuildHelperRequest); + keepDependencyInfo(incrementalBuildHelper, dependencyInfo); + } + else + { + getLog().debug( + "skip incrementalBuildHelper#afterRebuildExecution as the output directory doesn't exist"); + } + } - try - { - try - { - compilerResult = compiler.performCompile( compilerConfiguration ); - } - catch ( CompilerNotImplementedException cnie ) - { - List<CompilerError> messages = compiler.compile( compilerConfiguration ); - compilerResult = convertToCompilerResult( messages ); - } - } - catch ( Exception e ) - { - // TODO: don't catch Exception - throw new MojoExecutionException( "Fatal error compiling", e ); - } + List<CompilerMessage> warnings = new ArrayList<CompilerMessage>(); + List<CompilerMessage> errors = new ArrayList<CompilerMessage>(); + for (CompilerMessage message : compilerResult.getCompilerMessages()) + { + if (message.isError()) + { + errors.add(message); + } + else + { + warnings.add(message); + } + } - if ( useIncrementalCompilation ) - { - if ( incrementalBuildHelperRequest.getOutputDirectory().exists() ) + if (failOnError && !compilerResult.isSuccess()) + { + if (!warnings.isEmpty()) + { + getLog().info("-------------------------------------------------------------"); + getLog().warn("COMPILATION WARNING : "); + getLog().info("-------------------------------------------------------------"); + for (CompilerMessage warning : warnings) { - getLog().debug( "incrementalBuildHelper#afterRebuildExecution" ); - // now scan the same directory again and create a diff - incrementalBuildHelper.afterRebuildExecution( incrementalBuildHelperRequest ); + getLog().warn(warning.toString()); } - else - { - getLog().debug( - "skip incrementalBuildHelper#afterRebuildExecution as the output directory doesn't exist" ); - } - } + getLog().info(warnings.size() + ((warnings.size() > 1) ? " warnings " : " warning")); + getLog().info("-------------------------------------------------------------"); + } - List<CompilerMessage> warnings = new ArrayList<CompilerMessage>(); - List<CompilerMessage> errors = new ArrayList<CompilerMessage>(); - for ( CompilerMessage message : compilerResult.getCompilerMessages() ) - { - if ( message.isError() ) + if (!errors.isEmpty()) + { + getLog().info("-------------------------------------------------------------"); + getLog().error("COMPILATION ERROR : "); + getLog().info("-------------------------------------------------------------"); + for (CompilerMessage error : errors) { - errors.add( message ); + getLog().error(error.toString()); } - else - { - warnings.add( message ); - } - } + getLog().info(errors.size() + ((errors.size() > 1) ? " errors " : " error")); + getLog().info("-------------------------------------------------------------"); + } - if ( failOnError && !compilerResult.isSuccess() ) - { - if ( !warnings.isEmpty() ) - { - getLog().info( "-------------------------------------------------------------" ); - getLog().warn( "COMPILATION WARNING : " ); - getLog().info( "-------------------------------------------------------------" ); - for ( CompilerMessage warning : warnings ) - { - getLog().warn( warning.toString() ); - } - getLog().info( warnings.size() + ( ( warnings.size() > 1 ) ? " warnings " : " warning" ) ); - getLog().info( "-------------------------------------------------------------" ); - } + if (!errors.isEmpty()) + { + throw new CompilationFailureException(errors); + } + else + { + throw new CompilationFailureException(warnings); + } + } + else + { + for (CompilerMessage message : compilerResult.getCompilerMessages()) + { + getLog().warn(message.toString()); + } + } + } - if ( !errors.isEmpty() ) - { - getLog().info( "-------------------------------------------------------------" ); - getLog().error( "COMPILATION ERROR : " ); - getLog().info( "-------------------------------------------------------------" ); - for ( CompilerMessage error : errors ) - { - getLog().error( error.toString() ); - } - getLog().info( errors.size() + ( ( errors.size() > 1 ) ? " errors " : " error" ) ); - getLog().info( "-------------------------------------------------------------" ); - } + protected CompilerResult convertToCompilerResult(List<CompilerError> compilerErrors) + { + if (compilerErrors == null) + { + return new CompilerResult(); + } + List<CompilerMessage> messages = new ArrayList<CompilerMessage>(compilerErrors.size()); + boolean success = true; + for (CompilerError compilerError : compilerErrors) + { + messages.add( + new CompilerMessage(compilerError.getFile(), compilerError.getKind(), compilerError.getStartLine(), + compilerError.getStartColumn(), compilerError.getEndLine(), + compilerError.getEndColumn(), compilerError.getMessage())); + if (compilerError.isError()) + { + success = false; + } + } - if ( !errors.isEmpty() ) - { - throw new CompilationFailureException( errors ); - } - else - { - throw new CompilationFailureException( warnings ); - } - } - else - { - for ( CompilerMessage message : compilerResult.getCompilerMessages() ) - { - getLog().warn( message.toString() ); - } - } - } + return new CompilerResult(success, messages); + } - protected CompilerResult convertToCompilerResult( List<CompilerError> compilerErrors ) - { - if ( compilerErrors == null ) - { - return new CompilerResult(); - } - List<CompilerMessage> messages = new ArrayList<CompilerMessage>( compilerErrors.size() ); - boolean success = true; - for ( CompilerError compilerError : compilerErrors ) - { - messages.add( - new CompilerMessage( compilerError.getFile(), compilerError.getKind(), compilerError.getStartLine(), - compilerError.getStartColumn(), compilerError.getEndLine(), - compilerError.getEndColumn(), compilerError.getMessage() ) ); - if ( compilerError.isError() ) - { - success = false; - } - } + /** + * @return all source files for the compiler + */ + private Set<File> getCompileSources(Compiler compiler, CompilerConfiguration compilerConfiguration) + throws MojoExecutionException, CompilerException + { + String inputFileEnding = compiler.getInputFileEnding(compilerConfiguration); + if (StringUtils.isEmpty(inputFileEnding)) + { + // see MCOMPILER-199 GroovyEclipseCompiler doesn't set inputFileEnding + // so we can presume it's all files from the source directory + inputFileEnding = ".*"; + } + SourceInclusionScanner scanner = getSourceInclusionScanner(inputFileEnding); - return new CompilerResult( success, messages ); - } + SourceMapping mapping = getSourceMapping(compilerConfiguration, compiler); - /** - * @return all source files for the compiler - */ - private Set<File> getCompileSources( Compiler compiler, CompilerConfiguration compilerConfiguration ) - throws MojoExecutionException, CompilerException - { - String inputFileEnding = compiler.getInputFileEnding( compilerConfiguration ); - if ( StringUtils.isEmpty( inputFileEnding ) ) - { - // see MCOMPILER-199 GroovyEclipseCompiler doesn't set inputFileEnding - // so we can presume it's all files from the source directory - inputFileEnding = ".*"; - } - SourceInclusionScanner scanner = getSourceInclusionScanner( inputFileEnding ); + scanner.addSourceMapping(mapping); - SourceMapping mapping = getSourceMapping( compilerConfiguration, compiler ); + Set<File> compileSources = new HashSet<File>(); - scanner.addSourceMapping( mapping ); + for (String sourceRoot : getCompileSourceRoots()) + { + File rootFile = new File(sourceRoot); - Set<File> compileSources = new HashSet<File>(); + if (!rootFile.isDirectory()) + { + continue; + } - for ( String sourceRoot : getCompileSourceRoots() ) - { - File rootFile = new File( sourceRoot ); + try + { + compileSources.addAll(scanner.getIncludedSources(rootFile, null)); + } + catch (InclusionScanException e) + { + throw new MojoExecutionException( + "Error scanning source root: \'" + sourceRoot + "\' for stale files to recompile.", e); + } + } - if ( !rootFile.isDirectory() ) - { - continue; - } + return compileSources; + } - try - { - compileSources.addAll( scanner.getIncludedSources( rootFile, null ) ); - } - catch ( InclusionScanException e ) - { - throw new MojoExecutionException( - "Error scanning source root: \'" + sourceRoot + "\' for stale files to recompile.", e ); - } - } + /** + * @param compilerConfiguration + * @param compiler + * @return <code>true</code> if at least a single source file is newer than it's class file + */ + private boolean isSourceChanged(CompilerConfiguration compilerConfiguration, Compiler compiler) + throws CompilerException, MojoExecutionException + { + Set<File> staleSources + = computeStaleSources(compilerConfiguration, compiler, getSourceInclusionScanner(staleMillis)); - return compileSources; - } + if (getLog().isDebugEnabled()) + { + for (File f : staleSources) + { + getLog().debug("Stale source detected: " + f.getAbsolutePath()); + } + } + return staleSources != null && staleSources.size() > 0; + } - /** - * @param compilerConfiguration - * @param compiler - * @return <code>true</code> if at least a single source file is newer than it's class file - */ - private boolean isSourceChanged( CompilerConfiguration compilerConfiguration, Compiler compiler ) - throws CompilerException, MojoExecutionException - { - Set<File> staleSources = - computeStaleSources( compilerConfiguration, compiler, getSourceInclusionScanner( staleMillis ) ); + /** + * 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 + */ + protected int getRequestThreadCount() + { + try + { + Method getRequestMethod = session.getClass().getMethod("getRequest"); + Object mavenExecutionRequest = getRequestMethod.invoke(this.session); + Method getThreadCountMethod = mavenExecutionRequest.getClass().getMethod("getThreadCount"); + String threadCount = (String) getThreadCountMethod.invoke(mavenExecutionRequest); + return Integer.valueOf(threadCount); + } + catch (Exception e) + { + getLog().debug("unable to get threadCount for the current build: " + e.getMessage()); + } + return 1; + } - if ( getLog().isDebugEnabled() ) - { - for ( File f : staleSources ) - { - getLog().debug( "Stale source detected: " + f.getAbsolutePath() ); - } - } - return staleSources != null && staleSources.size() > 0; - } + 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(); + } - /** - * 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 - */ - protected int getRequestThreadCount() - { - try - { - Method getRequestMethod = session.getClass().getMethod( "getRequest" ); - Object mavenExecutionRequest = getRequestMethod.invoke( this.session ); - Method getThreadCountMethod = mavenExecutionRequest.getClass().getMethod( "getThreadCount" ); - String threadCount = (String) getThreadCountMethod.invoke( mavenExecutionRequest ); - return Integer.valueOf( threadCount ); - } - catch ( Exception e ) - { - getLog().debug( "unable to get threadCount for the current build: " + e.getMessage() ); - } - return 1; - } + private String getMemoryValue(String setting) + { + String value = null; - 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() ); - } + // Allow '128' or '128m' + if (isDigits(setting)) + { + value = setting + "m"; + } + else if ((isDigits(setting.substring(0, setting.length() - 1))) && (setting.toLowerCase().endsWith( + "m"))) + { + value = setting; + } + return value; + } - return new Date(); - } + //TODO remove the part with ToolchainManager lookup once we depend on + //3.0.9 (have it as prerequisite). Define as regular component field then. + private Toolchain getToolchain() + { + Toolchain tc = null; + if (toolchainManager != null) + { + tc = toolchainManager.getToolchainFromBuildContext("jdk", session); + } + return tc; + } + private boolean isDigits(String string) + { + for (int i = 0; i < string.length(); i++) + { + if (!Character.isDigit(string.charAt(i))) + { + return false; + } + } + return true; + } - private String getMemoryValue( String setting ) - { - String value = null; + private Set<File> computeStaleSources(CompilerConfiguration compilerConfiguration, Compiler compiler, + SourceInclusionScanner scanner) + throws MojoExecutionException, CompilerException + { + SourceMapping mapping = getSourceMapping(compilerConfiguration, compiler); - // Allow '128' or '128m' - if ( isDigits( setting ) ) - { - value = setting + "m"; - } - else if ( ( isDigits( setting.substring( 0, setting.length() - 1 ) ) ) && ( setting.toLowerCase().endsWith( - "m" ) ) ) - { - value = setting; - } - return value; - } + File outputDirectory; + CompilerOutputStyle outputStyle = compiler.getCompilerOutputStyle(); + if (outputStyle == CompilerOutputStyle.ONE_OUTPUT_FILE_FOR_ALL_INPUT_FILES) + { + outputDirectory = buildDirectory; + } + else + { + outputDirectory = getOutputDirectory(); + } - //TODO remove the part with ToolchainManager lookup once we depend on - //3.0.9 (have it as prerequisite). Define as regular component field then. - private Toolchain getToolchain() - { - Toolchain tc = null; - if ( toolchainManager != null ) - { - tc = toolchainManager.getToolchainFromBuildContext( "jdk", session ); - } - return tc; - } + scanner.addSourceMapping(mapping); - private boolean isDigits( String string ) - { - for ( int i = 0; i < string.length(); i++ ) - { - if ( !Character.isDigit( string.charAt( i ) ) ) + Set<File> staleSources = new HashSet<File>(); + + for (String sourceRoot : getCompileSourceRoots()) + { + File rootFile = new File(sourceRoot); + + if (!rootFile.isDirectory()) + { + continue; + } + + try + { + staleSources.addAll(scanner.getIncludedSources(rootFile, outputDirectory)); + } + catch (InclusionScanException e) + { + throw new MojoExecutionException( + "Error scanning source root: \'" + sourceRoot + "\' for stale files to recompile.", e); + } + } + + 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? + */ + private static List<String> removeEmptyCompileSourceRoots(List<String> compileSourceRootsList) + { + List<String> newCompileSourceRootsList = new ArrayList<String>(); + if (compileSourceRootsList != null) + { + // copy as I may be modifying it + for (String srcDir : compileSourceRootsList) + { + if (!newCompileSourceRootsList.contains(srcDir) && new File(srcDir).exists()) { - return false; + newCompileSourceRootsList.add(srcDir); } - } - return true; - } + } + } + return newCompileSourceRootsList; + } - private Set<File> computeStaleSources( CompilerConfiguration compilerConfiguration, Compiler compiler, - SourceInclusionScanner scanner ) - throws MojoExecutionException, CompilerException - { - SourceMapping mapping = getSourceMapping( compilerConfiguration, compiler ); + /** + * We just compare the timestamps of all local dependency files (inter-module dependency classpath) and the own generated classes 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(IncrementalBuildHelper incrementalBuildHelper, Map<File, byte[]> dependencyInfo) throws MojoExecutionException + { + 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; + } - File outputDirectory; - CompilerOutputStyle outputStyle = compiler.getCompilerOutputStyle(); - if ( outputStyle == CompilerOutputStyle.ONE_OUTPUT_FILE_FOR_ALL_INPUT_FILES ) - { - outputDirectory = buildDirectory; - } - else - { - outputDirectory = getOutputDirectory(); - } + if (fileExtensions == null || fileExtensions.isEmpty()) + { + fileExtensions = new ArrayList<String>(); + fileExtensions.add(".class"); + } - scanner.addSourceMapping( mapping ); + Date buildStartTime = getBuildStartTime(); - Set<File> staleSources = new HashSet<File>(); + final File mojoConfigFile = new File(incrementalBuildHelper.getMojoStatusDirectory(), DEPENDENCY_INFO_FILENAME); + if (!mojoConfigFile.exists()) + { + getLog().debug("No file " + DEPENDENCY_INFO_FILENAME + " - assume rebuild."); + return true; + } - for ( String sourceRoot : getCompileSourceRoots() ) - { - File rootFile = new File( sourceRoot ); + Map<File, byte[]> origDependencyInfo; + try + { + FileInputStream fis = new FileInputStream(mojoConfigFile); + ObjectInputStream ois = new ObjectInputStream(fis); + origDependencyInfo = (Map<File, byte[]>) ois.readObject(); + ois.close(); + } + catch (FileNotFoundException ex) + { + getLog().debug("No file " + DEPENDENCY_INFO_FILENAME + " - assume rebuild.", ex); + return true; + } + catch (IOException ex) + { + getLog().debug("Can't read file " + DEPENDENCY_INFO_FILENAME + " - assume rebuild.", ex); + return true; + } + catch (ClassNotFoundException ex) + { + getLog().debug("Unexpected exception - assume rebuild.", ex); + return true; + } - if ( !rootFile.isDirectory() ) + for (String classPathElement : getClasspathElements()) + { + File artifactPath = new File(classPathElement); + if (artifactPath.isDirectory()) + { + // ProjectArtifacts are artifacts which are available in the local project + // that's the only ones we are interested in now. + if (hasNewFile(artifactPath, buildStartTime)) { - continue; + getLog().debug("New dependency detected: " + artifactPath.getAbsolutePath()); + return true; } - - try + } + else if (artifactPath.isFile()) + { + // + dependent jar files and class files. + final File checksum_file = new File(artifactPath.getAbsolutePath() + ".sha1"); + byte[] checksum = dependencyInfo.get(checksum_file); + if (checksum == null) { - staleSources.addAll( scanner.getIncludedSources( rootFile, outputDirectory ) ); + // ignore such dependency. } - catch ( InclusionScanException e ) + + byte[] origChecksum = origDependencyInfo.get(checksum_file); + if (checksum == null) { - throw new MojoExecutionException( - "Error scanning source root: \'" + sourceRoot + "\' for stale files to recompile.", e ); + // ignore such dependency. } - } + if (origChecksum == null || !Arrays.equals(origChecksum, checksum)) + { + getLog().debug("New dependency detected: " + artifactPath.getAbsolutePath()); + return true; + } + } + } - return staleSources; - } + // obviously there was no new file detected. + return false; + } - private SourceMapping getSourceMapping( CompilerConfiguration compilerConfiguration, Compiler compiler ) - throws CompilerException, MojoExecutionException - { - CompilerOutputStyle outputStyle = compiler.getCompilerOutputStyle(); + /** + * @param classPathEntry entry to check + * @param buildStartTime time build start + * @return if any changes occured + */ + private boolean hasNewFile(File classPathEntry, Date buildStartTime) + { + if (!classPathEntry.exists()) + { + return false; + } - 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 ) ); + if (classPathEntry.isFile()) + { + return classPathEntry.lastModified() >= buildStartTime.getTime() && fileExtensions.contains( + FileUtils.getExtension(classPathEntry.getName())); + } - } - else - { - throw new MojoExecutionException( "Unknown compiler output style: '" + outputStyle + "'." ); - } - return mapping; - } + File[] children = classPathEntry.listFiles(); - /** - * @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? - */ - private static List<String> removeEmptyCompileSourceRoots( List<String> compileSourceRootsList ) - { - List<String> newCompileSourceRootsList = new ArrayList<String>(); - if ( compileSourceRootsList != null ) - { - // copy as I may be modifying it - for ( String srcDir : compileSourceRootsList ) - { - if ( !newCompileSourceRootsList.contains( srcDir ) && new File( srcDir ).exists() ) - { - newCompileSourceRootsList.add( srcDir ); - } - } - } - return newCompileSourceRootsList; - } + for (File child : children) + { + if (hasNewFile(child, buildStartTime)) + { + return true; + } + } - /** - * We just compare the timestamps of all local dependency files (inter-module dependency classpath) - * and the own generated classes - * 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() - { - 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; - } + return false; + } - if ( fileExtensions == null || fileExtensions.isEmpty() ) - { - fileExtensions = new ArrayList<String>(); - fileExtensions.add( ".class" ); - } + private Map<File, byte[]> createDependencyInfo() + { + Map<File, byte[]> dependencyInfo = new HashMap<File, byte[]>(); - Date buildStartTime = getBuildStartTime(); + for (String classPathElement : getClasspathElements()) + { + File artifactPath = new File(classPathElement); + if (artifactPath.isFile()) + { + // + dependent jar files and class files. + File checksum_file = new File(artifactPath.getAbsolutePath() + ".sha1"); + if (!checksum_file.exists()) + { + continue; // jar files without checksum are not supported. + } + byte[] checksum = new byte[(int) checksum_file.length()]; + try + { + FileInputStream fis = new FileInputStream(checksum_file); + fis.read(checksum, 0, (int) checksum_file.length()); + fis.close(); - for ( String classPathElement : getClasspathElements() ) - { - // ProjectArtifacts are artifacts which are available in the local project - // that's the only ones we are interested in now. - File artifactPath = new File( classPathElement ); - if ( artifactPath.isDirectory() ) + dependencyInfo.put(checksum_file, checksum); + } + catch (FileNotFoundException ex) { - if ( hasNewFile( artifactPath, buildStartTime ) ) - { - getLog().debug( "New dependency detected: " + artifactPath.getAbsolutePath() ); - return true; - } + getLog().debug("Can't read md5 file " + checksum_file + " - ignore it.", ex); } - } + catch (IOException ex) + { + getLog().debug("Can't read md5 file " + checksum_file + " - ignore it.", ex); + } + } + } - // obviously there was no new file detected. - return false; - } + return dependencyInfo; + } - /** - * @param classPathEntry entry to check - * @param buildStartTime time build start - * @return if any changes occured - */ - private boolean hasNewFile( File classPathEntry, Date buildStartTime ) - { - if ( !classPathEntry.exists() ) - { - return false; - } + private void keepDependencyInfo(IncrementalBuildHelper incrementalBuildHelper, Map<File, byte[]> dependencyInfo) throws MojoExecutionException + { + final File mojoConfigFile = new File(incrementalBuildHelper.getMojoStatusDirectory(), DEPENDENCY_INFO_FILENAME); + + if (dependencyInfo == null) + { + if (mojoConfigFile.exists()) + { + mojoConfigFile.delete(); + } + return; + } - if ( classPathEntry.isFile() ) - { - return classPathEntry.lastModified() >= buildStartTime.getTime() && fileExtensions.contains( - FileUtils.getExtension( classPathEntry.getName() ) ); - } - - File[] children = classPathEntry.listFiles(); - - for ( File child : children ) - { - if ( hasNewFile( child, buildStartTime ) ) - { - return true; - } - } - - return false; - } + try + { + FileOutputStream fos = new FileOutputStream(mojoConfigFile); + ObjectOutputStream oos = new ObjectOutputStream(fos); + oos.writeObject(dependencyInfo); + oos.close(); + } + catch (FileNotFoundException ex) + { + getLog().debug("Unexpected exception", ex); + } + catch (IOException ex) + { + getLog().debug("Can't write file " + DEPENDENCY_INFO_FILENAME, ex); + } + } } > Incremental build does not track inter-module dependencies. > ----------------------------------------------------------- > > Key: MCOMPILER-278 > URL: https://issues.apache.org/jira/browse/MCOMPILER-278 > Project: Maven Compiler Plugin > Issue Type: Bug > Affects Versions: 3.1 > Reporter: Michael Zav'yalov > > When useIncrementalCompilation=true the plugin actually assumes that this > incremental compilation is supported by compiler itself (I state it because > of all source files are always sent to compiler). > But plugin provides an additional optimization - it can detect that there are > no changes at all, so calling compiler can be skipped. It is especially > critical for javac that does not support incremental build since 1.3. > Unfortunately, plugin ignores claspath dependencies that are not directory, > i.e. - jars, so when dependent jar is modified (in incompatible way) - our > project is not re-compiled. -- This message was sent by Atlassian JIRA (v6.3.4#6332)