[KT-51517] Pass external cinterop dependencies to Commonizer.

This commit is contained in:
konstantin.tskhovrebov
2022-09-21 14:33:11 +02:00
committed by Space
parent a209098a6c
commit c9184c6781
13 changed files with 187 additions and 12 deletions
@@ -700,6 +700,20 @@ class CommonizerIT : BaseGradleIT() {
}
}
@Test
fun `test KT-51517 commonization with transitive cinterop`() {
with(Project("commonize-kt-51517-transitive-cinterop")) {
build(":app:assemble") {
assertSuccessful()
assertTasksExecuted(":lib:transformCommonMainCInteropDependenciesMetadata")
assertTasksExecuted(":lib:commonizeCInterop")
assertTasksExecuted(":lib:compileCommonMainKotlinMetadata")
assertTasksExecuted(":app:commonizeCInterop")
assertTasksExecuted(":app:compileNativeMainKotlinMetadata")
}
}
}
private fun preparedProject(name: String): Project {
return Project(name).apply {
setupWorkingDir()
@@ -0,0 +1,50 @@
plugins {
kotlin("multiplatform")
}
kotlin {
jvm()
listOf(linuxX64(), linuxArm64(), mingwX64()).forEach {
it.compilations.getByName("main") {
cinterops.create("yummy") {
val nativeLibs = rootDir.resolve("native-libs")
defFile = nativeLibs.resolve("yummy.def")
compilerOpts += "-I" + nativeLibs.absolutePath
}
}
}
sourceSets {
val commonMain by getting
val linuxX64Main by getting
val linuxArm64Main by getting
val mingwX64Main by getting
val linuxMain by creating {
linuxX64Main.dependsOn(this)
linuxArm64Main.dependsOn(this)
}
val nativeMain by creating {
this.dependsOn(commonMain)
linuxMain.dependsOn(this)
mingwX64Main.dependsOn(this)
dependencies {
implementation(project(":lib"))
}
}
val commonTest by getting
val linuxX64Test by getting
val linuxArm64Test by getting
val mingwX64Test by getting
val linuxTest by creating {
linuxX64Test.dependsOn(this)
linuxArm64Test.dependsOn(this)
}
val nativeTest by creating {
this.dependsOn(commonTest)
linuxTest.dependsOn(this)
mingwX64Test.dependsOn(this)
}
}
}
@@ -0,0 +1,16 @@
import dummy.LENGTH
import dummy.Y
import dummy.foo
import kotlinx.cinterop.cValue
import yummy.sel
import yummy.yummy
fun nativeMain() {
val y = cValue<Y> {
n = 42
}
yummy(y)
foo()
sel(LENGTH)
dummyMain()
}
@@ -0,0 +1,10 @@
plugins {
kotlin("multiplatform").apply(false)
}
allprojects {
repositories {
mavenLocal()
mavenCentral()
}
}
@@ -0,0 +1,15 @@
plugins {
kotlin("multiplatform")
}
kotlin {
listOf(linuxX64(), linuxArm64(), mingwX64()).forEach {
it.compilations.getByName("main") {
cinterops.create("dummy") {
val nativeLibs = rootDir.resolve("native-libs")
defFile = nativeLibs.resolve("dummy.def")
compilerOpts += "-I" + nativeLibs.absolutePath
}
}
}
}
@@ -0,0 +1,4 @@
#include "dummy.h"
void yummy(struct Y * ee);
void sel(int i);
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.gradle.targets.native.internal
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.FileCollection
import org.gradle.api.file.ProjectLayout
import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.ListProperty
@@ -13,21 +14,21 @@ import org.gradle.api.provider.Property
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.*
import org.gradle.process.ExecOperations
import org.jetbrains.kotlin.commonizer.CommonizerDependency
import org.jetbrains.kotlin.commonizer.TargetedCommonizerDependency
import org.jetbrains.kotlin.commonizer.allLeaves
import org.jetbrains.kotlin.compilerRunner.*
import org.jetbrains.kotlin.commonizer.*
import org.jetbrains.kotlin.compilerRunner.GradleCliCommonizer
import org.jetbrains.kotlin.compilerRunner.KotlinNativeCommonizerToolRunner
import org.jetbrains.kotlin.compilerRunner.KotlinToolRunner
import org.jetbrains.kotlin.compilerRunner.konanHome
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.mpp.KotlinSharedNativeCompilation
import org.jetbrains.kotlin.gradle.plugin.sources.DefaultKotlinSourceSet
import org.jetbrains.kotlin.gradle.plugin.sources.withDependsOnClosure
import org.jetbrains.kotlin.gradle.targets.native.internal.CInteropCommonizerTask.CInteropGist
import org.jetbrains.kotlin.gradle.tasks.CInteropProcess
import org.jetbrains.kotlin.gradle.utils.chainedFinalizeValueOnRead
import org.jetbrains.kotlin.gradle.utils.filesProvider
import org.jetbrains.kotlin.gradle.utils.listProperty
import org.jetbrains.kotlin.gradle.utils.property
import org.jetbrains.kotlin.konan.target.KonanTarget
@@ -48,7 +49,10 @@ internal open class CInteropCommonizerTask
sourceSets: Provider<Set<KotlinSourceSet>>,
@get:Classpath
val libraryFile: Provider<File>
val libraryFile: Provider<File>,
@get:Classpath
val dependencies: FileCollection
) {
@Suppress("unused") // Used for UP-TO-DATE check
@get:Input
@@ -65,6 +69,7 @@ internal open class CInteropCommonizerTask
if (identifier != other.identifier) return false
if (konanTarget != other.konanTarget) return false
if (libraryFile != other.libraryFile) return false
if (dependencies != other.dependencies) return false
if (allSourceSetNames != other.allSourceSetNames) return false
return true
@@ -75,6 +80,7 @@ internal open class CInteropCommonizerTask
var result = identifier.hashCode()
result = 31 * result + konanTarget.hashCode()
result = 31 * result + libraryFile.hashCode()
result = 31 * result + dependencies.hashCode()
result = 31 * result + allSourceSetNames.hashCode()
return result
}
@@ -109,18 +115,58 @@ internal open class CInteropCommonizerTask
private val commonizerLogLevel = project.commonizerLogLevel
private val additionalCommonizerSettings = project.additionalCommonizerSettings
data class CInteropCommonizerDependencies(
val commonizerTarget: CommonizerTarget,
val dependencies: FileCollection
)
/**
* For Gradle Configuration Cache support the Group-to-Dependencies relation should be pre-cached.
* It is used during execution phase.
*/
private val nativeDistributionDependenciesMap: Map<CInteropCommonizerGroup, Set<CommonizerDependency>> by lazy {
private val groupedCommonizerDependencies: Map<CInteropCommonizerGroup, List<CInteropCommonizerDependencies>> by lazy {
val multiplatformExtension = project.multiplatformExtensionOrNull ?: return@lazy emptyMap()
val sourceSetsByTarget = multiplatformExtension.sourceSets.groupBy { sourceSet ->
project.getCommonizerTarget(sourceSet)
}
val sourceSetsByGroup = multiplatformExtension.sourceSets.groupBy { sourceSet ->
CInteropCommonizerDependent.from(project, sourceSet)?.let { findInteropsGroup(it) }
}
getAllInteropsGroups().associateWith { group ->
(group.targets + group.targets.allLeaves()).flatMapTo(mutableSetOf()) { target ->
project.getNativeDistributionDependencies(target).map { dependency -> TargetedCommonizerDependency(target, dependency) }
(group.targets + group.targets.allLeaves()).map { target ->
val externalDependencyFiles = when (target) {
is LeafCommonizerTarget -> {
cinterops
.filter { cinterop -> cinterop.identifier in group.interops && cinterop.konanTarget == target.konanTarget }
.map { cinterop -> cinterop.dependencies }
}
is SharedCommonizerTarget -> {
val targetSourceSets = sourceSetsByTarget[target].orEmpty()
val groupSourceSets = sourceSetsByGroup[group].orEmpty().toSet()
targetSourceSets.intersect(groupSourceSets)
.filterIsInstance<DefaultKotlinSourceSet>()
.map { sourceSet -> project.createCInteropMetadataDependencyClasspath(sourceSet) }
}
}
val nativeDistributionDependencyFiles = project.getNativeDistributionDependencies(target)
CInteropCommonizerDependencies(
target,
project.files(externalDependencyFiles + nativeDistributionDependencyFiles)
)
}
}
}
@Suppress("unused") // Used for UP-TO-DATE check
@get:Classpath
protected val commonizerDependenciesClasspath: FileCollection
get() = project.files(
groupedCommonizerDependencies.values.flatten().map { it.dependencies }
)
@get:Nested
internal var cinterops = setOf<CInteropGist>()
private set
@@ -169,15 +215,21 @@ internal open class CInteropCommonizerTask
konanHome = konanHome,
outputTargets = group.targets,
inputLibraries = cinteropsForTarget.map { it.libraryFile.get() }.filter { it.exists() }.toSet(),
dependencyLibraries = getNativeDistributionDependencies(group),
dependencyLibraries = getCInteropCommonizerGroupDependencies(group),
outputDirectory = outputDirectory(group),
logLevel = commonizerLogLevel,
additionalSettings = additionalCommonizerSettings,
)
}
private fun getNativeDistributionDependencies(group: CInteropCommonizerGroup): Set<CommonizerDependency> {
val dependencies = nativeDistributionDependenciesMap[group]
private fun getCInteropCommonizerGroupDependencies(group: CInteropCommonizerGroup): Set<CommonizerDependency> {
val dependencies = groupedCommonizerDependencies[group]
?.flatMap { (target, dependencies) ->
dependencies.files
.filter { file -> file.exists() && (file.isDirectory || file.extension == "klib") }
.map { file -> TargetedCommonizerDependency(target, file) }
}
?.toSet()
requireNotNull(dependencies) { "Unexpected $group" }
return dependencies
@@ -242,6 +294,7 @@ private fun CInteropProcess.toGist(): CInteropGist {
konanTarget = konanTarget,
// FIXME support cinterop with PM20
sourceSets = project.provider { (settings.compilation as? KotlinCompilation<*>)?.kotlinSourceSets },
libraryFile = outputFileProvider
libraryFile = outputFileProvider,
dependencies = libraries
)
}