diff --git a/annotations/com/intellij/openapi/util/annotations.xml b/annotations/com/intellij/openapi/util/annotations.xml
index 4ca58460d8c..9a818132493 100644
--- a/annotations/com/intellij/openapi/util/annotations.xml
+++ b/annotations/com/intellij/openapi/util/annotations.xml
@@ -3,7 +3,12 @@
name='com.intellij.openapi.util.Conditions com.intellij.openapi.util.Condition<T> or(com.intellij.openapi.util.Condition<T>, com.intellij.openapi.util.Condition<T>)'>
- -
+
-
+
+
+
+
+ -
-
diff --git a/annotations/org/jetbrains/jps/annotations.xml b/annotations/org/jetbrains/jps/annotations.xml
new file mode 100644
index 00000000000..d2b275b23f6
--- /dev/null
+++ b/annotations/org/jetbrains/jps/annotations.xml
@@ -0,0 +1,11 @@
+
+
-
+
+
+ -
+
+
+ -
+
+
+
diff --git a/annotations/org/jetbrains/jps/cmdline/annotations.xml b/annotations/org/jetbrains/jps/cmdline/annotations.xml
new file mode 100644
index 00000000000..494db392431
--- /dev/null
+++ b/annotations/org/jetbrains/jps/cmdline/annotations.xml
@@ -0,0 +1,5 @@
+
+ -
+
+
+
diff --git a/annotations/org/jetbrains/jps/incremental/annotations.xml b/annotations/org/jetbrains/jps/incremental/annotations.xml
new file mode 100644
index 00000000000..b108d851a95
--- /dev/null
+++ b/annotations/org/jetbrains/jps/incremental/annotations.xml
@@ -0,0 +1,25 @@
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
diff --git a/ide-compiler-runner/src/org/jetbrains/jet/compiler/runner/CompilerEnvironment.java b/ide-compiler-runner/src/org/jetbrains/jet/compiler/runner/CompilerEnvironment.java
index ba3ebc58346..c883ae5ea7c 100644
--- a/ide-compiler-runner/src/org/jetbrains/jet/compiler/runner/CompilerEnvironment.java
+++ b/ide-compiler-runner/src/org/jetbrains/jet/compiler/runner/CompilerEnvironment.java
@@ -31,6 +31,7 @@ import static org.jetbrains.jet.cli.common.messages.CompilerMessageSeverity.ERRO
public final class CompilerEnvironment {
+ @NotNull
public static CompilerEnvironment getEnvironmentFor(
@NotNull KotlinPaths kotlinPaths,
@Nullable File outputDir,
diff --git a/ide-compiler-runner/src/org/jetbrains/jet/compiler/runner/SimpleOutputItem.java b/ide-compiler-runner/src/org/jetbrains/jet/compiler/runner/SimpleOutputItem.java
index a89936de4c2..970df11eefe 100644
--- a/ide-compiler-runner/src/org/jetbrains/jet/compiler/runner/SimpleOutputItem.java
+++ b/ide-compiler-runner/src/org/jetbrains/jet/compiler/runner/SimpleOutputItem.java
@@ -16,6 +16,8 @@
package org.jetbrains.jet.compiler.runner;
+import org.jetbrains.annotations.NotNull;
+
import java.io.File;
import java.util.Collection;
@@ -23,15 +25,17 @@ public class SimpleOutputItem {
private final Collection sourceFiles;
private final File outputFile;
- public SimpleOutputItem(Collection sourceFiles, File outputFile) {
+ public SimpleOutputItem(@NotNull Collection sourceFiles, @NotNull File outputFile) {
this.sourceFiles = sourceFiles;
this.outputFile = outputFile;
}
+ @NotNull
public Collection getSourceFiles() {
return sourceFiles;
}
+ @NotNull
public File getOutputFile() {
return outputFile;
}
diff --git a/jps-plugin/src/org/jetbrains/jet/jps/build/KotlinBuilder.java b/jps-plugin/src/org/jetbrains/jet/jps/build/KotlinBuilder.java
deleted file mode 100644
index f40738b42ac..00000000000
--- a/jps-plugin/src/org/jetbrains/jet/jps/build/KotlinBuilder.java
+++ /dev/null
@@ -1,404 +0,0 @@
-/*
- * Copyright 2010-2013 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.jetbrains.jet.jps.build;
-
-import com.intellij.openapi.util.Key;
-import com.intellij.openapi.util.io.FileUtil;
-import com.intellij.openapi.util.text.StringUtil;
-import com.intellij.util.Function;
-import com.intellij.util.containers.ContainerUtil;
-import com.intellij.util.containers.MultiMap;
-import gnu.trove.THashSet;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.jet.cli.common.KotlinVersion;
-import org.jetbrains.jet.cli.common.arguments.CommonCompilerArguments;
-import org.jetbrains.jet.cli.common.arguments.K2JSCompilerArguments;
-import org.jetbrains.jet.cli.common.arguments.K2JVMCompilerArguments;
-import org.jetbrains.jet.cli.common.messages.CompilerMessageLocation;
-import org.jetbrains.jet.cli.common.messages.CompilerMessageSeverity;
-import org.jetbrains.jet.cli.common.messages.MessageCollector;
-import org.jetbrains.jet.compiler.CompilerSettings;
-import org.jetbrains.jet.compiler.runner.CompilerEnvironment;
-import org.jetbrains.jet.compiler.runner.CompilerRunnerConstants;
-import org.jetbrains.jet.compiler.runner.OutputItemsCollectorImpl;
-import org.jetbrains.jet.compiler.runner.SimpleOutputItem;
-import org.jetbrains.jet.config.IncrementalCompilation;
-import org.jetbrains.jet.config.Services;
-import org.jetbrains.jet.jps.JpsKotlinCompilerSettings;
-import org.jetbrains.jet.jps.incremental.IncrementalCacheImpl;
-import org.jetbrains.jet.jps.incremental.IncrementalCacheProviderImpl;
-import org.jetbrains.jet.jps.incremental.IncrementalCacheStorageProvider;
-import org.jetbrains.jet.lang.resolve.kotlin.incremental.cache.IncrementalCacheProvider;
-import org.jetbrains.jet.preloading.ClassCondition;
-import org.jetbrains.jet.utils.PathUtil;
-import org.jetbrains.jet.utils.UtilsPackage;
-import org.jetbrains.jps.ModuleChunk;
-import org.jetbrains.jps.builders.DirtyFilesHolder;
-import org.jetbrains.jps.builders.java.JavaSourceRootDescriptor;
-import org.jetbrains.jps.incremental.*;
-import org.jetbrains.jps.incremental.java.JavaBuilder;
-import org.jetbrains.jps.incremental.messages.BuildMessage;
-import org.jetbrains.jps.incremental.messages.CompilerMessage;
-import org.jetbrains.jps.incremental.storage.BuildDataManager;
-import org.jetbrains.jps.model.JpsProject;
-import org.jetbrains.jps.model.module.JpsModule;
-
-import java.io.File;
-import java.io.FileFilter;
-import java.io.IOException;
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
-import java.util.*;
-
-import static org.jetbrains.jet.cli.common.messages.CompilerMessageLocation.NO_LOCATION;
-import static org.jetbrains.jet.cli.common.messages.CompilerMessageSeverity.*;
-import static org.jetbrains.jet.compiler.runner.CompilerRunnerConstants.INTERNAL_ERROR_PREFIX;
-import static org.jetbrains.jet.compiler.runner.KotlinCompilerRunner.runK2JsCompiler;
-import static org.jetbrains.jet.compiler.runner.KotlinCompilerRunner.runK2JvmCompiler;
-
-public class KotlinBuilder extends ModuleLevelBuilder {
- private static final Key> ALL_COMPILED_FILES_KEY = Key.create("_all_kotlin_compiled_files_");
- private static final Key> PROCESSED_TARGETS_WITH_REMOVED_FILES = Key.create("_processed_targets_with_removed_files_");
-
- public static final String KOTLIN_BUILDER_NAME = "Kotlin Builder";
- private static final List COMPILABLE_FILE_EXTENSIONS = Collections.singletonList("kt");
-
- private static final Function MODULE_NAME = new Function() {
- @Override
- public String fun(JpsModule module) {
- return module.getName();
- }
- };
-
- protected KotlinBuilder() {
- super(BuilderCategory.SOURCE_PROCESSOR);
- }
-
- @NotNull
- @Override
- public String getPresentableName() {
- return KOTLIN_BUILDER_NAME;
- }
-
- @Override
- public ExitCode build(
- CompileContext context,
- ModuleChunk chunk,
- DirtyFilesHolder dirtyFilesHolder,
- OutputConsumer outputConsumer
- ) throws ProjectBuildException, IOException {
- MessageCollector messageCollector = new MessageCollectorAdapter(context);
- // Workaround for Android Studio
- if (!isJavaPluginEnabled(context)) {
- messageCollector.report(INFO, "Kotlin JPS plugin is disabled", NO_LOCATION);
- return ExitCode.NOTHING_DONE;
- }
-
- ModuleBuildTarget representativeTarget = chunk.representativeTarget();
-
- // For non-incremental build: take all sources
- if (!dirtyFilesHolder.hasDirtyFiles() && !dirtyFilesHolder.hasRemovedFiles()) {
- return ExitCode.NOTHING_DONE;
- }
-
- boolean hasKotlinFiles = hasKotlinDirtyOrRemovedFiles(dirtyFilesHolder, chunk);
- if (!hasKotlinFiles) {
- return ExitCode.NOTHING_DONE;
- }
-
- messageCollector.report(INFO, "Kotlin JPS plugin version " + KotlinVersion.VERSION, NO_LOCATION);
-
- File outputDir = representativeTarget.getOutputDir();
-
- BuildDataManager dataManager = context.getProjectDescriptor().dataManager;
- Map incrementalCaches = UtilsPackage.newHashMapWithExpectedSize(chunk.getTargets().size());
- for (ModuleBuildTarget target : chunk.getTargets()) {
- incrementalCaches.put(target, dataManager.getStorage(target, IncrementalCacheStorageProvider.INSTANCE$));
- }
-
- Services compilerServices = new Services.Builder()
- .register(IncrementalCacheProvider.class, new IncrementalCacheProviderImpl(incrementalCaches))
- .build();
-
- CompilerEnvironment environment = CompilerEnvironment.getEnvironmentFor(
- PathUtil.getKotlinPathsForJpsPluginOrJpsTests(), outputDir, getClass().getClassLoader(), new ClassCondition() {
- @Override
- public boolean accept(String className) {
- return className.startsWith("org.jetbrains.jet.lang.resolve.kotlin.incremental.cache.") ||
- className.equals("org.jetbrains.jet.config.Services");
- }
- }, compilerServices);
- if (!environment.success()) {
- environment.reportErrorsTo(messageCollector);
- return ExitCode.ABORT;
- }
-
- assert outputDir != null : "CompilerEnvironment must have checked for outputDir to be not null, but it didn't";
-
- OutputItemsCollectorImpl outputItemCollector = new OutputItemsCollectorImpl();
-
- JpsProject project = representativeTarget.getModule().getProject();
- CommonCompilerArguments commonArguments = JpsKotlinCompilerSettings.getCommonCompilerArguments(project);
- commonArguments.verbose = true; // Make compiler report source to output files mapping
-
- CompilerSettings compilerSettings = JpsKotlinCompilerSettings.getCompilerSettings(project);
-
- final Set allCompiledFiles = getAllCompiledFilesContainer(context);
-
- if (JpsUtils.isJsKotlinModule(representativeTarget)) {
- if (chunk.getModules().size() > 1) {
- // We do not support circular dependencies, but if they are present, we do our best should not break the build,
- // so we simply yield a warning and report NOTHING_DONE
- messageCollector.report(
- WARNING, "Circular dependencies are not supported. " +
- "The following JS modules depend on each other: " + StringUtil.join(chunk.getModules(), MODULE_NAME, ", ") + ". " +
- "Kotlin is not compiled for these modules",
- NO_LOCATION);
- return ExitCode.NOTHING_DONE;
- }
-
- Collection sourceFiles = KotlinSourceFileCollector.getAllKotlinSourceFiles(representativeTarget);
- //List sourceFiles = KotlinSourceFileCollector.getDirtySourceFiles(dirtyFilesHolder);
-
- if (sourceFiles.isEmpty()) {
- return ExitCode.NOTHING_DONE;
- }
-
- File outputFile = new File(outputDir, representativeTarget.getModule().getName() + ".js");
- List libraryFiles = JpsJsModuleUtils.getLibraryFilesAndDependencies(representativeTarget);
- K2JSCompilerArguments k2JsArguments = JpsKotlinCompilerSettings.getK2JsCompilerArguments(project);
-
- runK2JsCompiler(commonArguments, k2JsArguments, compilerSettings, messageCollector, environment,
- outputItemCollector, sourceFiles, libraryFiles, outputFile);
- }
- else {
- if (chunk.getModules().size() > 1) {
- messageCollector.report(
- WARNING, "Circular dependencies are only partially supported. " +
- "The following modules depend on each other: " + StringUtil.join(chunk.getModules(), MODULE_NAME, ", ") + ". " +
- "Kotlin will compile them, but some strange effect may happen",
- NO_LOCATION);
- }
-
- MultiMap filesToCompile = KotlinSourceFileCollector.getDirtySourceFiles(dirtyFilesHolder);
- for (ModuleBuildTarget target : filesToCompile.keySet()) {
- filesToCompile.getModifiable(target).removeAll(allCompiledFiles);
- }
- allCompiledFiles.addAll(filesToCompile.values());
-
- Set processedTargetsWithRemoved = getProcessedTargetsWithRemovedFilesContainer(context);
-
- boolean haveRemovedFiles = false;
- for (ModuleBuildTarget target : chunk.getTargets()) {
- if (!KotlinSourceFileCollector.getRemovedKotlinFiles(dirtyFilesHolder, target).isEmpty()) {
- if (processedTargetsWithRemoved.add(target)) {
- haveRemovedFiles = true;
- }
- }
- }
-
- File moduleFile = KotlinBuilderModuleScriptGenerator
- .generateModuleDescription(context, chunk, filesToCompile, haveRemovedFiles);
- if (moduleFile == null) {
- // No Kotlin sources found
- return ExitCode.NOTHING_DONE;
- }
-
- K2JVMCompilerArguments k2JvmArguments = JpsKotlinCompilerSettings.getK2JvmCompilerArguments(project);
-
- runK2JvmCompiler(commonArguments, k2JvmArguments, compilerSettings, messageCollector, environment,
- moduleFile, outputItemCollector);
- moduleFile.delete();
- }
-
- // If there's only one target, this map is empty: get() always returns null, and the representativeTarget will be used below
- Map sourceToTarget = new HashMap();
- if (chunk.getTargets().size() > 1) {
- for (ModuleBuildTarget target : chunk.getTargets()) {
- for (File file : KotlinSourceFileCollector.getAllKotlinSourceFiles(target)) {
- sourceToTarget.put(file, target);
- }
- }
- }
-
- for (ModuleBuildTarget target : chunk.getTargets()) {
- incrementalCaches.get(target).clearCacheForRemovedFiles(
- KotlinSourceFileCollector.getRemovedKotlinFiles(dirtyFilesHolder, target), target.getOutputDir());
- }
-
- IncrementalCacheImpl.RecompilationDecision recompilationDecision = IncrementalCacheImpl.RecompilationDecision.DO_NOTHING;
-
- for (SimpleOutputItem outputItem : outputItemCollector.getOutputs()) {
- ModuleBuildTarget target = null;
- Collection sourceFiles = outputItem.getSourceFiles();
- if (!sourceFiles.isEmpty()) {
- target = sourceToTarget.get(sourceFiles.iterator().next());
- }
-
- if (target == null) {
- target = representativeTarget;
- }
-
- File outputFile = outputItem.getOutputFile();
-
- if (IncrementalCompilation.ENABLED) {
- IncrementalCacheImpl.RecompilationDecision newDecision = incrementalCaches.get(target).saveFileToCache(sourceFiles, outputFile);
- recompilationDecision = recompilationDecision.merge(newDecision);
- }
-
- outputConsumer.registerOutputFile(target, outputFile, paths(sourceFiles));
- }
-
- if (IncrementalCompilation.ENABLED) {
- if (recompilationDecision == IncrementalCacheImpl.RecompilationDecision.RECOMPILE_ALL) {
- allCompiledFiles.clear();
- return ExitCode.CHUNK_REBUILD_REQUIRED;
- }
- if (recompilationDecision == IncrementalCacheImpl.RecompilationDecision.COMPILE_OTHERS) {
- // TODO should mark dependencies as dirty, as well
- FSOperations.markDirty(context, chunk, new FileFilter() {
- @Override
- public boolean accept(@NotNull File file) {
- return !allCompiledFiles.contains(file);
- }
- });
- }
- return ExitCode.ADDITIONAL_PASS_REQUIRED;
- }
- else {
- return ExitCode.OK;
- }
- }
-
- private static Set getAllCompiledFilesContainer(CompileContext context) {
- Set allCompiledFiles = ALL_COMPILED_FILES_KEY.get(context);
- if (allCompiledFiles == null) {
- allCompiledFiles = new THashSet(FileUtil.FILE_HASHING_STRATEGY);
- ALL_COMPILED_FILES_KEY.set(context, allCompiledFiles);
- }
- return allCompiledFiles;
- }
-
- private static Set getProcessedTargetsWithRemovedFilesContainer(CompileContext context) {
- Set set = PROCESSED_TARGETS_WITH_REMOVED_FILES.get(context);
- if (set == null) {
- set = new HashSet();
- PROCESSED_TARGETS_WITH_REMOVED_FILES.set(context, set);
- }
- return set;
- }
-
- private static boolean hasKotlinDirtyOrRemovedFiles(
- @NotNull DirtyFilesHolder dirtyFilesHolder,
- @NotNull ModuleChunk chunk
- )
- throws IOException {
- if (!KotlinSourceFileCollector.getDirtySourceFiles(dirtyFilesHolder).isEmpty()) {
- return true;
- }
-
- for (ModuleBuildTarget target : chunk.getTargets()) {
- if (!KotlinSourceFileCollector.getRemovedKotlinFiles(dirtyFilesHolder, target).isEmpty()) {
- return true;
- }
- }
-
- return false;
- }
-
- private static boolean isJavaPluginEnabled(@NotNull CompileContext context) {
- try {
- // Using reflection for backward compatibility with IDEA 12
- Field javaPluginIsEnabledField = JavaBuilder.class.getDeclaredField("IS_ENABLED");
- return Modifier.isPublic(javaPluginIsEnabledField.getModifiers()) ? JavaBuilder.IS_ENABLED.get(context, Boolean.TRUE) : true;
- }
- catch (NoSuchFieldException e) {
- throw new IllegalArgumentException("Cannot check if Java Jps Plugin is enabled", e);
- }
- }
-
- private static Collection paths(Collection files) {
- Collection result = ContainerUtil.newArrayList();
- for (File file : files) {
- result.add(file.getPath());
- }
- return result;
- }
-
- public static class MessageCollectorAdapter implements MessageCollector {
-
- private final CompileContext context;
-
- public MessageCollectorAdapter(@NotNull CompileContext context) {
- this.context = context;
- }
-
- @Override
- public void report(
- @NotNull CompilerMessageSeverity severity,
- @NotNull String message,
- @NotNull CompilerMessageLocation location
- ) {
- String prefix = "";
- if (severity == EXCEPTION) {
- prefix = INTERNAL_ERROR_PREFIX;
- }
- context.processMessage(new CompilerMessage(
- CompilerRunnerConstants.KOTLIN_COMPILER_NAME,
- kind(severity),
- prefix + message + renderLocationIfNeeded(location),
- location.getPath(),
- -1, -1, -1,
- location.getLine(),
- location.getColumn()
- ));
- }
-
- private static String renderLocationIfNeeded(@NotNull CompilerMessageLocation location) {
- if (location == NO_LOCATION) return "";
-
- // Sometimes we report errors in JavaScript library stubs, i.e. files like core/javautil.kt
- // IDEA can't find these files, and does not display paths in Messages View, so we add the position information
- // to the error message itself:
- String pathname = String.valueOf(location.getPath());
- return new File(pathname).exists() ? "" : " (" + location + ")";
- }
-
- @NotNull
- private static BuildMessage.Kind kind(@NotNull CompilerMessageSeverity severity) {
- switch (severity) {
- case INFO:
- return BuildMessage.Kind.INFO;
- case ERROR:
- case EXCEPTION:
- return BuildMessage.Kind.ERROR;
- case WARNING:
- return BuildMessage.Kind.WARNING;
- case LOGGING:
- return BuildMessage.Kind.PROGRESS;
- default:
- throw new IllegalArgumentException("Unsupported severity: " + severity);
- }
- }
-
- }
-
- @Override
- public List getCompilableFileExtensions() {
- return COMPILABLE_FILE_EXTENSIONS;
- }
-}
diff --git a/jps-plugin/src/org/jetbrains/jet/jps/build/KotlinBuilder.kt b/jps-plugin/src/org/jetbrains/jet/jps/build/KotlinBuilder.kt
new file mode 100644
index 00000000000..a322984dea9
--- /dev/null
+++ b/jps-plugin/src/org/jetbrains/jet/jps/build/KotlinBuilder.kt
@@ -0,0 +1,323 @@
+/*
+ * Copyright 2010-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jetbrains.jet.jps.build
+
+import com.intellij.openapi.util.Key
+import com.intellij.openapi.util.io.FileUtil
+import gnu.trove.THashSet
+import org.jetbrains.jet.cli.common.KotlinVersion
+import org.jetbrains.jet.cli.common.messages.CompilerMessageLocation
+import org.jetbrains.jet.cli.common.messages.CompilerMessageSeverity
+import org.jetbrains.jet.cli.common.messages.MessageCollector
+import org.jetbrains.jet.compiler.runner.CompilerEnvironment
+import org.jetbrains.jet.compiler.runner.CompilerRunnerConstants
+import org.jetbrains.jet.compiler.runner.OutputItemsCollectorImpl
+import org.jetbrains.jet.config.Services
+import org.jetbrains.jet.config.IncrementalCompilation
+import org.jetbrains.jet.jps.JpsKotlinCompilerSettings
+import org.jetbrains.jet.jps.incremental.*
+import org.jetbrains.jet.lang.resolve.kotlin.incremental.cache.IncrementalCacheProvider
+import org.jetbrains.jet.utils.PathUtil
+import org.jetbrains.jps.ModuleChunk
+import org.jetbrains.jps.builders.DirtyFilesHolder
+import org.jetbrains.jps.builders.java.JavaSourceRootDescriptor
+import org.jetbrains.jps.incremental.*
+import org.jetbrains.jps.incremental.java.JavaBuilder
+import org.jetbrains.jps.incremental.messages.BuildMessage
+import org.jetbrains.jps.incremental.messages.CompilerMessage
+import java.io.File
+import java.io.IOException
+import java.lang.reflect.Modifier
+import java.util.*
+import org.jetbrains.jet.cli.common.messages.CompilerMessageLocation.NO_LOCATION
+import org.jetbrains.jet.cli.common.messages.CompilerMessageSeverity.*
+import org.jetbrains.jet.compiler.runner.CompilerRunnerConstants.INTERNAL_ERROR_PREFIX
+import org.jetbrains.jet.compiler.runner.KotlinCompilerRunner.runK2JsCompiler
+import org.jetbrains.jet.compiler.runner.KotlinCompilerRunner.runK2JvmCompiler
+import org.jetbrains.jet.utils.keysToMap
+import org.jetbrains.jps.incremental.ModuleLevelBuilder.ExitCode.*
+
+public class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
+
+ override fun getPresentableName() = "Kotlin Builder"
+
+ override fun getCompilableFileExtensions() = arrayListOf("kt")
+
+ override fun build(
+ context: CompileContext,
+ chunk: ModuleChunk,
+ dirtyFilesHolder: DirtyFilesHolder,
+ outputConsumer: ModuleLevelBuilder.OutputConsumer
+ ): ModuleLevelBuilder.ExitCode {
+ val messageCollector = MessageCollectorAdapter(context)
+ // Workaround for Android Studio
+ if (!isJavaPluginEnabled(context)) {
+ messageCollector.report(INFO, "Kotlin JPS plugin is disabled", NO_LOCATION)
+ return NOTHING_DONE
+ }
+
+ val representativeTarget = chunk.representativeTarget()
+
+ // For non-incremental build: take all sources
+ if (!dirtyFilesHolder.hasDirtyFiles() && !dirtyFilesHolder.hasRemovedFiles()) {
+ return NOTHING_DONE
+ }
+
+ if (!hasKotlinDirtyOrRemovedFiles(dirtyFilesHolder, chunk)) {
+ return NOTHING_DONE
+ }
+
+ messageCollector.report(INFO, "Kotlin JPS plugin version " + KotlinVersion.VERSION, NO_LOCATION)
+
+ val outputDir = representativeTarget.getOutputDir()
+
+ val dataManager = context.getProjectDescriptor().dataManager
+ val incrementalCaches = chunk.getTargets().keysToMap { dataManager.getStorage(it, IncrementalCacheStorageProvider) }
+
+ val compilerServices = Services.Builder()
+ .register(javaClass(), IncrementalCacheProviderImpl(incrementalCaches))
+ .build()
+
+ val environment = CompilerEnvironment.getEnvironmentFor(
+ PathUtil.getKotlinPathsForJpsPluginOrJpsTests(),
+ outputDir,
+ javaClass.getClassLoader(),
+ { className ->
+ className!!.startsWith("org.jetbrains.jet.lang.resolve.kotlin.incremental.cache.")
+ || className == "org.jetbrains.jet.config.Services"
+ },
+ compilerServices
+ )
+
+ if (!environment.success()) {
+ environment.reportErrorsTo(messageCollector)
+ return ABORT
+ }
+
+ assert(outputDir != null, "CompilerEnvironment must have checked for outputDir to be not null, but it didn't")
+
+ val outputItemCollector = OutputItemsCollectorImpl()
+
+ val project = representativeTarget.getModule().getProject()!!
+ val commonArguments = JpsKotlinCompilerSettings.getCommonCompilerArguments(project)
+ commonArguments.verbose = true // Make compiler report source to output files mapping
+
+ val compilerSettings = JpsKotlinCompilerSettings.getCompilerSettings(project)
+
+ val allCompiledFiles = getAllCompiledFilesContainer(context)
+
+ if (JpsUtils.isJsKotlinModule(representativeTarget)) {
+ if (chunk.getModules().size() > 1) {
+ // We do not support circular dependencies, but if they are present, we do our best should not break the build,
+ // so we simply yield a warning and report NOTHING_DONE
+ messageCollector.report(WARNING, "Circular dependencies are not supported. "
+ + "The following JS modules depend on each other: "
+ + chunk.getModules().map { it.getName() }.joinToString(", ")
+ + ". "
+ + "Kotlin is not compiled for these modules", NO_LOCATION)
+ return NOTHING_DONE
+ }
+
+ val sourceFiles = KotlinSourceFileCollector.getAllKotlinSourceFiles(representativeTarget)
+ //List sourceFiles = KotlinSourceFileCollector.getDirtySourceFiles(dirtyFilesHolder);
+
+ if (sourceFiles.isEmpty()) {
+ return NOTHING_DONE
+ }
+
+ val outputFile = File(outputDir, representativeTarget.getModule().getName() + ".js")
+ val libraryFiles = JpsJsModuleUtils.getLibraryFilesAndDependencies(representativeTarget)
+ val k2JsArguments = JpsKotlinCompilerSettings.getK2JsCompilerArguments(project)
+
+ runK2JsCompiler(commonArguments, k2JsArguments, compilerSettings, messageCollector, environment, outputItemCollector, sourceFiles, libraryFiles, outputFile)
+ }
+ else {
+ if (chunk.getModules().size() > 1) {
+ messageCollector.report(WARNING, "Circular dependencies are only partially supported. "
+ + "The following modules depend on each other: "
+ + chunk.getModules().map { it.getName() }.joinToString(", ")
+ + ". "
+ + "Kotlin will compile them, but some strange effect may happen", NO_LOCATION)
+ }
+
+ val filesToCompile = KotlinSourceFileCollector.getDirtySourceFiles(dirtyFilesHolder)
+ for (target in filesToCompile.keySet()) {
+ filesToCompile.getModifiable(target).removeAll(allCompiledFiles)
+ }
+ allCompiledFiles.addAll(filesToCompile.values())
+
+ val processedTargetsWithRemoved = getProcessedTargetsWithRemovedFilesContainer(context)
+
+ var haveRemovedFiles = false
+ for (target in chunk.getTargets()) {
+ if (!KotlinSourceFileCollector.getRemovedKotlinFiles(dirtyFilesHolder, target).isEmpty()) {
+ if (processedTargetsWithRemoved.add(target)) {
+ haveRemovedFiles = true
+ }
+ }
+ }
+
+ val moduleFile = KotlinBuilderModuleScriptGenerator.generateModuleDescription(context, chunk, filesToCompile, haveRemovedFiles)
+ if (moduleFile == null) {
+ // No Kotlin sources found
+ return NOTHING_DONE
+ }
+
+ val k2JvmArguments = JpsKotlinCompilerSettings.getK2JvmCompilerArguments(project)
+
+ runK2JvmCompiler(commonArguments, k2JvmArguments, compilerSettings, messageCollector, environment, moduleFile, outputItemCollector)
+ moduleFile.delete()
+ }
+
+ // If there's only one target, this map is empty: get() always returns null, and the representativeTarget will be used below
+ val sourceToTarget = HashMap()
+ if (chunk.getTargets().size() > 1) {
+ for (target in chunk.getTargets()) {
+ for (file in KotlinSourceFileCollector.getAllKotlinSourceFiles(target)) {
+ sourceToTarget.put(file, target)
+ }
+ }
+ }
+
+ for ((target, cache) in incrementalCaches) {
+ cache.clearCacheForRemovedFiles(
+ KotlinSourceFileCollector.getRemovedKotlinFiles(dirtyFilesHolder, target),
+ target.getOutputDir()!!
+ )
+ }
+
+ var recompilationDecision = IncrementalCacheImpl.RecompilationDecision.DO_NOTHING
+
+ for (outputItem in outputItemCollector.getOutputs()) {
+ var target: ModuleBuildTarget? = null
+ val sourceFiles = outputItem.getSourceFiles()
+ if (!sourceFiles.isEmpty()) {
+ target = sourceToTarget[sourceFiles.iterator().next()]
+ }
+
+ if (target == null) {
+ target = representativeTarget
+ }
+
+ val outputFile = outputItem.getOutputFile()
+
+ if (IncrementalCompilation.ENABLED) {
+ val newDecision = incrementalCaches[target]!!.saveFileToCache(sourceFiles, outputFile)
+ recompilationDecision = recompilationDecision.merge(newDecision)
+ }
+
+ outputConsumer.registerOutputFile(target, outputFile, sourceFiles.map { it.getPath() })
+ }
+
+ if (IncrementalCompilation.ENABLED) {
+ if (recompilationDecision == IncrementalCacheImpl.RecompilationDecision.RECOMPILE_ALL) {
+ allCompiledFiles.clear()
+ return CHUNK_REBUILD_REQUIRED
+ }
+ if (recompilationDecision == IncrementalCacheImpl.RecompilationDecision.COMPILE_OTHERS) {
+ // TODO should mark dependencies as dirty, as well
+ FSOperations.markDirty(context, chunk, { file -> !allCompiledFiles.contains(file) })
+ }
+ return ADDITIONAL_PASS_REQUIRED
+ }
+
+ return OK
+ }
+
+ public class MessageCollectorAdapter(private val context: CompileContext) : MessageCollector {
+
+ override fun report(severity: CompilerMessageSeverity, message: String, location: CompilerMessageLocation) {
+ var prefix = ""
+ if (severity == EXCEPTION) {
+ prefix = INTERNAL_ERROR_PREFIX
+ }
+ context.processMessage(CompilerMessage(
+ CompilerRunnerConstants.KOTLIN_COMPILER_NAME,
+ kind(severity),
+ prefix + message + renderLocationIfNeeded(location),
+ location.getPath(),
+ -1, -1, -1,
+ location.getLine().toLong(), location.getColumn().toLong()
+ ))
+ }
+
+ private fun renderLocationIfNeeded(location: CompilerMessageLocation): String {
+ if (location == NO_LOCATION) return ""
+
+ // Sometimes we report errors in JavaScript library stubs, i.e. files like core/javautil.kt
+ // IDEA can't find these files, and does not display paths in Messages View, so we add the position information
+ // to the error message itself:
+ val pathname = "" + location.getPath()
+ return if (File(pathname).exists()) "" else " (" + location + ")"
+ }
+
+ private fun kind(severity: CompilerMessageSeverity): BuildMessage.Kind {
+ return when (severity) {
+ INFO -> BuildMessage.Kind.INFO
+ ERROR, EXCEPTION -> BuildMessage.Kind.ERROR
+ WARNING -> BuildMessage.Kind.WARNING
+ LOGGING -> BuildMessage.Kind.PROGRESS
+ else -> throw IllegalArgumentException("Unsupported severity: " + severity)
+ }
+ }
+
+ }
+}
+
+private val ALL_COMPILED_FILES_KEY = Key.create>("_all_kotlin_compiled_files_")
+private fun getAllCompiledFilesContainer(context: CompileContext): MutableSet {
+ var allCompiledFiles = ALL_COMPILED_FILES_KEY.get(context)
+ if (allCompiledFiles == null) {
+ allCompiledFiles = THashSet(FileUtil.FILE_HASHING_STRATEGY)
+ ALL_COMPILED_FILES_KEY.set(context, allCompiledFiles)
+ }
+ return allCompiledFiles!!
+}
+
+private val PROCESSED_TARGETS_WITH_REMOVED_FILES = Key.create>("_processed_targets_with_removed_files_")
+private fun getProcessedTargetsWithRemovedFilesContainer(context: CompileContext): MutableSet {
+ var set = PROCESSED_TARGETS_WITH_REMOVED_FILES.get(context)
+ if (set == null) {
+ set = HashSet()
+ PROCESSED_TARGETS_WITH_REMOVED_FILES.set(context, set)
+ }
+ return set!!
+}
+
+private fun hasKotlinDirtyOrRemovedFiles(
+ dirtyFilesHolder: DirtyFilesHolder,
+ chunk: ModuleChunk
+): Boolean {
+ if (!KotlinSourceFileCollector.getDirtySourceFiles(dirtyFilesHolder).isEmpty()) {
+ return true
+ }
+
+ return chunk.getTargets().any { !KotlinSourceFileCollector.getRemovedKotlinFiles(dirtyFilesHolder, it).isEmpty() }
+}
+
+private fun isJavaPluginEnabled(context: CompileContext): Boolean {
+ try {
+ // Using reflection for backward compatibility with IDEA 12
+ val javaPluginIsEnabledField = javaClass().getDeclaredField("IS_ENABLED")
+ return if (Modifier.isPublic(javaPluginIsEnabledField.getModifiers())) JavaBuilder.IS_ENABLED[context, true] else true
+ }
+ catch (e: NoSuchFieldException) {
+ throw IllegalArgumentException("Cannot check if Java Jps Plugin is enabled", e)
+ }
+
+}
+
diff --git a/jps-plugin/src/org/jetbrains/jet/jps/build/KotlinSourceFileCollector.java b/jps-plugin/src/org/jetbrains/jet/jps/build/KotlinSourceFileCollector.java
index 04566c5e5fc..c1e77ab0d3d 100644
--- a/jps-plugin/src/org/jetbrains/jet/jps/build/KotlinSourceFileCollector.java
+++ b/jps-plugin/src/org/jetbrains/jet/jps/build/KotlinSourceFileCollector.java
@@ -40,6 +40,7 @@ import java.util.List;
public class KotlinSourceFileCollector {
// For incremental compilation
+ @NotNull
public static MultiMap getDirtySourceFiles(DirtyFilesHolder dirtyFilesHolder)
throws IOException
{