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.UnusedSourceSetsChecker
|
||||
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.targets.jvm.KotlinJvmTarget
|
||||
import org.jetbrains.kotlin.gradle.util.*
|
||||
@@ -810,17 +809,17 @@ class NewMultiplatformIT : BaseGradleIT() {
|
||||
testMonotonousCheck(
|
||||
"languageSettings.languageVersion = '1.3'",
|
||||
"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(
|
||||
"languageSettings.enableLanguageFeature('InlineClasses')",
|
||||
SourceSetConsistencyChecks.unstableFeaturesHint
|
||||
"The dependent source set must enable all unstable language features that its dependency has."
|
||||
)
|
||||
|
||||
testMonotonousCheck(
|
||||
"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
|
||||
|
||||
+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.toModuleDependency
|
||||
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.lowerCamelCaseName
|
||||
import org.jetbrains.kotlin.project.model.KotlinModuleDependency
|
||||
@@ -47,6 +49,10 @@ open class KotlinGradleFragmentInternal @Inject constructor(
|
||||
).forEach { getConfiguration ->
|
||||
project.addExtendsFromRelation(getConfiguration(this), getConfiguration(other.get())) // todo eager instantiation; fix?
|
||||
}
|
||||
|
||||
project.whenEvaluated {
|
||||
kotlinGradleFragmentConsistencyChecker.runAllChecks(this@KotlinGradleFragmentInternal, other.get())
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkCanRefine(other: KotlinGradleFragment) {
|
||||
@@ -99,3 +105,13 @@ internal fun KotlinModuleFragment.disambiguateName(simpleName: String) =
|
||||
|
||||
val KotlinGradleFragment.refinesClosure: Set<KotlinGradleFragment>
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
internal val defaultSourceSetLanguageSettingsChecker =
|
||||
FragmentConsistencyChecker<KotlinSourceSet>(
|
||||
unitsName = "source sets",
|
||||
name = { name },
|
||||
checks = FragmentConsistencyChecks<KotlinSourceSet>(
|
||||
unitName = "source set",
|
||||
languageSettings = { languageSettings }
|
||||
).allChecks
|
||||
)
|
||||
|
||||
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
|
||||
// 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