Add language consistency checks for fragments
Migrate exiting consistency checker for source sets to be generic and reuse it for fragments
This commit is contained in:
committed by
Sergey Igushkin
parent
de4221afd8
commit
c3264a2979
+3
-4
@@ -21,7 +21,6 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJvmWithJavaTargetPreset
|
|||||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinMultiplatformPlugin
|
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinMultiplatformPlugin
|
||||||
import org.jetbrains.kotlin.gradle.plugin.mpp.UnusedSourceSetsChecker
|
import org.jetbrains.kotlin.gradle.plugin.mpp.UnusedSourceSetsChecker
|
||||||
import org.jetbrains.kotlin.gradle.plugin.sources.METADATA_CONFIGURATION_NAME_SUFFIX
|
import org.jetbrains.kotlin.gradle.plugin.sources.METADATA_CONFIGURATION_NAME_SUFFIX
|
||||||
import org.jetbrains.kotlin.gradle.plugin.sources.SourceSetConsistencyChecks
|
|
||||||
import org.jetbrains.kotlin.gradle.plugin.sources.UnsatisfiedSourceSetVisibilityException
|
import org.jetbrains.kotlin.gradle.plugin.sources.UnsatisfiedSourceSetVisibilityException
|
||||||
import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget
|
import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget
|
||||||
import org.jetbrains.kotlin.gradle.util.*
|
import org.jetbrains.kotlin.gradle.util.*
|
||||||
@@ -810,17 +809,17 @@ class NewMultiplatformIT : BaseGradleIT() {
|
|||||||
testMonotonousCheck(
|
testMonotonousCheck(
|
||||||
"languageSettings.languageVersion = '1.3'",
|
"languageSettings.languageVersion = '1.3'",
|
||||||
"languageSettings.languageVersion = '1.4'",
|
"languageSettings.languageVersion = '1.4'",
|
||||||
SourceSetConsistencyChecks.languageVersionCheckHint
|
"The language version of the dependent source set must be greater than or equal to that of its dependency."
|
||||||
)
|
)
|
||||||
|
|
||||||
testMonotonousCheck(
|
testMonotonousCheck(
|
||||||
"languageSettings.enableLanguageFeature('InlineClasses')",
|
"languageSettings.enableLanguageFeature('InlineClasses')",
|
||||||
SourceSetConsistencyChecks.unstableFeaturesHint
|
"The dependent source set must enable all unstable language features that its dependency has."
|
||||||
)
|
)
|
||||||
|
|
||||||
testMonotonousCheck(
|
testMonotonousCheck(
|
||||||
"languageSettings.useExperimentalAnnotation('kotlin.ExperimentalUnsignedTypes')",
|
"languageSettings.useExperimentalAnnotation('kotlin.ExperimentalUnsignedTypes')",
|
||||||
SourceSetConsistencyChecks.experimentalAnnotationsInUseHint
|
"The dependent source set must use all experimental annotations that its dependency uses."
|
||||||
)
|
)
|
||||||
|
|
||||||
// check that enabling a bugfix feature and progressive mode or advancing API level
|
// check that enabling a bugfix feature and progressive mode or advancing API level
|
||||||
|
|||||||
+16
@@ -14,6 +14,8 @@ import org.jetbrains.kotlin.gradle.plugin.*
|
|||||||
import org.jetbrains.kotlin.gradle.plugin.mpp.DefaultKotlinDependencyHandler
|
import org.jetbrains.kotlin.gradle.plugin.mpp.DefaultKotlinDependencyHandler
|
||||||
import org.jetbrains.kotlin.gradle.plugin.mpp.toModuleDependency
|
import org.jetbrains.kotlin.gradle.plugin.mpp.toModuleDependency
|
||||||
import org.jetbrains.kotlin.gradle.plugin.sources.DefaultLanguageSettingsBuilder
|
import org.jetbrains.kotlin.gradle.plugin.sources.DefaultLanguageSettingsBuilder
|
||||||
|
import org.jetbrains.kotlin.gradle.plugin.sources.FragmentConsistencyChecker
|
||||||
|
import org.jetbrains.kotlin.gradle.plugin.sources.FragmentConsistencyChecks
|
||||||
import org.jetbrains.kotlin.gradle.utils.addExtendsFromRelation
|
import org.jetbrains.kotlin.gradle.utils.addExtendsFromRelation
|
||||||
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
|
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
|
||||||
import org.jetbrains.kotlin.project.model.KotlinModuleDependency
|
import org.jetbrains.kotlin.project.model.KotlinModuleDependency
|
||||||
@@ -47,6 +49,10 @@ open class KotlinGradleFragmentInternal @Inject constructor(
|
|||||||
).forEach { getConfiguration ->
|
).forEach { getConfiguration ->
|
||||||
project.addExtendsFromRelation(getConfiguration(this), getConfiguration(other.get())) // todo eager instantiation; fix?
|
project.addExtendsFromRelation(getConfiguration(this), getConfiguration(other.get())) // todo eager instantiation; fix?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
project.whenEvaluated {
|
||||||
|
kotlinGradleFragmentConsistencyChecker.runAllChecks(this@KotlinGradleFragmentInternal, other.get())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkCanRefine(other: KotlinGradleFragment) {
|
private fun checkCanRefine(other: KotlinGradleFragment) {
|
||||||
@@ -99,3 +105,13 @@ internal fun KotlinModuleFragment.disambiguateName(simpleName: String) =
|
|||||||
|
|
||||||
val KotlinGradleFragment.refinesClosure: Set<KotlinGradleFragment>
|
val KotlinGradleFragment.refinesClosure: Set<KotlinGradleFragment>
|
||||||
get() = (this as KotlinModuleFragment).refinesClosure.map { it as KotlinGradleFragment }.toSet()
|
get() = (this as KotlinModuleFragment).refinesClosure.map { it as KotlinGradleFragment }.toSet()
|
||||||
|
|
||||||
|
internal val kotlinGradleFragmentConsistencyChecker =
|
||||||
|
FragmentConsistencyChecker<KotlinGradleFragment>(
|
||||||
|
unitsName = "fragments",
|
||||||
|
name = { name },
|
||||||
|
checks = FragmentConsistencyChecks<KotlinGradleFragment>(
|
||||||
|
unitName = "fragment",
|
||||||
|
languageSettings = { languageSettings }
|
||||||
|
).allChecks
|
||||||
|
)
|
||||||
+91
@@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2021 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.gradle.plugin.sources
|
||||||
|
|
||||||
|
import org.gradle.api.InvalidUserDataException
|
||||||
|
import org.jetbrains.kotlin.config.LanguageFeature
|
||||||
|
import org.jetbrains.kotlin.config.LanguageVersion
|
||||||
|
import org.jetbrains.kotlin.project.model.LanguageSettings
|
||||||
|
|
||||||
|
internal class ConsistencyCheck<T, S>(
|
||||||
|
val name: String,
|
||||||
|
val getValue: (T) -> S,
|
||||||
|
val leftExtendsRightConsistently: (S, S) -> Boolean,
|
||||||
|
val consistencyConditionHint: String
|
||||||
|
)
|
||||||
|
|
||||||
|
internal class FragmentConsistencyChecks<T>(
|
||||||
|
unitName: String, // "fragment" or "source set"
|
||||||
|
private val languageSettings: T.() -> LanguageSettings
|
||||||
|
) {
|
||||||
|
private val defaultLanguageVersion = LanguageVersion.LATEST_STABLE
|
||||||
|
|
||||||
|
private val languageVersionCheckHint =
|
||||||
|
"The language version of the dependent $unitName must be greater than or equal to that of its dependency."
|
||||||
|
|
||||||
|
val languageVersionCheck = ConsistencyCheck<T, LanguageVersion>(
|
||||||
|
name = "language version",
|
||||||
|
getValue = { unit ->
|
||||||
|
unit.languageSettings().languageVersion?.let { parseLanguageVersionSetting(it) } ?: defaultLanguageVersion
|
||||||
|
},
|
||||||
|
leftExtendsRightConsistently = { left, right -> left >= right },
|
||||||
|
consistencyConditionHint = languageVersionCheckHint
|
||||||
|
)
|
||||||
|
|
||||||
|
private val unstableFeaturesHint = "The dependent $unitName must enable all unstable language features that its dependency has."
|
||||||
|
|
||||||
|
val unstableFeaturesCheck = ConsistencyCheck<T, Set<LanguageFeature>>(
|
||||||
|
name = "unstable language feature set",
|
||||||
|
getValue = { unit ->
|
||||||
|
unit.languageSettings().enabledLanguageFeatures
|
||||||
|
.map { parseLanguageFeature(it)!! }
|
||||||
|
.filterTo(mutableSetOf()) { it.kind == LanguageFeature.Kind.UNSTABLE_FEATURE }
|
||||||
|
},
|
||||||
|
leftExtendsRightConsistently = { left, right -> left.containsAll(right) },
|
||||||
|
consistencyConditionHint = unstableFeaturesHint
|
||||||
|
)
|
||||||
|
|
||||||
|
private val experimentalAnnotationsInUseHint = "The dependent $unitName must use all experimental annotations that its dependency uses."
|
||||||
|
|
||||||
|
val experimentalAnnotationsCheck = ConsistencyCheck<T, Set<String>>(
|
||||||
|
name = "set of experimental annotations in use",
|
||||||
|
getValue = { unit -> unit.languageSettings().experimentalAnnotationsInUse },
|
||||||
|
leftExtendsRightConsistently = { left, right -> left.containsAll(right) },
|
||||||
|
consistencyConditionHint = experimentalAnnotationsInUseHint
|
||||||
|
)
|
||||||
|
|
||||||
|
val allChecks = listOf(languageVersionCheck, unstableFeaturesCheck, experimentalAnnotationsCheck)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class FragmentConsistencyChecker<T>(
|
||||||
|
private val unitsName: String,
|
||||||
|
private val name: T.() -> String,
|
||||||
|
val checks: List<ConsistencyCheck<T, *>>
|
||||||
|
) {
|
||||||
|
fun <S> runSingleCheck(
|
||||||
|
dependent: T,
|
||||||
|
dependency: T,
|
||||||
|
check: ConsistencyCheck<T, S>
|
||||||
|
) {
|
||||||
|
val leftValue = check.getValue(dependent)
|
||||||
|
val rightValue = check.getValue(dependency)
|
||||||
|
|
||||||
|
if (!check.leftExtendsRightConsistently(leftValue, rightValue)) {
|
||||||
|
throw InvalidUserDataException(
|
||||||
|
"Inconsistent settings for Kotlin $unitsName: '${dependent.name()}' depends on '${dependency.name()}'\n" +
|
||||||
|
"'${dependent.name()}': ${check.name} is ${leftValue}\n" +
|
||||||
|
"'${dependency.name()}': ${check.name} is ${rightValue}\n" +
|
||||||
|
check.consistencyConditionHint
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun runAllChecks(dependent: T, dependency: T) {
|
||||||
|
for (check in checks) {
|
||||||
|
runSingleCheck(dependent, dependency, check)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+11
@@ -202,6 +202,17 @@ class DefaultKotlinSourceSet(
|
|||||||
//endregion
|
//endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
internal val defaultSourceSetLanguageSettingsChecker =
|
||||||
|
FragmentConsistencyChecker<KotlinSourceSet>(
|
||||||
|
unitsName = "source sets",
|
||||||
|
name = { name },
|
||||||
|
checks = FragmentConsistencyChecks<KotlinSourceSet>(
|
||||||
|
unitName = "source set",
|
||||||
|
languageSettings = { languageSettings }
|
||||||
|
).allChecks
|
||||||
|
)
|
||||||
|
|
||||||
private fun KotlinSourceSet.checkForCircularDependencies() {
|
private fun KotlinSourceSet.checkForCircularDependencies() {
|
||||||
// If adding an edge creates a cycle, than the source node of the edge belongs to the cycle, so run DFS from that node
|
// If adding an edge creates a cycle, than the source node of the edge belongs to the cycle, so run DFS from that node
|
||||||
// to check whether it became reachable from itself
|
// to check whether it became reachable from itself
|
||||||
|
|||||||
-89
@@ -1,89 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010-2018 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.gradle.plugin.sources
|
|
||||||
|
|
||||||
import org.gradle.api.InvalidUserDataException
|
|
||||||
import org.jetbrains.kotlin.config.LanguageFeature
|
|
||||||
import org.jetbrains.kotlin.config.LanguageVersion
|
|
||||||
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
|
|
||||||
|
|
||||||
internal class ConsistencyCheck<T, S>(
|
|
||||||
val name: String,
|
|
||||||
val getValue: (T) -> S,
|
|
||||||
val leftExtendsRightConsistently: (S, S) -> Boolean,
|
|
||||||
val consistencyConditionHint: String
|
|
||||||
)
|
|
||||||
|
|
||||||
object SourceSetConsistencyChecks {
|
|
||||||
private val defaultLanguageVersion = LanguageVersion.LATEST_STABLE
|
|
||||||
|
|
||||||
const val languageVersionCheckHint =
|
|
||||||
"The language version of the dependent source set must be greater than or equal to that of its dependency."
|
|
||||||
|
|
||||||
internal val languageVersionCheck = ConsistencyCheck<KotlinSourceSet, LanguageVersion>(
|
|
||||||
name = "language version",
|
|
||||||
getValue = { sourceSet ->
|
|
||||||
sourceSet.languageSettings.languageVersion?.let { parseLanguageVersionSetting(it) } ?: defaultLanguageVersion
|
|
||||||
},
|
|
||||||
leftExtendsRightConsistently = { left, right -> left >= right },
|
|
||||||
consistencyConditionHint = languageVersionCheckHint
|
|
||||||
)
|
|
||||||
|
|
||||||
const val unstableFeaturesHint = "The dependent source set must enable all unstable language features that its dependency has."
|
|
||||||
|
|
||||||
internal val unstableFeaturesCheck = ConsistencyCheck<KotlinSourceSet, Set<LanguageFeature>>(
|
|
||||||
name = "unstable language feature set",
|
|
||||||
getValue = { sourceSet ->
|
|
||||||
sourceSet.languageSettings.enabledLanguageFeatures
|
|
||||||
.map { parseLanguageFeature(it)!! }
|
|
||||||
.filterTo(mutableSetOf()) { it.kind == LanguageFeature.Kind.UNSTABLE_FEATURE }
|
|
||||||
},
|
|
||||||
leftExtendsRightConsistently = { left, right -> left.containsAll(right) },
|
|
||||||
consistencyConditionHint = unstableFeaturesHint
|
|
||||||
)
|
|
||||||
|
|
||||||
const val experimentalAnnotationsInUseHint = "The dependent source set must use all experimental annotations that its dependency uses."
|
|
||||||
|
|
||||||
internal val experimentalAnnotationsCheck = ConsistencyCheck<KotlinSourceSet, Set<String>>(
|
|
||||||
name = "set of experimental annotations in use",
|
|
||||||
getValue = { sourceSet -> sourceSet.languageSettings.experimentalAnnotationsInUse },
|
|
||||||
leftExtendsRightConsistently = { left, right -> left.containsAll(right) },
|
|
||||||
consistencyConditionHint = experimentalAnnotationsInUseHint
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
internal class SourceSetConsistencyChecker(
|
|
||||||
val checks: List<ConsistencyCheck<KotlinSourceSet, *>>
|
|
||||||
) {
|
|
||||||
fun <S> runSingleCheck(
|
|
||||||
dependent: KotlinSourceSet,
|
|
||||||
dependency: KotlinSourceSet,
|
|
||||||
check: ConsistencyCheck<KotlinSourceSet, S>
|
|
||||||
) {
|
|
||||||
val leftValue = check.getValue(dependent)
|
|
||||||
val rightValue = check.getValue(dependency)
|
|
||||||
|
|
||||||
if (!check.leftExtendsRightConsistently(leftValue, rightValue)) {
|
|
||||||
throw InvalidUserDataException(
|
|
||||||
"Inconsistent settings for Kotlin source sets: '${dependent.name}' depends on '${dependency.name}'\n" +
|
|
||||||
"'${dependent.name}': ${check.name} is ${leftValue}\n" +
|
|
||||||
"'${dependency.name}': ${check.name} is ${rightValue}\n" +
|
|
||||||
check.consistencyConditionHint
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun runAllChecks(dependent: KotlinSourceSet, dependency: KotlinSourceSet) {
|
|
||||||
for (check in checks) {
|
|
||||||
runSingleCheck(dependent, dependency, check)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal val defaultSourceSetLanguageSettingsChecker = with(SourceSetConsistencyChecks) {
|
|
||||||
// We don't check the progressive mode, since the features it enables are bugfixes
|
|
||||||
SourceSetConsistencyChecker(listOf(languageVersionCheck, experimentalAnnotationsCheck, unstableFeaturesCheck))
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user