[Gradle] Migrate checks for non-disambiguated targets

This commit is contained in:
Dmitry Savvinov
2023-03-27 16:22:38 +02:00
parent 1a5cbe1245
commit 4bef185ccb
7 changed files with 92 additions and 78 deletions
@@ -48,46 +48,6 @@ class MppDiagnosticsIt : KGPBaseTest() {
}
}
@GradleTest
@TestMetadata("new-mpp-lib-and-app/sample-lib-gradle-kotlin-dsl")
fun testReportTargetsOfTheSamplePlatformAndWithTheSameAttributes(gradleVersion: GradleVersion) {
project("new-mpp-lib-and-app/sample-lib-gradle-kotlin-dsl", gradleVersion) {
// A hack to make project compatible with GradleTestKit infrastructure
buildGradleKts.replaceText(
"""id("org.jetbrains.kotlin.multiplatform").version("<pluginMarkerVersion>")""",
"""id("org.jetbrains.kotlin.multiplatform")""",
)
buildGradleKts.appendText("""
val distinguishAttribute = Attribute.of(String::class.java)
fun org.jetbrains.kotlin.gradle.plugin.KotlinTarget.applyDistinguishingAttributeIfSet(value: String) {
if (project.properties.containsKey("applyDistinguishingAttribute")) {
attributes {
attribute(distinguishAttribute, value)
}
}
}
kotlin {
jvm("jvm2") { applyDistinguishingAttributeIfSet("jvm2") }
linuxArm64("linuxArm_A") { applyDistinguishingAttributeIfSet("linuxArm_A") }
linuxArm64("linuxArm_B") { applyDistinguishingAttributeIfSet("linuxArm_B") }
}
""".trimIndent())
val warningMessage = """w: The following targets are not distinguishable:
| * 'jvm2', 'jvm6'
| * 'linuxArm_A', 'linuxArm_B'""".trimMargin()
build {
assertOutputContains(warningMessage)
}
build(buildOptions = defaultBuildOptions.copy(freeArgs = listOf("-PapplyDistinguishingAttribute"))) {
assertOutputDoesNotContain(warningMessage)
}
}
}
private fun TestProject.checkDeprecatedProperties(isDeprecationExpected: Boolean) {
build {
if (isDeprecationExpected)
@@ -99,6 +99,7 @@ internal interface KotlinGradleProjectChecker {
MissingNativeStdlibChecker,
UnusedSourceSetsChecker,
AndroidSourceSetLayoutV1SourceSetsNotFoundChecker,
TargetsWithAmbiguousConsumableConfigurationsChecker
)
}
}
@@ -186,4 +186,15 @@ object KotlinToolingDiagnostics {
""".trimIndent()
)
}
object TargetsNeedDisambiguation : ToolingDiagnosticFactory(WARNING) {
operator fun invoke(targetGroupsRendered: String) = build(
"""
|The following targets are not distinguishable:
|$targetGroupsRendered
|Use an additional attribute to disambiguate them
|See https://kotlinlang.org/docs/multiplatform-set-up-targets.html#distinguish-several-targets-for-one-platform for more details
""".trimMargin()
)
}
}
@@ -0,0 +1,44 @@
/*
* 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.gradle.plugin.diagnostics.checkers
import org.jetbrains.kotlin.gradle.plugin.KotlinPluginLifecycle
import org.jetbrains.kotlin.gradle.plugin.await
import org.jetbrains.kotlin.gradle.plugin.diagnostics.*
import org.jetbrains.kotlin.gradle.plugin.diagnostics.KotlinGradleProjectChecker
import org.jetbrains.kotlin.gradle.plugin.diagnostics.KotlinGradleProjectCheckerContext
import org.jetbrains.kotlin.gradle.utils.toMap
/**
* Report scenario when there are two targets of the same platform without distinguishing attribute
*/
internal object TargetsWithAmbiguousConsumableConfigurationsChecker : KotlinGradleProjectChecker {
override suspend fun KotlinGradleProjectCheckerContext.runChecks(collector: KotlinToolingDiagnosticsCollector) {
// Need all configurations to be created/set up
KotlinPluginLifecycle.Stage.ReadyForExecution.await()
val allTargets = multiplatformExtension?.targets ?: return
val nonDistinguishableTargets = allTargets
.mapNotNull { target ->
val configuration = project.configurations.findByName(target.apiElementsConfigurationName) ?: return@mapNotNull null
target.name to configuration
}
.groupBy { (_, consumableConfiguration) -> consumableConfiguration.attributes.toMap() }
.values
.filter { targetGroup -> targetGroup.size > 1 }
.map { targetGroup -> targetGroup.map { (targetName, _) -> targetName } }
if (nonDistinguishableTargets.isEmpty()) return
val nonUniqueTargetsString = nonDistinguishableTargets.joinToString(separator = "\n") { targets ->
val targetsListString = targets.joinToString { targetName -> "'$targetName'" }
" * $targetsListString"
}
collector.reportOncePerGradleProject(project, KotlinToolingDiagnostics.TargetsNeedDisambiguation(nonUniqueTargetsString))
}
}
@@ -6,7 +6,6 @@
package org.jetbrains.kotlin.gradle.plugin.mpp.internal
import org.gradle.api.Project
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.Companion.kotlinPropertiesProvider
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_MPP_ENABLE_COMPATIBILITY_METADATA_VARIANT
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_MPP_ENABLE_GRANULAR_SOURCE_SETS_METADATA
@@ -15,54 +14,17 @@ import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLI
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_NATIVE_DEPENDENCY_PROPAGATION
import org.jetbrains.kotlin.gradle.plugin.diagnostics.KotlinToolingDiagnostics
import org.jetbrains.kotlin.gradle.plugin.diagnostics.kotlinToolingDiagnosticsCollector
import org.jetbrains.kotlin.gradle.utils.SingleWarningPerBuild
import org.jetbrains.kotlin.gradle.utils.runProjectConfigurationHealthCheckWhenEvaluated
import org.jetbrains.kotlin.gradle.utils.toMap
import org.jetbrains.kotlin.tooling.core.UnsafeApi
internal fun runDeprecationDiagnostics(project: Project) {
checkAndReportDeprecatedMppProperties(project)
handleHierarchicalStructureFlagsMigration(project)
project.runProjectConfigurationHealthCheckWhenEvaluated {
reportTargetsWithNonUniqueConsumableConfigurations(project)
checkAndReportPreHmppDependenciesUsage(project)
}
}
/**
* Report scenario when there are two targets of the same platform without distinguishing attribute
*/
private fun reportTargetsWithNonUniqueConsumableConfigurations(project: Project) {
// Wrap diagnostic check again to afterEvaluate to make sure that it gets executed the last
// Since Multiplatform plugin updates consumable configurations in afterEvaluate blocks
project.afterEvaluate {
val allTargets = project.multiplatformExtension.targets
val nonDistinguishableTargets = allTargets
.mapNotNull { target ->
val configuration = project.configurations.findByName(target.apiElementsConfigurationName) ?: return@mapNotNull null
target.name to configuration
}
.groupBy { (_, consumableConfiguration) -> consumableConfiguration.attributes.toMap() }
.values
.filter { targetGroup -> targetGroup.size > 1 }
.map { targetGroup -> targetGroup.map { (targetName, _) -> targetName } }
if (nonDistinguishableTargets.isEmpty()) return@afterEvaluate
val nonUniqueTargetsString = nonDistinguishableTargets.joinToString(separator = "\n") { targets ->
val targetsListString = targets.joinToString { targetName -> "'$targetName'" }
" * $targetsListString"
}
SingleWarningPerBuild.show(
project, "w: The following targets are not distinguishable:\n$nonUniqueTargetsString" +
"\nUse distinguish attribute. " +
"See https://kotlinlang.org/docs/multiplatform-set-up-targets.html#distinguish-several-targets-for-one-platform for more details."
)
}
}
/**
* Declared properties have to be captured during plugin application phase before the HMPP migration util sets them.
* Warnings have to be reported only for successfully evaluated projects without errors.
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.gradle.unitTests.diagnosticsTests
import org.gradle.api.attributes.Attribute
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJvmWithJavaTargetPreset
import org.jetbrains.kotlin.gradle.util.*
import org.junit.Test
@@ -122,4 +123,33 @@ class MppDiagnosticsFunctionalTest {
}
}
}
@Test
fun targetsDisambiguation() {
checkDiagnosticsWithMppProject("targetsDisambiguation") {
kotlin {
val distinguishAttribute = Attribute.of(String::class.java)
// Simple case: no disambiguation -> warning reported
linuxArm64("linuxArm_A") { }
linuxArm64("linuxArm_B") { }
// Some targets are disambiguated and some are not -> warning reported, only on targets without attribute
jvm("jvm_A") { attributes { attribute(distinguishAttribute, "jvm1") } }
jvm("jvm_B") { attributes { attribute(distinguishAttribute, "jvm2") } }
jvm("jvm_C")
jvm("jvm_D")
// Targets formally have attribute, but values are the same -> warning reported
js("js_A") {
browser()
attributes { attribute(distinguishAttribute, "js") }
}
js("js_B") {
browser()
attributes { attribute(distinguishAttribute, "js") }
}
}
}
}
}
@@ -0,0 +1,6 @@
[TargetsNeedDisambiguation | WARNING] The following targets are not distinguishable:
* 'js_A', 'js_B'
* 'jvm_C', 'jvm_D'
* 'linuxArm_A', 'linuxArm_B'
Use an additional attribute to disambiguate them
See https://kotlinlang.org/docs/multiplatform-set-up-targets.html#distinguish-several-targets-for-one-platform for more details