From 7dfbf28d9347cdbff11f3f5d17df075f1f56fa20 Mon Sep 17 00:00:00 2001 From: Yahor Berdnikau Date: Tue, 1 Aug 2023 10:26:10 +0200 Subject: [PATCH] [Gradle] Introduce compilerOptions for Kotlin external target Kotlin external target now properly exposes compilerOptions which could be used by target users to configure target compilations compiler options. ^KT-57292 In Progress --- .../build.gradle.kts | 17 ++++-- .../gradle/android/PrototypeAndroidTarget.kt | 17 ++++++ .../api/kotlin-gradle-plugin.api | 1 + .../external/DecoratedExternalKotlinTarget.kt | 15 ++++- .../mpp/external/ExternalKotlinTargetImpl.kt | 2 + .../external/createExternalKotlinTarget.kt | 58 ++++++++++++++++++- .../unitTests/ProjectCompilerOptionsTests.kt | 57 ++++++++++++++++-- .../gradle/util/externalTargetApiUtils.kt | 24 +++++++- 8 files changed, 178 insertions(+), 13 deletions(-) diff --git a/libraries/tools/kotlin-gradle-plugin-tcs-android/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-tcs-android/build.gradle.kts index 85c0c06f375..38f3fdd1a2c 100644 --- a/libraries/tools/kotlin-gradle-plugin-tcs-android/build.gradle.kts +++ b/libraries/tools/kotlin-gradle-plugin-tcs-android/build.gradle.kts @@ -6,17 +6,26 @@ plugins { dependencies { compileOnly(gradleKotlinDsl()) compileOnly("com.android.tools.build:gradle:7.2.1") - compileOnly(project(":kotlin-gradle-plugin")) + compileOnly(project(":kotlin-gradle-plugin")) { + capabilities { + requireCapability("org.jetbrains.kotlin:kotlin-gradle-plugin-common") + } + } + compileOnly(project(":kotlin-gradle-plugin-api")) { + capabilities { + requireCapability("org.jetbrains.kotlin:kotlin-gradle-plugin-api-common") + } + } compileOnly(project(":kotlin-gradle-plugin-idea")) } configureKotlinCompileTasksGradleCompatibility() kotlin { - sourceSets.all { - languageSettings.optIn("org.jetbrains.kotlin.gradle.ExternalKotlinTargetApi") + compilerOptions { + optIn.add("org.jetbrains.kotlin.gradle.ExternalKotlinTargetApi") /* Workaround for KT-54823 */ - languageSettings.optIn("org.jetbrains.kotlin.gradle.InternalKotlinGradlePluginApi") + optIn.add("org.jetbrains.kotlin.gradle.InternalKotlinGradlePluginApi") } } diff --git a/libraries/tools/kotlin-gradle-plugin-tcs-android/src/main/kotlin/org/jetbrains/kotlin/gradle/android/PrototypeAndroidTarget.kt b/libraries/tools/kotlin-gradle-plugin-tcs-android/src/main/kotlin/org/jetbrains/kotlin/gradle/android/PrototypeAndroidTarget.kt index ab20614fda8..65c968b74ed 100644 --- a/libraries/tools/kotlin-gradle-plugin-tcs-android/src/main/kotlin/org/jetbrains/kotlin/gradle/android/PrototypeAndroidTarget.kt +++ b/libraries/tools/kotlin-gradle-plugin-tcs-android/src/main/kotlin/org/jetbrains/kotlin/gradle/android/PrototypeAndroidTarget.kt @@ -5,8 +5,11 @@ package org.jetbrains.kotlin.gradle.android +import org.gradle.api.Action import org.gradle.api.NamedDomainObjectContainer import org.gradle.kotlin.dsl.getByType +import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi +import org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompilerOptions import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension import org.jetbrains.kotlin.gradle.plugin.mpp.external.DecoratedExternalKotlinTarget @@ -23,4 +26,18 @@ class PrototypeAndroidTarget( @Suppress("unchecked_cast") override val compilations: NamedDomainObjectContainer get() = super.compilations as NamedDomainObjectContainer + + @ExperimentalKotlinGradlePluginApi + override val compilerOptions: KotlinJvmCompilerOptions + get() = super.compilerOptions as KotlinJvmCompilerOptions + + @ExperimentalKotlinGradlePluginApi + fun compilerOptions(configure: KotlinJvmCompilerOptions.() -> Unit) { + configure(compilerOptions) + } + + @ExperimentalKotlinGradlePluginApi + fun compilerOptions(configure: Action) { + configure.execute(compilerOptions) + } } \ No newline at end of file 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 e90b5df8121..d5857e5e812 100644 --- a/libraries/tools/kotlin-gradle-plugin/api/kotlin-gradle-plugin.api +++ b/libraries/tools/kotlin-gradle-plugin/api/kotlin-gradle-plugin.api @@ -734,6 +734,7 @@ public class org/jetbrains/kotlin/gradle/plugin/mpp/external/DecoratedExternalKo public fun getArtifactsTaskName ()Ljava/lang/String; public fun getAttributes ()Lorg/gradle/api/attributes/AttributeContainer; public fun getCompilations ()Lorg/gradle/api/NamedDomainObjectContainer; + public fun getCompilerOptions ()Lorg/jetbrains/kotlin/gradle/dsl/KotlinCommonCompilerOptions; public fun getComponents ()Ljava/util/Set; public fun getDisambiguationClassifier ()Ljava/lang/String; public synthetic fun getExtras ()Lorg/jetbrains/kotlin/tooling/core/Extras; diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/external/DecoratedExternalKotlinTarget.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/external/DecoratedExternalKotlinTarget.kt index 6485232751f..f7a800c4b09 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/external/DecoratedExternalKotlinTarget.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/external/DecoratedExternalKotlinTarget.kt @@ -8,7 +8,7 @@ package org.jetbrains.kotlin.gradle.plugin.mpp.external import org.gradle.api.artifacts.Configuration import org.gradle.api.logging.Logger import org.jetbrains.kotlin.gradle.ExternalKotlinTargetApi -import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension +import org.jetbrains.kotlin.gradle.dsl.* import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension import org.jetbrains.kotlin.gradle.dsl.multiplatformExtensionOrNull import org.jetbrains.kotlin.gradle.plugin.KotlinTarget @@ -30,6 +30,19 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.InternalKotlinTarget * // Covariant override that allows for narrowing the type of compilations that this target contains * override val compilations: NamedDomainObjectContainer * get() = super.compilations as NamedDomainObjectContainer + * + * // Covariant override of target compiler options that should be used to configure all target compilation compiler options + * override val compilerOptions: KotlinJvmCompilerOptions + * get() = super.compilerOptions as KotlinJvmCompilerOptions + * + * // Optionally follow api exposed in Kotlin built-in targets to configure compiler options + * fun compilerOptions(configure: KotlinJvmCompilerOptions.() -> Unit) { + * configure(compilerOptions) + * } + * + * fun compilerOptions(configure: Action) { + * configure.execute(compilerOptions) + * } * } * ``` * diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/external/ExternalKotlinTargetImpl.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/external/ExternalKotlinTargetImpl.kt index 97778504849..5b8f650d12b 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/external/ExternalKotlinTargetImpl.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/external/ExternalKotlinTargetImpl.kt @@ -14,6 +14,7 @@ import org.gradle.api.logging.Logging import org.gradle.api.publish.maven.MavenPublication import org.gradle.api.tasks.TaskProvider import org.jetbrains.kotlin.gradle.InternalKotlinGradlePluginApi +import org.jetbrains.kotlin.gradle.dsl.KotlinCommonCompilerOptions import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType import org.jetbrains.kotlin.gradle.plugin.KotlinTargetComponent @@ -27,6 +28,7 @@ internal class ExternalKotlinTargetImpl internal constructor( override val targetName: String, override val platformType: KotlinPlatformType, override val publishable: Boolean, + override val compilerOptions: KotlinCommonCompilerOptions, val apiElementsConfiguration: Configuration, val runtimeElementsConfiguration: Configuration, val sourcesElementsConfiguration: Configuration, diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/external/createExternalKotlinTarget.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/external/createExternalKotlinTarget.kt index e0d1eb8aaf5..9820def1baa 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/external/createExternalKotlinTarget.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/external/createExternalKotlinTarget.kt @@ -12,7 +12,11 @@ import org.gradle.api.attributes.Category import org.gradle.api.attributes.Usage import org.gradle.jvm.tasks.Jar import org.jetbrains.kotlin.gradle.ExternalKotlinTargetApi -import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension +import org.jetbrains.kotlin.gradle.dsl.* +import org.jetbrains.kotlin.gradle.dsl.KotlinCommonCompilerOptionsDefault +import org.jetbrains.kotlin.gradle.dsl.KotlinJsCompilerOptionsDefault +import org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompilerOptionsDefault +import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType import org.jetbrains.kotlin.gradle.plugin.KotlinTarget import org.jetbrains.kotlin.gradle.plugin.ide.kotlinIdeMultiplatformImport import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinUsages @@ -22,6 +26,7 @@ import org.jetbrains.kotlin.gradle.tasks.locateOrRegisterTask import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName import org.jetbrains.kotlin.gradle.utils.markConsumable import org.jetbrains.kotlin.gradle.utils.named +import org.jetbrains.kotlin.gradle.utils.newInstance /** * Creates an adhoc/external Kotlin Target which can be maintained and evolved outside the kotlin.git repository. @@ -55,11 +60,21 @@ fun KotlinMultiplatformExtension.createExter target.project.locateOrRegisterTask(lowerCamelCaseName(descriptor.targetName, "jar")) } + val compilerOptions = when (descriptor.platformType) { + KotlinPlatformType.androidJvm, + KotlinPlatformType.jvm -> project.objects.newInstance() + KotlinPlatformType.wasm, + KotlinPlatformType.js -> project.objects.newInstance() + KotlinPlatformType.common -> project.objects.newInstance() + KotlinPlatformType.native -> project.objects.newInstance() + } + val target = ExternalKotlinTargetImpl( project = project, targetName = descriptor.targetName, platformType = descriptor.platformType, publishable = true, + compilerOptions = compilerOptions, apiElementsConfiguration = apiElementsConfiguration, runtimeElementsConfiguration = runtimeElementsConfiguration, sourcesElementsConfiguration = sourcesElementsConfiguration, @@ -70,6 +85,8 @@ fun KotlinMultiplatformExtension.createExter artifactsTaskLocator = artifactsTaskLocator ) + target.wireCompilerOptionsToCompilations(descriptor.platformType) + target.setupApiElements(apiElementsConfiguration) target.setupApiElements(apiElementsPublishedConfiguration) target.setupRuntimeElements(runtimeElementsConfiguration) @@ -130,3 +147,42 @@ private fun ExternalKotlinTargetImpl.setupRuntimeElements(configuration: Configu private fun ExternalKotlinTargetImpl.setupSourcesElements(configuration: Configuration) { configuration.configureSourcesPublicationAttributes(this) } + +private fun ExternalKotlinTargetImpl.wireCompilerOptionsToCompilations(platformType: KotlinPlatformType) { + when (platformType) { + KotlinPlatformType.androidJvm, + KotlinPlatformType.jvm -> { + compilations.configureEach { + KotlinJvmCompilerOptionsHelper.syncOptionsAsConvention( + compilerOptions as KotlinJvmCompilerOptions, + it.compilerOptions.options as KotlinJvmCompilerOptions + ) + } + } + KotlinPlatformType.wasm, + KotlinPlatformType.js -> { + compilations.configureEach { + KotlinJsCompilerOptionsHelper.syncOptionsAsConvention( + compilerOptions as KotlinJsCompilerOptions, + it.compilerOptions.options as KotlinJsCompilerOptions + ) + } + } + KotlinPlatformType.common -> { + compilations.configureEach { + KotlinCommonCompilerOptionsHelper.syncOptionsAsConvention( + compilerOptions, + it.compilerOptions.options + ) + } + } + KotlinPlatformType.native -> { + compilations.configureEach { + KotlinNativeCompilerOptionsHelper.syncOptionsAsConvention( + compilerOptions as KotlinNativeCompilerOptions, + it.compilerOptions.options as KotlinNativeCompilerOptions + ) + } + } + } +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/ProjectCompilerOptionsTests.kt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/ProjectCompilerOptionsTests.kt index 89a9b00a0a8..b669cf66820 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/ProjectCompilerOptionsTests.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/ProjectCompilerOptionsTests.kt @@ -6,14 +6,15 @@ package org.jetbrains.kotlin.gradle.unitTests import org.gradle.api.Project -import org.jetbrains.kotlin.gradle.dsl.KotlinCommonCompilerOptions -import org.jetbrains.kotlin.gradle.dsl.KotlinJsCompilerOptions -import org.jetbrains.kotlin.gradle.dsl.KotlinNativeCompilerOptions +import org.jetbrains.kotlin.gradle.dsl.* import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinMetadataTarget +import org.jetbrains.kotlin.gradle.plugin.mpp.external.createCompilation +import org.jetbrains.kotlin.gradle.plugin.mpp.external.createExternalKotlinTarget import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask +import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile as KotlinJvmCompileTask import org.jetbrains.kotlin.gradle.tasks.withType -import org.jetbrains.kotlin.gradle.util.buildProjectWithMPP +import org.jetbrains.kotlin.gradle.util.* import org.jetbrains.kotlin.gradle.utils.named import kotlin.test.Test import kotlin.test.assertEquals @@ -174,6 +175,50 @@ class ProjectCompilerOptionsTests { assertEquals(false, project.kotlinCommonTask("compileKotlinMetadata").compilerOptions.progressiveMode.get()) } + @Test + fun externalTargetDsl() { + val project = buildProjectWithMPP() + project.runLifecycleAwareTest { + with(multiplatformExtension) { + val target = createExternalKotlinTarget { defaults() } + target.createCompilation { defaults(this@with) } + target.createCompilation { defaults(this@with, "test") } + + target.compilerOptions { + javaParameters.set(true) + } + } + } + + assertEquals(true, project.kotlinJvmTask("compileFakeKotlinFake").compilerOptions.javaParameters.get()) + assertEquals(true, project.kotlinJvmTask("compileTestKotlinFake").compilerOptions.javaParameters.get()) + } + + @Test + fun externalTaskOptionsOverridesTargetOptions() { + val project = buildProjectWithMPP() + project.runLifecycleAwareTest { + tasks.withType().configureEach { + if (it.name == "compileFakeKotlinFake") { + it.compilerOptions.javaParameters.set(false) + } + } + + with(multiplatformExtension) { + val target = createExternalKotlinTarget { defaults() } + target.createCompilation { defaults(this@with) } + target.createCompilation { defaults(this@with, "test") } + + target.compilerOptions { + javaParameters.set(true) + } + } + } + + assertEquals(false, project.kotlinJvmTask("compileFakeKotlinFake").compilerOptions.javaParameters.get()) + assertEquals(true, project.kotlinJvmTask("compileTestKotlinFake").compilerOptions.javaParameters.get()) + } + private fun Project.kotlinNativeTask(name: String): KotlinCompilationTask = tasks .named>(name) .get() @@ -182,6 +227,10 @@ class ProjectCompilerOptionsTests { .named>(name) .get() + private fun Project.kotlinJvmTask(name: String): KotlinCompilationTask = tasks + .named>(name) + .get() + private fun Project.kotlinCommonTask(name: String): KotlinCompilationTask = tasks .named>(name) .get() diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/util/externalTargetApiUtils.kt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/util/externalTargetApiUtils.kt index fb0a463addd..1582017a968 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/util/externalTargetApiUtils.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/util/externalTargetApiUtils.kt @@ -5,14 +5,32 @@ package org.jetbrains.kotlin.gradle.util -import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension +import org.gradle.api.NamedDomainObjectContainer +import org.jetbrains.kotlin.gradle.dsl.* +import org.jetbrains.kotlin.gradle.plugin.HasCompilerOptions import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType import org.jetbrains.kotlin.gradle.plugin.mpp.external.* import org.jetbrains.kotlin.gradle.plugin.mpp.external.ExternalKotlinCompilationDescriptor.CompilationFactory import org.jetbrains.kotlin.gradle.plugin.mpp.external.ExternalKotlinTargetDescriptor.TargetFactory -class FakeCompilation(delegate: Delegate) : DecoratedExternalKotlinCompilation(delegate) -class FakeTarget(delegate: Delegate) : DecoratedExternalKotlinTarget(delegate) +class FakeCompilation(delegate: Delegate) : DecoratedExternalKotlinCompilation(delegate) { + @Suppress("UNCHECKED_CAST") + override val compilerOptions: HasCompilerOptions + get() = super.compilerOptions as HasCompilerOptions +} +class FakeTarget(delegate: Delegate) : DecoratedExternalKotlinTarget(delegate) { + + @Suppress("UNCHECKED_CAST") + override val compilations: NamedDomainObjectContainer + get() = super.compilations as NamedDomainObjectContainer + + override val compilerOptions: KotlinJvmCompilerOptions + get() = super.compilerOptions as KotlinJvmCompilerOptions + + fun compilerOptions(configure: KotlinJvmCompilerOptions.() -> Unit) { + configure(compilerOptions) + } +} fun ExternalKotlinTargetDescriptorBuilder.defaults() { targetName = "fake"