From 5a9aa43aa5fcefcd29c8e895ec7b2110b5ef20fb Mon Sep 17 00:00:00 2001 From: Dmitry Savvinov Date: Wed, 19 Apr 2023 17:36:28 +0200 Subject: [PATCH] [Gradle] Migrate disabled cinterops commonization warning --- ...latformProjectConfigurationHealthChecks.kt | 84 ------------------- .../gradle/plugin/KotlinPluginWrapper.kt | 4 - .../diagnostics/KotlinGradleProjectChecker.kt | 3 +- .../diagnostics/KotlinToolingDiagnostics.kt | 23 ++++- ...nteropCommonizationInHmppProjectChecker.kt | 54 ++++++++++++ ...isabledCInteropCommonizationWarningTest.kt | 30 +++---- 6 files changed, 94 insertions(+), 104 deletions(-) delete mode 100644 libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinMultiplatformProjectConfigurationHealthChecks.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/checkers/DisabledCinteropCommonizationInHmppProjectChecker.kt diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinMultiplatformProjectConfigurationHealthChecks.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinMultiplatformProjectConfigurationHealthChecks.kt deleted file mode 100644 index f6033dfdf16..00000000000 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinMultiplatformProjectConfigurationHealthChecks.kt +++ /dev/null @@ -1,84 +0,0 @@ -/* - * 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 - -import org.gradle.api.Project -import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension -import org.jetbrains.kotlin.gradle.dsl.kotlinExtension -import org.jetbrains.kotlin.gradle.dsl.multiplatformExtensionOrNull -import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.Companion.kotlinPropertiesProvider -import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_MPP_ENABLE_CINTEROP_COMMONIZATION -import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinMetadataTarget -import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinSharedNativeCompilation -import org.jetbrains.kotlin.gradle.targets.android.findAndroidTarget -import org.jetbrains.kotlin.gradle.targets.native.internal.CInteropCommonizerDependent -import org.jetbrains.kotlin.gradle.targets.native.internal.from -import org.jetbrains.kotlin.gradle.targets.native.internal.isAllowCommonizer -import org.jetbrains.kotlin.gradle.utils.findAppliedAndroidPluginIdOrNull -import org.jetbrains.kotlin.gradle.utils.future -import org.jetbrains.kotlin.gradle.utils.runProjectConfigurationHealthCheck - -private class KotlinMultiplatformProjectConfigurationException(message: String) : Exception(message) - -internal fun Project.runMissingKotlinTargetsProjectConfigurationHealthCheck() = project.runProjectConfigurationHealthCheck { - val isNoTargetsInitialized = (project.kotlinExtension as KotlinMultiplatformExtension) - .targets - .none { it !is KotlinMetadataTarget } - - if (isNoTargetsInitialized) { - throw KotlinMultiplatformProjectConfigurationException( - """ - Please initialize at least one Kotlin target in '${project.name} (${project.path})'. - Read more https://kotlinlang.org/docs/reference/building-mpp-with-gradle.html#setting-up-targets - """.trimIndent() - ) - } -} - -internal fun Project.runDisabledCInteropCommonizationOnHmppProjectConfigurationHealthCheck( - warningLogger: (warningMessage: String) -> Unit = project.logger::warn -) { - if (kotlinPropertiesProvider.ignoreDisabledCInteropCommonization) return - if (isAllowCommonizer() && !kotlinPropertiesProvider.enableCInteropCommonization) { - val multiplatformExtension = multiplatformExtensionOrNull ?: return - - val sharedCompilationsWithInterops = multiplatformExtension.targets.flatMap { it.compilations } - .filterIsInstance() - .mapNotNull { compilation -> - val cinteropDependent = future { CInteropCommonizerDependent.from(compilation) }.getOrThrow() ?: return@mapNotNull null - compilation to cinteropDependent - } - .toMap() - - val affectedCompilations = sharedCompilationsWithInterops.keys - val affectedCInterops = sharedCompilationsWithInterops.values.flatMap { it.interops }.toSet() - - - /* CInterop commonizer would not affect the project: No compilation that would actually benefit */ - if (affectedCompilations.isEmpty()) return - if (affectedCInterops.isEmpty()) return - - warningLogger( - """ - [WARNING] The project is using Kotlin Multiplatform with hierarchical structure and disabled 'cinterop commonization' - See: https://kotlinlang.org/docs/mpp-share-on-platforms.html#use-native-libraries-in-the-hierarchical-structure - - 'cinterop commonization' can be enabled in your 'gradle.properties' - kotlin.mpp.enableCInteropCommonization=true - - To hide this message, add to your 'gradle.properties' - $KOTLIN_MPP_ENABLE_CINTEROP_COMMONIZATION.nowarn=true - - The following source sets are affected: - ${affectedCompilations.map { it.defaultSourceSetName }.sorted().joinToString(", ", "[", "]")} - - The following cinterops are affected: - ${affectedCInterops.map { it.toString() }.sorted().joinToString(", ", "[", "]")} - - """.trimIndent() - ) - } -} diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt index da3c4f475a5..a9a23df653c 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt @@ -385,10 +385,6 @@ abstract class AbstractKotlinMultiplatformPluginWrapper : KotlinBasePluginWrappe override val projectExtensionClass: KClass get() = KotlinMultiplatformExtension::class - - override fun whenBuildEvaluated(project: Project) { - project.runDisabledCInteropCommonizationOnHmppProjectConfigurationHealthCheck() - } } abstract class AbstractKotlinPm20PluginWrapper( 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 bc65de260e6..d57cef3d6ef 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 @@ -101,7 +101,8 @@ internal interface KotlinGradleProjectChecker { AndroidSourceSetLayoutV1SourceSetsNotFoundChecker, TargetsWithAmbiguousConsumableConfigurationsChecker, AndroidPluginWithoutAndroidTargetChecker, - NoKotlinTargetsDeclaredChecker + NoKotlinTargetsDeclaredChecker, + DisabledCinteropCommonizationInHmppProjectChecker ) } } 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 b12b9faaa92..d56b9de2ea7 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 @@ -213,7 +213,7 @@ object KotlinToolingDiagnostics { object Kotlin12XMppDeprecation : ToolingDiagnosticFactory(WARNING) { operator fun invoke() = build( """ - The 'org.jetbrains.kotlin.platform.*' plugins are deprecated and will no longer be available in Kotlin 1.4. + The 'org.jetbrains.kotlin.platform.*' plugins are deprecated and are no longer available since Kotlin 1.4. Please migrate the project to the 'org.jetbrains.kotlin.multiplatform' plugin. See: https://kotlinlang.org/docs/reference/building-mpp-with-gradle.html """.trimIndent() @@ -248,4 +248,25 @@ object KotlinToolingDiagnostics { """.trimIndent() ) } + + object DisabledCinteropsCommonizationInHmppProject : ToolingDiagnosticFactory(WARNING) { + operator fun invoke(affectedSourceSetsString: String, affectedCinteropsString: String) = build( + """ + The project is using Kotlin Multiplatform with hierarchical structure and disabled 'cinterop commonization' + See: https://kotlinlang.org/docs/mpp-share-on-platforms.html#use-native-libraries-in-the-hierarchical-structure + + 'cinterop commonization' can be enabled in your 'gradle.properties' + kotlin.mpp.enableCInteropCommonization=true + + To hide this message, add to your 'gradle.properties' + ${PropertiesProvider.PropertyNames.KOTLIN_MPP_ENABLE_CINTEROP_COMMONIZATION}.nowarn=true + + The following source sets are affected: + $affectedSourceSetsString + + The following cinterops are affected: + $affectedCinteropsString + """.trimIndent() + ) + } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/checkers/DisabledCinteropCommonizationInHmppProjectChecker.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/checkers/DisabledCinteropCommonizationInHmppProjectChecker.kt new file mode 100644 index 00000000000..33d5ad8b115 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/checkers/DisabledCinteropCommonizationInHmppProjectChecker.kt @@ -0,0 +1,54 @@ +/* + * 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.KotlinGradleProjectChecker +import org.jetbrains.kotlin.gradle.plugin.diagnostics.KotlinGradleProjectCheckerContext +import org.jetbrains.kotlin.gradle.plugin.diagnostics.KotlinToolingDiagnostics +import org.jetbrains.kotlin.gradle.plugin.diagnostics.KotlinToolingDiagnosticsCollector +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinSharedNativeCompilation +import org.jetbrains.kotlin.gradle.targets.native.internal.CInteropCommonizerDependent +import org.jetbrains.kotlin.gradle.targets.native.internal.from +import org.jetbrains.kotlin.gradle.targets.native.internal.isAllowCommonizer + +internal object DisabledCinteropCommonizationInHmppProjectChecker : KotlinGradleProjectChecker { + override suspend fun KotlinGradleProjectCheckerContext.runChecks(collector: KotlinToolingDiagnosticsCollector) { + // `isAllowCommonizer` is not lifecycle-aware, but requires afterEvaluate-phase, so have to await manually + KotlinPluginLifecycle.Stage.ReadyForExecution.await() + + if (multiplatformExtension == null + || !project.isAllowCommonizer() + || kotlinPropertiesProvider.enableCInteropCommonization + || kotlinPropertiesProvider.ignoreDisabledCInteropCommonization + ) return + + val sharedCompilationsWithInterops = multiplatformExtension.awaitTargets() + .flatMap { it.compilations } + .filterIsInstance() + .mapNotNull { compilation -> + val cinteropDependent = CInteropCommonizerDependent.from(compilation) ?: return@mapNotNull null + compilation to cinteropDependent + } + .toMap() + + val affectedCompilations = sharedCompilationsWithInterops.keys + val affectedCInterops = sharedCompilationsWithInterops.values.flatMap { it.interops }.toSet() + + /* CInterop commonizer would not affect the project: No compilation that would actually benefit */ + if (affectedCompilations.isEmpty()) return + if (affectedCInterops.isEmpty()) return + + val affectedSourceSetsString = affectedCompilations.map { it.defaultSourceSet.name }.sorted().joinToString(", ", "[", "]") + val affectedCinteropsString = affectedCInterops.map { it.toString() }.sorted().joinToString(", ", "[", "]") + + collector.reportOncePerGradleProject( + project, + KotlinToolingDiagnostics.DisabledCinteropsCommonizationInHmppProject(affectedSourceSetsString, affectedCinteropsString) + ) + } +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/DisabledCInteropCommonizationWarningTest.kt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/DisabledCInteropCommonizationWarningTest.kt index 28bf59aec1c..7ca420c66a3 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/DisabledCInteropCommonizationWarningTest.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/DisabledCInteropCommonizationWarningTest.kt @@ -11,16 +11,14 @@ import org.gradle.api.Project import org.gradle.api.internal.project.ProjectInternal import org.gradle.testfixtures.ProjectBuilder import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_MPP_ENABLE_CINTEROP_COMMONIZATION +import org.jetbrains.kotlin.gradle.plugin.diagnostics.KotlinToolingDiagnostics +import org.jetbrains.kotlin.gradle.plugin.diagnostics.kotlinToolingDiagnosticsCollector import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget -import org.jetbrains.kotlin.gradle.plugin.runDisabledCInteropCommonizationOnHmppProjectConfigurationHealthCheck import org.jetbrains.kotlin.gradle.util.applyMultiplatformPlugin import org.jetbrains.kotlin.gradle.util.enableCInteropCommonization import org.jetbrains.kotlin.gradle.util.enableHierarchicalStructureByDefault import org.jetbrains.kotlin.gradle.util.propertiesExtension -import kotlin.test.Test -import kotlin.test.assertNotNull -import kotlin.test.assertNull -import kotlin.test.assertTrue +import kotlin.test.* class DisabledCInteropCommonizationWarningTest { @@ -32,7 +30,7 @@ class DisabledCInteropCommonizationWarningTest { project.setupNativeTargetsWithCInterops() project.evaluate() - val warningMessage = project.runHealthCheckAndGetWarning() + val warningMessage = project.getWarningMessage() assertNotNull(warningMessage, "Expected a warning message to be logged") @@ -65,7 +63,7 @@ class DisabledCInteropCommonizationWarningTest { project.evaluate() assertNull( - project.runHealthCheckAndGetWarning(), + project.getWarningMessage(), "Expected no warning message shown when explicitly ignored" ) } @@ -76,7 +74,7 @@ class DisabledCInteropCommonizationWarningTest { project.setupNativeTargetsWithCInterops() project.evaluate() assertNull( - project.runHealthCheckAndGetWarning(), + project.getWarningMessage(), "Expected no error message shown when cinterop commonization is enabled" ) } @@ -88,7 +86,7 @@ class DisabledCInteropCommonizationWarningTest { project.setupNativeTargetsWithCInterops() project.evaluate() assertNull( - project.runHealthCheckAndGetWarning(), + project.getWarningMessage(), "Expected no error message shown when hmpp is disabled" ) } @@ -99,7 +97,7 @@ class DisabledCInteropCommonizationWarningTest { project.setupNativeTargets() project.evaluate() assertNull( - project.runHealthCheckAndGetWarning(), + project.getWarningMessage(), "Expected no error message shown when no cinterops are defined" ) } @@ -129,8 +127,12 @@ private fun Project.setupNativeTargetsWithCInterops(): List } } -private fun Project.runHealthCheckAndGetWarning(): String? { - var message: String? = null - runDisabledCInteropCommonizationOnHmppProjectConfigurationHealthCheck { message = it } - return message +private fun Project.getWarningMessage(): String? { + val diagnostics = project.kotlinToolingDiagnosticsCollector.getDiagnosticsForProject(this).filter { + it.id == KotlinToolingDiagnostics.DisabledCinteropsCommonizationInHmppProject.id + } + + if (diagnostics.size > 1) fail("Unexpected multiple diagnostics reported:\n\n" + diagnostics.joinToString(separator = "\n\n")) + + return diagnostics.singleOrNull()?.message }