diff --git a/jps-plugin/src/org/jetbrains/jet/jps/build/KotlinBuilder.java b/jps-plugin/src/org/jetbrains/jet/jps/build/KotlinBuilder.java index 49159f3cbff..1cd94d78c3f 100644 --- a/jps-plugin/src/org/jetbrains/jet/jps/build/KotlinBuilder.java +++ b/jps-plugin/src/org/jetbrains/jet/jps/build/KotlinBuilder.java @@ -134,8 +134,7 @@ public class KotlinBuilder extends ModuleLevelBuilder { return ExitCode.NOTHING_DONE; } - List sourceFiles = KotlinSourceFileCollector.getAllKotlinSourceFiles(representativeTarget); - //List sourceFiles = KotlinSourceFileCollector.getDirtySourceFiles(dirtyFilesHolder); + List sourceFiles = KotlinSourceFileCollector.getDirtySourceFiles(dirtyFilesHolder); if (sourceFiles.isEmpty()) { return ExitCode.NOTHING_DONE; @@ -157,7 +156,7 @@ public class KotlinBuilder extends ModuleLevelBuilder { NO_LOCATION); } - File moduleFile = KotlinBuilderModuleScriptGenerator.generateModuleDescription(context, chunk); + File moduleFile = KotlinBuilderModuleScriptGenerator.generateModuleDescription(context, dirtyFilesHolder); if (moduleFile == null) { // No Kotlin sources found return ExitCode.NOTHING_DONE; @@ -169,15 +168,9 @@ public class KotlinBuilder extends ModuleLevelBuilder { moduleFile, outputItemCollector); } - // 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); - } - } - } + Map sourceToTarget = KotlinSourceFileCollector.getMapDirtySourcesToTarget(dirtyFilesHolder); + + boolean isAllRegistered = false; for (SimpleOutputItem outputItem : outputItemCollector.getOutputs()) { BuildTarget target = null; @@ -186,15 +179,21 @@ public class KotlinBuilder extends ModuleLevelBuilder { target = sourceToTarget.get(sourceFiles.iterator().next()); } else { - messageCollector.report(ERROR, - INTERNAL_ERROR_PREFIX + "outputItem.sourceFiles is null or empty, outputItem = " + outputItem, - NO_LOCATION); + messageCollector.report(EXCEPTION, "KotlinBuilder: outputItem.sourceFiles is null or empty, outputItem = " + outputItem, NO_LOCATION); } - outputConsumer.registerOutputFile( - target != null ? target : representativeTarget, - outputItem.getOutputFile(), - paths(outputItem.getSourceFiles())); + //TODO FIX: Hack for compile all files if someone changed, because we don't have incremental compilation. + if (!isAllRegistered) { + isAllRegistered = true; + sourceFiles = sourceToTarget.keySet(); + } + + if (target == null) { + messageCollector.report(EXCEPTION, "KotlinBuilder: target is null for outputItem = " + outputItem, NO_LOCATION); + } + else { + outputConsumer.registerOutputFile(target, outputItem.getOutputFile(), paths(sourceFiles)); + } } return ExitCode.OK; diff --git a/jps-plugin/src/org/jetbrains/jet/jps/build/KotlinBuilderModuleScriptGenerator.java b/jps-plugin/src/org/jetbrains/jet/jps/build/KotlinBuilderModuleScriptGenerator.java index f105f36047c..4d3a2133601 100644 --- a/jps-plugin/src/org/jetbrains/jet/jps/build/KotlinBuilderModuleScriptGenerator.java +++ b/jps-plugin/src/org/jetbrains/jet/jps/build/KotlinBuilderModuleScriptGenerator.java @@ -17,13 +17,14 @@ package org.jetbrains.jet.jps.build; import com.intellij.openapi.util.io.FileUtil; +import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.compiler.runner.KotlinModuleDescriptionBuilder; import org.jetbrains.jet.compiler.runner.KotlinModuleDescriptionBuilderFactory; import org.jetbrains.jet.compiler.runner.KotlinModuleXmlBuilderFactory; -import org.jetbrains.jps.ModuleChunk; +import org.jetbrains.jps.builders.DirtyFilesHolder; import org.jetbrains.jps.builders.java.JavaSourceRootDescriptor; import org.jetbrains.jps.incremental.CompileContext; import org.jetbrains.jps.incremental.ModuleBuildTarget; @@ -46,27 +47,39 @@ import java.util.*; import static org.jetbrains.jet.compiler.runner.KotlinModuleDescriptionBuilder.DependencyProcessor; import static org.jetbrains.jet.compiler.runner.KotlinModuleDescriptionBuilder.DependencyProvider; import static org.jetbrains.jet.jps.build.JpsUtils.getAllDependencies; +import static org.jetbrains.jet.jps.build.KotlinSourceFileCollector.getMapTargetToDirtySources; public class KotlinBuilderModuleScriptGenerator { public static final KotlinModuleDescriptionBuilderFactory FACTORY = KotlinModuleXmlBuilderFactory.INSTANCE; @Nullable - public static File generateModuleDescription(CompileContext context, ModuleChunk chunk) - throws IOException - { + public static File generateModuleDescription( + CompileContext context, + DirtyFilesHolder dirtyFilesHolder + ) throws IOException { KotlinModuleDescriptionBuilder builder = FACTORY.create(); boolean noSources = true; - Set outputDirs = new HashSet(); - for (ModuleBuildTarget target : chunk.getTargets()) { - outputDirs.add(getOutputDir(target)); - } - for (ModuleBuildTarget target : chunk.getTargets()) { + Map> target2sources = getMapTargetToDirtySources(dirtyFilesHolder); + + Set targets = target2sources.keySet(); + Set outputDirs = ContainerUtil.map2Set(targets, new Function() { + @Override + public File fun(ModuleBuildTarget target) { + return getOutputDir(target); + } + }); + + for (ModuleBuildTarget target : targets) { File outputDir = getOutputDir(target); - List sourceFiles = KotlinSourceFileCollector.getAllKotlinSourceFiles(target); + List sourceFiles = target2sources.get(target); + if (sourceFiles == null) { + sourceFiles = Collections.emptyList(); + } + noSources &= sourceFiles.isEmpty(); builder.addModule( @@ -82,7 +95,8 @@ public class KotlinBuilderModuleScriptGenerator { if (noSources) return null; - File scriptFile = new File(getOutputDir(chunk.representativeTarget()), "script." + FACTORY.getFileExtension()); + ModuleBuildTarget representativeTarget = targets.iterator().next(); + File scriptFile = new File(getOutputDir(representativeTarget), "script." + FACTORY.getFileExtension()); writeScriptToFile(context, builder.asText(), scriptFile); 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 029a59ff91e..2f71e54e3d9 100644 --- a/jps-plugin/src/org/jetbrains/jet/jps/build/KotlinSourceFileCollector.java +++ b/jps-plugin/src/org/jetbrains/jet/jps/build/KotlinSourceFileCollector.java @@ -18,6 +18,7 @@ package org.jetbrains.jet.jps.build; import com.intellij.openapi.util.io.FileUtil; import com.intellij.util.Processor; +import com.intellij.util.SmartList; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.jps.builders.DirtyFilesHolder; @@ -29,7 +30,7 @@ import org.jetbrains.jps.model.module.JpsModuleSourceRoot; import java.io.File; import java.io.IOException; -import java.util.List; +import java.util.*; public class KotlinSourceFileCollector { // For incremental compilation @@ -50,6 +51,49 @@ public class KotlinSourceFileCollector { return sourceFiles; } + public static Map> getMapTargetToDirtySources( + DirtyFilesHolder dirtyFilesHolder + ) throws IOException { + final Map> target2sources = new HashMap>(); + + dirtyFilesHolder.processDirtyFiles(new FileProcessor() { + @Override + public boolean apply(ModuleBuildTarget target, File file, JavaSourceRootDescriptor root) throws IOException { + List sources = target2sources.get(target); + if (sources == null) { + sources = new SmartList(); + target2sources.put(target, sources); + } + + if (isKotlinSourceFile(file)) { + sources.add(file); + } + return true; + } + }); + + return target2sources; + } + + + public static Map getMapDirtySourcesToTarget( + DirtyFilesHolder dirtyFilesHolder + ) throws IOException { + final Map source2target = new HashMap(); + + dirtyFilesHolder.processDirtyFiles(new FileProcessor() { + @Override + public boolean apply(ModuleBuildTarget target, File file, JavaSourceRootDescriptor root) throws IOException { + if (isKotlinSourceFile(file)) { + source2target.put(file, target); + } + return true; + } + }); + + return source2target; + } + @NotNull public static List getAllKotlinSourceFiles(@NotNull ModuleBuildTarget target) { final List result = ContainerUtil.newArrayList(); diff --git a/jps-plugin/test/org/jetbrains/jet/jps/build/KotlinJpsBuildTestCase.java b/jps-plugin/test/org/jetbrains/jet/jps/build/KotlinJpsBuildTestCase.java index 1108dab9584..8c5d57d4293 100644 --- a/jps-plugin/test/org/jetbrains/jet/jps/build/KotlinJpsBuildTestCase.java +++ b/jps-plugin/test/org/jetbrains/jet/jps/build/KotlinJpsBuildTestCase.java @@ -19,6 +19,7 @@ package org.jetbrains.jet.jps.build; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.io.FileUtil; import com.intellij.testFramework.LightVirtualFile; +import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.jet.codegen.NamespaceCodegen; import org.jetbrains.jet.lang.resolve.name.FqName; @@ -73,14 +74,34 @@ public class KotlinJpsBuildTestCase extends AbstractKotlinJpsBuildTestCase { public void testKotlinProject() { doTest(); - assertOutputDeleted("src/test1.kt", "_DefaultPackage", "kotlinProject"); + assertOutputDeletedPackageInModule("kotlinProject", "src/test1.kt", "_DefaultPackage"); + } + + public void testExcludeFolderInSourceRoot() { + doTest(); + + JpsModule module = myProject.getModules().get(0); + assertFileExistsInOutput(module, "Foo.class"); + + assertOutputDeletedInModule("kotlinProject", "src/foo.kt", "Foo"); + } + + public void testExcludeModuleFolderInSourceRootOfAnotherModule() { + doTest(); + + for (JpsModule module : myProject.getModules()) { + assertFileExistsInOutput(module, "Foo.class"); + } + + assertOutputDeletedInModule("kotlinProject", "src/foo.kt", "Foo"); + assertOutputDeletedInModule("module2", "src/module2/src/foo.kt", "Foo"); } public void testKotlinProjectTwoFilesInOnePackage() { doTest(); - assertOutputDeleted("src/test1.kt", "_DefaultPackage", "kotlinProject"); - assertOutputDeleted("src/test2.kt", "_DefaultPackage", "kotlinProject"); + assertOutputDeletedPackageInModule("kotlinProject", "src/test1.kt", "_DefaultPackage"); + assertOutputDeletedPackageInModule("kotlinProject", "src/test2.kt", "_DefaultPackage"); } public void testKotlinJavaProject() { @@ -126,8 +147,8 @@ public class KotlinJpsBuildTestCase extends AbstractKotlinJpsBuildTestCase { } result.assertSuccessful(); - assertOutputDeleted("src/kt2.kt", "kt2.Kt2Package", "kotlinProject"); - assertOutputDeleted("module2/src/kt1.kt", "kt1.Kt1Package", "module2"); + assertOutputDeletedPackageInModule("kotlinProject", "src/kt2.kt", "kt2.Kt2Package"); + assertOutputDeletedPackageInModule("module2", "module2/src/kt1.kt", "kt1.Kt1Package"); } public void testReexportedDependency() { @@ -166,14 +187,35 @@ public class KotlinJpsBuildTestCase extends AbstractKotlinJpsBuildTestCase { return builder.toString(); } - private void assertOutputDeleted(String sourceFileName, String packageClassFqName, String moduleName) { + private void assertOutputDeletedPackageInModule(String moduleName, String sourceFileName, String packageClassFqNames) { + File file = new File(workDir, sourceFileName); + String[] packageClasses = { packageClassFqNames, getInternalNameForPackagePartClass(file, packageClassFqNames) }; + + assertOutputDeletedInModule(moduleName, sourceFileName, packageClasses); + } + + private void assertOutputDeletedInModule(final String moduleName, String sourceFileName, String... classFqNames) { + String[] paths = ContainerUtil.map2Array(classFqNames, String.class, new Function() { + @Override + public String fun(String classFqName) { + return outputPathInModuleByClassFqName(moduleName, classFqName); + } + }); + + assertOutputDeleted(sourceFileName, paths); + } + + private void assertOutputDeleted(String sourceFileName, String... paths) { File file = new File(workDir, sourceFileName); change(file.getAbsolutePath()); makeAll().assertSuccessful(); + assertDeleted(paths); + } + + private static String outputPathInModuleByClassFqName(String moduleName, String classFqName) { String outputDirPrefix = "out/production/" + moduleName + "/"; - assertDeleted(outputDirPrefix + packageClassFqName.replace('.', '/') + ".class", - outputDirPrefix + getInternalNameForPackagePartClass(file, packageClassFqName) + ".class"); + return outputDirPrefix + classFqName.replace('.', '/') + ".class"; } private static String getInternalNameForPackagePartClass(File sourceFile, String packageClassFqName) { diff --git a/jps-plugin/testData/ExcludeFolderInSourceRoot/kotlinProject.iml b/jps-plugin/testData/ExcludeFolderInSourceRoot/kotlinProject.iml new file mode 100644 index 00000000000..54f25b2ccec --- /dev/null +++ b/jps-plugin/testData/ExcludeFolderInSourceRoot/kotlinProject.iml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/jps-plugin/testData/ExcludeFolderInSourceRoot/kotlinProject.ipr b/jps-plugin/testData/ExcludeFolderInSourceRoot/kotlinProject.ipr new file mode 100644 index 00000000000..90747786771 --- /dev/null +++ b/jps-plugin/testData/ExcludeFolderInSourceRoot/kotlinProject.ipr @@ -0,0 +1,14 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/jps-plugin/testData/ExcludeFolderInSourceRoot/src/exclude/foo.kt b/jps-plugin/testData/ExcludeFolderInSourceRoot/src/exclude/foo.kt new file mode 100644 index 00000000000..43c42f1453b --- /dev/null +++ b/jps-plugin/testData/ExcludeFolderInSourceRoot/src/exclude/foo.kt @@ -0,0 +1 @@ +class Foo \ No newline at end of file diff --git a/jps-plugin/testData/ExcludeFolderInSourceRoot/src/foo.kt b/jps-plugin/testData/ExcludeFolderInSourceRoot/src/foo.kt new file mode 100644 index 00000000000..43c42f1453b --- /dev/null +++ b/jps-plugin/testData/ExcludeFolderInSourceRoot/src/foo.kt @@ -0,0 +1 @@ +class Foo \ No newline at end of file diff --git a/jps-plugin/testData/ExcludeModuleFolderInSourceRootOfAnotherModule/kotlinProject.iml b/jps-plugin/testData/ExcludeModuleFolderInSourceRootOfAnotherModule/kotlinProject.iml new file mode 100644 index 00000000000..5cff40145f7 --- /dev/null +++ b/jps-plugin/testData/ExcludeModuleFolderInSourceRootOfAnotherModule/kotlinProject.iml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/jps-plugin/testData/ExcludeModuleFolderInSourceRootOfAnotherModule/kotlinProject.ipr b/jps-plugin/testData/ExcludeModuleFolderInSourceRootOfAnotherModule/kotlinProject.ipr new file mode 100644 index 00000000000..35b084995b6 --- /dev/null +++ b/jps-plugin/testData/ExcludeModuleFolderInSourceRootOfAnotherModule/kotlinProject.ipr @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/jps-plugin/testData/ExcludeModuleFolderInSourceRootOfAnotherModule/src/foo.kt b/jps-plugin/testData/ExcludeModuleFolderInSourceRootOfAnotherModule/src/foo.kt new file mode 100644 index 00000000000..43c42f1453b --- /dev/null +++ b/jps-plugin/testData/ExcludeModuleFolderInSourceRootOfAnotherModule/src/foo.kt @@ -0,0 +1 @@ +class Foo \ No newline at end of file diff --git a/jps-plugin/testData/ExcludeModuleFolderInSourceRootOfAnotherModule/src/module2/module2.iml b/jps-plugin/testData/ExcludeModuleFolderInSourceRootOfAnotherModule/src/module2/module2.iml new file mode 100644 index 00000000000..3b1ba79ed61 --- /dev/null +++ b/jps-plugin/testData/ExcludeModuleFolderInSourceRootOfAnotherModule/src/module2/module2.iml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/jps-plugin/testData/ExcludeModuleFolderInSourceRootOfAnotherModule/src/module2/src/foo.kt b/jps-plugin/testData/ExcludeModuleFolderInSourceRootOfAnotherModule/src/module2/src/foo.kt new file mode 100644 index 00000000000..43c42f1453b --- /dev/null +++ b/jps-plugin/testData/ExcludeModuleFolderInSourceRootOfAnotherModule/src/module2/src/foo.kt @@ -0,0 +1 @@ +class Foo \ No newline at end of file