diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/mpp/MppDeprecatedPropertiesIt.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/mpp/MppDeprecatedPropertiesIt.kt new file mode 100644 index 00000000000..e52b0cd7d20 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/mpp/MppDeprecatedPropertiesIt.kt @@ -0,0 +1,55 @@ +/* + * 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.mpp + +import org.gradle.util.GradleVersion +import org.jetbrains.kotlin.gradle.testbase.* +import kotlin.io.path.appendText +import kotlin.test.assertFalse +import kotlin.test.assertTrue + +@MppGradlePluginTests +class MppDeprecatedPropertiesIt : KGPBaseTest() { + @GradleTest + fun testDeprecations(gradleVersion: GradleVersion) { + project("mppDeprecatedProperties", gradleVersion) { + checkDeprecations(isDeprecationExpected = false) + + this.gradleProperties.appendText( + defaultFlags.entries.joinToString( + prefix = System.lineSeparator(), + postfix = System.lineSeparator(), + separator = System.lineSeparator(), + ) { (prop, value) -> "$prop=$value" } + ) + + checkDeprecations(isDeprecationExpected = true) + } + } + + private fun TestProject.checkDeprecations(isDeprecationExpected: Boolean) { + build { + val assert: (Boolean, String) -> Unit = if (isDeprecationExpected) ::assertTrue else ::assertFalse + val warnings = output.lines().filter { it.startsWith("w:") }.toSet() + + defaultFlags.keys.forEach { flag -> + assert( + warnings.any { warning -> Regex(".*$flag.*is deprecated.*").matches(warning) }, + "A deprecation warning for the '$flag' should have been reported", + ) + } + } + } + + private val defaultFlags: Map + get() = mapOf( + "kotlin.mpp.enableGranularSourceSetsMetadata" to "true", + "kotlin.mpp.enableCompatibilityMetadataVariant" to "false", + "kotlin.internal.mpp.hierarchicalStructureByDefault" to "true", + "kotlin.mpp.hierarchicalStructureSupport" to "true", + "kotlin.native.enableDependencyPropagation" to "false", + ) +} diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/mppDeprecatedProperties/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/mppDeprecatedProperties/build.gradle.kts new file mode 100644 index 00000000000..84ed38adf19 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/mppDeprecatedProperties/build.gradle.kts @@ -0,0 +1,7 @@ +plugins { + kotlin("multiplatform") +} + +kotlin { + jvm() +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/PropertiesProvider.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/PropertiesProvider.kt index 2c9a684d148..56c1fd50647 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/PropertiesProvider.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/PropertiesProvider.kt @@ -21,6 +21,7 @@ import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLI import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_MPP_ANDROID_SOURCE_SET_LAYOUT_VERSION import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_MPP_ANDROID_SOURCE_SET_LAYOUT_VERSION_1_NO_WARN import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_MPP_ENABLE_CINTEROP_COMMONIZATION +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 import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_MPP_ENABLE_INTRANSITIVE_METADATA_CONFIGURATION import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_MPP_ENABLE_OPTIMISTIC_NUMBER_COMMONIZATION @@ -216,7 +217,7 @@ internal class PropertiesProvider private constructor(private val project: Proje val enableCompatibilityMetadataVariant: Boolean get() { - return (booleanProperty("kotlin.mpp.enableCompatibilityMetadataVariant") ?: !mppHierarchicalStructureByDefault) + return (booleanProperty(KOTLIN_MPP_ENABLE_COMPATIBILITY_METADATA_VARIANT) ?: !mppHierarchicalStructureByDefault) } val enableKotlinToolingMetadataArtifact: Boolean @@ -522,7 +523,7 @@ internal class PropertiesProvider private constructor(private val project: Proje * 1. Project properties (-P, gradle.properties, etc...) * 2. `local.properties` */ - private fun property(propName: String): String? = + internal fun property(propName: String): String? = if (project.hasProperty(propName)) { project.property(propName) as? String } else { @@ -549,6 +550,7 @@ internal class PropertiesProvider private constructor(private val project: Proje const val KOTLIN_STDLIB_DEFAULT_DEPENDENCY = "kotlin.stdlib.default.dependency" const val KOTLIN_STDLIB_JDK_VARIANTS_VERSION_ALIGNMENT = "kotlin.stdlib.jdk.variants.version.alignment" const val KOTLIN_MPP_ENABLE_GRANULAR_SOURCE_SETS_METADATA = "kotlin.mpp.enableGranularSourceSetsMetadata" + const val KOTLIN_MPP_ENABLE_COMPATIBILITY_METADATA_VARIANT = "kotlin.mpp.enableCompatibilityMetadataVariant" const val KOTLIN_MPP_ENABLE_CINTEROP_COMMONIZATION = "kotlin.mpp.enableCInteropCommonization" const val KOTLIN_MPP_HIERARCHICAL_STRUCTURE_BY_DEFAULT = "kotlin.internal.mpp.hierarchicalStructureByDefault" const val KOTLIN_MPP_HIERARCHICAL_STRUCTURE_SUPPORT = "kotlin.mpp.hierarchicalStructureSupport" diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinMultiplatformPlugin.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinMultiplatformPlugin.kt index 01f137c34d0..5380718a87b 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinMultiplatformPlugin.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinMultiplatformPlugin.kt @@ -22,6 +22,7 @@ import org.jetbrains.kotlin.gradle.plugin.ide.kotlinIdeMultiplatformImport import org.jetbrains.kotlin.gradle.plugin.ide.locateOrRegisterIdeResolveDependenciesTask import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinMultiplatformPlugin.Companion.sourceSetFreeCompilerArgsPropertyName import org.jetbrains.kotlin.gradle.plugin.mpp.apple.addBuildListenerForXcode +import org.jetbrains.kotlin.gradle.plugin.mpp.internal.checkAndReportDeprecatedMppProperties import org.jetbrains.kotlin.gradle.plugin.mpp.internal.checkAndReportDeprecatedNativeTargets import org.jetbrains.kotlin.gradle.plugin.mpp.internal.handleHierarchicalStructureFlagsMigration import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.* @@ -49,6 +50,7 @@ class KotlinMultiplatformPlugin : Plugin { override fun apply(project: Project) { checkGradleCompatibility("the Kotlin Multiplatform plugin", GradleVersion.version("6.0")) + checkAndReportDeprecatedMppProperties(project) handleHierarchicalStructureFlagsMigration(project) checkAndReportDeprecatedNativeTargets(project) 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 1852881bd9a..d6fd72e5984 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,8 +7,16 @@ 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.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 +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_NATIVE_DEPENDENCY_PROPAGATION import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget +import org.jetbrains.kotlin.gradle.plugin.whenEvaluated import org.jetbrains.kotlin.gradle.utils.SingleWarningPerBuild +import org.jetbrains.kotlin.gradle.utils.hasSyncErrors import org.jetbrains.kotlin.gradle.utils.runProjectConfigurationHealthCheckWhenEvaluated import org.jetbrains.kotlin.konan.target.KonanTarget @@ -22,4 +30,35 @@ internal fun checkAndReportDeprecatedNativeTargets(project: Project) { "w: The following deprecated kotlin native targets were used in the project: ${usedDeprecatedTargets.joinToString { it.targetName }}" ) } -} \ No newline at end of file +} + +/** + * 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. + */ +internal fun checkAndReportDeprecatedMppProperties(project: Project) { + val projectProperties = project.kotlinPropertiesProvider + + val warnings = deprecatedMppProperties.mapNotNull { propertyName -> + projectProperties.property(propertyName)?.let { getMppDeprecationWarningMessageForProperty(propertyName) } + } + + project.whenEvaluated { + if (!project.hasSyncErrors()) { + warnings.forEach { message -> + SingleWarningPerBuild.show(project, message) + } + } + } +} + +internal val deprecatedMppProperties: List = listOf( + KOTLIN_MPP_ENABLE_COMPATIBILITY_METADATA_VARIANT, + KOTLIN_MPP_ENABLE_GRANULAR_SOURCE_SETS_METADATA, + KOTLIN_MPP_HIERARCHICAL_STRUCTURE_BY_DEFAULT, + KOTLIN_MPP_HIERARCHICAL_STRUCTURE_SUPPORT, + KOTLIN_NATIVE_DEPENDENCY_PROPAGATION, +) + +internal fun getMppDeprecationWarningMessageForProperty(property: String): String = + "w: The property '$property' is deprecated and is scheduled for removal in the stable Kotlin Multiplatform." diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/runProjectConfigurationHealthCheck.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/runProjectConfigurationHealthCheck.kt index 9d0453b17fb..0fe63d531e8 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/runProjectConfigurationHealthCheck.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/runProjectConfigurationHealthCheck.kt @@ -60,13 +60,16 @@ import org.jetbrains.kotlin.gradle.plugin.whenEvaluated */ internal inline fun Project.runProjectConfigurationHealthCheck(check: Project.() -> Unit) { /* Running configuration checks on a failed project will only lead to false positive error messages */ - if (state.failure != null || (inLenientMode() && syncExceptionsAreNotEmpty())) { + if (project.hasSyncErrors()) { return } check() } +internal fun Project.hasSyncErrors(): Boolean = + state.failure != null || (inLenientMode() && syncExceptionsAreNotEmpty()) + // ClassPathModeExceptionCollector is available only via 'gradleKotlinDsl()' dependency which brings in full Gradle jar private fun Project.syncExceptionsAreNotEmpty(): Boolean { val classPathModeExceptionCollectionClass = Class.forName("org.gradle.kotlin.dsl.provider.ClassPathModeExceptionCollector")