Provide incremental compilation for Maven
#KT-11916 fixed
To use the IC either:
1. set the `kotlin.compiler.incremental` property to `true` in a pom.xml:
```
<properties>
<kotlin.compiler.incremental>true</kotlin.compiler.incremental>
</properties>
```
2. pass the `kotlin.compiler.incremental` property in a command line:
```
mvn install -Dkotlin.compiler.incremental=true
```
When IC is on Kotlin plugin is expected to print the warning in the log:
```
Using experimental Kotlin incremental compilation
```
After each call an incremental compiler will also log how many files it has compiled:
```
Compiled %SOME_NUMBER% Kotlin files using incremental compiler
```
Note that the first build will be non-incremental.
For more diagnostic information (such as an exact list of compiled files) use the `kotlin.compiler.incremental.log.level` system property:
```
mvn install -Dkotlin.compiler.incremental=true -Dkotlin.compiler.incremental.log.level=info
```
To force the rebuild just run the 'clean' goal:
```
mvn clean install
```
This commit is contained in:
+72
-2
@@ -20,13 +20,17 @@ import org.apache.maven.plugin.MojoExecutionException;
|
||||
import org.apache.maven.plugins.annotations.*;
|
||||
import org.apache.maven.project.MavenProject;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.cli.common.CLICompiler;
|
||||
import org.jetbrains.kotlin.cli.common.ExitCode;
|
||||
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments;
|
||||
import org.jetbrains.kotlin.cli.common.messages.MessageCollector;
|
||||
import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler;
|
||||
import org.jetbrains.kotlin.incremental.IncrementalJvmCompilerRunnerKt;
|
||||
import org.jetbrains.kotlin.maven.incremental.MavenICReporter;
|
||||
import org.jetbrains.kotlin.maven.kapt.AnnotationProcessingManager;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
import static com.intellij.openapi.util.text.StringUtil.join;
|
||||
import static org.jetbrains.kotlin.maven.Util.filterClassPath;
|
||||
@@ -65,6 +69,26 @@ public class K2JVMCompileMojo extends KotlinCompileMojoBase<K2JVMCompilerArgumen
|
||||
@Parameter(property = "kotlin.compiler.scriptTemplates", required = false, readonly = false)
|
||||
protected List<String> scriptTemplates;
|
||||
|
||||
@Parameter(property = "kotlin.compiler.incremental", defaultValue = "false", required = false, readonly = false)
|
||||
private boolean myIncremental;
|
||||
|
||||
@Parameter(property = "kotlin.compiler.incremental.cache.root", defaultValue = "${project.build.directory}/kotlin-ic", required = false, readonly = false)
|
||||
public String incrementalCachesRoot;
|
||||
|
||||
@NotNull
|
||||
private File getCachesDir() {
|
||||
return new File(incrementalCachesRoot, getSourceSetName());
|
||||
}
|
||||
|
||||
protected boolean isIncremental() {
|
||||
return myIncremental;
|
||||
}
|
||||
|
||||
private boolean isIncrementalSystemProperty() {
|
||||
String value = System.getProperty("kotlin.incremental");
|
||||
return value != null && value.equals("true");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getRelatedSourceRoots(MavenProject project) {
|
||||
return project.getCompileSourceRoots();
|
||||
@@ -141,4 +165,50 @@ public class K2JVMCompileMojo extends KotlinCompileMojoBase<K2JVMCompilerArgumen
|
||||
arguments.scriptTemplates = scriptTemplates.toArray(new String[0]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ExitCode execCompiler(
|
||||
CLICompiler<K2JVMCompilerArguments> compiler,
|
||||
MessageCollector messageCollector,
|
||||
K2JVMCompilerArguments arguments,
|
||||
List<File> sourceRoots
|
||||
) throws MojoExecutionException {
|
||||
if (isIncremental()) {
|
||||
return runIncrementalCompiler(messageCollector, arguments, sourceRoots);
|
||||
}
|
||||
|
||||
return super.execCompiler(compiler, messageCollector, arguments, sourceRoots);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private ExitCode runIncrementalCompiler(
|
||||
MessageCollector messageCollector,
|
||||
K2JVMCompilerArguments arguments,
|
||||
List<File> sourceRoots
|
||||
) throws MojoExecutionException {
|
||||
getLog().warn("Using experimental Kotlin incremental compilation");
|
||||
File cachesDir = getCachesDir();
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
cachesDir.mkdirs();
|
||||
|
||||
MavenICReporter icReporter = MavenICReporter.get(getLog());
|
||||
|
||||
try {
|
||||
IncrementalJvmCompilerRunnerKt.makeIncrementally(cachesDir, sourceRoots, arguments, messageCollector, icReporter);
|
||||
|
||||
int compiledKtFilesCount = icReporter.getCompiledKotlinFiles().size();
|
||||
getLog().info("Compiled " + icReporter.getCompiledKotlinFiles().size() + " Kotlin files using incremental compiler");
|
||||
}
|
||||
catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
return ExitCode.INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
if (messageCollector.hasErrors()) {
|
||||
return ExitCode.COMPILATION_ERROR;
|
||||
}
|
||||
else {
|
||||
return ExitCode.OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+35
-19
@@ -38,6 +38,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.cli.common.CLICompiler;
|
||||
import org.jetbrains.kotlin.cli.common.ExitCode;
|
||||
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments;
|
||||
import org.jetbrains.kotlin.cli.common.messages.MessageCollector;
|
||||
import org.jetbrains.kotlin.config.KotlinCompilerVersion;
|
||||
import org.jetbrains.kotlin.config.Services;
|
||||
|
||||
@@ -192,18 +193,33 @@ public abstract class KotlinCompileMojoBase<A extends CommonCompilerArguments> e
|
||||
A arguments = createCompilerArguments();
|
||||
CLICompiler<A> compiler = createCompiler();
|
||||
|
||||
List<File> sourceRoots = getSourceRoots();
|
||||
|
||||
configureCompilerArguments(arguments, compiler);
|
||||
printCompilerArgumentsIfDebugEnabled(arguments, compiler);
|
||||
|
||||
MavenPluginLogMessageCollector messageCollector = new MavenPluginLogMessageCollector(getLog());
|
||||
|
||||
ExitCode exitCode = compiler.exec(messageCollector, Services.EMPTY, arguments);
|
||||
ExitCode exitCode = execCompiler(compiler, messageCollector, arguments, sourceRoots);
|
||||
|
||||
if (exitCode != ExitCode.OK) {
|
||||
messageCollector.throwKotlinCompilerException();
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected ExitCode execCompiler(
|
||||
CLICompiler<A> compiler,
|
||||
MessageCollector messageCollector,
|
||||
A arguments,
|
||||
List<File> sourceRoots
|
||||
) throws MojoExecutionException {
|
||||
for (File sourceRoot : sourceRoots) {
|
||||
arguments.freeArgs.add(sourceRoot.getPath());
|
||||
}
|
||||
return compiler.exec(messageCollector, Services.EMPTY, arguments);
|
||||
}
|
||||
|
||||
private boolean hasKotlinFilesInSources() throws MojoExecutionException {
|
||||
List<File> sources = getSourceDirs();
|
||||
if (sources == null || sources.isEmpty()) return false;
|
||||
@@ -400,32 +416,34 @@ public abstract class KotlinCompileMojoBase<A extends CommonCompilerArguments> e
|
||||
return pluginOptions;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private List<File> getSourceRoots() throws MojoExecutionException {
|
||||
List<File> sourceRoots = new ArrayList<File>();
|
||||
for (File sourceDir : getSourceDirs()) {
|
||||
if (sourceDir.exists()) {
|
||||
sourceRoots.add(sourceDir);
|
||||
}
|
||||
else {
|
||||
getLog().warn("Source root doesn't exist: " + sourceDir);
|
||||
}
|
||||
}
|
||||
if (sourceRoots.isEmpty()) {
|
||||
throw new MojoExecutionException("No source roots to compile");
|
||||
}
|
||||
getLog().info("Compiling Kotlin sources from " + sourceRoots);
|
||||
return sourceRoots;
|
||||
}
|
||||
|
||||
private void configureCompilerArguments(@NotNull A arguments, @NotNull CLICompiler<A> compiler) throws MojoExecutionException {
|
||||
if (getLog().isDebugEnabled()) {
|
||||
arguments.verbose = true;
|
||||
}
|
||||
|
||||
List<String> sources = new ArrayList<String>();
|
||||
for (File source : getSourceDirs()) {
|
||||
if (source.exists()) {
|
||||
sources.add(source.getPath());
|
||||
}
|
||||
else {
|
||||
getLog().warn("Source root doesn't exist: " + source);
|
||||
}
|
||||
}
|
||||
|
||||
if (sources.isEmpty()) {
|
||||
throw new MojoExecutionException("No source roots to compile");
|
||||
}
|
||||
|
||||
arguments.suppressWarnings = nowarn;
|
||||
arguments.languageVersion = languageVersion;
|
||||
arguments.apiVersion = apiVersion;
|
||||
arguments.multiPlatform = multiPlatform;
|
||||
|
||||
getLog().info("Compiling Kotlin sources from " + sources);
|
||||
|
||||
configureSpecificCompilerArguments(arguments);
|
||||
|
||||
try {
|
||||
@@ -435,8 +453,6 @@ public abstract class KotlinCompileMojoBase<A extends CommonCompilerArguments> e
|
||||
throw new MojoExecutionException(e.getMessage());
|
||||
}
|
||||
|
||||
arguments.freeArgs.addAll(sources);
|
||||
|
||||
if (arguments.noInline) {
|
||||
getLog().info("Method inlining is turned off");
|
||||
}
|
||||
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
package org.jetbrains.kotlin.maven.incremental;
|
||||
|
||||
import kotlin.jvm.functions.Function0;
|
||||
import org.apache.maven.plugin.logging.Log;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.cli.common.ExitCode;
|
||||
import org.jetbrains.kotlin.incremental.ICReporter;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class MavenICReporter implements ICReporter {
|
||||
private static final String IC_LOG_LEVEL_PROPERTY_NAME = "kotlin.compiler.incremental.log.level";
|
||||
private static final String IC_LOG_LEVEL_NONE = "none";
|
||||
private static final String IC_LOG_LEVEL_INFO = "info";
|
||||
private static final String IC_LOG_LEVEL_DEBUG = "debug";
|
||||
|
||||
public static MavenICReporter get(@NotNull final Log log) {
|
||||
String logLevel = System.getProperty(IC_LOG_LEVEL_PROPERTY_NAME);
|
||||
if (logLevel == null) {
|
||||
if (log.isDebugEnabled()) {
|
||||
logLevel = IC_LOG_LEVEL_DEBUG;
|
||||
}
|
||||
else {
|
||||
logLevel = IC_LOG_LEVEL_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
if (logLevel.equalsIgnoreCase(IC_LOG_LEVEL_INFO)) {
|
||||
return MavenICReporter.info(log);
|
||||
}
|
||||
else if (logLevel.equalsIgnoreCase(IC_LOG_LEVEL_DEBUG)) {
|
||||
return MavenICReporter.debug(log);
|
||||
}
|
||||
else {
|
||||
if (!logLevel.equalsIgnoreCase(IC_LOG_LEVEL_NONE)) {
|
||||
log.warn("Unknown incremental compilation log level '" + logLevel + "'," +
|
||||
"possible values: " + IC_LOG_LEVEL_NONE + ", " + IC_LOG_LEVEL_INFO + ", " + IC_LOG_LEVEL_DEBUG);
|
||||
}
|
||||
|
||||
return MavenICReporter.noLog();
|
||||
}
|
||||
}
|
||||
|
||||
private static MavenICReporter info(@NotNull final Log log) {
|
||||
return new MavenICReporter() {
|
||||
@Override
|
||||
protected boolean isLogEnabled() {
|
||||
return log.isInfoEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void log(String str) {
|
||||
log.info(str);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static MavenICReporter debug(@NotNull final Log log) {
|
||||
return new MavenICReporter() {
|
||||
@Override
|
||||
protected boolean isLogEnabled() {
|
||||
return log.isDebugEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void log(String str) {
|
||||
log.debug(str);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static MavenICReporter noLog() {
|
||||
return new MavenICReporter();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private final Set<File> compiledKotlinFiles = new HashSet<File>();
|
||||
|
||||
protected boolean isLogEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void log(String str) {
|
||||
}
|
||||
|
||||
private MavenICReporter() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void report(Function0<String> getMessage) {
|
||||
if (isLogEnabled()) {
|
||||
log(getMessage.invoke());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reportCompileIteration(Collection<? extends File> sourceFiles, ExitCode exitCode) {
|
||||
compiledKotlinFiles.addAll(sourceFiles);
|
||||
if (isLogEnabled()) {
|
||||
log("Kotlin compile iteration: " + pathsAsString(sourceFiles));
|
||||
log("Exit code: " + exitCode.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String pathsAsString(Iterable<? extends File> files) {
|
||||
return ICReporter.DefaultImpls.pathsAsString(this, files);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String pathsAsString(File... files) {
|
||||
return ICReporter.DefaultImpls.pathsAsString(this, files);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Set<File> getCompiledKotlinFiles() {
|
||||
return compiledKotlinFiles;
|
||||
}
|
||||
}
|
||||
+5
@@ -180,4 +180,9 @@ public class KaptJVMCompilerMojo extends K2JVMCompileMojo {
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isIncremental() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user