From 56de13fe2f77ce8485494c745f3d23073567c957 Mon Sep 17 00:00:00 2001 From: "sebastian.sellmair" Date: Mon, 7 Feb 2022 17:09:49 +0100 Subject: [PATCH] [Gradle][MPP] Show (potential) warning on disabled cinterop commonization ^KT-51176 Verification Pending --- ...isabledCInteropCommonizationWarningTest.kt | 122 ++++++++++++++++++ .../gradle/MultiplatformExtensionTest.kt | 4 +- ...latformProjectConfigurationHealthChecks.kt | 37 ++++++ .../gradle/plugin/KotlinPluginWrapper.kt | 1 + .../kotlin/gradle/plugin/KotlinProperties.kt | 5 +- 5 files changed, 166 insertions(+), 3 deletions(-) create mode 100644 libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/DisabledCInteropCommonizationWarningTest.kt diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/DisabledCInteropCommonizationWarningTest.kt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/DisabledCInteropCommonizationWarningTest.kt new file mode 100644 index 00000000000..c315ae7a53c --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/DisabledCInteropCommonizationWarningTest.kt @@ -0,0 +1,122 @@ +/* + * Copyright 2010-2022 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. + */ + +@file:Suppress("FunctionName") + +package org.jetbrains.kotlin.gradle + +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.mpp.KotlinNativeTarget +import org.jetbrains.kotlin.gradle.plugin.runDisabledCInteropCommonizationOnHmppProjectConfigurationHealthCheck +import kotlin.test.Test +import kotlin.test.assertNotNull +import kotlin.test.assertNull +import kotlin.test.assertTrue + +class DisabledCInteropCommonizationWarningTest { + + private val project by lazy { ProjectBuilder.builder().build() as ProjectInternal } + + @Test + fun `test warning shows affected source sets and compilations`() { + project.enableCInteropCommonization(false) + project.setupNativeTargetsWithCInterops() + project.evaluate() + + val warningMessage = project.runHealthCheckAndGetWarning() + + assertNotNull(warningMessage, "Expected a warning message to be logged") + + assertTrue( + "[commonMain, nativeMain]" in warningMessage, + "Expected source sets being mentioned in the warning message. Found\n\n$warningMessage" + ) + + assertTrue( + "[metadata/commonMain, metadata/nativeMain]" in warningMessage, + "Expected compilations being mentioned in the warning message. Found\n\n$warningMessage" + ) + } + + @Test + fun `test warning is not shown when explicitly ignored`() { + project.enableCInteropCommonization(false) + project.setupNativeTargetsWithCInterops() + project.propertiesExtension.set("$KOTLIN_MPP_ENABLE_CINTEROP_COMMONIZATION.nowarn", "true") + project.evaluate() + + assertNull( + project.runHealthCheckAndGetWarning(), + "Expected no warning message shown when explictily ignored" + ) + } + + @Test + fun `test warning is not shown when cinterop commonization is enabled`() { + project.enableCInteropCommonization(true) + project.setupNativeTargetsWithCInterops() + project.evaluate() + assertNull( + project.runHealthCheckAndGetWarning(), + "Expected no error message shown when cinterop commonization is enabled" + ) + } + + @Test + fun `test warning is not shown when hierarchical structure is disabled`() { + project.enableCInteropCommonization(false) + project.enableHierarchicalStructureByDefault(false) + project.setupNativeTargetsWithCInterops() + project.evaluate() + assertNull( + project.runHealthCheckAndGetWarning(), + "Expected no error message shown when hmpp is disabled" + ) + } + + @Test + fun `test warning is not shown when no cinterops are defined`() { + project.enableCInteropCommonization(false) + project.setupNativeTargets() + project.evaluate() + assertNull( + project.runHealthCheckAndGetWarning(), + "Expected no error message shown when no cinterops are defined" + ) + } +} + +private fun Project.setupNativeTargets(): List { + val kotlin = applyMultiplatformPlugin() + val linuxX64 = kotlin.linuxX64() + val linuxArm64 = kotlin.linuxArm64() + + val commonMain = kotlin.sourceSets.getByName("commonMain") + val nativeMain = kotlin.sourceSets.create("nativeMain") + val linuxX64Main = kotlin.sourceSets.getByName("linuxX64Main") + val linuxArm64Main = kotlin.sourceSets.getByName("linuxArm64Main") + + nativeMain.dependsOn(commonMain) + linuxX64Main.dependsOn(nativeMain) + linuxArm64Main.dependsOn(nativeMain) + + return listOf(linuxX64, linuxArm64) +} + +private fun Project.setupNativeTargetsWithCInterops(): List { + return setupNativeTargets().onEach { target -> + target.compilations.getByName("main").cinterops.create("dummy1") + target.compilations.getByName("main").cinterops.create("dummy2") + } +} + +private fun Project.runHealthCheckAndGetWarning(): String? { + var message: String? = null + runDisabledCInteropCommonizationOnHmppProjectConfigurationHealthCheck { message = it } + return message +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/MultiplatformExtensionTest.kt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/MultiplatformExtensionTest.kt index 965d8973144..ee0ef40fa5d 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/MultiplatformExtensionTest.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/MultiplatformExtensionTest.kt @@ -98,6 +98,6 @@ fun Project.enableCInteropCommonization(enabled: Boolean = true) { propertiesExtension.set(KOTLIN_MPP_ENABLE_CINTEROP_COMMONIZATION, enabled.toString()) } -fun Project.enableHierarchicalStructureByDefault() { - propertiesExtension.set(KOTLIN_MPP_HIERARCHICAL_STRUCTURE_BY_DEFAULT, "true") +fun Project.enableHierarchicalStructureByDefault(enabled: Boolean = true) { + propertiesExtension.set(KOTLIN_MPP_HIERARCHICAL_STRUCTURE_BY_DEFAULT, enabled.toString()) } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinMultiplatformProjectConfigurationHealthChecks.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinMultiplatformProjectConfigurationHealthChecks.kt index 90d6ae62096..e3cdd92e9f8 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinMultiplatformProjectConfigurationHealthChecks.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinMultiplatformProjectConfigurationHealthChecks.kt @@ -8,9 +8,14 @@ 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.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.androidPluginIds import org.jetbrains.kotlin.gradle.utils.runProjectConfigurationHealthCheck @@ -62,3 +67,35 @@ internal fun Project.runMissingAndroidTargetProjectConfigurationHealthCheck( """.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 affectedCompilations = multiplatformExtension.targets.flatMap { it.compilations } + .filterIsInstance() + .filter { compilation -> CInteropCommonizerDependent.from(compilation)?.interops.orEmpty().isNotEmpty() } + + /* CInterop commonizer would not affect the project: No compilation that would actually benefit */ + if (affectedCompilations.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 + + The following source sets are affected: + ${affectedCompilations.map { it.defaultSourceSetName }.sorted().joinToString(", ", "[", "]")} + + The following compilations are affected: + ${affectedCompilations.map { "${it.target.name}/${it.compilationName}" }.sorted().joinToString(", ", "[", "]")} + """.trimIndent() + ) + } +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt index 671b0c6ee23..95918563c67 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt @@ -250,6 +250,7 @@ open class KotlinMultiplatformPluginWrapper : KotlinBasePluginWrapper() { override fun whenBuildEvaluated(project: Project) { project.runMissingAndroidTargetProjectConfigurationHealthCheck() project.runMissingKotlinTargetsProjectConfigurationHealthCheck() + project.runDisabledCInteropCommonizationOnHmppProjectConfigurationHealthCheck() } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinProperties.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinProperties.kt index c09dd7c000a..621c6a68727 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinProperties.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinProperties.kt @@ -15,9 +15,9 @@ import org.jetbrains.kotlin.gradle.plugin.Kotlin2JsPlugin.Companion.NOWARN_2JS_F import org.jetbrains.kotlin.gradle.plugin.KotlinJsCompilerType.Companion.jsCompilerProperty import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_MPP_ENABLE_CINTEROP_COMMONIZATION import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_MPP_ENABLE_GRANULAR_SOURCE_SETS_METADATA +import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_MPP_ENABLE_OPTIMISTIC_NUMBER_COMMONIZATION import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_MPP_HIERARCHICAL_STRUCTURE_BY_DEFAULT import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_MPP_HIERARCHICAL_STRUCTURE_SUPPORT -import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_MPP_ENABLE_OPTIMISTIC_NUMBER_COMMONIZATION import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_NATIVE_DEPENDENCY_PROPAGATION import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinMultiplatformPlugin import org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatsService @@ -226,6 +226,9 @@ internal class PropertiesProvider private constructor(private val project: Proje val ignoreAbsentAndroidMultiplatformTarget: Boolean get() = booleanProperty("kotlin.mpp.absentAndroidTarget.nowarn") ?: false + val ignoreDisabledCInteropCommonization: Boolean + get() = booleanProperty("$KOTLIN_MPP_ENABLE_CINTEROP_COMMONIZATION.nowarn") ?: false + val ignoreIncorrectNativeDependencies: Boolean? get() = booleanProperty(KOTLIN_NATIVE_IGNORE_INCORRECT_DEPENDENCIES)