[MPP] Enable cinterop commonization if CocoaPods plugin is applied

^KT-58712
This commit is contained in:
Timofey Solonin
2023-06-27 20:57:30 +02:00
committed by Space Team
parent caaade1f4f
commit 4695d9088a
16 changed files with 170 additions and 115 deletions
@@ -515,7 +515,7 @@ class CocoaPodsIT : KGPBaseTest() {
@GradleTest
fun testCinteropCommonizationOff(gradleVersion: GradleVersion) {
nativeProjectWithCocoapodsAndIosAppPodFile(cocoapodsCommonizationProjectName, gradleVersion) {
buildWithCocoapodsWrapper(":commonize") {
buildWithCocoapodsWrapper(":commonize", "-Pkotlin.mpp.enableCInteropCommonization=false") {
assertTasksExecuted(":commonizeNativeDistribution")
assertTasksNotExecuted(":cinteropAFNetworkingIosArm64")
assertTasksNotExecuted(":cinteropAFNetworkingIosX64")
@@ -527,8 +527,21 @@ class CocoaPodsIT : KGPBaseTest() {
@DisplayName("Cinterop commonization on")
@GradleTest
fun testCinteropCommonizationOn(gradleVersion: GradleVersion) {
testCinteropCommonizationExecutes(gradleVersion, buildArguments=arrayOf("-Pkotlin.mpp.enableCInteropCommonization=true"))
}
@DisplayName("Cinterop commonization unspecified")
@GradleTest
fun testCinteropCommonizationUnspecified(gradleVersion: GradleVersion) {
testCinteropCommonizationExecutes(gradleVersion, buildArguments=emptyArray())
}
private fun testCinteropCommonizationExecutes(
gradleVersion: GradleVersion,
buildArguments: Array<String>
) {
nativeProjectWithCocoapodsAndIosAppPodFile(cocoapodsCommonizationProjectName, gradleVersion) {
buildWithCocoapodsWrapper(":commonize", "-Pkotlin.mpp.enableCInteropCommonization=true") {
buildWithCocoapodsWrapper(":commonize", *buildArguments) {
assertTasksExecuted(":commonizeNativeDistribution")
assertTasksExecuted(":cinteropAFNetworkingIosArm64")
assertTasksExecuted(":cinteropAFNetworkingIosX64")
@@ -380,9 +380,16 @@ internal class PropertiesProvider private constructor(private val project: Proje
/**
* Enables experimental commonization of user defined c-interop libraries.
*/
val enableCInteropCommonization: Boolean
get() = booleanProperty(KOTLIN_MPP_ENABLE_CINTEROP_COMMONIZATION) ?: false
val enableCInteropCommonization: Boolean?
get() = booleanProperty(KOTLIN_MPP_ENABLE_CINTEROP_COMMONIZATION)
private val enableCInteropCommonizationSetByExternalPluginKey = "kotlin.internal.mpp.enableCInteropCommonization.setByExternalPlugin"
internal var enableCInteropCommonizationSetByExternalPlugin: Boolean?
get() = booleanProperty(enableCInteropCommonizationSetByExternalPluginKey)
set(value) {
project.extensions.extraProperties.set(enableCInteropCommonizationSetByExternalPluginKey, "$value")
}
val commonizerLogLevel: String?
get() = this.property("kotlin.mpp.commonizerLogLevel")
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.gradle.plugin.diagnostics.KotlinGradleProjectChecker
import org.jetbrains.kotlin.gradle.plugin.diagnostics.KotlinToolingDiagnostics
import org.jetbrains.kotlin.gradle.plugin.diagnostics.KotlinToolingDiagnosticsCollector
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinSharedNativeCompilation
import org.jetbrains.kotlin.gradle.targets.native.internal.*
import org.jetbrains.kotlin.gradle.targets.native.internal.CInteropCommonizerDependent
import org.jetbrains.kotlin.gradle.targets.native.internal.from
import org.jetbrains.kotlin.gradle.targets.native.internal.isAllowCommonizer
@@ -23,7 +24,7 @@ internal object DisabledCinteropCommonizationInHmppProjectChecker : KotlinGradle
if (multiplatformExtension == null
|| !project.isAllowCommonizer()
|| kotlinPropertiesProvider.enableCInteropCommonization
|| project.cInteropCommonizationEnabled()
|| kotlinPropertiesProvider.ignoreDisabledCInteropCommonization
) return
@@ -18,6 +18,7 @@ import org.jetbrains.kotlin.gradle.targets.native.internal.cinteropMetadataDepen
import org.jetbrains.kotlin.gradle.targets.native.internal.commonizerTarget
import org.jetbrains.kotlin.gradle.targets.native.internal.createCInteropMetadataDependencyClasspathForIde
import org.jetbrains.kotlin.gradle.tasks.locateTask
import org.jetbrains.kotlin.gradle.utils.future
internal object IdeCInteropMetadataDependencyClasspathResolver : IdeDependencyResolver, IdeDependencyResolver.WithBuildDependencies {
override fun resolve(sourceSet: KotlinSourceSet): Set<IdeaKotlinDependency> {
@@ -26,7 +27,7 @@ internal object IdeCInteropMetadataDependencyClasspathResolver : IdeDependencyRe
val project = sourceSet.project
project.locateDependencyTask(sourceSet) ?: return emptySet()
val cinteropFiles = project.createCInteropMetadataDependencyClasspathForIde(sourceSet)
val cinteropFiles = project.future { createCInteropMetadataDependencyClasspathForIde(sourceSet) }.getOrThrow()
return project.resolveCInteropDependencies(cinteropFiles)
}
@@ -12,6 +12,8 @@ import org.jetbrains.kotlin.gradle.plugin.ide.IdeDependencyResolver
import org.jetbrains.kotlin.gradle.plugin.sources.DefaultKotlinSourceSet
import org.jetbrains.kotlin.gradle.targets.native.internal.cinteropCommonizerDependencies
import org.jetbrains.kotlin.gradle.targets.native.internal.copyCommonizeCInteropForIdeTask
import org.jetbrains.kotlin.gradle.utils.future
import org.jetbrains.kotlin.gradle.utils.lenient
internal object IdeCommonizedCinteropDependencyResolver : IdeDependencyResolver, IdeDependencyResolver.WithBuildDependencies {
override fun resolve(sourceSet: KotlinSourceSet): Set<IdeaKotlinDependency> {
@@ -23,6 +25,6 @@ internal object IdeCommonizedCinteropDependencyResolver : IdeDependencyResolver,
}
override fun dependencies(project: Project): Iterable<Any> {
return listOfNotNull(project.copyCommonizeCInteropForIdeTask)
return listOfNotNull(project.future { copyCommonizeCInteropForIdeTask() }.getOrThrow())
}
}
@@ -85,8 +85,8 @@ private suspend fun KotlinMultiplatformExtension.sourceSetsMetadataOutputs(): Ma
private suspend fun KotlinMultiplatformExtension.cInteropMetadataOfSourceSets(
sourceSets: Iterable<KotlinSourceSet>
): Map<KotlinSourceSet, SourceSetMetadataOutputs.CInterop?> {
val taskForCLI = project.commonizeCInteropTask ?: return emptyMap()
val taskForIde = project.copyCommonizeCInteropForIdeTask ?: return emptyMap()
val taskForCLI = project.commonizeCInteropTask() ?: return emptyMap()
val taskForIde = project.copyCommonizeCInteropForIdeTask() ?: return emptyMap()
return sourceSets.associateWith { sourceSet ->
val dependent = CInteropCommonizerDependent.from(sourceSet) ?: return@associateWith null
@@ -37,6 +37,7 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.util.copyAttributes
import org.jetbrains.kotlin.gradle.plugin.sources.DefaultLanguageSettingsBuilder
import org.jetbrains.kotlin.gradle.targets.metadata.isKotlinGranularMetadataEnabled
import org.jetbrains.kotlin.gradle.targets.native.*
import org.jetbrains.kotlin.gradle.targets.native.internal.*
import org.jetbrains.kotlin.gradle.targets.native.internal.commonizeCInteropTask
import org.jetbrains.kotlin.gradle.targets.native.internal.createCInteropApiElementsKlibArtifact
import org.jetbrains.kotlin.gradle.targets.native.internal.locateOrCreateCInteropApiElementsConfiguration
@@ -149,8 +150,12 @@ open class KotlinNativeTargetConfigurator<T : KotlinNativeTarget> : AbstractKotl
it.enabled = compilation.konanTarget.enabledOnCurrentHost
}
project.commonizeCInteropTask?.configure { commonizeCInteropTask ->
commonizeCInteropTask.from(interopTask)
project.launch {
project.commonizeCInteropTask()?.configure { commonizeCInteropTask ->
commonizeCInteropTask.from(interopTask)
}
project.copyCommonizeCInteropForIdeTask()
}
val interopOutput = project.files(interopTask.map { it.outputFileProvider })
@@ -21,6 +21,7 @@ import org.gradle.api.tasks.wrapper.Wrapper
import org.jetbrains.kotlin.daemon.common.trimQuotes
import org.jetbrains.kotlin.gradle.dsl.*
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.Companion.kotlinPropertiesProvider
import org.jetbrains.kotlin.gradle.plugin.addExtension
import org.jetbrains.kotlin.gradle.plugin.cocoapods.CocoapodsExtension.CocoapodsDependency
import org.jetbrains.kotlin.gradle.plugin.diagnostics.reportDiagnostic
@@ -825,10 +826,15 @@ open class KotlinCocoapodsPlugin : Plugin<Project> {
}
}
// Enable cinterop commonization if it is not explicitly specified
private fun Project.enableCInteropCommonizationSetByExternalPlugin() {
kotlinPropertiesProvider.enableCInteropCommonizationSetByExternalPlugin = true
}
override fun apply(project: Project): Unit = with(project) {
pluginManager.withPlugin("kotlin-multiplatform") {
enableCInteropCommonizationSetByExternalPlugin()
val kotlinExtension = project.multiplatformExtension
val kotlinArtifactsExtension = project.kotlinArtifactsExtension
val cocoapodsExtension = project.objects.newInstance(CocoapodsExtension::class.java, this)
@@ -22,7 +22,7 @@ internal fun Project.includeCommonizedCInteropMetadata(
}
internal suspend fun Project.includeCommonizedCInteropMetadata(metadataKlib: Zip, compilation: KotlinSharedNativeCompilation) {
val commonizerTask = commonizeCInteropTask?.get() ?: return
val commonizerTask = commonizeCInteropTask()?.get() ?: return
val commonizerDependencyToken = CInteropCommonizerDependent.from(compilation) ?: return
val outputDirectory = commonizerTask.commonizedOutputDirectory(commonizerDependencyToken) ?: return
@@ -30,8 +30,8 @@ internal fun Project.setupCInteropCommonizerDependencies() {
}
}
private fun Project.setupCInteropCommonizerDependenciesForCompilation(compilation: KotlinSharedNativeCompilation) {
val cinteropCommonizerTask = project.commonizeCInteropTask ?: return
private fun Project.setupCInteropCommonizerDependenciesForCompilation(compilation: KotlinSharedNativeCompilation) = launch {
val cinteropCommonizerTask = project.commonizeCInteropTask() ?: return@launch
compilation.compileDependencyFiles += filesProvider {
val cinteropCommonizerDependent = future { CInteropCommonizerDependent.from(compilation) }.getOrThrow()
@@ -45,15 +45,15 @@ private fun Project.setupCInteropCommonizerDependenciesForCompilation(compilatio
* This will use the [Project.copyCommonizeCInteropForIdeTask] over the regular cinterop commonization task.
* The copying task prevent red code within the IDE after cleaning the build output.
*/
private fun Project.setupCInteropCommonizerDependenciesForIde(sourceSet: DefaultKotlinSourceSet) {
private fun Project.setupCInteropCommonizerDependenciesForIde(sourceSet: DefaultKotlinSourceSet) = launch {
addIntransitiveMetadataDependencyIfPossible(sourceSet, cinteropCommonizerDependencies(sourceSet))
}
internal fun Project.cinteropCommonizerDependencies(sourceSet: DefaultKotlinSourceSet): FileCollection {
val cinteropCommonizerTask = project.copyCommonizeCInteropForIdeTask ?: return project.files()
return filesProvider {
future {
val cinteropCommonizerTask = project.copyCommonizeCInteropForIdeTask() ?: return@future project.files()
val directlyDependent = CInteropCommonizerDependent.from(sourceSet)
val associateDependent = CInteropCommonizerDependent.fromAssociateCompilations(sourceSet)
@@ -16,13 +16,14 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.metadataDependencyResolutionsOrEmp
import org.jetbrains.kotlin.gradle.plugin.sources.DefaultKotlinSourceSet
import org.jetbrains.kotlin.gradle.plugin.sources.metadataTransformation
import org.jetbrains.kotlin.gradle.utils.filesProvider
import org.jetbrains.kotlin.gradle.utils.future
import java.io.File
internal fun Project.createCInteropMetadataDependencyClasspath(sourceSet: DefaultKotlinSourceSet): FileCollection {
internal suspend fun Project.createCInteropMetadataDependencyClasspath(sourceSet: DefaultKotlinSourceSet): FileCollection {
return createCInteropMetadataDependencyClasspath(sourceSet, forIde = false)
}
internal fun Project.createCInteropMetadataDependencyClasspathForIde(sourceSet: DefaultKotlinSourceSet): FileCollection {
internal suspend fun Project.createCInteropMetadataDependencyClasspathForIde(sourceSet: DefaultKotlinSourceSet): FileCollection {
return createCInteropMetadataDependencyClasspath(sourceSet, forIde = true)
}
@@ -30,7 +31,7 @@ internal fun Project.createCInteropMetadataDependencyClasspathForIde(sourceSet:
* @param forIde: A different task for dependency transformation will be used. This task will not use the regular 'build' directory
* as transformation output to ensure IDE still being able to resolve the dependencies even when the project is cleaned.
*/
internal fun Project.createCInteropMetadataDependencyClasspath(sourceSet: DefaultKotlinSourceSet, forIde: Boolean): FileCollection {
internal suspend fun Project.createCInteropMetadataDependencyClasspath(sourceSet: DefaultKotlinSourceSet, forIde: Boolean): FileCollection {
val dependencyTransformationTask = if (forIde) locateOrRegisterCInteropMetadataDependencyTransformationTaskForIde(sourceSet)
else locateOrRegisterCInteropMetadataDependencyTransformationTask(sourceSet)
if (dependencyTransformationTask == null) return project.files()
@@ -99,7 +100,7 @@ private fun Project.createCInteropMetadataDependencyClasspathFromAssociatedCompi
.filter { (_, otherCommonizerTarget) -> otherCommonizerTarget.targets.containsAll(commonizerTarget.targets) }
.minByOrNull { (_, otherCommonizerTarget) -> otherCommonizerTarget.targets.size } ?: return@files emptySet<File>()
createCInteropMetadataDependencyClasspath(associatedSourceSet, forIde)
project.future { createCInteropMetadataDependencyClasspath(associatedSourceSet, forIde) }.getOrThrow()
}
}
@@ -41,10 +41,10 @@ internal val KotlinSourceSet.cinteropMetadataDependencyTransformationTaskName: S
internal val KotlinSourceSet.cinteropMetadataDependencyTransformationForIdeTaskName: String
get() = lowerCamelCaseName("transform", name, "CInteropDependenciesMetadataForIde")
internal fun Project.locateOrRegisterCInteropMetadataDependencyTransformationTask(
internal suspend fun Project.locateOrRegisterCInteropMetadataDependencyTransformationTask(
sourceSet: DefaultKotlinSourceSet,
): TaskProvider<CInteropMetadataDependencyTransformationTask>? {
if (!kotlinPropertiesProvider.enableCInteropCommonization) return null
if (!cInteropCommonizationEnabled()) return null
return locateOrRegisterTask(
sourceSet.cinteropMetadataDependencyTransformationTaskName,
@@ -61,10 +61,10 @@ internal fun Project.locateOrRegisterCInteropMetadataDependencyTransformationTas
)
}
internal fun Project.locateOrRegisterCInteropMetadataDependencyTransformationTaskForIde(
internal suspend fun Project.locateOrRegisterCInteropMetadataDependencyTransformationTaskForIde(
sourceSet: DefaultKotlinSourceSet,
): TaskProvider<CInteropMetadataDependencyTransformationTask>? {
if (!kotlinPropertiesProvider.enableCInteropCommonization) return null
if (!cInteropCommonizationEnabled()) return null
return locateOrRegisterTask(
sourceSet.cinteropMetadataDependencyTransformationForIdeTaskName,
@@ -16,8 +16,11 @@ import org.gradle.api.tasks.TaskProvider
import org.gradle.work.DisableCachingByDefault
import org.jetbrains.kotlin.compilerRunner.maybeCreateCommonizerClasspathConfiguration
import org.jetbrains.kotlin.gradle.internal.isInIdeaSync
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.KotlinPluginLifecycle
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider
import org.jetbrains.kotlin.gradle.plugin.getKotlinPluginVersion
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.Companion.kotlinPropertiesProvider
import org.jetbrains.kotlin.gradle.plugin.await
import org.jetbrains.kotlin.gradle.plugin.ide.Idea222Api
import org.jetbrains.kotlin.gradle.plugin.ide.ideaImportDependsOn
import org.jetbrains.kotlin.gradle.plugin.whenEvaluated
@@ -26,7 +29,12 @@ import org.jetbrains.kotlin.gradle.tasks.locateOrRegisterTask
import java.io.File
import javax.inject.Inject
internal val Project.isCInteropCommonizationEnabled: Boolean get() = PropertiesProvider(this).enableCInteropCommonization
internal suspend fun Project.cInteropCommonizationEnabled(): Boolean {
KotlinPluginLifecycle.Stage.AfterEvaluateBuildscript.await()
return kotlinPropertiesProvider.enableCInteropCommonization
?: kotlinPropertiesProvider.enableCInteropCommonizationSetByExternalPlugin
?: false
}
internal val Project.isIntransitiveMetadataConfigurationEnabled: Boolean
get() = PropertiesProvider(this).enableIntransitiveMetadataConfiguration
@@ -67,55 +75,55 @@ internal val Project.runCommonizerTask: TaskProvider<Task>
}
)
internal val Project.commonizeCInteropTask: TaskProvider<CInteropCommonizerTask>?
get() {
if (isCInteropCommonizationEnabled) {
return locateOrRegisterTask(
"commonizeCInterop",
invokeWhenRegistered = {
val task = this
private const val commonizeCInteropTaskName = "commonizeCInterop"
internal suspend fun Project.commonizeCInteropTask(): TaskProvider<CInteropCommonizerTask>? {
if (cInteropCommonizationEnabled()) {
return locateOrRegisterTask(
commonizeCInteropTaskName,
invokeWhenRegistered = {
val task = this
commonizeTask.dependsOn(this)
whenEvaluated {
commonizeNativeDistributionTask?.let(task::dependsOn)
}
},
configureTask = {
group = "interop"
description = "Invokes the commonizer on c-interop bindings of the project"
kotlinPluginVersion.set(getKotlinPluginVersion())
commonizerClasspath.from(project.maybeCreateCommonizerClasspathConfiguration())
customJvmArgs.set(PropertiesProvider(project).commonizerJvmArgs)
}
)
}
return null
}
internal suspend fun Project.copyCommonizeCInteropForIdeTask(): TaskProvider<CopyCommonizeCInteropForIdeTask>? {
val commonizeCInteropTask = commonizeCInteropTask()
if (commonizeCInteropTask != null) {
return locateOrRegisterTask(
"copyCommonizeCInteropForIde",
invokeWhenRegistered = {
@OptIn(Idea222Api::class)
ideaImportDependsOn(this)
/* Older IDEs will still call 'runCommonizer' -> 'commonize' tasks */
if (isInIdeaSync) {
commonizeTask.dependsOn(this)
whenEvaluated {
commonizeNativeDistributionTask?.let(task::dependsOn)
}
},
configureTask = {
group = "interop"
description = "Invokes the commonizer on c-interop bindings of the project"
kotlinPluginVersion.set(getKotlinPluginVersion())
commonizerClasspath.from(project.maybeCreateCommonizerClasspathConfiguration())
customJvmArgs.set(PropertiesProvider(project).commonizerJvmArgs)
}
)
}
return null
}
internal val Project.copyCommonizeCInteropForIdeTask: TaskProvider<CopyCommonizeCInteropForIdeTask>?
get() {
val commonizeCInteropTask = commonizeCInteropTask
if (commonizeCInteropTask != null) {
return locateOrRegisterTask(
"copyCommonizeCInteropForIde",
invokeWhenRegistered = {
@OptIn(Idea222Api::class)
ideaImportDependsOn(this)
/* Older IDEs will still call 'runCommonizer' -> 'commonize' tasks */
if (isInIdeaSync) {
commonizeTask.dependsOn(this)
}
},
configureTask = {
group = "interop"
description = "Copies the output of ${commonizeCInteropTask.get().name} into " +
"the root projects .gradle folder for the IDE"
}
)
}
return null
},
configureTask = {
group = "interop"
description = "Copies the output of $commonizeCInteropTaskName into " +
"the root projects .gradle folder for the IDE"
}
)
}
return null
}
internal val Project.commonizeNativeDistributionTask: TaskProvider<NativeDistributionCommonizerTask>?
get() {
@@ -9,13 +9,14 @@ import org.gradle.api.provider.Provider
import org.gradle.api.tasks.*
import org.gradle.work.DisableCachingByDefault
import org.gradle.work.NormalizeLineEndings
import org.jetbrains.kotlin.gradle.utils.future
import java.io.File
@DisableCachingByDefault
internal open class CopyCommonizeCInteropForIdeTask : AbstractCInteropCommonizerTask() {
private val commonizeCInteropTask: TaskProvider<CInteropCommonizerTask>
get() = project.commonizeCInteropTask ?: throw IllegalStateException("Missing commonizeCInteropTask")
get() = project.future { project.commonizeCInteropTask() }.getOrThrow() ?: throw IllegalStateException("Missing commonizeCInteropTask")
@get:IgnoreEmptyDirectories
@get:InputFiles
@@ -7,7 +7,6 @@
package org.jetbrains.kotlin.gradle.unitTests
import org.gradle.api.GradleException
import org.jetbrains.kotlin.commonizer.CommonizerTarget
import org.jetbrains.kotlin.commonizer.SharedCommonizerTarget
import org.jetbrains.kotlin.gradle.util.MultiplatformExtensionTest
@@ -19,12 +18,17 @@ import org.jetbrains.kotlin.gradle.targets.native.internal.commonizeCInteropTask
import org.jetbrains.kotlin.gradle.util.enableCInteropCommonization
import org.jetbrains.kotlin.gradle.util.main
import org.jetbrains.kotlin.gradle.util.runLifecycleAwareTest
import org.jetbrains.kotlin.gradle.utils.Future
import org.jetbrains.kotlin.gradle.utils.future
import org.jetbrains.kotlin.konan.target.KonanTarget.*
import kotlin.test.*
class CInteropCommonizerTaskTest : MultiplatformExtensionTest() {
private val task: CInteropCommonizerTask get() = project.commonizeCInteropTask?.get() ?: fail("Missing commonizeCInteropTask")
private val task: Future<CInteropCommonizerTask>
get() = project.future {
project.commonizeCInteropTask()?.get() ?: fail("Missing commonizeCInteropTask")
}
@BeforeTest
override fun setup() {
@@ -34,7 +38,7 @@ class CInteropCommonizerTaskTest : MultiplatformExtensionTest() {
}
@Test
fun `commonizeCInteropTask configuration - avoids cinterop task configuration`() {
fun `commonizeCInteropTask configuration - avoids cinterop task configuration`() = project.runLifecycleAwareTest {
project.enableCInteropCommonization(true)
listOf(
@@ -46,12 +50,12 @@ class CInteropCommonizerTaskTest : MultiplatformExtensionTest() {
}
}
project.commonizeCInteropTask?.get() ?: fail("Expected commonizeCInteropTask to be present")
project.evaluate()
project.commonizeCInteropTask()?.get() ?: fail("Expected commonizeCInteropTask to be present")
}
@Test
fun `nativeMain linux macos`() = project.runLifecycleAwareTest {
val task = this@CInteropCommonizerTaskTest.task.await()
val linuxInterop = kotlin.linuxX64("linux").compilations.getByName("main").cinterops.create("anyInteropName")
val macosInterop = kotlin.macosX64("macos").compilations.getByName("main").cinterops.create("anyInteropName")
@@ -106,6 +110,7 @@ class CInteropCommonizerTaskTest : MultiplatformExtensionTest() {
@Test
fun `nativeMain iosMain linux macos iosX64 iosArm64`() = project.runLifecycleAwareTest {
val task = this@CInteropCommonizerTaskTest.task.await()
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
@@ -159,8 +164,9 @@ class CInteropCommonizerTaskTest : MultiplatformExtensionTest() {
}
private fun `nativeTest nativeMain linux macos`(
nativeTestDependsOnNativeMain: Boolean
nativeTestDependsOnNativeMain: Boolean,
) = project.runLifecycleAwareTest {
val task = this@CInteropCommonizerTaskTest.task.await()
val linuxInterop = kotlin.linuxX64("linux").compilations.getByName("main").cinterops.create("anyInteropName").identifier
val macosInterop = kotlin.macosX64("macos").compilations.getByName("main").cinterops.create("anyInteropName").identifier
@@ -222,8 +228,9 @@ class CInteropCommonizerTaskTest : MultiplatformExtensionTest() {
}
private fun `nativeTest nativeMain linux macos - test compilation defines custom cinterop`(
nativeTestDependsOnNativeMain: Boolean
nativeTestDependsOnNativeMain: Boolean,
) = project.runLifecycleAwareTest {
val task = this@CInteropCommonizerTaskTest.task.await()
val linuxInterop = kotlin.linuxX64("linux").compilations.getByName("main").cinterops.create("anyInteropName").identifier
val macosInterop = kotlin.macosX64("macos").compilations.getByName("main").cinterops.create("anyInteropName").identifier
kotlin.linuxX64("linux").compilations.getByName("test").cinterops.create("anyOtherName").identifier
@@ -285,6 +292,7 @@ class CInteropCommonizerTaskTest : MultiplatformExtensionTest() {
}
private fun `hierarchical project`(testSourceSetsDependOnMainSourceSets: Boolean) = project.runLifecycleAwareTest {
val task = this@CInteropCommonizerTaskTest.task.await()
/* Define targets */
val linux = kotlin.linuxX64("linux")
val macos = kotlin.macosX64("macos")
@@ -11,21 +11,20 @@ import org.gradle.api.Project
import org.gradle.api.internal.TaskInternal
import org.gradle.testfixtures.ProjectBuilder
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension
import org.jetbrains.kotlin.gradle.plugin.KotlinPluginLifecycle
import org.jetbrains.kotlin.gradle.plugin.await
import org.jetbrains.kotlin.gradle.plugin.sources.DefaultKotlinSourceSet
import org.jetbrains.kotlin.gradle.targets.native.internal.locateOrRegisterCInteropMetadataDependencyTransformationTask
import org.jetbrains.kotlin.gradle.targets.native.internal.locateOrRegisterCInteropMetadataDependencyTransformationTaskForIde
import org.jetbrains.kotlin.gradle.util.MultiplatformExtensionTest
import org.jetbrains.kotlin.gradle.util.buildProjectWithMPP
import org.jetbrains.kotlin.gradle.util.enableCInteropCommonization
import org.jetbrains.kotlin.gradle.util.kotlin
import org.jetbrains.kotlin.gradle.util.*
import java.io.File
import kotlin.test.*
class CInteropMetadataDependencyTransformationTaskTest : MultiplatformExtensionTest() {
@Test
fun `task not registered when cinterop commonization is disabled`() {
project.enableCInteropCommonization(false)
fun `task not registered when cinterop commonization is disabled`() = project.runLifecycleAwareTest {
enableCInteropCommonization(false)
kotlin.linuxArm64()
kotlin.linuxX64()
@@ -40,13 +39,13 @@ class CInteropMetadataDependencyTransformationTaskTest : MultiplatformExtensionT
linuxX64Main.dependsOn(linuxMain)
/* Expect no tasks being registered without the cinterop commonization feature flag */
assertNull(project.locateOrRegisterCInteropMetadataDependencyTransformationTask(linuxMain))
assertNull(project.locateOrRegisterCInteropMetadataDependencyTransformationTaskForIde(linuxMain))
assertNull(locateOrRegisterCInteropMetadataDependencyTransformationTask(linuxMain))
assertNull(locateOrRegisterCInteropMetadataDependencyTransformationTaskForIde(linuxMain))
}
@Test
fun `test task ordering`() {
project.enableCInteropCommonization(true)
fun `test task ordering`() = project.runLifecycleAwareTest {
enableCInteropCommonization(true)
kotlin.linuxX64()
kotlin.linuxArm64()
@@ -67,16 +66,17 @@ class CInteropMetadataDependencyTransformationTaskTest : MultiplatformExtensionT
linuxX64Test.dependsOn(nativeTest)
linuxArm64Test.dependsOn(nativeTest)
project.evaluate()
val nativeTestTransformationTask = project.locateOrRegisterCInteropMetadataDependencyTransformationTask(nativeTest)
KotlinPluginLifecycle.Stage.ReadyForExecution.await()
val nativeTestTransformationTask = locateOrRegisterCInteropMetadataDependencyTransformationTask(nativeTest)
assertNotNull(nativeTestTransformationTask, "Expected transformation task registered for 'nativeTest'")
assertEquals(
listOf(commonMain, commonTest, nativeMain).flatMap { sourceSet ->
listOf(
project.locateOrRegisterCInteropMetadataDependencyTransformationTask(sourceSet as DefaultKotlinSourceSet)?.get()
locateOrRegisterCInteropMetadataDependencyTransformationTask(sourceSet as DefaultKotlinSourceSet)?.get()
?: fail("Expected transformation task registered for '${sourceSet.name}'"),
project.locateOrRegisterCInteropMetadataDependencyTransformationTaskForIde(sourceSet)?.get()
locateOrRegisterCInteropMetadataDependencyTransformationTaskForIde(sourceSet)?.get()
?: fail("Expected transformation task registered for '${sourceSet.name}'(forIde)")
)
}.toSet(),
@@ -101,31 +101,33 @@ class CInteropMetadataDependencyTransformationTaskTest : MultiplatformExtensionT
linuxArm64Main.dependsOn(linuxMain)
linuxX64Main.dependsOn(linuxMain)
project.evaluate()
project.runLifecycleAwareTest {
KotlinPluginLifecycle.Stage.ReadyForExecution.await()
listOf(
"commonMain", "jvmMain", "linuxArm64Main", "linuxX64Main"
).map { sourceSetName -> kotlin.sourceSets.getByName(sourceSetName) }.forEach { sourceSet ->
val task = project.locateOrRegisterCInteropMetadataDependencyTransformationTask(sourceSet as DefaultKotlinSourceSet)
?: return@forEach
listOf(
"commonMain", "jvmMain", "linuxArm64Main", "linuxX64Main"
).map { sourceSetName -> kotlin.sourceSets.getByName(sourceSetName) }.forEach { sourceSet ->
val task = locateOrRegisterCInteropMetadataDependencyTransformationTask(sourceSet as DefaultKotlinSourceSet)
?: return@forEach
assertFalse(
task.get().onlyIf.isSatisfiedBy(task.get() as TaskInternal),
"Expected task ${task.name} to be disabled (not a shared native source set)"
assertFalse(
task.get().onlyIf.isSatisfiedBy(task.get() as TaskInternal),
"Expected task ${task.name} to be disabled (not a shared native source set)"
)
}
val linuxMainTask = locateOrRegisterCInteropMetadataDependencyTransformationTaskForIde(linuxMain)
?: fail("Expected transformation task registered for 'linuxMain'")
assertTrue(
linuxMainTask.get().onlyIf.isSatisfiedBy(linuxMainTask.get() as TaskInternal),
"Expected task ${linuxMainTask.name} to be enabled"
)
}
val linuxMainTask = project.locateOrRegisterCInteropMetadataDependencyTransformationTaskForIde(linuxMain)
?: fail("Expected transformation task registered for 'linuxMain'")
assertTrue(
linuxMainTask.get().onlyIf.isSatisfiedBy(linuxMainTask.get() as TaskInternal),
"Expected task ${linuxMainTask.name} to be enabled"
)
}
@Test
fun `test IDE task outputs doesnt conflict`() {
fun `test IDE task outputs doesnt conflict`() = project.runLifecycleAwareTest {
fun projectWithCinterops(name: String, parent: Project? = null) = buildProjectWithMPP(
projectBuilder = { withName(name); if (parent != null) withParent(parent) }
) {
@@ -137,7 +139,7 @@ class CInteropMetadataDependencyTransformationTaskTest : MultiplatformExtensionT
}
}.also { it.evaluate() }
fun Project.transformationTaskOutputs(): Set<File> {
suspend fun Project.transformationTaskOutputs(): Set<File> {
val kotlin = multiplatformExtension
val nativeMain = kotlin.sourceSets.findByName("nativeMain") ?: fail("Expected source set 'nativeMain")
val cinteropTransformationTaskProvider = locateOrRegisterCInteropMetadataDependencyTransformationTaskForIde(
@@ -154,7 +156,7 @@ class CInteropMetadataDependencyTransformationTaskTest : MultiplatformExtensionT
.minus(File(".gradle/kotlin/kotlinTransformedCInteropMetadataLibraries"))
}
fun assertTasksOutputsDoesntIntersect(a: Project, b: Project) {
suspend fun assertTasksOutputsDoesntIntersect(a: Project, b: Project) {
val outputsA = a.transformationTaskOutputs()
val outputsB = b.transformationTaskOutputs()