From 6efd04edc0eb8c76cef10336ce7d961e19d04a28 Mon Sep 17 00:00:00 2001 From: "sebastian.sellmair" Date: Tue, 16 Feb 2021 11:17:04 +0100 Subject: [PATCH] Implement CInteropCommonizerTask to support commonization of user-defined cinterops ^KT-40975 Verification Pending --- .../gradle/CInteropCommonizerTaskTest.kt | 143 +++++++++++ .../gradle/SourceSetCommonizerTargetTest.kt | 166 +++++++++++++ .../kotlin/gradle/plugin/KotlinProperties.kt | 5 +- .../gradle/plugin/mpp/kotlinCompilations.kt | 8 + .../KotlinMetadataTargetConfigurator.kt | 18 +- .../targets/native/DefaultCInteropSettings.kt | 4 + .../native/KotlinNativeTargetConfigurator.kt | 5 + .../native/internal/CInteropCommonizerTask.kt | 230 ++++++++++++++++++ .../native/internal/CInteropIdentifier.kt | 20 ++ .../native/internal/CommonizerTasks.kt | 76 ++++++ .../GetCommonizerTargetOfCompilation.kt | 25 ++ .../GetCommonizerTargetOfSourceSet.kt | 70 ++++++ .../KotlinNativePlatformDependencies.kt | 22 +- ...kt => NativeDistributionCommonizerTask.kt} | 3 +- .../commonizer/CommonizerTarget.kt | 2 +- 15 files changed, 772 insertions(+), 25 deletions(-) create mode 100644 libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/CInteropCommonizerTaskTest.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/SourceSetCommonizerTargetTest.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/CInteropCommonizerTask.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/CInteropIdentifier.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/CommonizerTasks.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/GetCommonizerTargetOfCompilation.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/GetCommonizerTargetOfSourceSet.kt rename libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/{CommonizerTask.kt => NativeDistributionCommonizerTask.kt} (99%) diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/CInteropCommonizerTaskTest.kt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/CInteropCommonizerTaskTest.kt new file mode 100644 index 00000000000..c75b13ffcfb --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/CInteropCommonizerTaskTest.kt @@ -0,0 +1,143 @@ +/* + * Copyright 2010-2020 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. + */ + +/* Associate compilations are not yet supported by the IDE. KT-34102 */ +@file:Suppress("invisible_reference", "invisible_member", "FunctionName", "DuplicatedCode") + +package org.jetbrains.kotlin.gradle + +import org.gradle.api.internal.project.ProjectInternal +import org.gradle.api.plugins.ExtraPropertiesExtension +import org.gradle.testfixtures.ProjectBuilder +import org.jetbrains.kotlin.descriptors.commonizer.CommonizerTarget +import org.jetbrains.kotlin.descriptors.commonizer.SharedCommonizerTarget +import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension +import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinSharedNativeCompilation +import org.jetbrains.kotlin.gradle.targets.native.internal.CInteropCommonizationParameters +import org.jetbrains.kotlin.gradle.targets.native.internal.CInteropCommonizerTask +import org.jetbrains.kotlin.gradle.targets.native.internal.commonizeCInteropTask +import org.jetbrains.kotlin.konan.target.KonanTarget.* +import kotlin.test.* + +class CInteropCommonizerTaskTest { + + private lateinit var project: ProjectInternal + private lateinit var kotlin: KotlinMultiplatformExtension + private val task: CInteropCommonizerTask get() = project.commonizeCInteropTask?.get() ?: fail("Missing commonizeCInteropTask") + + @BeforeTest + fun setup() { + project = ProjectBuilder.builder().build() as ProjectInternal + project.extensions.getByType(ExtraPropertiesExtension::class.java).set("kotlin.mpp.enableGranularSourceSetsMetadata", "true") + project.extensions.getByType(ExtraPropertiesExtension::class.java).set("kotlin.mpp.enableCInteropCommonization", "true") + project.plugins.apply("kotlin-multiplatform") + kotlin = project.extensions.getByName("kotlin") as KotlinMultiplatformExtension + } + + + @Test + fun `nativeMain linux macos`() { + val linuxInterop = kotlin.linuxX64("linux").compilations.getByName("main").cinterops.create("anyInteropName") + val macosInterop = kotlin.macosX64("macos").compilations.getByName("main").cinterops.create("anyInteropName") + + val commonMain = kotlin.sourceSets.getByName("commonMain") + val nativeMain = kotlin.sourceSets.create("nativeMain") + val linuxMain = kotlin.sourceSets.getByName("linuxMain") + val macosMain = kotlin.sourceSets.getByName("macosMain") + + nativeMain.dependsOn(commonMain) + linuxMain.dependsOn(nativeMain) + macosMain.dependsOn(nativeMain) + + project.evaluate() + val nativeMainCompilation = kotlin.targets.flatMap { it.compilations } + .filterIsInstance() + .single { it.defaultSourceSet == nativeMain } + + assertEquals( + CInteropCommonizationParameters( + CommonizerTarget(LINUX_X64, MACOS_X64), setOf(linuxInterop.identifier, macosInterop.identifier) + ), + task.findSupportingCommonizationParameters(nativeMainCompilation) + ) + } + + @Test + fun `nativeMain linux macos (no macos interop defined)`() { + kotlin.linuxX64("linux").compilations.getByName("main").cinterops.create("anyInteropName") + kotlin.macosX64("macos") + + val commonMain = kotlin.sourceSets.getByName("commonMain") + val nativeMain = kotlin.sourceSets.create("nativeMain") + val linuxMain = kotlin.sourceSets.getByName("linuxMain") + val macosMain = kotlin.sourceSets.getByName("macosMain") + + nativeMain.dependsOn(commonMain) + linuxMain.dependsOn(nativeMain) + macosMain.dependsOn(nativeMain) + + project.evaluate() + val nativeMainCompilation = kotlin.targets.flatMap { it.compilations } + .filterIsInstance() + .single { it.defaultSourceSet == nativeMain } + + assertNull( + task.findSupportingCommonizationParameters(nativeMainCompilation), + "Expected no CInteropCommonizerTarget from nativeMain, since one target has not defined any cinterop" + ) + } + + + @Test + fun `nativeMain iosMain linux macos iosX64 iosArm64`() { + val linuxInterop = kotlin.linuxX64("linux").compilations.getByName("main").cinterops.create("anyInteropName").identifier + val macosInterop = kotlin.macosX64("macos").compilations.getByName("main").cinterops.create("anyInteropName").identifier + val iosX64Interop = kotlin.iosX64("iosX64").compilations.getByName("main").cinterops.create("anyInteropName").identifier + val iosArm64Interop = kotlin.iosArm64("iosArm64").compilations.getByName("main").cinterops.create("anyInteropName").identifier + + val commonMain = kotlin.sourceSets.getByName("commonMain") + val nativeMain = kotlin.sourceSets.create("nativeMain") + val iosMain = kotlin.sourceSets.create("iosMain") + val linuxMain = kotlin.sourceSets.getByName("linuxMain") + val macosMain = kotlin.sourceSets.getByName("macosMain") + val iosX64Main = kotlin.sourceSets.getByName("iosX64Main") + val iosArm64Main = kotlin.sourceSets.getByName("iosArm64Main") + + nativeMain.dependsOn(commonMain) + linuxMain.dependsOn(nativeMain) + macosMain.dependsOn(nativeMain) + iosMain.dependsOn(nativeMain) + iosX64Main.dependsOn(iosMain) + iosArm64Main.dependsOn(iosMain) + + project.evaluate() + + assertEquals( + CInteropCommonizationParameters( + SharedCommonizerTarget(CommonizerTarget(IOS_X64, IOS_ARM64), CommonizerTarget(MACOS_X64), CommonizerTarget(LINUX_X64)), + setOf(linuxInterop, macosInterop, iosX64Interop, iosArm64Interop) + ), task.findSupportingCommonizationParameters(sharedNativeCompilation(nativeMain)) + ) + + assertEquals( + CInteropCommonizationParameters( + SharedCommonizerTarget(CommonizerTarget(IOS_X64, IOS_ARM64), CommonizerTarget(MACOS_X64), CommonizerTarget(LINUX_X64)), + setOf(linuxInterop, macosInterop, iosX64Interop, iosArm64Interop) + ), task.findSupportingCommonizationParameters(sharedNativeCompilation(iosMain)) + ) + + assertTrue( + task.findSupportingCommonizationParameters(sharedNativeCompilation(iosMain))!! in + task.findSupportingCommonizationParameters(sharedNativeCompilation(nativeMain))!!, + "Expected CInteropCommonizerTarget of iosMain to be fully contained in nativeMain" + ) + } + + private fun sharedNativeCompilation(sourceSet: KotlinSourceSet): KotlinSharedNativeCompilation { + return kotlin.targets.flatMap { it.compilations }.filterIsInstance() + .single { it.defaultSourceSet == sourceSet } + } +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/SourceSetCommonizerTargetTest.kt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/SourceSetCommonizerTargetTest.kt new file mode 100644 index 00000000000..01fe7ee5fea --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/SourceSetCommonizerTargetTest.kt @@ -0,0 +1,166 @@ +/* + * Copyright 2010-2020 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. + */ + +/* Associate compilations are not yet supported by the IDE. KT-34102 */ +@file:Suppress("invisible_reference", "invisible_member", "FunctionName", "DuplicatedCode") + +package org.jetbrains.kotlin.gradle + +import org.gradle.api.Project +import org.gradle.testfixtures.ProjectBuilder +import org.jetbrains.kotlin.descriptors.commonizer.CommonizerTarget +import org.jetbrains.kotlin.descriptors.commonizer.SharedCommonizerTarget +import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension +import org.jetbrains.kotlin.gradle.targets.native.internal.getCommonizerTarget +import org.jetbrains.kotlin.konan.target.KonanTarget.* +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNull + +class SourceSetCommonizerTargetTest { + + private lateinit var project: Project + private lateinit var kotlin: KotlinMultiplatformExtension + + @BeforeTest + fun setup() { + project = ProjectBuilder.builder().build() + project.plugins.apply("kotlin-multiplatform") + kotlin = project.extensions.getByName("kotlin") as KotlinMultiplatformExtension + + } + + @Test + fun `linux macos`() { + kotlin.linuxX64("linux") + kotlin.macosX64("macos") + + val commonMain = kotlin.sourceSets.getByName("commonMain") + val commonTest = kotlin.sourceSets.getByName("commonTest") + val linuxMain = kotlin.sourceSets.getByName("linuxMain") + val macosMain = kotlin.sourceSets.getByName("macosMain") + val linuxTest = kotlin.sourceSets.getByName("linuxTest") + val macosTest = kotlin.sourceSets.getByName("macosTest") + + assertEquals(CommonizerTarget(LINUX_X64), project.getCommonizerTarget(linuxMain)) + assertEquals(CommonizerTarget(LINUX_X64), project.getCommonizerTarget(linuxTest)) + assertEquals(CommonizerTarget(MACOS_X64), project.getCommonizerTarget(macosMain)) + assertEquals(CommonizerTarget(MACOS_X64), project.getCommonizerTarget(macosTest)) + assertEquals(CommonizerTarget(LINUX_X64, MACOS_X64), project.getCommonizerTarget(commonMain)) + assertEquals(CommonizerTarget(LINUX_X64, MACOS_X64), project.getCommonizerTarget(commonTest)) + } + + @Test + fun `nativeMain linux macos`() { + kotlin.linuxX64("linux") + kotlin.macosX64("macos") + + val commonMain = kotlin.sourceSets.getByName("commonMain") + val commonTest = kotlin.sourceSets.getByName("commonTest") + val nativeMain = kotlin.sourceSets.create("nativeMain") + val linuxMain = kotlin.sourceSets.getByName("linuxMain") + val macosMain = kotlin.sourceSets.getByName("macosMain") + val linuxTest = kotlin.sourceSets.getByName("linuxTest") + val macosTest = kotlin.sourceSets.getByName("macosTest") + + nativeMain.dependsOn(commonMain) + linuxMain.dependsOn(nativeMain) + macosMain.dependsOn(nativeMain) + + assertEquals(CommonizerTarget(LINUX_X64), project.getCommonizerTarget(linuxMain)) + assertEquals(CommonizerTarget(LINUX_X64), project.getCommonizerTarget(linuxTest)) + assertEquals(CommonizerTarget(MACOS_X64), project.getCommonizerTarget(macosMain)) + assertEquals(CommonizerTarget(MACOS_X64), project.getCommonizerTarget(macosTest)) + + assertEquals(CommonizerTarget(LINUX_X64, MACOS_X64), project.getCommonizerTarget(nativeMain)) + assertEquals(CommonizerTarget(LINUX_X64, MACOS_X64), project.getCommonizerTarget(commonMain)) + assertEquals(CommonizerTarget(LINUX_X64, MACOS_X64), project.getCommonizerTarget(commonTest)) + } + + @Test + fun `nativeMain iosMain linux macos iosX64 iosArm64`() { + kotlin.linuxX64("linux") + kotlin.macosX64("macos") + kotlin.iosX64("iosX64") + kotlin.iosArm64("iosArm64") + + val commonMain = kotlin.sourceSets.getByName("commonMain") + val commonTest = kotlin.sourceSets.getByName("commonTest") + val nativeMain = kotlin.sourceSets.create("nativeMain") + val iosMain = kotlin.sourceSets.create("iosMain") + val linuxMain = kotlin.sourceSets.getByName("linuxMain") + val macosMain = kotlin.sourceSets.getByName("macosMain") + val linuxTest = kotlin.sourceSets.getByName("linuxTest") + val macosTest = kotlin.sourceSets.getByName("macosTest") + val iosX64Main = kotlin.sourceSets.getByName("iosX64Main") + val iosX64Test = kotlin.sourceSets.getByName("iosX64Test") + val iosArm64Main = kotlin.sourceSets.getByName("iosArm64Main") + val iosArm64Test = kotlin.sourceSets.getByName("iosArm64Test") + + nativeMain.dependsOn(commonMain) + linuxMain.dependsOn(nativeMain) + macosMain.dependsOn(nativeMain) + iosMain.dependsOn(nativeMain) + iosX64Main.dependsOn(iosMain) + iosArm64Main.dependsOn(iosMain) + + assertEquals(CommonizerTarget(LINUX_X64), project.getCommonizerTarget(linuxMain)) + assertEquals(CommonizerTarget(LINUX_X64), project.getCommonizerTarget(linuxTest)) + assertEquals(CommonizerTarget(MACOS_X64), project.getCommonizerTarget(macosMain)) + assertEquals(CommonizerTarget(MACOS_X64), project.getCommonizerTarget(macosTest)) + assertEquals(CommonizerTarget(IOS_X64), project.getCommonizerTarget(iosX64Test)) + assertEquals(CommonizerTarget(IOS_X64), project.getCommonizerTarget(iosX64Test)) + assertEquals(CommonizerTarget(IOS_ARM64), project.getCommonizerTarget(iosArm64Main)) + assertEquals(CommonizerTarget(IOS_ARM64), project.getCommonizerTarget(iosArm64Test)) + assertEquals(CommonizerTarget(IOS_X64, IOS_ARM64), project.getCommonizerTarget(iosMain)) + assertEquals( + SharedCommonizerTarget(CommonizerTarget(IOS_X64, IOS_ARM64), CommonizerTarget(MACOS_X64), CommonizerTarget(LINUX_X64)), + project.getCommonizerTarget(nativeMain) + ) + assertEquals( + SharedCommonizerTarget(CommonizerTarget(IOS_X64, IOS_ARM64), CommonizerTarget(MACOS_X64), CommonizerTarget(LINUX_X64)), + project.getCommonizerTarget(commonMain) + ) + /* No test hierarchy declared */ + assertEquals( + CommonizerTarget(IOS_ARM64, IOS_X64, LINUX_X64, MACOS_X64), + project.getCommonizerTarget(commonTest) + ) + } + + @Test + fun `nativeMain linux macos jvm`() { + kotlin.linuxX64("linux") + kotlin.macosX64("macos") + kotlin.jvm("jvm") + + val commonMain = kotlin.sourceSets.getByName("commonMain") + val commonTest = kotlin.sourceSets.getByName("commonTest") + val nativeMain = kotlin.sourceSets.create("nativeMain") + val linuxMain = kotlin.sourceSets.getByName("linuxMain") + val macosMain = kotlin.sourceSets.getByName("macosMain") + val linuxTest = kotlin.sourceSets.getByName("linuxTest") + val macosTest = kotlin.sourceSets.getByName("macosTest") + val jvmMain = kotlin.sourceSets.getByName("jvmMain") + val jvmTest = kotlin.sourceSets.getByName("jvmTest") + + nativeMain.dependsOn(commonMain) + linuxMain.dependsOn(nativeMain) + macosMain.dependsOn(nativeMain) + + assertEquals(CommonizerTarget(LINUX_X64), project.getCommonizerTarget(linuxMain)) + assertEquals(CommonizerTarget(LINUX_X64), project.getCommonizerTarget(linuxTest)) + assertEquals(CommonizerTarget(MACOS_X64), project.getCommonizerTarget(macosMain)) + assertEquals(CommonizerTarget(MACOS_X64), project.getCommonizerTarget(macosTest)) + assertNull(project.getCommonizerTarget(jvmMain), "Expected jvmMain to have no commonizer target") + assertNull(project.getCommonizerTarget(jvmTest), "Expected jvmTest to have no commonizer target") + + assertEquals(CommonizerTarget(LINUX_X64, MACOS_X64), project.getCommonizerTarget(nativeMain)) + + assertNull(project.getCommonizerTarget(commonMain), "Expected commonMain to have no commonizer target") + assertNull(project.getCommonizerTarget(commonTest), "Expected commonTest to have no commonizer target") + } +} 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 05a6a5f3acf..348bf795217 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 @@ -206,6 +206,9 @@ internal class PropertiesProvider private constructor(private val project: Proje val commonizerJvmArgs: String? get() = property("kotlin.commonizer.jvmArgs") + val enableCInteropCommonization: Boolean + get() = booleanProperty("kotlin.mpp.enableCInteropCommonization") ?: false + /** * Dependencies caching strategy for all targets that support caches. */ @@ -300,4 +303,4 @@ internal class PropertiesProvider private constructor(private val project: Proje ?: PropertiesProvider(project) // Fallback if multiple class loaders are involved } } -} \ No newline at end of file +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinCompilations.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinCompilations.kt index 15e12ea9359..a75801a914d 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinCompilations.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinCompilations.kt @@ -42,6 +42,14 @@ internal fun KotlinCompilation<*>.composeName(prefix: String? = null, suffix: St internal fun KotlinCompilation<*>.isMain(): Boolean = name == KotlinCompilation.MAIN_COMPILATION_NAME +/** + * see https://youtrack.jetbrains.com/issue/KT-45412 + * Some implementations of [KotlinCompilation] are not including their [KotlinCompilation.defaultSourceSet] into [kotlinSourceSet]s + * This helper function might disappear in the future, once the behaviour of those [KotlinCompilation] implementations is streamlined. + * @return [KotlinCompilation.kotlinSourceSets] + [KotlinCompilation.defaultSourceSet] + */ +internal val KotlinCompilation<*>.kotlinSourceSetsIncludingDefault: Set get() = kotlinSourceSets + defaultSourceSet + abstract class AbstractKotlinCompilation( target: KotlinTarget, override val compilationName: String diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/metadata/KotlinMetadataTargetConfigurator.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/metadata/KotlinMetadataTargetConfigurator.kt index dcda3359b7d..65ced4fda5c 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/metadata/KotlinMetadataTargetConfigurator.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/metadata/KotlinMetadataTargetConfigurator.kt @@ -23,10 +23,8 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.* import org.jetbrains.kotlin.gradle.plugin.mpp.CompilationSourceSetUtil.compilationsBySourceSets import org.jetbrains.kotlin.gradle.plugin.sources.* import org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatsService +import org.jetbrains.kotlin.gradle.targets.native.internal.commonizeCInteropTask import org.jetbrains.kotlin.gradle.tasks.* -import org.jetbrains.kotlin.gradle.tasks.KotlinTasksProvider -import org.jetbrains.kotlin.gradle.tasks.locateTask -import org.jetbrains.kotlin.gradle.tasks.registerTask import org.jetbrains.kotlin.gradle.utils.addExtendsFromRelation import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName import org.jetbrains.kotlin.statistics.metrics.BooleanMetrics @@ -293,7 +291,9 @@ class KotlinMetadataTargetConfigurator(kotlinPluginVersion: String) : val platformCompilations = compilationsBySourceSets(project).getValue(sourceSet) - val isNativeSourceSet = platformCompilations.all { compilation -> compilation.target is KotlinNativeTarget } + val isNativeSourceSet = platformCompilations.all { compilation -> + compilation.target is KotlinNativeTarget || compilation is KotlinSharedNativeCompilation + } val compilationFactory: KotlinCompilationFactory> = when { isNativeSourceSet -> KotlinSharedNativeCompilationFactory( @@ -320,6 +320,16 @@ class KotlinMetadataTargetConfigurator(kotlinPluginVersion: String) : compileDependencyFiles = project.files() } } + + if (this is KotlinSharedNativeCompilation) { + project.commonizeCInteropTask?.let { commonizeCInteropTask -> + val commonizerOutput = project.files(commonizeCInteropTask.map { it.getLibraries(this) }) + compileDependencyFiles = compileDependencyFiles.plus(commonizerOutput) + kotlinSourceSetsIncludingDefault.forEach { sourceSet -> + project.dependencies.add(sourceSet.implementationMetadataConfigurationName, commonizerOutput) + } + } + } } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/DefaultCInteropSettings.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/DefaultCInteropSettings.kt index a2566f6a942..941c9217dc9 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/DefaultCInteropSettings.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/DefaultCInteropSettings.kt @@ -15,6 +15,7 @@ import org.gradle.api.provider.Property import org.gradle.util.ConfigureUtil import org.jetbrains.kotlin.gradle.plugin.CInteropSettings import org.jetbrains.kotlin.gradle.plugin.CInteropSettings.IncludeDirectories +import org.jetbrains.kotlin.gradle.targets.native.internal.CInteropIdentifier import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName import java.io.File import javax.inject.Inject @@ -42,6 +43,9 @@ open class DefaultCInteropSettings @Inject constructor( override fun getName(): String = name + internal val identifier: CInteropIdentifier + get() = CInteropIdentifier(compilation.compileKotlinTaskName, name) + val target: KotlinNativeTarget get() = compilation.target diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeTargetConfigurator.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeTargetConfigurator.kt index 4d3032b3f7c..d45b3c633ab 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeTargetConfigurator.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeTargetConfigurator.kt @@ -31,6 +31,7 @@ import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.Companion.KOTLIN_NA import org.jetbrains.kotlin.gradle.plugin.mpp.* import org.jetbrains.kotlin.gradle.targets.metadata.isKotlinGranularMetadataEnabled import org.jetbrains.kotlin.gradle.targets.native.* +import org.jetbrains.kotlin.gradle.targets.native.internal.commonizeCInteropTask import org.jetbrains.kotlin.gradle.targets.native.tasks.KotlinNativeHostTest import org.jetbrains.kotlin.gradle.targets.native.tasks.KotlinNativeSimulatorTest import org.jetbrains.kotlin.gradle.targets.native.tasks.KotlinNativeTest @@ -307,6 +308,10 @@ open class KotlinNativeTargetConfigurator( it.enabled = compilation.konanTarget.enabledOnCurrentHost } + project.commonizeCInteropTask?.configure { commonizeCInteropTask -> + commonizeCInteropTask.from((interopTask.get())) + } + val interopOutput = project.files(interopTask.map { it.outputFileProvider }) with(compilation) { // Register the interop library as a dependency of the compilation to make IDE happy. diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/CInteropCommonizerTask.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/CInteropCommonizerTask.kt new file mode 100644 index 00000000000..b71f62cccf0 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/CInteropCommonizerTask.kt @@ -0,0 +1,230 @@ +/* + * 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.targets.native.internal + +import org.gradle.api.DefaultTask +import org.gradle.api.Project +import org.gradle.api.file.FileCollection +import org.gradle.api.provider.Provider +import org.gradle.api.tasks.* +import org.gradle.workers.WorkParameters +import org.jetbrains.kotlin.compilerRunner.GradleCliCommonizer +import org.jetbrains.kotlin.compilerRunner.konanHome +import org.jetbrains.kotlin.descriptors.commonizer.CommonizerTarget +import org.jetbrains.kotlin.descriptors.commonizer.HierarchicalCommonizerOutputLayout +import org.jetbrains.kotlin.descriptors.commonizer.SharedCommonizerTarget +import org.jetbrains.kotlin.descriptors.commonizer.prettyName +import org.jetbrains.kotlin.gradle.dsl.multiplatformExtensionOrNull +import org.jetbrains.kotlin.gradle.plugin.CInteropSettings +import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation +import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet +import org.jetbrains.kotlin.gradle.plugin.mpp.DefaultCInteropSettings +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeCompilation +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinSharedNativeCompilation +import org.jetbrains.kotlin.gradle.plugin.mpp.kotlinSourceSetsIncludingDefault +import org.jetbrains.kotlin.gradle.plugin.sources.resolveAllDependsOnSourceSets +import org.jetbrains.kotlin.gradle.targets.native.internal.CInteropCommonizerTask.CInteropGist +import org.jetbrains.kotlin.gradle.tasks.CInteropProcess +import org.jetbrains.kotlin.konan.target.KonanTarget +import java.io.File + +internal open class CInteropCommonizerTask : DefaultTask() { + + internal data class CInteropGist( + @get:Input val identifier: CInteropIdentifier, + @get:Input val konanTarget: KonanTarget, + @get:Internal val sourceSets: Provider>, + @get:Classpath val libraryFile: Provider, + @get:Classpath val dependencies: FileCollection + ) { + @Suppress("unused") // Used for UP-TO-DATE check + @get:Input + val allSourceSetNames: Provider> = sourceSets.map { it.resolveAllDependsOnSourceSets().map(Any::toString) } + } + + @get:Nested + internal var cinterops = setOf() + private set + + @OutputDirectories + fun getAllOutputDirectories(): Set { + return getCommonizationParameters().map { outputDirectory(it) }.toSet() + } + + fun from(vararg tasks: CInteropProcess) = from( + tasks.toList() + .onEach { task -> this.dependsOn(task) } + .map { task -> task.toGist() } + ) + + internal fun from(vararg cinterop: CInteropGist) { + from(cinterop.toList()) + } + + internal fun from(cinterops: List) { + this.cinterops += cinterops + } + + fun exclude(vararg tasks: CInteropProcess) { + exclude(tasks.map { it.settings.identifier }) + } + + fun exclude(vararg settings: CInteropSettings) { + exclude(settings.mapNotNull { (it as? DefaultCInteropSettings)?.identifier }) + } + + internal fun exclude(interopIdentifiers: List) { + this.cinterops = this.cinterops.filterTo(mutableSetOf()) { it.identifier !in interopIdentifiers } + } + + @TaskAction + internal fun commonizeCInteropLibraries() { + getCommonizationParameters().forEach(::commonize) + } + + private fun commonize(parameters: CInteropCommonizationParameters) { + val cinteropsForTarget = cinterops.filter { cinterop -> cinterop.identifier in parameters.interops } + outputDirectory(parameters).deleteRecursively() + if (cinteropsForTarget.isEmpty()) return + GradleCliCommonizer(project).commonizeLibraries( + konanHome = project.file(project.konanHome), + outputCommonizerTarget = parameters.commonizerTarget, + inputLibraries = cinteropsForTarget.map { it.libraryFile.get() }.toSet(), + dependencyLibraries = emptySet(),/*cinteropsForTarget.flatMap { it.dependencies.files }.toSet() */ // TODO pass real dependencies + outputDirectory = outputDirectory(parameters) + ) + } + + private fun outputDirectory(parameters: CInteropCommonizationParameters): File { + return project.buildDir.resolve("classes/kotlin/commonizer") + .resolve(parameters.commonizerTarget.prettyName) + .resolve(parameters.interops.map { it.interopName }.distinct().joinToString("-")) + } + + internal fun getLibraries(compilation: KotlinSharedNativeCompilation): FileCollection { + val fileProvider = project.provider> { + val parameters = findSupportingCommonizationParameters(compilation) ?: return@provider emptySet() + HierarchicalCommonizerOutputLayout + .getTargetDirectory(outputDirectory(parameters), parameters.commonizerTarget) + .listFiles().orEmpty().toSet() + } + + return project.files(fileProvider) { fileCollection -> + fileCollection.builtBy(this) + } + } + + @Nested + internal fun getCommonizationParameters(): Set { + val sharedNativeCompilations = (project.multiplatformExtensionOrNull ?: return emptySet()) + .targets.flatMap { it.compilations } + .filterIsInstance() + + fun getCommonizationParameters(compilation: KotlinSharedNativeCompilation): CInteropCommonizationParameters? { + return CInteropCommonizationParameters( + commonizerTarget = project.getCommonizerTarget(compilation) as? SharedCommonizerTarget ?: return null, + interops = project.getDependingNativeCompilations(compilation) + /* If a depending native compilation has no interop, then commonization is useless */ + .flatMap { nativeCompilation -> nativeCompilation.cinterops.ifEmpty { return null } } + .map { interop -> interop.identifier } + .toSet() + ) + } + + return sharedNativeCompilations.mapNotNull(::getCommonizationParameters).toSet() + .run(::removeNotRegisteredInterops) + .run(::removeEmptyInterops) + .run(::removeRedundantParameters) + } + + internal fun findSupportingCommonizationParameters(compilation: KotlinSharedNativeCompilation): CInteropCommonizationParameters? { + val supportedParameters = getCommonizationParameters().filter { parameters -> parameters.supports(compilation) } + if (supportedParameters.isEmpty()) return null + assert(supportedParameters.size == 1) { + "Unnecessary work detected: Multiple commonization parameters seem to be doing redundant work" + } + return supportedParameters.first() + } + + private fun CInteropCommonizationParameters.supports( + compilation: KotlinSharedNativeCompilation + ): Boolean { + val registeredInterops = cinterops.map { it.identifier } + val commonizerTargetOfCompilation = project.getCommonizerTarget(compilation) ?: return false + val interopsOfCompilation = project.getDependingNativeCompilations(compilation) + .flatMap { it.cinterops }.map { it.identifier } + .filter { interop -> interop in registeredInterops } + + return commonizerTarget.contains(commonizerTargetOfCompilation) && interops.containsAll(interopsOfCompilation) + } +} + +private fun CInteropProcess.toGist(): CInteropGist { + return CInteropGist( + identifier = settings.identifier, + konanTarget = konanTarget, + sourceSets = project.provider { settings.compilation.kotlinSourceSetsIncludingDefault }, + libraryFile = outputFileProvider, + dependencies = project.files(project.provider { settings.dependencyFiles }) + ) +} + +internal data class CInteropCommonizationParameters( + @get:Input val commonizerTarget: SharedCommonizerTarget, + @get:Input val interops: Set +) : WorkParameters { + operator fun contains(other: CInteropCommonizationParameters) = + this.interops.containsAll(other.interops) && this.commonizerTarget.contains(other.commonizerTarget) +} + +private fun CInteropCommonizerTask.removeNotRegisteredInterops( + parameters: Set +): Set { + val registeredInterops = this.cinterops.map { it.identifier } + return parameters.mapTo(mutableSetOf()) { params -> + params.copy(interops = params.interops.filterTo(mutableSetOf()) { interop -> interop in registeredInterops }) + } +} + +private fun removeEmptyInterops(parameters: Set): Set { + return parameters.filterTo(mutableSetOf()) { it.interops.isNotEmpty() } +} + +private fun removeRedundantParameters(parameters: Set): Set { + return parameters.filterNotTo(mutableSetOf()) { current -> + parameters.any { other -> + other !== current && current in other + } + } +} + +private operator fun CommonizerTarget.contains(other: CommonizerTarget): Boolean { + if (this == other) return true + if (this !is SharedCommonizerTarget) return false + return targets.any { child -> other in child } +} + +private fun Project.getDependingNativeCompilations(compilation: KotlinSharedNativeCompilation): Set { + /** + * Some implementations of [KotlinCompilation] do not contain the default source set in + * [KotlinCompilation.kotlinSourceSets] or [KotlinCompilation.allKotlinSourceSets] + * see KT-45412 + */ + fun KotlinCompilation<*>.allParticipatingSourceSets(): Set { + return kotlinSourceSetsIncludingDefault + kotlinSourceSetsIncludingDefault.resolveAllDependsOnSourceSets() + } + + val multiplatformExtension = multiplatformExtensionOrNull ?: return emptySet() + val allParticipatingSourceSetsOfCompilation = compilation.allParticipatingSourceSets() + + return multiplatformExtension.targets + .flatMap { target -> target.compilations } + .filterIsInstance() + .filter { nativeCompilation -> nativeCompilation.allParticipatingSourceSets().containsAll(allParticipatingSourceSetsOfCompilation) } + .toSet() +} + + diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/CInteropIdentifier.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/CInteropIdentifier.kt new file mode 100644 index 00000000000..137cd2a3508 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/CInteropIdentifier.kt @@ -0,0 +1,20 @@ +/* + * 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.targets.native.internal + +import org.gradle.api.tasks.Input + +/** + * Project unique identifier for all cinterops + */ +data class CInteropIdentifier internal constructor( + val compilationTaskName: String, + val cinteropName: String +) { + @get:Input + val uniqueName: String = "cinterop:$compilationTaskName/$cinteropName" + override fun toString(): String = uniqueName +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/CommonizerTasks.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/CommonizerTasks.kt new file mode 100644 index 00000000000..229ec9629dd --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/CommonizerTasks.kt @@ -0,0 +1,76 @@ +/* + * 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.targets.native.internal + +import org.gradle.api.Project +import org.gradle.api.Task +import org.gradle.api.tasks.TaskProvider +import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider +import org.jetbrains.kotlin.gradle.tasks.dependsOn +import org.jetbrains.kotlin.gradle.tasks.locateTask +import org.jetbrains.kotlin.gradle.tasks.registerTask + +internal val Project.isCInteropCommonizationEnabled: Boolean get() = PropertiesProvider(this).enableCInteropCommonization + +internal val Project.commonizeTask: TaskProvider + get() = locateOrRegisterTask( + "commonize", + invokeWhenRegistered = { @Suppress("deprecation") runCommonizerTask.dependsOn(this) }, + configureTask = { + group = "interop" + description = "Aggregator task for all c-interop & Native distribution commonizer tasks" + } + ) + + +/** + * Keeping this task/task name for IDE compatibility which is invoking 'runCommonizer' during sync + */ +@Deprecated("Use 'commonizeTask' instead. Keeping the task for IDE compatibility", replaceWith = ReplaceWith("commonizeTask")) +internal val Project.runCommonizerTask: TaskProvider + get() = locateOrRegisterTask( + "runCommonizer", + invokeWhenRegistered = { dependsOn(commonizeTask) }, + configureTask = { + group = "interop" + description = "[Deprecated: Use 'commonize' instead]" + } + ) + +internal val Project.commonizeCInteropTask: TaskProvider? + get() { + if (isCInteropCommonizationEnabled) { + return locateOrRegisterTask( + "commonizeCInterop", + invokeWhenRegistered = { commonizeTask.dependsOn(this) }, + configureTask = { + group = "interop" + description = "Invokes the commonizer on c-interop bindings of the project" + } + ) + } + return null + } + +internal val Project.commonizeNativeDistributionTask: TaskProvider + get() = locateOrRegisterTask( + "commonizeNativeDistribution", + invokeWhenRegistered = { commonizeTask.dependsOn(this) }, + configureTask = { + group = "interop" + description = "Invokes the commonizer on the platform libraries provided by the Kotlin/Native distribution" + } + ) + +private inline fun Project.locateOrRegisterTask( + name: String, + args: List = emptyList(), + invokeWhenRegistered: (TaskProvider.() -> Unit) = {}, + noinline configureTask: (T.() -> Unit)? = null +): TaskProvider { + locateTask(name)?.let { return it } + return registerTask(name, args, configureTask).also(invokeWhenRegistered) +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/GetCommonizerTargetOfCompilation.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/GetCommonizerTargetOfCompilation.kt new file mode 100644 index 00000000000..e8d71b985b6 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/GetCommonizerTargetOfCompilation.kt @@ -0,0 +1,25 @@ +/* + * Copyright 2010-2020 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.targets.native.internal + +import org.gradle.api.Project +import org.jetbrains.kotlin.descriptors.commonizer.CommonizerTarget +import org.jetbrains.kotlin.descriptors.commonizer.LeafCommonizerTarget +import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeCompilation +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinSharedNativeCompilation + +internal fun Project.getCommonizerTarget(compilation: KotlinCompilation<*>): CommonizerTarget? { + if (compilation is KotlinNativeCompilation) { + return LeafCommonizerTarget(compilation.konanTarget) + } + + if (compilation is KotlinSharedNativeCompilation) { + return getCommonizerTarget(compilation.defaultSourceSet) ?: CommonizerTarget(compilation.konanTargets) + } + + return null +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/GetCommonizerTargetOfSourceSet.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/GetCommonizerTargetOfSourceSet.kt new file mode 100644 index 00000000000..b7abe549bc6 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/GetCommonizerTargetOfSourceSet.kt @@ -0,0 +1,70 @@ +/* + * Copyright 2010-2020 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.targets.native.internal + +import org.gradle.api.Project +import org.jetbrains.kotlin.descriptors.commonizer.CommonizerTarget +import org.jetbrains.kotlin.descriptors.commonizer.LeafCommonizerTarget +import org.jetbrains.kotlin.descriptors.commonizer.SharedCommonizerTarget +import org.jetbrains.kotlin.gradle.dsl.multiplatformExtensionOrNull +import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation +import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet +import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSetContainer +import org.jetbrains.kotlin.gradle.plugin.mpp.CompilationSourceSetUtil.compilationsBySourceSets +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeCompilation +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinSharedNativeCompilation +import org.jetbrains.kotlin.konan.target.KonanTarget + +internal fun Project.getCommonizerTarget(sourceSet: KotlinSourceSet): CommonizerTarget? { + val kotlin = multiplatformExtensionOrNull ?: return null + val sourceSetsDependingOnThisSourceSet = kotlin.resolveSourceSetsDirectlyDependingOn(sourceSet) + if (sourceSetsDependingOnThisSourceSet.isEmpty()) { + return getLeafCommonizerTarget(sourceSet) + } + + val dependeeCommonizerTargets = sourceSetsDependingOnThisSourceSet + .map { sourceSetDependingOnThisSourceSet -> getCommonizerTarget(sourceSetDependingOnThisSourceSet) ?: return null } + .toSet() + + return when { + dependeeCommonizerTargets.isEmpty() -> throw IllegalStateException() + dependeeCommonizerTargets.size == 1 -> dependeeCommonizerTargets.single() + else -> SharedCommonizerTarget(dependeeCommonizerTargets) + } +} + +private fun Project.getLeafCommonizerTarget(sourceSet: KotlinSourceSet): LeafCommonizerTarget? { + val konanTargets = compilationsBySourceSets(this)[sourceSet].orEmpty() + .flatMap { compilation -> compilation.konanTargets } + + return when { + konanTargets.isEmpty() -> null + konanTargets.size == 1 -> LeafCommonizerTarget(konanTargets.single()) + else -> error("Source set ${sourceSet.name} is not a leaf source set. konanTargets=$konanTargets") + } + +} + +private fun KotlinSourceSetContainer.resolveSourceSetsDirectlyDependingOn(sourceSet: KotlinSourceSet): Set { + val dependeeSourceSets = sourceSets + .filter { candidateSourceSet -> candidateSourceSet != sourceSet } + .filter { candidateSourceSet -> sourceSet in candidateSourceSet.dependsOn } + + fun isTransitiveDependee(sourceSet: KotlinSourceSet): Boolean { + return sourceSet.dependsOn.any { it in dependeeSourceSets || isTransitiveDependee(it) } + } + + return dependeeSourceSets.filterNot(::isTransitiveDependee).toSet() +} + +private val KotlinCompilation<*>.konanTargets: Set + get() { + return when (this) { + is KotlinSharedNativeCompilation -> konanTargets.toSet() + is KotlinNativeCompilation -> setOf(konanTarget) + else -> emptySet() + } + } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/KotlinNativePlatformDependencies.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/KotlinNativePlatformDependencies.kt index ebc5ca21cfc..7aef1a2f21f 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/KotlinNativePlatformDependencies.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/KotlinNativePlatformDependencies.kt @@ -10,19 +10,13 @@ import org.gradle.api.file.FileCollection import org.jetbrains.kotlin.compilerRunner.konanHome import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension import org.jetbrains.kotlin.gradle.dsl.multiplatformExtensionOrNull -import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType -import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet -import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider +import org.jetbrains.kotlin.gradle.plugin.* import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.Companion.KOTLIN_NATIVE_HOME -import org.jetbrains.kotlin.gradle.plugin.compareVersionNumbers -import org.jetbrains.kotlin.gradle.plugin.getKotlinPluginVersion import org.jetbrains.kotlin.gradle.plugin.mpp.CompilationSourceSetUtil import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeCompilation import org.jetbrains.kotlin.gradle.targets.metadata.getMetadataCompilationForSourceSet import org.jetbrains.kotlin.gradle.targets.metadata.isKotlinGranularMetadataEnabled import org.jetbrains.kotlin.gradle.targets.native.internal.NativePlatformDependency.* -import org.jetbrains.kotlin.gradle.tasks.registerTask -import org.jetbrains.kotlin.gradle.tasks.withType import org.jetbrains.kotlin.gradle.utils.SingleWarningPerBuild import org.jetbrains.kotlin.konan.library.* import org.jetbrains.kotlin.konan.target.KonanTarget @@ -105,16 +99,10 @@ private class NativePlatformDependencyResolver(val project: Project, val kotlinV check(!alreadyResolved) alreadyResolved = true - val targetGroups: List = dependencies.keys.filterIsInstance() - - val commonizerTaskProvider = project.registerTask( - COMMONIZER_TASK_NAME, - CommonizerTask::class.java - ) { commonizerTask -> - commonizerTask.targetGroups = targetGroups.map { it.targets }.toSet() + project.commonizeNativeDistributionTask.configure { commonizerTask -> + commonizerTask.targetGroups = dependencies.keys.filterIsInstance().map { it.targets }.toSet() } - // then, resolve dependencies one by one dependencies.forEach { (dependency, actions) -> val fileCollection = when (dependency) { @@ -144,7 +132,7 @@ private class NativePlatformDependencyResolver(val project: Project, val kotlinV val commonizedLibsDir = project.nativeDistributionCommonizerOutputDirectory(dependency.targets) project.files(Callable { libsInCommonDir(commonizedLibsDir) - }).builtBy(commonizerTaskProvider) + }).builtBy(project.commonizeNativeDistributionTask) } @@ -153,7 +141,7 @@ private class NativePlatformDependencyResolver(val project: Project, val kotlinV val commonizedLibsDir = project.nativeDistributionCommonizerOutputDirectory(dependency.common.targets) project.files(Callable { libsInPlatformDir(commonizedLibsDir, dependency.target) + libsInCommonDir(commonizedLibsDir) - }).builtBy(commonizerTaskProvider) + }).builtBy(project.commonizeNativeDistributionTask) } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/CommonizerTask.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/NativeDistributionCommonizerTask.kt similarity index 99% rename from libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/CommonizerTask.kt rename to libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/NativeDistributionCommonizerTask.kt index 5b05b239bd2..dcc3daac3ab 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/CommonizerTask.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/NativeDistributionCommonizerTask.kt @@ -25,11 +25,10 @@ import java.nio.file.attribute.* import java.time.* import java.util.* -internal const val COMMONIZER_TASK_NAME = "runCommonizer" internal typealias KonanTargetGroup = Set -internal open class CommonizerTask : DefaultTask() { +internal open class NativeDistributionCommonizerTask : DefaultTask() { private val konanHome = project.file(project.konanHome) diff --git a/native/commonizer-api/src/org/jetbrains/kotlin/descriptors/commonizer/CommonizerTarget.kt b/native/commonizer-api/src/org/jetbrains/kotlin/descriptors/commonizer/CommonizerTarget.kt index cc7123b1895..97bf6d5986a 100644 --- a/native/commonizer-api/src/org/jetbrains/kotlin/descriptors/commonizer/CommonizerTarget.kt +++ b/native/commonizer-api/src/org/jetbrains/kotlin/descriptors/commonizer/CommonizerTarget.kt @@ -13,7 +13,7 @@ import java.io.Serializable // N.B. TargetPlatform/SimplePlatform are non exhaustive enough to address both target platforms such as // JVM, JS and concrete Kotlin/Native targets, e.g. macos_x64, ios_x64, linux_x64. public sealed class CommonizerTarget : Serializable { - final override fun toString(): String = identityString + final override fun toString(): String = prettyName } public data class LeafCommonizerTarget public constructor(val name: String) : CommonizerTarget() {