[IC] Baseline fix for common sources getting access to platform declarations
K2-only issue. In an incremental build, sourceSet boundary isn't preserved in certain conditions: SourceSet A depends on SourceSet B A and B overload a function, for example: A -> fun foo(p: Parent) // (1) B -> fun foo(c: Child) // (2) If some source file in A is calling foo(..), only (1) is supposed to be visible for that call site. However, if a) it is an incremental build, b) the declaration of (1) is not a part of the compilation set, and c) call to foo(c: Child) is applicable, then (2) would be called from the generated code. So, the build result is not consistent between the full build and an incremental build. As a workaround, we fallback to a non-incremental build, if any source from A needs to be compiled. To enable "risky" incremental builds, use Gradle property kotlin.internal.incremental.enableUnsafeOptimizationsForMultiplatform=true ^KT-62686 ^KT-63837 Fixed Merge-request: KT-MR-13695 Merged-by: Evgenii Mazhukin <evgenii.mazhukin@jetbrains.com>
This commit is contained in:
committed by
Space Team
parent
ad6513e247
commit
de4953adf6
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.incremental
|
||||
|
||||
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
|
||||
import org.jetbrains.kotlin.config.LanguageVersion
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* Holds current mapping from source files to fragments, in the K2 KMP context
|
||||
*
|
||||
* Note: for KT-62686, we don't really care about removed files: if a source isn't recompiled,
|
||||
* it can't get illegal lookups.
|
||||
*
|
||||
* So there's no need to store previous source-to-fragment mapping
|
||||
*/
|
||||
class FragmentContext(
|
||||
/**
|
||||
* Map from path to source to this source's fragment
|
||||
*/
|
||||
private val fileToFragment: Map<String, String>,
|
||||
/**
|
||||
* If a fragment isn't refined by any other fragments, it's allowed to have incremental compilation.
|
||||
* Otherwise, issues from KT-62686 are applicable.
|
||||
*/
|
||||
private val leafFragments: Set<String>
|
||||
) {
|
||||
/**
|
||||
* Returns true, if any file from dirtySet is a part of refined fragment (for example: common, apple, linux).
|
||||
* It is relevant to KT-62686, because in k2 "refined" fragments aren't supposed to see symbols from "refining" fragments,
|
||||
* but they do.
|
||||
*
|
||||
* Use of `absolutePath` is coordinated with K2MultiplatformStructure.fragmentSourcesCompilerArgs
|
||||
*/
|
||||
fun dirtySetTouchesNonLeafFragments(dirtySet: Iterable<File>): Boolean {
|
||||
return dirtySet.any { file ->
|
||||
!leafFragments.contains(fileToFragment[file.absolutePath])
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private fun canCreateFragmentContext(args: CommonCompilerArguments): Boolean {
|
||||
// fragmentContext solves k2-only issues; also, in k1 mode we don't even expect the fragment-related args
|
||||
val isCorrectLanguageVersion = (LanguageVersion.fromVersionString(args.languageVersion) ?: LanguageVersion.LATEST_STABLE).usesK2
|
||||
|
||||
val hasAllRequiredArguments = listOf(args.fragments, args.fragmentRefines, args.fragmentSources).none {
|
||||
it.isNullOrEmpty()
|
||||
}
|
||||
return isCorrectLanguageVersion && hasAllRequiredArguments
|
||||
}
|
||||
|
||||
fun fromCompilerArguments(args: CommonCompilerArguments): FragmentContext? {
|
||||
if (!canCreateFragmentContext(args)) {
|
||||
return null
|
||||
}
|
||||
|
||||
val fileToFragment = args.fragmentSources!!.associate {
|
||||
// expected format: -Xfragment-sources=jvmMain:/tmp/<..>/kotlin/main.kt,<...>
|
||||
val fragmentSource = it.split(":", limit=2)
|
||||
Pair(fragmentSource.last(), fragmentSource.first())
|
||||
}
|
||||
val refinedFragments = args.fragmentRefines!!.map { fragmentRefine ->
|
||||
// expected format: -Xfragment-refines=jvmMain:commonMain,<...>
|
||||
fragmentRefine.split(":", limit = 2).last()
|
||||
}.toSet()
|
||||
val leafFragments = args.fragments!!.toSet() - refinedFragments
|
||||
|
||||
return FragmentContext(fileToFragment, leafFragments)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,7 @@ class IncrementalCompilationContext(
|
||||
*/
|
||||
val trackChangesInLookupCache: Boolean = false,
|
||||
val icFeatures: IncrementalCompilationFeatures = IncrementalCompilationFeatures.DEFAULT_CONFIGURATION,
|
||||
val fragmentContext: FragmentContext? = null,
|
||||
) {
|
||||
@Deprecated("This constructor is scheduled to be removed. KSP is using it")
|
||||
constructor(
|
||||
|
||||
@@ -19,7 +19,7 @@ import java.io.Serializable
|
||||
*
|
||||
* To add a new feature toggle, consider this checklist:
|
||||
*
|
||||
* 1. Gradle input: see [AbstractKotlinCompile.getIncrementalCompilationFeatures].
|
||||
* 1. Gradle input: see [AbstractKotlinCompile.makeIncrementalCompilationFeatures].
|
||||
* Platform-agnostic properties should be the default, but you can extend one of the subclasses, if needed.
|
||||
* If new property might affect the result of compilation, annotate it with @Input.
|
||||
* 2. Gradle.properties support: declare the property in [org.jetbrains.kotlin.gradle.plugin.PropertiesProvider].
|
||||
@@ -48,11 +48,17 @@ data class IncrementalCompilationFeatures(
|
||||
* Requires [preciseCompilationResultsBackup]
|
||||
*/
|
||||
val keepIncrementalCompilationCachesInMemory: Boolean = false,
|
||||
/**
|
||||
* By default, with k2 KMP, we recompile the whole module, if any common sources are recompiled.
|
||||
* It provides consistent builds at the cost of compilation speed. (See KT-62686 for the underlying issue.)
|
||||
* You can enable "unsafeIC" to use pre-2.0 behavior with potentially incorrect incremental builds.
|
||||
*/
|
||||
val enableUnsafeIncrementalCompilationForMultiplatform: Boolean = false,
|
||||
) : Serializable {
|
||||
|
||||
companion object {
|
||||
val DEFAULT_CONFIGURATION = IncrementalCompilationFeatures()
|
||||
|
||||
const val serialVersionUID: Long = 0
|
||||
const val serialVersionUID: Long = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.incremental
|
||||
|
||||
/**
|
||||
* Normally we can RequireRebuild based on input's change,
|
||||
* but the decision can also come later, between incremental compilation steps.
|
||||
*
|
||||
* This exception is used for KT-63837 where common sources need to be rebuilt for consistency.
|
||||
*/
|
||||
class RequireRebuildForCorrectnessInKMPException() : Exception()
|
||||
Reference in New Issue
Block a user