From aa683d3b2acd1c8ef733d400088dbd1ab696ec8c Mon Sep 17 00:00:00 2001 From: "Aleksei.Cherepanov" Date: Mon, 1 Feb 2021 13:08:43 +0300 Subject: [PATCH] JPS: Fix JvmMultifileClass processing for IR backend #KT-44644 Fixed --- .../kotlin/incremental/IncrementalJvmCache.kt | 4 ++ .../build/IncrementalJvmJpsTestGenerated.java | 5 ++ .../kotlin/jps/build/KotlinBuilder.kt | 46 +++++++++++++++++-- .../build.log | 7 +-- .../other/multifileClassFileAdded/build.log | 17 ++++++- .../other/multifileClassFileChanged/build.log | 6 ++- .../other/multifileDependantUsage/build.log | 25 ++++++++++ .../other/multifileDependantUsage/partA.kt | 4 ++ .../other/multifileDependantUsage/partB.kt | 10 ++++ .../multifileDependantUsage/partB.kt.new.1 | 4 ++ .../multifileDependantUsage/usagePartB.kt | 1 + .../multifilePackagePartMethodAdded/build.log | 14 +++--- .../multifilePartsWithProperties/build.log | 8 +--- tests/mute-common.csv | 5 -- 14 files changed, 127 insertions(+), 29 deletions(-) create mode 100644 jps-plugin/testData/incremental/withJava/other/multifileDependantUsage/build.log create mode 100644 jps-plugin/testData/incremental/withJava/other/multifileDependantUsage/partA.kt create mode 100644 jps-plugin/testData/incremental/withJava/other/multifileDependantUsage/partB.kt create mode 100644 jps-plugin/testData/incremental/withJava/other/multifileDependantUsage/partB.kt.new.1 create mode 100644 jps-plugin/testData/incremental/withJava/other/multifileDependantUsage/usagePartB.kt diff --git a/build-common/src/org/jetbrains/kotlin/incremental/IncrementalJvmCache.kt b/build-common/src/org/jetbrains/kotlin/incremental/IncrementalJvmCache.kt index 84d75a417d6..35884af0d79 100644 --- a/build-common/src/org/jetbrains/kotlin/incremental/IncrementalJvmCache.kt +++ b/build-common/src/org/jetbrains/kotlin/incremental/IncrementalJvmCache.kt @@ -94,6 +94,10 @@ open class IncrementalJvmCache( fun sourcesByInternalName(internalName: String): Collection = internalNameToSource[internalName] + fun getAllPartsOfMultifileFacade(facade: JvmClassName): Collection? { + return multifileFacadeToParts[facade] + } + fun isMultifileFacade(className: JvmClassName): Boolean = className in multifileFacadeToParts diff --git a/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/build/IncrementalJvmJpsTestGenerated.java b/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/build/IncrementalJvmJpsTestGenerated.java index c8de15fe074..fcaad30416a 100644 --- a/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/build/IncrementalJvmJpsTestGenerated.java +++ b/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/build/IncrementalJvmJpsTestGenerated.java @@ -2261,6 +2261,11 @@ public class IncrementalJvmJpsTestGenerated extends AbstractIncrementalJvmJpsTes runTest("jps-plugin/testData/incremental/withJava/other/multifilePartsWithProperties/"); } + @TestMetadata("multifileDependantUsage") + public void testMultifileDependantUsage() throws Exception { + runTest("jps-plugin/testData/incremental/withJava/other/multifileDependantUsage/"); + } + @TestMetadata("optionalParameter") public void testOptionalParameter() throws Exception { runTest("jps-plugin/testData/incremental/withJava/other/optionalParameter/"); 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 04afe480d2c..7266485c1ca 100644 --- a/jps-plugin/src/org/jetbrains/kotlin/jps/build/KotlinBuilder.kt +++ b/jps-plugin/src/org/jetbrains/kotlin/jps/build/KotlinBuilder.kt @@ -29,6 +29,7 @@ import org.jetbrains.jps.incremental.ModuleLevelBuilder.ExitCode.* import org.jetbrains.jps.incremental.java.JavaBuilder import org.jetbrains.jps.model.JpsProject import org.jetbrains.kotlin.build.GeneratedFile +import org.jetbrains.kotlin.build.GeneratedJvmClass import org.jetbrains.kotlin.cli.common.ExitCode import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity @@ -48,6 +49,7 @@ import org.jetbrains.kotlin.jps.incremental.JpsLookupStorageManager import org.jetbrains.kotlin.jps.model.kotlinKind import org.jetbrains.kotlin.jps.targets.KotlinJvmModuleBuildTarget import org.jetbrains.kotlin.jps.targets.KotlinModuleBuildTarget +import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.preloading.ClassCondition import org.jetbrains.kotlin.utils.KotlinPaths @@ -439,6 +441,9 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) { } val generatedFiles = getGeneratedFiles(context, chunk, environment.outputItemsCollector) + + markDirtyComplementaryMultifileClasses(generatedFiles, kotlinContext, incrementalCaches, fsOperations) + val kotlinTargets = kotlinContext.targetsBinding for ((target, outputItems) in generatedFiles) { val kotlinTarget = kotlinTargets[target] ?: error("Could not find Kotlin target for JPS target $target") @@ -526,14 +531,23 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) { for (target in kotlinChunk.targets) { val cache = incrementalCaches[target] val jpsTarget = target.jpsModuleBuildTarget + val targetDirtyFiles = dirtyFilesHolder.byTarget[jpsTarget] - if (cache != null && targetDirtyFiles != null) { - val complementaryFiles = cache.getComplementaryFilesRecursive( - targetDirtyFiles.dirty.keys + targetDirtyFiles.removed - ) + val dirtyFiles = targetDirtyFiles.dirty.keys + targetDirtyFiles.removed + val complementaryFiles = cache.getComplementaryFilesRecursive(dirtyFiles) - fsOperations.markFilesForCurrentRound(jpsTarget, complementaryFiles) + // Get all parts of @JvmMultifileClass file for simultaneous rebuild + var dirtyMultifileClassFiles: Collection = emptyList() + if (cache is IncrementalJvmCache) { + dirtyMultifileClassFiles = cache.classesBySources(dirtyFiles) + .filter { cache.isMultifileFacade(it) } + .flatMap { cache.getAllPartsOfMultifileFacade(it).orEmpty() } + .flatMap { cache.sourcesByInternalName(it) } + .distinct() + .filter { !dirtyFiles.contains(it) } + } + fsOperations.markFilesForCurrentRound(jpsTarget, complementaryFiles + dirtyMultifileClassFiles) cache.markDirty(targetDirtyFiles.dirty.keys + targetDirtyFiles.removed) } @@ -666,6 +680,28 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) { lookupStorage.addAll(lookupTracker.lookups, lookupTracker.pathInterner.values) } } + + private fun markDirtyComplementaryMultifileClasses( + generatedFiles: Map>, + kotlinContext: KotlinCompileContext, + incrementalCaches: Map, JpsIncrementalCache>, + fsOperations: FSOperationsHelper + ) { + for ((target, files) in generatedFiles) { + val kotlinModuleBuilderTarget = kotlinContext.targetsBinding[target] ?: continue + val cache = incrementalCaches[kotlinModuleBuilderTarget] as? IncrementalJvmCache ?: continue + val generated = files.filterIsInstance() + val multifileClasses = generated.filter { it.outputClass.classHeader.kind == KotlinClassHeader.Kind.MULTIFILE_CLASS } + val expectedAllParts = multifileClasses.flatMap { cache.getAllPartsOfMultifileFacade(it.outputClass.className).orEmpty() } + if (multifileClasses.isEmpty()) continue + val actualParts = generated.filter { it.outputClass.classHeader.kind == KotlinClassHeader.Kind.MULTIFILE_CLASS_PART } + .map { it.outputClass.className.toString() } + if (!actualParts.containsAll(expectedAllParts)) { + fsOperations.markFiles(expectedAllParts.flatMap { cache.sourcesByInternalName(it) } + + multifileClasses.flatMap { it.sourceFiles }) + } + } + } } private class JpsICReporter : ICReporterBase() { diff --git a/jps-plugin/testData/incremental/withJava/other/multifileClassAddTopLevelFunWithDefault/build.log b/jps-plugin/testData/incremental/withJava/other/multifileClassAddTopLevelFunWithDefault/build.log index b13957b6b52..0a37a7011af 100644 --- a/jps-plugin/testData/incremental/withJava/other/multifileClassAddTopLevelFunWithDefault/build.log +++ b/jps-plugin/testData/incremental/withJava/other/multifileClassAddTopLevelFunWithDefault/build.log @@ -5,7 +5,11 @@ Cleaning output files: out/production/module/test/Test.class out/production/module/test/Test__BKt.class End of files +Cleaning output files: + out/production/module/test/Test__AKt.class +End of files Compiling files: + src/a.kt src/b.kt End of files Marked as dirty by Kotlin: @@ -14,12 +18,9 @@ Exit code: ADDITIONAL_PASS_REQUIRED ------------------------------------------ Cleaning output files: out/production/module/META-INF/module.kotlin_module - out/production/module/test/Test.class - out/production/module/test/Test__AKt.class out/production/module/usage/UsageKt.class End of files Compiling files: - src/a.kt src/usage.kt End of files Exit code: OK diff --git a/jps-plugin/testData/incremental/withJava/other/multifileClassFileAdded/build.log b/jps-plugin/testData/incremental/withJava/other/multifileClassFileAdded/build.log index 2fb46435909..063de2e6436 100644 --- a/jps-plugin/testData/incremental/withJava/other/multifileClassFileAdded/build.log +++ b/jps-plugin/testData/incremental/withJava/other/multifileClassFileAdded/build.log @@ -3,5 +3,20 @@ Compiling files: src/b.kt End of files -Exit code: OK +Marked as dirty by Kotlin: + src/a.kt + src/b.kt +Exit code: ADDITIONAL_PASS_REQUIRED ------------------------------------------ +Cleaning output files: + out/production/module/META-INF/module.kotlin_module + out/production/module/test/Test.class + out/production/module/test/Test__AKt.class + out/production/module/test/Test__BKt.class +End of files +Compiling files: + src/a.kt + src/b.kt +End of files +Exit code: OK +------------------------------------------ \ No newline at end of file diff --git a/jps-plugin/testData/incremental/withJava/other/multifileClassFileChanged/build.log b/jps-plugin/testData/incremental/withJava/other/multifileClassFileChanged/build.log index 0aeeb8ca95f..b9ec7f67acd 100644 --- a/jps-plugin/testData/incremental/withJava/other/multifileClassFileChanged/build.log +++ b/jps-plugin/testData/incremental/withJava/other/multifileClassFileChanged/build.log @@ -5,8 +5,12 @@ Cleaning output files: out/production/module/test/Test.class out/production/module/test/Test__BKt.class End of files +Cleaning output files: + out/production/module/test/Test__AKt.class +End of files Compiling files: + src/a.kt src/b.kt End of files Exit code: OK ------------------------------------------- +------------------------------------------ \ No newline at end of file diff --git a/jps-plugin/testData/incremental/withJava/other/multifileDependantUsage/build.log b/jps-plugin/testData/incremental/withJava/other/multifileDependantUsage/build.log new file mode 100644 index 00000000000..ea02e9ce400 --- /dev/null +++ b/jps-plugin/testData/incremental/withJava/other/multifileDependantUsage/build.log @@ -0,0 +1,25 @@ +================ Step #1 ================= + +Marked as dirty by Kotlin: + src/partB.kt + src/usagePartB.kt +Cleaning output files: + out/production/module/META-INF/module.kotlin_module + out/production/module/OuterClass$InnerClass.class + out/production/module/OuterClass.class + out/production/module/UsagePartBKt.class + out/production/module/Utils.class + out/production/module/Utils__PartBKt.class +End of files +Cleaning output files: + out/production/module/Utils__PartAKt.class +End of files +Compiling files: + src/partA.kt + src/partB.kt + src/usagePartB.kt +End of files +Exit code: ABORT +------------------------------------------ +COMPILATION FAILED +Unresolved reference: OuterClass \ No newline at end of file diff --git a/jps-plugin/testData/incremental/withJava/other/multifileDependantUsage/partA.kt b/jps-plugin/testData/incremental/withJava/other/multifileDependantUsage/partA.kt new file mode 100644 index 00000000000..457823b86a7 --- /dev/null +++ b/jps-plugin/testData/incremental/withJava/other/multifileDependantUsage/partA.kt @@ -0,0 +1,4 @@ +@file:JvmName("Utils") +@file:JvmMultifileClass + +val aVal: Int get() = 0 \ No newline at end of file diff --git a/jps-plugin/testData/incremental/withJava/other/multifileDependantUsage/partB.kt b/jps-plugin/testData/incremental/withJava/other/multifileDependantUsage/partB.kt new file mode 100644 index 00000000000..bab0026e072 --- /dev/null +++ b/jps-plugin/testData/incremental/withJava/other/multifileDependantUsage/partB.kt @@ -0,0 +1,10 @@ +@file:JvmName("Utils") +@file:JvmMultifileClass + +val bVal: Int get() = 0 + +class OuterClass{ + inner class InnerClass { + val getZero: Int get() = 0 + } +} \ No newline at end of file diff --git a/jps-plugin/testData/incremental/withJava/other/multifileDependantUsage/partB.kt.new.1 b/jps-plugin/testData/incremental/withJava/other/multifileDependantUsage/partB.kt.new.1 new file mode 100644 index 00000000000..27b19de3c69 --- /dev/null +++ b/jps-plugin/testData/incremental/withJava/other/multifileDependantUsage/partB.kt.new.1 @@ -0,0 +1,4 @@ +@file:JvmName("Utils") +@file:JvmMultifileClass + +val bVal: Int get() = 0 diff --git a/jps-plugin/testData/incremental/withJava/other/multifileDependantUsage/usagePartB.kt b/jps-plugin/testData/incremental/withJava/other/multifileDependantUsage/usagePartB.kt new file mode 100644 index 00000000000..306a92e2773 --- /dev/null +++ b/jps-plugin/testData/incremental/withJava/other/multifileDependantUsage/usagePartB.kt @@ -0,0 +1 @@ +fun zero() = OuterClass().InnerClass().getZero \ No newline at end of file diff --git a/jps-plugin/testData/incremental/withJava/other/multifilePackagePartMethodAdded/build.log b/jps-plugin/testData/incremental/withJava/other/multifilePackagePartMethodAdded/build.log index 86910844d44..d2f88033cbb 100644 --- a/jps-plugin/testData/incremental/withJava/other/multifilePackagePartMethodAdded/build.log +++ b/jps-plugin/testData/incremental/withJava/other/multifilePackagePartMethodAdded/build.log @@ -5,11 +5,16 @@ Cleaning output files: out/production/module/Utils.class out/production/module/Utils__PartBKt.class End of files +Cleaning output files: + out/production/module/Utils__PartAKt.class + out/production/module/Utils__PartCKt.class +End of files Compiling files: + src/partA.kt src/partB.kt + src/partC.kt End of files Marked as dirty by Kotlin: - src/partA.kt src/useFooF.kt src/useFooG.kt Exit code: ADDITIONAL_PASS_REQUIRED @@ -18,15 +23,10 @@ Cleaning output files: out/production/module/META-INF/module.kotlin_module out/production/module/UseFooFKt.class out/production/module/UseFooGKt.class - out/production/module/Utils.class - out/production/module/Utils__PartAKt.class - out/production/module/Utils__PartCKt.class End of files Compiling files: - src/partA.kt - src/partC.kt src/useFooF.kt src/useFooG.kt End of files Exit code: OK ------------------------------------------- +------------------------------------------ \ No newline at end of file diff --git a/jps-plugin/testData/incremental/withJava/other/multifilePartsWithProperties/build.log b/jps-plugin/testData/incremental/withJava/other/multifilePartsWithProperties/build.log index 260d0b077bf..2894c2c0a84 100644 --- a/jps-plugin/testData/incremental/withJava/other/multifilePartsWithProperties/build.log +++ b/jps-plugin/testData/incremental/withJava/other/multifilePartsWithProperties/build.log @@ -5,18 +5,12 @@ Cleaning output files: out/production/module/Utils.class out/production/module/Utils__PartBKt.class End of files -Compiling files: - src/partB.kt -End of files -Exit code: OK ------------------------------------------- Cleaning output files: - out/production/module/META-INF/module.kotlin_module - out/production/module/Utils.class out/production/module/Utils__PartAKt.class End of files Compiling files: src/partA.kt + src/partB.kt End of files Exit code: OK ------------------------------------------ \ No newline at end of file diff --git a/tests/mute-common.csv b/tests/mute-common.csv index edb53357168..bf13f1265df 100644 --- a/tests/mute-common.csv +++ b/tests/mute-common.csv @@ -86,11 +86,6 @@ org.jetbrains.kotlin.jps.build.IncrementalJvmJpsTestGenerated.Jvm.testCircularDe org.jetbrains.kotlin.jps.build.IncrementalJvmJpsTestGenerated.Jvm.testCircularDependencySamePackageUnchanged, Temporary muted due to problems with IC and JVM IR backend,, org.jetbrains.kotlin.jps.build.IncrementalJvmJpsTestGenerated.Jvm.testCircularDependencyTopLevelFunctions, Temporary muted due to problems with IC and JVM IR backend,, org.jetbrains.kotlin.jps.build.IncrementalJvmJpsTestGenerated.Jvm.testCircularDependencyWithAccessToInternal, Temporary muted due to problems with IC and JVM IR backend,, -org.jetbrains.kotlin.jps.build.IncrementalJvmJpsTestGenerated.WithJava.Other.testMultifileClassAddTopLevelFunWithDefault, Temporary muted due to problems with IC and JVM IR backend,, -org.jetbrains.kotlin.jps.build.IncrementalJvmJpsTestGenerated.WithJava.Other.testMultifileClassFileAdded, Temporary muted due to problems with IC and JVM IR backend,, -org.jetbrains.kotlin.jps.build.IncrementalJvmJpsTestGenerated.WithJava.Other.testMultifileClassFileChanged, Temporary muted due to problems with IC and JVM IR backend,, -org.jetbrains.kotlin.jps.build.IncrementalJvmJpsTestGenerated.WithJava.Other.testMultifilePackagePartMethodAdded, Temporary muted due to problems with IC and JVM IR backend,, -org.jetbrains.kotlin.jps.build.IncrementalJvmJpsTestGenerated.WithJava.Other.testMultifilePartsWithProperties, Temporary muted due to problems with IC and JVM IR backend,, org.jetbrains.kotlin.jps.build.KotlinJpsBuildTest.testCircularDependenciesDifferentPackages, Temporary muted due to problems with IC and JVM IR backend,, org.jetbrains.kotlin.jps.build.KotlinJpsBuildTest.testCircularDependenciesSamePackage, Temporary muted due to problems with IC and JVM IR backend,, org.jetbrains.kotlin.jps.build.KotlinJpsBuildTest.testCircularDependenciesSamePackageWithTests, Temporary muted due to problems with IC and JVM IR backend,,