diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/mpp/MppDiagnosticsIt.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/mpp/MppDiagnosticsIt.kt index c50cbc143e9..f87c20e0584 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/mpp/MppDiagnosticsIt.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/mpp/MppDiagnosticsIt.kt @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.gradle.mpp import org.gradle.util.GradleVersion import org.jetbrains.kotlin.gradle.testbase.* +import org.jetbrains.kotlin.gradle.util.replaceText import kotlin.io.path.appendText import kotlin.io.path.writeText import kotlin.test.assertFalse @@ -50,6 +51,45 @@ class MppDiagnosticsIt : KGPBaseTest() { } } + @GradleTest + 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("")""", + """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 { val assert: (Boolean, String) -> Unit = if (isDeprecationExpected) ::assertTrue else ::assertFalse diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/internal/deprecationDiagnostics.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/internal/deprecationDiagnostics.kt index 94d5896a726..017954bdc47 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/internal/deprecationDiagnostics.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/internal/deprecationDiagnostics.kt @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.gradle.plugin.mpp.internal import org.gradle.api.Project import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension +import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension import org.jetbrains.kotlin.gradle.dsl.multiplatformExtensionOrNull import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet.Companion.COMMON_MAIN_SOURCE_SET_NAME import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.Companion.kotlinPropertiesProvider @@ -19,6 +20,7 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget import org.jetbrains.kotlin.gradle.plugin.sources.internal 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.konan.target.KonanTarget import org.jetbrains.kotlin.tooling.core.UnsafeApi @@ -28,6 +30,7 @@ internal fun runDeprecationDiagnostics(project: Project) { checkAndReportDeprecatedSourceSetsLayouts(project) project.runProjectConfigurationHealthCheckWhenEvaluated { checkAndReportDeprecatedNativeTargets(project) + reportTargetsWithNonUniqueConsumableConfigurations(project) } } @@ -41,6 +44,35 @@ private fun checkAndReportDeprecatedNativeTargets(project: 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 + .groupBy { target -> project.configurations.getByName(target.apiElementsConfigurationName).attributes.toMap() } + .values + .filter { targetGroup -> targetGroup.size > 1 } + + if (nonDistinguishableTargets.isEmpty()) return@afterEvaluate + + val nonUniqueTargetsString = nonDistinguishableTargets.joinToString(separator = "\n") { targets -> + val targetsListString = targets.joinToString { "'${it.name}'" } + " * $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. diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/gradleAttributesContainerUtils.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/gradleAttributesContainerUtils.kt new file mode 100644 index 00000000000..c67fe65ca3c --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/gradleAttributesContainerUtils.kt @@ -0,0 +1,22 @@ +/* + * 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.utils + +import org.gradle.api.attributes.Attribute +import org.gradle.api.attributes.AttributeContainer + +/** + * KGP's internal analog of [org.gradle.api.internal.attributes.AttributeContainerInternal.asMap] + * Can be used to compare attributes + */ +internal fun AttributeContainer.toMap(): Map, Any?> { + val result = mutableMapOf, Any?>() + for (key in keySet()) { + result[key] = getAttribute(key) + } + + return result +} \ No newline at end of file