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 53d19f0cdc8..b6731ad202d 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 @@ -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("")""", - """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) diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/KotlinGradleProjectChecker.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/KotlinGradleProjectChecker.kt index 18c6174d593..fb931060cfe 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/KotlinGradleProjectChecker.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/KotlinGradleProjectChecker.kt @@ -99,6 +99,7 @@ internal interface KotlinGradleProjectChecker { MissingNativeStdlibChecker, UnusedSourceSetsChecker, AndroidSourceSetLayoutV1SourceSetsNotFoundChecker, + TargetsWithAmbiguousConsumableConfigurationsChecker ) } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/KotlinToolingDiagnostics.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/KotlinToolingDiagnostics.kt index ebdfda29d1b..7daab5cf789 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/KotlinToolingDiagnostics.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/KotlinToolingDiagnostics.kt @@ -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() + ) + } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/checkers/TargetsWithAmbiguousConsumableConfigurationsChecker.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/checkers/TargetsWithAmbiguousConsumableConfigurationsChecker.kt new file mode 100644 index 00000000000..76a7655b7f1 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/checkers/TargetsWithAmbiguousConsumableConfigurationsChecker.kt @@ -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)) + } +} 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 c8a7899b078..7d07b0f2311 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 @@ -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. diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/diagnosticsTests/MppDiagnosticsFunctionalTest.kt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/diagnosticsTests/MppDiagnosticsFunctionalTest.kt index 00115c640c0..aa8d88f93b4 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/diagnosticsTests/MppDiagnosticsFunctionalTest.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/diagnosticsTests/MppDiagnosticsFunctionalTest.kt @@ -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") } + } + } + } + } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/resources/expectedDiagnostics/targetsDisambiguation.txt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/resources/expectedDiagnostics/targetsDisambiguation.txt new file mode 100644 index 00000000000..52306f1d3a6 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/resources/expectedDiagnostics/targetsDisambiguation.txt @@ -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