From 7a5e0e110758ef1f973b65e2bed2cc8a6a2a7c9f Mon Sep 17 00:00:00 2001 From: Alexey Tsvetkov Date: Tue, 19 Sep 2017 06:30:00 +0300 Subject: [PATCH] Request CHUNK_REBUILD when IC is off and there are dirty Kotlin files Otherwise unexpected compile error might happen, when there are Groovy files, but they are not dirty, so Groovy builder does not generate source stubs, and Kotlin builder is filtering out output directory from classpath (because it may contain outdated Java classes). Previously the issue was not detected, because it was not possible to turn off the IC completely (in JPS), only switch to the legacy IC. #KT-20138 --- .../kotlin/jps/build/KotlinJpsBuildTest.kt | 106 +++++++++++++++--- .../kotlin/jps/build/KotlinBuilder.kt | 8 +- .../incrementalOff/build.log | 12 ++ .../incrementalOffOn/build.log | 6 + 4 files changed, 117 insertions(+), 15 deletions(-) diff --git a/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/build/KotlinJpsBuildTest.kt b/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/build/KotlinJpsBuildTest.kt index cd7b295f01e..cce47a59f34 100644 --- a/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/build/KotlinJpsBuildTest.kt +++ b/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/build/KotlinJpsBuildTest.kt @@ -24,7 +24,6 @@ import com.intellij.openapi.util.text.StringUtil import com.intellij.openapi.vfs.StandardFileSystems import com.intellij.testFramework.LightVirtualFile import com.intellij.testFramework.UsefulTestCase -import com.intellij.util.ArrayUtil import com.intellij.util.io.URLUtil import com.intellij.util.io.ZipUtil import org.jetbrains.jps.ModuleChunk @@ -43,6 +42,7 @@ import org.jetbrains.jps.incremental.ModuleLevelBuilder import org.jetbrains.jps.incremental.messages.BuildMessage import org.jetbrains.jps.incremental.messages.CompilerMessage import org.jetbrains.jps.model.JpsModuleRootModificationUtil +import org.jetbrains.jps.model.JpsProject import org.jetbrains.jps.model.java.JavaSourceRootType import org.jetbrains.jps.model.java.JpsJavaDependencyScope import org.jetbrains.jps.model.java.JpsJavaExtensionService @@ -510,7 +510,14 @@ open class KotlinJpsBuildTest : AbstractKotlinJpsBuildTestCase() { assertFilesExistInOutput(module, "Foo.class", "Bar.class") assertFilesNotExistInOutput(module, *EXCLUDE_FILES) - checkWhen(touch("src/foo.kt"), null, arrayOf(klass("kotlinProject", "Foo"))) + if (IncrementalCompilation.isEnabled()) { + checkWhen(touch("src/foo.kt"), null, arrayOf(klass("kotlinProject", "Foo"))) + } + else { + val allClasses = myProject.outputPaths() + checkWhen(touch("src/foo.kt"), null, allClasses) + } + checkWhen(touch("src/Excluded.kt"), null, NOTHING) checkWhen(touch("src/dir/YetAnotherExcluded.kt"), null, NOTHING) } @@ -522,8 +529,15 @@ open class KotlinJpsBuildTest : AbstractKotlinJpsBuildTestCase() { assertFilesExistInOutput(module, "Foo.class", "Bar.class") assertFilesNotExistInOutput(module, *EXCLUDE_FILES) - checkWhen(touch("src/foo.kt"), null, arrayOf(klass("kotlinProject", "Foo"))) - checkWhen(touch("src/dir/subdir/bar.kt"), null, arrayOf(klass("kotlinProject", "Bar"))) + if (IncrementalCompilation.isEnabled()) { + checkWhen(touch("src/foo.kt"), null, arrayOf(klass("kotlinProject", "Foo"))) + checkWhen(touch("src/dir/subdir/bar.kt"), null, arrayOf(klass("kotlinProject", "Bar"))) + } + else { + val allClasses = myProject.outputPaths() + checkWhen(touch("src/foo.kt"), null, allClasses) + checkWhen(touch("src/dir/subdir/bar.kt"), null, allClasses) + } checkWhen(touch("src/dir/Excluded.kt"), null, NOTHING) checkWhen(touch("src/dir/subdir/YetAnotherExcluded.kt"), null, NOTHING) @@ -536,7 +550,13 @@ open class KotlinJpsBuildTest : AbstractKotlinJpsBuildTestCase() { assertFilesExistInOutput(module, "Foo.class", "Bar.class") assertFilesNotExistInOutput(module, *EXCLUDE_FILES) - checkWhen(touch("src/foo.kt"), null, arrayOf(klass("kotlinProject", "Foo"))) + if (IncrementalCompilation.isEnabled()) { + checkWhen(touch("src/foo.kt"), null, arrayOf(klass("kotlinProject", "Foo"))) + } + else { + val allClasses = myProject.outputPaths() + checkWhen(touch("src/foo.kt"), null, allClasses) + } checkWhen(touch("src/exclude/Excluded.kt"), null, NOTHING) checkWhen(touch("src/exclude/YetAnotherExcluded.kt"), null, NOTHING) @@ -547,8 +567,15 @@ open class KotlinJpsBuildTest : AbstractKotlinJpsBuildTestCase() { fun testKotlinProjectTwoFilesInOnePackage() { doTest() - checkWhen(touch("src/test1.kt"), null, packageClasses("kotlinProject", "src/test1.kt", "_DefaultPackage")) - checkWhen(touch("src/test2.kt"), null, packageClasses("kotlinProject", "src/test2.kt", "_DefaultPackage")) + if (IncrementalCompilation.isEnabled()) { + checkWhen(touch("src/test1.kt"), null, packageClasses("kotlinProject", "src/test1.kt", "_DefaultPackage")) + checkWhen(touch("src/test2.kt"), null, packageClasses("kotlinProject", "src/test2.kt", "_DefaultPackage")) + } + else { + val allClasses = myProject.outputPaths() + checkWhen(touch("src/test1.kt"), null, allClasses) + checkWhen(touch("src/test2.kt"), null, allClasses) + } checkWhen(arrayOf(del("src/test1.kt"), del("src/test2.kt")), NOTHING, arrayOf(packagePartClass("kotlinProject", "src/test1.kt", "_DefaultPackage"), @@ -596,8 +623,15 @@ open class KotlinJpsBuildTest : AbstractKotlinJpsBuildTestCase() { result.assertSuccessful() - checkWhen(touch("src/kt2.kt"), null, packageClasses("kotlinProject", "src/kt2.kt", "kt2.Kt2Kt")) - checkWhen(touch("module2/src/kt1.kt"), null, packageClasses("module2", "module2/src/kt1.kt", "kt1.Kt1Kt")) + if (IncrementalCompilation.isEnabled()) { + checkWhen(touch("src/kt2.kt"), null, packageClasses("kotlinProject", "src/kt2.kt", "kt2.Kt2Kt")) + checkWhen(touch("module2/src/kt1.kt"), null, packageClasses("module2", "module2/src/kt1.kt", "kt1.Kt1Kt")) + } + else { + val allClasses = myProject.outputPaths() + checkWhen(touch("src/kt2.kt"), null, allClasses) + checkWhen(touch("module2/src/kt1.kt"), null, allClasses) + } } fun testCircularDependenciesSamePackage() { @@ -611,8 +645,16 @@ open class KotlinJpsBuildTest : AbstractKotlinJpsBuildTestCase() { UsefulTestCase.assertSameElements(getMethodsOfClass(facadeWithA), "", "a", "getA") UsefulTestCase.assertSameElements(getMethodsOfClass(facadeWithB), "", "b", "getB", "setB") - checkWhen(touch("module1/src/a.kt"), null, packageClasses("module1", "module1/src/a.kt", "test.TestPackage")) - checkWhen(touch("module2/src/b.kt"), null, packageClasses("module2", "module2/src/b.kt", "test.TestPackage")) + + if (IncrementalCompilation.isEnabled()) { + checkWhen(touch("module1/src/a.kt"), null, packageClasses("module1", "module1/src/a.kt", "test.TestPackage")) + checkWhen(touch("module2/src/b.kt"), null, packageClasses("module2", "module2/src/b.kt", "test.TestPackage")) + } + else { + val allClasses = myProject.outputPaths() + checkWhen(touch("module1/src/a.kt"), null, allClasses) + checkWhen(touch("module2/src/b.kt"), null, allClasses) + } } fun testCircularDependenciesSamePackageWithTests() { @@ -626,8 +668,15 @@ open class KotlinJpsBuildTest : AbstractKotlinJpsBuildTestCase() { UsefulTestCase.assertSameElements(getMethodsOfClass(facadeWithA), "", "a", "funA", "getA") UsefulTestCase.assertSameElements(getMethodsOfClass(facadeWithB), "", "b", "funB", "getB", "setB") - checkWhen(touch("module1/src/a.kt"), null, packageClasses("module1", "module1/src/a.kt", "test.TestPackage")) - checkWhen(touch("module2/src/b.kt"), null, packageClasses("module2", "module2/src/b.kt", "test.TestPackage")) + if (IncrementalCompilation.isEnabled()) { + checkWhen(touch("module1/src/a.kt"), null, packageClasses("module1", "module1/src/a.kt", "test.TestPackage")) + checkWhen(touch("module2/src/b.kt"), null, packageClasses("module2", "module2/src/b.kt", "test.TestPackage")) + } + else { + val allProductionClasses = myProject.outputPaths(tests = false) + checkWhen(touch("module1/src/a.kt"), null, allProductionClasses) + checkWhen(touch("module2/src/b.kt"), null, allProductionClasses) + } } fun testInternalFromAnotherModule() { @@ -849,7 +898,13 @@ open class KotlinJpsBuildTest : AbstractKotlinJpsBuildTestCase() { initProject(JVM_MOCK_RUNTIME) buildAllModules().assertSuccessful() - checkWhen(touch("src/utils.kt"), null, packageClasses("kotlinProject", "src/utils.kt", "_DefaultPackage")) + if (IncrementalCompilation.isEnabled()) { + checkWhen(touch("src/utils.kt"), null, packageClasses("kotlinProject", "src/utils.kt", "_DefaultPackage")) + } + else { + val allClasses = findModule("kotlinProject").outputFilesPaths() + checkWhen(touch("src/utils.kt"), null, allClasses.toTypedArray()) + } val storageRoot = BuildDataPathsImpl(myDataStorageRoot).dataStorageRoot assertTrue(File(storageRoot, "targets/java-production/kotlinProject/kotlin").exists()) @@ -1074,6 +1129,29 @@ open class KotlinJpsBuildTest : AbstractKotlinJpsBuildTestCase() { return klass(moduleName, AsmUtil.internalNameByFqNameWithoutInnerClasses(packagePartFqName)) } + private fun JpsProject.outputPaths(production: Boolean = true, tests: Boolean = true) = + modules.flatMap { it.outputFilesPaths(production = production, tests = tests) }.toTypedArray() + + private fun JpsModule.outputFilesPaths(production: Boolean = true, tests: Boolean = true): List { + val outputFiles = arrayListOf() + if (production) { + prodOut.walk().filterTo(outputFiles) { it.isFile } + } + if (tests) { + testsOut.walk().filterTo(outputFiles) { it.isFile } + } + return outputFiles.map { it.relativeTo(workDir).path } + } + + private val JpsModule.prodOut: File + get() = outDir(forTests = false) + + private val JpsModule.testsOut: File + get() = outDir(forTests = true) + + private fun JpsModule.outDir(forTests: Boolean) = + JpsJavaExtensionService.getInstance().getOutputDirectory(this, forTests)!! + protected enum class Operation { CHANGE, DELETE diff --git a/jps-plugin/src/org/jetbrains/kotlin/jps/build/KotlinBuilder.kt b/jps-plugin/src/org/jetbrains/kotlin/jps/build/KotlinBuilder.kt index 3c1c5e223df..73780a77220 100644 --- a/jps-plugin/src/org/jetbrains/kotlin/jps/build/KotlinBuilder.kt +++ b/jps-plugin/src/org/jetbrains/kotlin/jps/build/KotlinBuilder.kt @@ -238,7 +238,13 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) { val isChunkRebuilding = JavaBuilderUtil.isForcedRecompilationAllJavaModules(context) || targets.any { rebuildAfterCacheVersionChanged[it] == true } - if (!hasKotlinDirtyOrRemovedFiles(dirtyFilesHolder, chunk)) { + if (hasKotlinDirtyOrRemovedFiles(dirtyFilesHolder, chunk)) { + if (!isChunkRebuilding && !IncrementalCompilation.isEnabled()) { + targets.forEach { rebuildAfterCacheVersionChanged[it] = true } + return CHUNK_REBUILD_REQUIRED + } + } + else { if (isChunkRebuilding) { targets.forEach { hasKotlin[it] = false } } diff --git a/jps-plugin/testData/incremental/changeIncrementalOption/incrementalOff/build.log b/jps-plugin/testData/incremental/changeIncrementalOption/incrementalOff/build.log index 141a6fc4b0e..337c3ad1c41 100644 --- a/jps-plugin/testData/incremental/changeIncrementalOption/incrementalOff/build.log +++ b/jps-plugin/testData/incremental/changeIncrementalOption/incrementalOff/build.log @@ -9,6 +9,12 @@ Cleaning output files: out/production/module2/META-INF/module2.kotlin_module out/production/module2/foo/AKt.class End of files +Exit code: CHUNK_REBUILD_REQUIRED +------------------------------------------ +Cleaning output files: + out/production/module2/foo/BKt.class + out/production/module2/foo/CKt.class +End of files Compiling files: module2/src/a.kt module2/src/b.kt @@ -33,6 +39,12 @@ Cleaning output files: out/production/module2/META-INF/module2.kotlin_module out/production/module2/foo/AKt.class End of files +Exit code: CHUNK_REBUILD_REQUIRED +------------------------------------------ +Cleaning output files: + out/production/module2/foo/BKt.class + out/production/module2/foo/CKt.class +End of files Compiling files: module2/src/a.kt module2/src/b.kt diff --git a/jps-plugin/testData/incremental/changeIncrementalOption/incrementalOffOn/build.log b/jps-plugin/testData/incremental/changeIncrementalOption/incrementalOffOn/build.log index fb1064db698..8a34d6e3f46 100644 --- a/jps-plugin/testData/incremental/changeIncrementalOption/incrementalOffOn/build.log +++ b/jps-plugin/testData/incremental/changeIncrementalOption/incrementalOffOn/build.log @@ -9,6 +9,12 @@ Cleaning output files: out/production/module2/META-INF/module2.kotlin_module out/production/module2/foo/AKt.class End of files +Exit code: CHUNK_REBUILD_REQUIRED +------------------------------------------ +Cleaning output files: + out/production/module2/foo/BKt.class + out/production/module2/foo/CKt.class +End of files Compiling files: module2/src/a.kt module2/src/b.kt