[Gradle] Draft implementation of Configurations for Commonized CInterops

Introduce *CommonizedCInterop & *CommonizedCInteropElements configurations
and fix CInteropCommonizerTask to consume dependencies / expose artifacts
via these configurations.
This commit is contained in:
Anton Lakotka
2023-08-08 02:31:53 +02:00
committed by Space Team
parent 640d1e3fc8
commit b37ebb47d9
4 changed files with 122 additions and 9 deletions
@@ -479,10 +479,10 @@ open class CommonizerIT : KGPBaseTest() {
@GradleTest
fun testCommonizationWithTwoCInteropCommonizerGroups(gradleVersion: GradleVersion) {
nativeProject("commonize-kt-57796-twoCInteropCommonizerGroups", gradleVersion) {
build(":app:commonizeCIntero") {
assertTasksExecuted(":lib:transformCommonMainCInteropDependenciesMetadata")
build(":app:commonizeCInterop") {
assertTasksNotExecuted(":lib:transformCommonMainCInteropDependenciesMetadata")
assertTasksExecuted(":lib:commonizeCInterop")
assertTasksExecuted(":app:transformCommonMainCInteropDependenciesMetadata")
assertTasksNotExecuted(":app:transformCommonMainCInteropDependenciesMetadata")
assertTasksExecuted(":app:commonizeCInterop")
}
}
@@ -28,6 +28,8 @@ import org.jetbrains.kotlin.gradle.targets.js.KotlinWasmTargetType
import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrTargetPreset
import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinWasmTargetPreset
import org.jetbrains.kotlin.gradle.targets.native.createFatFrameworks
import org.jetbrains.kotlin.gradle.targets.native.internal.commonizeCInteropTask
import org.jetbrains.kotlin.gradle.targets.native.internal.createCommonizedCInteropApiElementsKlibArtifact
import org.jetbrains.kotlin.gradle.targets.native.tasks.artifact.registerKotlinArtifactsExtension
import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompileTool
import org.jetbrains.kotlin.gradle.utils.checkGradleCompatibility
@@ -73,7 +75,13 @@ class KotlinMultiplatformPlugin : Plugin<Project> {
// Ensure that the instance is created and configured during apply
project.kotlinIdeMultiplatformImport
project.locateOrRegisterIdeResolveDependenciesTask()
// FIXME: This code creates consumable configurations for Commonized CInterops
// FIXME: Unfortunately, it also creates [commonizeCInteropTask] eagerly
// FIXME: Decouple [allInteropGroups] from task internals and then create configuration without task materialization.
project.launch {
val task = project.commonizeCInteropTask()?.get() ?: return@launch
project.createCommonizedCInteropApiElementsKlibArtifact(task)
}
project.addBuildListenerForXcode()
project.whenEvaluated { kotlinMultiplatformExtension.createFatFrameworks() }
}
@@ -137,6 +137,7 @@ internal abstract class CInteropCommonizerTask
val sourceSetsByGroup = multiplatformExtension.sourceSets.groupBy { sourceSet ->
CInteropCommonizerDependent.from(sourceSet)?.let { findInteropsGroup(it) }
}
allInteropGroups.await().associateWith { group ->
(group.targets + group.targets.allLeaves()).map { target ->
val externalDependencyFiles: List<FileCollection> = when (target) {
@@ -157,7 +158,19 @@ internal abstract class CInteropCommonizerTask
will provide the same dependencies (since cinterops are just based upon KonanTarget)
*/
.take(1)
.map { sourceSet -> project.createCInteropMetadataDependencyClasspath(sourceSet) }
.map { sourceSet ->
val configuration = project.locateOrCreateCommonizedCInteropDependencyConfiguration(sourceSet)
if (configuration != null) {
configuration.incoming
// Lenient is required since not every dependency exposes commonized CInterop KLibs
// Only Projects with CInterops would expose their commonization results.
// See: [createCommonizedCInteropApiElementsKlibArtifact]
.artifactView { it.isLenient = true }
.files
} else {
project.files()
}
}
}
}
@@ -226,6 +239,14 @@ internal abstract class CInteropCommonizerTask
?.flatMap { (target, dependencies) ->
dependencies.files
.filter { file -> file.exists() && (file.isDirectory || file.extension == "klib") }
.flatMap flatMap2@{ dir ->
// FIXME: dir can be either a klib dir or parent dir of few klib dirs
// FIXME: Find a way to expose exact klib-dirs in consumable configuration
if (!dir.isDirectory) return@flatMap2 listOf(dir)
val subDirs = dir.listFiles().orEmpty().toList()
fun List<File>.isKlibDir() = any { it.isDirectory && it.name == "default" }
if (subDirs.isKlibDir()) return@flatMap2 listOf(dir) else subDirs
}
.map { file -> TargetedCommonizerDependency(target, file) }
}
?.toSet()
@@ -9,17 +9,21 @@ import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration
import org.gradle.api.attributes.*
import org.gradle.api.tasks.TaskProvider
import org.jetbrains.kotlin.commonizer.CommonizerOutputFileLayout
import org.jetbrains.kotlin.commonizer.SharedCommonizerTarget
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.KotlinNativeTargetConfigurator.NativeArtifactFormat
import org.jetbrains.kotlin.gradle.plugin.KotlinPluginLifecycle.Stage.AfterFinaliseDsl
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
import org.jetbrains.kotlin.gradle.plugin.categoryByName
import org.jetbrains.kotlin.gradle.plugin.internal.artifactTypeAttribute
import org.jetbrains.kotlin.gradle.plugin.launchInStage
import org.jetbrains.kotlin.gradle.plugin.mpp.*
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.util.copyAttributes
import org.jetbrains.kotlin.gradle.plugin.usesPlatformOf
import org.jetbrains.kotlin.gradle.plugin.sources.internal
import org.jetbrains.kotlin.gradle.targets.native.internal.CInteropKlibLibraryElements.cinteropKlibLibraryElements
import org.jetbrains.kotlin.gradle.tasks.CInteropProcess
import org.jetbrains.kotlin.gradle.utils.markConsumable
import org.jetbrains.kotlin.gradle.utils.markResolvable
internal fun createCInteropApiElementsKlibArtifact(
target: KotlinNativeTarget,
@@ -37,6 +41,23 @@ internal fun createCInteropApiElementsKlibArtifact(
}
}
internal suspend fun Project.createCommonizedCInteropApiElementsKlibArtifact(
interopTask: CInteropCommonizerTask
) {
for (commonizerGroup in interopTask.allInteropGroups.await()) {
for (sharedCommonizerTargets in commonizerGroup.targets) {
val configuration = locateOrCreateCommonizedCInteropApiElementsConfiguration(sharedCommonizerTargets)
val artifactPath = CommonizerOutputFileLayout.resolveCommonizedDirectory(interopTask.outputDirectory(commonizerGroup), sharedCommonizerTargets)
project.artifacts.add(configuration.name, artifactPath) { artifact ->
artifact.extension = "klib"
artifact.type = "klib"
artifact.classifier = "cinterop-" + sharedCommonizerTargets.dashedIdentityString()
artifact.builtBy(interopTask)
}
}
}
}
internal fun Project.locateOrCreateCInteropDependencyConfiguration(
compilation: KotlinNativeCompilation,
): Configuration {
@@ -48,8 +69,7 @@ internal fun Project.locateOrCreateCInteropDependencyConfiguration(
return configurations.create(compilation.cInteropDependencyConfigurationName).apply {
extendsFrom(compileOnlyConfiguration, implementationConfiguration)
isVisible = false
isCanBeResolved = true
isCanBeConsumed = false
markResolvable()
/* Deferring attributes to wait for compilation.attributes to be configured by user*/
launchInStage(AfterFinaliseDsl) {
@@ -63,9 +83,46 @@ internal fun Project.locateOrCreateCInteropDependencyConfiguration(
}
}
private fun Configuration.applyAttributesForCommonizerTarget(commonizerTarget: SharedCommonizerTarget) {
attributes.attribute(KotlinPlatformType.attribute, KotlinPlatformType.native)
// TODO: [KotlinNativeTarget.konanTargetAttribute] is a public attribute. It should be better to introduce dedicated private attribute
// TODO: for commonized target
attributes.attribute(KotlinNativeTarget.konanTargetAttribute, commonizerTarget.dashedIdentityString())
}
internal suspend fun Project.locateOrCreateCommonizedCInteropDependencyConfiguration(
sourceSet: KotlinSourceSet,
): Configuration? {
val commonizerTarget = sourceSet.commonizerTarget.await() ?: return null
if (commonizerTarget !is SharedCommonizerTarget) return null
configurations.findByName(commonizerTarget.commonizedCInteropDependencyConfigurationName)?.let { return it }
val configuration = configurations.create(commonizerTarget.commonizedCInteropDependencyConfigurationName).apply {
isVisible = false
markResolvable()
/* Deferring attributes to wait for compilation.attributes to be configured by user*/
launchInStage(AfterFinaliseDsl) {
// Extends from Metadata Configuration associated with given source set to ensure matching
extendsFrom(sourceSet.internal.resolvableMetadataConfiguration)
applyAttributesForCommonizerTarget(commonizerTarget)
attributes.attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, cinteropKlibLibraryElements())
attributes.attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage::class.java, KotlinUsages.KOTLIN_CINTEROP))
attributes.attribute(Category.CATEGORY_ATTRIBUTE, project.categoryByName(Category.LIBRARY))
description = "Commonized CInterop dependencies for targets: '$commonizerTarget'."
}
}
return configuration
}
internal val KotlinNativeCompilation.cInteropDependencyConfigurationName: String
get() = compilation.disambiguateName("CInterop")
internal val SharedCommonizerTarget.commonizedCInteropDependencyConfigurationName: String
get() = dashedIdentityString() + "CInterop"
internal fun Project.locateOrCreateCInteropApiElementsConfiguration(target: KotlinTarget): Configuration {
val configurationName = cInteropApiElementsConfigurationName(target)
configurations.findByName(configurationName)?.let { return it }
@@ -86,10 +143,37 @@ internal fun Project.locateOrCreateCInteropApiElementsConfiguration(target: Kotl
}
}
internal fun Project.locateOrCreateCommonizedCInteropApiElementsConfiguration(commonizerTarget: SharedCommonizerTarget): Configuration {
val configurationName = commonizedCInteropApiElementsConfigurationName(commonizerTarget)
configurations.findByName(configurationName)?.let { return it }
return configurations.create(configurationName).apply {
markConsumable()
/* Deferring attributes to wait for target.attributes to be configured by user */
launchInStage(AfterFinaliseDsl) {
applyAttributesForCommonizerTarget(commonizerTarget)
attributes.attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, cinteropKlibLibraryElements())
attributes.attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage::class.java, KotlinUsages.KOTLIN_CINTEROP))
attributes.attribute(Category.CATEGORY_ATTRIBUTE, project.categoryByName(Category.LIBRARY))
attributes.attribute(artifactTypeAttribute, NativeArtifactFormat.KLIB)
}
}
}
private fun cInteropApiElementsConfigurationName(target: KotlinTarget): String {
return target.name + "CInteropApiElements"
}
/**
* Same as [org.jetbrains.kotlin.commonizer.identityString] but target segments separated with dash
*/
private fun SharedCommonizerTarget.dashedIdentityString() = targets.map { it.name }.sorted().joinToString("-")
private fun commonizedCInteropApiElementsConfigurationName(commonizerTarget: SharedCommonizerTarget): String {
return commonizerTarget.dashedIdentityString() + "CInteropApiElements"
}
internal object CInteropKlibLibraryElements {
const val CINTEROP_KLIB = "cinterop-klib"