diff --git a/jps/jps-plugin/src/org/jetbrains/jet/jps/build/KotlinBuilder.java b/jps/jps-plugin/src/org/jetbrains/jet/jps/build/KotlinBuilder.java index 7df7be35870..24dcee1aa65 100644 --- a/jps/jps-plugin/src/org/jetbrains/jet/jps/build/KotlinBuilder.java +++ b/jps/jps-plugin/src/org/jetbrains/jet/jps/build/KotlinBuilder.java @@ -16,7 +16,6 @@ package org.jetbrains.jet.jps.build; -import com.google.common.collect.Lists; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.io.StreamUtil; @@ -237,7 +236,7 @@ public class KotlinBuilder extends ModuleLevelBuilder { } cache.clearCacheForRemovedFiles(moduleIdsAndFiles, outDirectories); - boolean significantChanges = false; + IncrementalCacheImpl.RecompilationDecision recompilationDecision = IncrementalCacheImpl.RecompilationDecision.DO_NOTHING; for (SimpleOutputItem outputItem : outputItemCollector.getOutputs()) { BuildTarget target = null; @@ -253,17 +252,20 @@ public class KotlinBuilder extends ModuleLevelBuilder { File outputFile = outputItem.getOutputFile(); if (IncrementalCompilation.ENABLED) { - if (cache.saveFileToCache(target.getId(), sourceFiles, outputFile)) { - significantChanges = true; - } + IncrementalCacheImpl.RecompilationDecision newDecision = cache.saveFileToCache(target.getId(), sourceFiles, outputFile); + recompilationDecision = recompilationDecision.merge(newDecision); } outputConsumer.registerOutputFile(target, outputFile, paths(sourceFiles)); } if (IncrementalCompilation.ENABLED) { - // TODO should mark dependencies as dirty, as well - if (significantChanges) { + 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) { diff --git a/jps/jps-plugin/src/org/jetbrains/jet/jps/incremental/IncrementalCacheImpl.kt b/jps/jps-plugin/src/org/jetbrains/jet/jps/incremental/IncrementalCacheImpl.kt index 4250d96496d..c63fa5d1bf3 100644 --- a/jps/jps-plugin/src/org/jetbrains/jet/jps/incremental/IncrementalCacheImpl.kt +++ b/jps/jps-plugin/src/org/jetbrains/jet/jps/incremental/IncrementalCacheImpl.kt @@ -16,6 +16,7 @@ package org.jetbrains.jet.jps.incremental +import org.jetbrains.jet.jps.incremental.IncrementalCacheImpl.RecompilationDecision.* import java.io.File import com.intellij.util.io.PersistentHashMap import java.io.DataOutput @@ -56,10 +57,10 @@ public class IncrementalCacheImpl(val baseDir: File): IncrementalCache { private val inlineFunctionsMap = InlineFunctionsMap() private val packagePartMap = PackagePartMap() - public fun saveFileToCache(moduleId: String, sourceFiles: Collection, classFile: File): Boolean { + public fun saveFileToCache(moduleId: String, sourceFiles: Collection, classFile: File): RecompilationDecision { val fileBytes = classFile.readBytes() val classNameAndHeader = VirtualFileKotlinClass.readClassNameAndHeader(fileBytes) - if (classNameAndHeader == null) return false + if (classNameAndHeader == null) return RecompilationDecision.DO_NOTHING val (className, header) = classNameAndHeader val annotationDataEncoded = header.annotationData @@ -67,10 +68,13 @@ public class IncrementalCacheImpl(val baseDir: File): IncrementalCache { val data = BitEncoding.decodeBytes(annotationDataEncoded) when (header.kind) { KotlinClassHeader.Kind.PACKAGE_FACADE -> { - return protoMap.put(moduleId, className, data) + return if (protoMap.put(moduleId, className, data)) COMPILE_OTHERS else DO_NOTHING } KotlinClassHeader.Kind.CLASS -> { - return inlineFunctionsMap.process(moduleId, sourceFiles.first(), fileBytes) or protoMap.put(moduleId, className, data) + val inlinesChanged = inlineFunctionsMap.process(moduleId, sourceFiles.first(), fileBytes) + val protoChanged = protoMap.put(moduleId, className, data) + + return if (inlinesChanged) RECOMPILE_ALL else if (protoChanged) COMPILE_OTHERS else DO_NOTHING } else -> { throw IllegalStateException("Unexpected kind with annotationData: ${header.kind}") @@ -80,10 +84,12 @@ public class IncrementalCacheImpl(val baseDir: File): IncrementalCache { if (header.syntheticClassKind == JvmAnnotationNames.KotlinSyntheticClass.Kind.PACKAGE_PART) { assert(sourceFiles.size == 1) { "Package part from several source files: $sourceFiles" } packagePartMap.putPackagePartSourceData(moduleId, sourceFiles.first(), className) - return inlineFunctionsMap.process(moduleId, sourceFiles.first(), fileBytes) or constantsMap.process(moduleId, sourceFiles.first(), fileBytes) + val inlinesChanged = inlineFunctionsMap.process(moduleId, sourceFiles.first(), fileBytes) + val constantsChanged = constantsMap.process(moduleId, sourceFiles.first(), fileBytes) + return if (inlinesChanged) RECOMPILE_ALL else if (constantsChanged) COMPILE_OTHERS else DO_NOTHING } - return false + return DO_NOTHING } public fun clearCacheForRemovedFiles(moduleIdsAndFiles: Collection>, outDirectories: Map) { @@ -286,7 +292,7 @@ public class IncrementalCacheImpl(val baseDir: File): IncrementalCache { return moduleId + File.pathSeparator + sourceFile.getAbsolutePath() } - private fun getInlineFunctionsMap(bytes: ByteArray): Map { + private fun getInlineFunctionsMap(bytes: ByteArray): Map? { val result = HashMap() ClassReader(bytes).accept(object : ClassVisitor(Opcodes.ASM5) { @@ -315,21 +321,23 @@ public class IncrementalCacheImpl(val baseDir: File): IncrementalCache { }, 0) - return result + return if (result.isEmpty()) null else result } public fun process(moduleId: String, file: File, bytes: ByteArray): Boolean { return put(moduleId, file, getInlineFunctionsMap(bytes)) } - private fun put(moduleId: String, file: File, inlineFunctionsMap: Map): Boolean { + private fun put(moduleId: String, file: File, inlineFunctionsMap: Map?): Boolean { val key = getKey(moduleId, file) val oldMap = map[key] if (oldMap == inlineFunctionsMap) { return false } - map.put(key, inlineFunctionsMap) + if (inlineFunctionsMap != null) { + map.put(key, inlineFunctionsMap) + } return true } @@ -436,6 +444,16 @@ public class IncrementalCacheImpl(val baseDir: File): IncrementalCache { map.close() } } + + enum class RecompilationDecision { + DO_NOTHING + COMPILE_OTHERS + RECOMPILE_ALL + + fun merge(other: RecompilationDecision): RecompilationDecision { + return if (other.ordinal() > this.ordinal()) other else this + } + } } private object ByteArrayExternalizer: DataExternalizer { diff --git a/jps/jps-plugin/test/org/jetbrains/jet/jps/build/IncrementalJpsTestGenerated.java b/jps/jps-plugin/test/org/jetbrains/jet/jps/build/IncrementalJpsTestGenerated.java index 1628024b0a6..40920fdd674 100644 --- a/jps/jps-plugin/test/org/jetbrains/jet/jps/build/IncrementalJpsTestGenerated.java +++ b/jps/jps-plugin/test/org/jetbrains/jet/jps/build/IncrementalJpsTestGenerated.java @@ -105,7 +105,12 @@ public class IncrementalJpsTestGenerated extends AbstractIncrementalJpsTest { public void testIndependentClasses() throws Exception { doTest("jps-plugin/testData/incremental/independentClasses/"); } - + + @TestMetadata("inlineFunctionsCircularDependency") + public void testInlineFunctionsCircularDependency() throws Exception { + doTest("jps-plugin/testData/incremental/inlineFunctionsCircularDependency/"); + } + @TestMetadata("multiplePackagesModified") public void testMultiplePackagesModified() throws Exception { doTest("jps-plugin/testData/incremental/multiplePackagesModified/"); diff --git a/jps/jps-plugin/testData/incremental/classInlineFunctionChanged/build.log b/jps/jps-plugin/testData/incremental/classInlineFunctionChanged/build.log index dc4ee3554aa..41261841973 100644 --- a/jps/jps-plugin/testData/incremental/classInlineFunctionChanged/build.log +++ b/jps/jps-plugin/testData/incremental/classInlineFunctionChanged/build.log @@ -5,9 +5,11 @@ Compiling files: src/inline.kt End of files Cleaning output files: +out/production/module/inline/Klass.class out/production/module/usage/UsagePackage-usage-*.class out/production/module/usage/UsagePackage.class End of files Compiling files: +src/inline.kt src/usage.kt End of files diff --git a/jps/jps-plugin/testData/incremental/inlineFunctionsCircularDependency/a.kt b/jps/jps-plugin/testData/incremental/inlineFunctionsCircularDependency/a.kt new file mode 100644 index 00000000000..8ebdd1cbc44 --- /dev/null +++ b/jps/jps-plugin/testData/incremental/inlineFunctionsCircularDependency/a.kt @@ -0,0 +1,10 @@ +package test + +inline fun a(body: () -> Unit) { + println("i'm inline function a") + body() +} + +fun main(args: Array) { + b { println("to be inlined") } +} diff --git a/jps/jps-plugin/testData/incremental/inlineFunctionsCircularDependency/a.kt.new b/jps/jps-plugin/testData/incremental/inlineFunctionsCircularDependency/a.kt.new new file mode 100644 index 00000000000..b2916dd19e8 --- /dev/null +++ b/jps/jps-plugin/testData/incremental/inlineFunctionsCircularDependency/a.kt.new @@ -0,0 +1,10 @@ +package test + +inline fun a(body: () -> Unit) { + body() + println("i'm inline function a") +} + +fun main(args: Array) { + b { println("to be inlined") } +} diff --git a/jps/jps-plugin/testData/incremental/inlineFunctionsCircularDependency/b.kt b/jps/jps-plugin/testData/incremental/inlineFunctionsCircularDependency/b.kt new file mode 100644 index 00000000000..4f60bafa38c --- /dev/null +++ b/jps/jps-plugin/testData/incremental/inlineFunctionsCircularDependency/b.kt @@ -0,0 +1,7 @@ +package test + +inline fun b(body: () -> Unit) { + println("I'm inline function b") + body() + a { println("To be inlined from b") } +} diff --git a/jps/jps-plugin/testData/incremental/inlineFunctionsCircularDependency/build.log b/jps/jps-plugin/testData/incremental/inlineFunctionsCircularDependency/build.log new file mode 100644 index 00000000000..41097177bde --- /dev/null +++ b/jps/jps-plugin/testData/incremental/inlineFunctionsCircularDependency/build.log @@ -0,0 +1,16 @@ +Cleaning output files: +out/production/module/test/TestPackage-a-*.class +out/production/module/test/TestPackage.class +End of files +Compiling files: +src/a.kt +End of files +Cleaning output files: +out/production/module/test/TestPackage-a-*.class +out/production/module/test/TestPackage-b-*.class +out/production/module/test/TestPackage.class +End of files +Compiling files: +src/a.kt +src/b.kt +End of files diff --git a/jps/jps-plugin/testData/incremental/packageInlineFunctionChanged/build.log b/jps/jps-plugin/testData/incremental/packageInlineFunctionChanged/build.log index 341ab0b4fd2..2b175459118 100644 --- a/jps/jps-plugin/testData/incremental/packageInlineFunctionChanged/build.log +++ b/jps/jps-plugin/testData/incremental/packageInlineFunctionChanged/build.log @@ -6,9 +6,12 @@ Compiling files: src/inline.kt End of files Cleaning output files: +out/production/module/inline/InlinePackage-inline-*.class +out/production/module/inline/InlinePackage.class out/production/module/usage/UsagePackage-usage-*.class out/production/module/usage/UsagePackage.class End of files Compiling files: +src/inline.kt src/usage.kt End of files \ No newline at end of file