diff --git a/libraries/tools/kotlin-gradle-plugin/api/kotlin-gradle-plugin.api b/libraries/tools/kotlin-gradle-plugin/api/kotlin-gradle-plugin.api index bfa58ed241a..0682a6cd9ea 100644 --- a/libraries/tools/kotlin-gradle-plugin/api/kotlin-gradle-plugin.api +++ b/libraries/tools/kotlin-gradle-plugin/api/kotlin-gradle-plugin.api @@ -789,6 +789,15 @@ public final class org/jetbrains/kotlin/gradle/plugin/mpp/external/ExternalKotli public static final fun ExternalKotlinCompilationDescriptor (Lkotlin/jvm/functions/Function1;)Lorg/jetbrains/kotlin/gradle/plugin/mpp/external/ExternalKotlinCompilationDescriptor; } +public abstract interface class org/jetbrains/kotlin/gradle/plugin/mpp/external/ExternalKotlinTargetApiUtils { + public abstract fun enableKgpBasedDependencyResolution (Z)V + public abstract fun publishJvmEnvironmentAttribute (Z)V +} + +public final class org/jetbrains/kotlin/gradle/plugin/mpp/external/ExternalKotlinTargetApiUtilsKt { + public static final fun getExternalKotlinTargetApiUtils (Lorg/gradle/api/Project;)Lorg/jetbrains/kotlin/gradle/plugin/mpp/external/ExternalKotlinTargetApiUtils; +} + public abstract interface class org/jetbrains/kotlin/gradle/plugin/mpp/external/ExternalKotlinTargetConfigurationDescriptor { public abstract fun getConfigure ()Lkotlin/jvm/functions/Function2; } 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 d17705fb054..90f7b7ff7f6 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 @@ -40,6 +40,7 @@ import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLI import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_MPP_HIERARCHICAL_STRUCTURE_SUPPORT import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_MPP_IMPORT_ENABLE_KGP_DEPENDENCY_RESOLUTION import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_MPP_IMPORT_ENABLE_SLOW_SOURCES_JAR_RESOLVER +import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_PUBLISH_JVM_ENVIRONMENT_ATTRIBUTE import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_NATIVE_DEPENDENCY_PROPAGATION import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_NATIVE_IGNORE_DISABLED_TARGETS import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_NATIVE_SUPPRESS_EXPERIMENTAL_ARTIFACTS_DSL_WARNING @@ -271,6 +272,9 @@ internal class PropertiesProvider private constructor(private val project: Proje val ignoreHmppDeprecationWarnings: Boolean? get() = booleanProperty(KOTLIN_MPP_DEPRECATED_PROPERTIES_NO_WARN) + val publishJvmEnvironmentAttribute: Boolean + get() = booleanProperty(KOTLIN_PUBLISH_JVM_ENVIRONMENT_ATTRIBUTE) ?: false + /** * Enables individual test task reporting for aggregated test tasks. * @@ -633,6 +637,7 @@ internal class PropertiesProvider private constructor(private val project: Proje const val KOTLIN_CREATE_DEFAULT_MULTIPLATFORM_PUBLICATIONS = "kotlin.internal.mpp.createDefaultMultiplatformPublications" const val KOTLIN_RUN_COMPILER_VIA_BUILD_TOOLS_API = "kotlin.compiler.runViaBuildToolsApi" const val KOTLIN_MPP_ALLOW_LEGACY_DEPENDENCIES = "kotlin.mpp.allow.legacy.dependencies" + const val KOTLIN_PUBLISH_JVM_ENVIRONMENT_ATTRIBUTE = "kotlin.publishJvmEnvironmentAttribute" const val KOTLIN_EXPERIMENTAL_TRY_K2 = "kotlin.experimental.tryK2" const val KOTLIN_INTERNAL_VERBOSE_DIAGNOSTICS = "kotlin.internal.verboseDiagnostics" const val KOTLIN_SUPPRESS_GRADLE_PLUGIN_WARNINGS = "kotlin.suppressGradlePluginWarnings" diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinSoftwareComponent.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinSoftwareComponent.kt index 93c78a5dffb..cfdb4cc56cc 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinSoftwareComponent.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinSoftwareComponent.kt @@ -22,6 +22,7 @@ import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension import org.jetbrains.kotlin.gradle.plugin.* import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation.Companion.MAIN_COMPILATION_NAME import org.jetbrains.kotlin.gradle.plugin.KotlinPluginLifecycle.Stage.AfterFinaliseCompilations +import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.Companion.kotlinPropertiesProvider import org.jetbrains.kotlin.gradle.utils.markResolvable import org.jetbrains.kotlin.gradle.targets.metadata.* import org.jetbrains.kotlin.gradle.targets.metadata.COMMON_MAIN_ELEMENTS_CONFIGURATION_NAME @@ -35,7 +36,7 @@ import org.jetbrains.kotlin.util.capitalizeDecapitalize.toLowerCaseAsciiOnly abstract class KotlinSoftwareComponent( private val project: Project, private val name: String, - protected val kotlinTargets: Iterable + protected val kotlinTargets: Iterable, ) : SoftwareComponentInternal, ComponentWithVariants { override fun getName(): String = name @@ -220,6 +221,8 @@ class DefaultKotlinUsageContext( override fun getGlobalExcludes(): Set = emptySet() + private val publishJvmEnvironmentAttribute get() = project.kotlinPropertiesProvider.publishJvmEnvironmentAttribute + private fun filterOutNonPublishableAttributes(attributes: Set>): Set> = attributes.filterTo(mutableSetOf()) { it != ProjectLocalConfigurations.ATTRIBUTE && @@ -234,9 +237,17 @@ class DefaultKotlinUsageContext( * 2. If this attribute is published, but not present on all the variants in a multiplatform library, and is also * missing on the consumer side (like Gradle < 7.0, Kotlin 1.6.0), then there is a * case when Gradle fails to choose a variant in a completely reasonable setup. + * + * UPD: 1.9.20: + * We should now be ready to publish the 'jvm environment' attribute. + * It will however be rolled out as 'opt-in' first (as safety measure). + * We expect that the 'external Android target' will opt-into publishing this attribute, + * as it will switch to KotlinPlatformType.jvm and requires this additional attribute to disambiguate + * Android from the JVM */ - it.name != "org.gradle.jvm.environment" + (it.name != "org.gradle.jvm.environment" || publishJvmEnvironmentAttribute) } + } internal fun Iterable.publishableUsages() = this diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/external/ExternalKotlinTargetApiUtils.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/external/ExternalKotlinTargetApiUtils.kt new file mode 100644 index 00000000000..b656eb9dd33 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/external/ExternalKotlinTargetApiUtils.kt @@ -0,0 +1,31 @@ +/* + * 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.mpp.external + +import org.gradle.api.Project +import org.jetbrains.kotlin.gradle.ExternalKotlinTargetApi +import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_MPP_IMPORT_ENABLE_KGP_DEPENDENCY_RESOLUTION +import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_PUBLISH_JVM_ENVIRONMENT_ATTRIBUTE +import org.jetbrains.kotlin.gradle.plugin.extraProperties + +@ExternalKotlinTargetApi +interface ExternalKotlinTargetApiUtils { + fun enableKgpBasedDependencyResolution(enable: Boolean = true) + fun publishJvmEnvironmentAttribute(publish: Boolean = true) +} + +@ExternalKotlinTargetApi +val Project.externalKotlinTargetApiUtils: ExternalKotlinTargetApiUtils + get() = object : ExternalKotlinTargetApiUtils { + + override fun enableKgpBasedDependencyResolution(enable: Boolean) { + project.extraProperties.set(KOTLIN_MPP_IMPORT_ENABLE_KGP_DEPENDENCY_RESOLUTION, enable.toString()) + } + + override fun publishJvmEnvironmentAttribute(publish: Boolean) { + project.extraProperties.set(KOTLIN_PUBLISH_JVM_ENVIRONMENT_ATTRIBUTE, publish.toString()) + } + } diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/ExternalKotlinTargetApiUtilsTest.kt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/ExternalKotlinTargetApiUtilsTest.kt new file mode 100644 index 00000000000..aa4d8ae4c1a --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/ExternalKotlinTargetApiUtilsTest.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. + */ + +@file:Suppress("FunctionName") + +package org.jetbrains.kotlin.gradle.unitTests + +import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.Companion.kotlinPropertiesProvider +import org.jetbrains.kotlin.gradle.plugin.mpp.external.externalKotlinTargetApiUtils +import org.jetbrains.kotlin.gradle.util.buildProjectWithMPP +import kotlin.test.* + +class ExternalKotlinTargetApiUtilsTest { + @Test + fun `test - enableKgpBasedDependencyResolution`() { + val project = buildProjectWithMPP() + assertNull(project.kotlinPropertiesProvider.enableKgpDependencyResolution) + + project.externalKotlinTargetApiUtils.enableKgpBasedDependencyResolution(true) + assertEquals(true, project.kotlinPropertiesProvider.enableKgpDependencyResolution) + + project.externalKotlinTargetApiUtils.enableKgpBasedDependencyResolution(false) + assertEquals(false, project.kotlinPropertiesProvider.enableKgpDependencyResolution) + + project.externalKotlinTargetApiUtils.enableKgpBasedDependencyResolution() + assertEquals(true, project.kotlinPropertiesProvider.enableKgpDependencyResolution) + } + + @Test + fun `test - publishJvmEnvironmentAttribute`() { + val project = buildProjectWithMPP() + + project.externalKotlinTargetApiUtils.publishJvmEnvironmentAttribute(true) + assertTrue(project.kotlinPropertiesProvider.publishJvmEnvironmentAttribute) + + project.externalKotlinTargetApiUtils.publishJvmEnvironmentAttribute(false) + assertFalse(project.kotlinPropertiesProvider.publishJvmEnvironmentAttribute) + + project.externalKotlinTargetApiUtils.publishJvmEnvironmentAttribute() + assertTrue(project.kotlinPropertiesProvider.publishJvmEnvironmentAttribute) + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/PublishJvmEnvironmentAttributeTest.kt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/PublishJvmEnvironmentAttributeTest.kt new file mode 100644 index 00000000000..be301dd967b --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/PublishJvmEnvironmentAttributeTest.kt @@ -0,0 +1,86 @@ +/* + * 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. + */ + +@file:Suppress("FunctionName") + +package org.jetbrains.kotlin.gradle.unitTests + +import org.gradle.api.attributes.java.TargetJvmEnvironment +import org.gradle.api.attributes.java.TargetJvmEnvironment.STANDARD_JVM +import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension +import org.jetbrains.kotlin.gradle.plugin.KotlinTarget +import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.Companion.kotlinPropertiesProvider +import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_PUBLISH_JVM_ENVIRONMENT_ATTRIBUTE +import org.jetbrains.kotlin.gradle.plugin.configurationResult +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinUsageContext +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinVariantWithMetadataVariant +import org.jetbrains.kotlin.gradle.plugin.mpp.containsMultiplatformAttributes +import org.jetbrains.kotlin.gradle.plugin.mpp.internal +import org.jetbrains.kotlin.gradle.util.buildProjectWithMPP +import org.jetbrains.kotlin.gradle.util.propertiesExtension +import org.jetbrains.kotlin.gradle.util.runLifecycleAwareTest +import org.jetbrains.kotlin.gradle.utils.named +import kotlin.test.* + +class PublishJvmEnvironmentAttributeTest { + @Test + fun `test - default value`() = buildProjectWithMPP().runLifecycleAwareTest { + val kotlin = multiplatformExtension + kotlin.jvm() + configurationResult.await() + assertFalse(kotlinPropertiesProvider.publishJvmEnvironmentAttribute) + assertNoJvmEnvironmentAttribute(kotlin.jvm()) + } + + @Test + fun `test - publishJvmEnvironmentAttribute disabled`() = buildProjectWithMPP().runLifecycleAwareTest { + val kotlin = multiplatformExtension + kotlin.jvm() + + project.propertiesExtension.set(KOTLIN_PUBLISH_JVM_ENVIRONMENT_ATTRIBUTE, "false") + + configurationResult.await() + assertFalse(kotlinPropertiesProvider.publishJvmEnvironmentAttribute) + assertNoJvmEnvironmentAttribute(kotlin.jvm()) + } + + @Test + fun `test - publishJvmEnvironmentAttribute enabled`() = buildProjectWithMPP().runLifecycleAwareTest { + val kotlin = multiplatformExtension + kotlin.jvm() + + project.propertiesExtension.set(KOTLIN_PUBLISH_JVM_ENVIRONMENT_ATTRIBUTE, "true") + + configurationResult.await() + assertTrue(kotlinPropertiesProvider.publishJvmEnvironmentAttribute) + assertJvmEnvironmentAttributeEquals(kotlin.jvm(), project.objects.named(STANDARD_JVM)) + } + + private fun assertNoJvmEnvironmentAttribute(target: KotlinTarget) { + target.forEachUsage { usage -> + usage.attributes.containsMultiplatformAttributes + assertNull( + usage.attributes.getAttribute(TargetJvmEnvironment.TARGET_JVM_ENVIRONMENT_ATTRIBUTE), + "Expected no jvm environment attribute to be set on usage '$usage" + ) + } + } + + private fun assertJvmEnvironmentAttributeEquals(target: KotlinTarget, value: TargetJvmEnvironment) { + target.forEachUsage { usage -> + usage.attributes.containsMultiplatformAttributes + assertEquals( + value, usage.attributes.getAttribute(TargetJvmEnvironment.TARGET_JVM_ENVIRONMENT_ATTRIBUTE), + ) + } + } + + private fun KotlinTarget.forEachUsage(action: (usage: KotlinUsageContext) -> Unit) { + val component = internal.kotlinComponents.singleOrNull() ?: fail("Expected a single component. Found: ${components}") + component as KotlinVariantWithMetadataVariant + component.usages.ifEmpty { fail("Expected at least one 'usage'") } + component.usages.forEach(action) + } +}