[Gradle] Use 'commonized cinterop configurations' as single way of sharing cinterop commonizer output
^KT-59596 Verification Pending
This commit is contained in:
committed by
Space Team
parent
b2bef4ad41
commit
9866336a40
+4
-1
@@ -25,7 +25,6 @@ import org.gradle.api.logging.Logging
|
||||
import org.gradle.api.model.ObjectFactory
|
||||
import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry
|
||||
import org.jetbrains.kotlin.compilerRunner.maybeCreateCommonizerClasspathConfiguration
|
||||
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
|
||||
import org.jetbrains.kotlin.gradle.dsl.*
|
||||
import org.jetbrains.kotlin.gradle.internal.KOTLIN_BUILD_TOOLS_API_IMPL
|
||||
import org.jetbrains.kotlin.gradle.internal.KOTLIN_COMPILER_EMBEDDABLE
|
||||
@@ -54,6 +53,8 @@ import org.jetbrains.kotlin.gradle.targets.js.nodejs.UnameExecutor
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.addNpmDependencyExtension
|
||||
import org.jetbrains.kotlin.gradle.targets.metadata.isKotlinGranularMetadataEnabled
|
||||
import org.jetbrains.kotlin.gradle.targets.native.internal.CInteropKlibLibraryElements
|
||||
import org.jetbrains.kotlin.gradle.targets.native.internal.CInteropCommonizerArtifactTypeAttribute
|
||||
import org.jetbrains.kotlin.gradle.targets.native.internal.CommonizerTargetAttribute
|
||||
import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompileTool
|
||||
import org.jetbrains.kotlin.gradle.testing.internal.KotlinTestsRegistry
|
||||
import org.jetbrains.kotlin.gradle.tooling.registerBuildKotlinToolingMetadataTask
|
||||
@@ -205,6 +206,8 @@ abstract class DefaultKotlinBasePlugin : KotlinBasePlugin {
|
||||
KotlinWasmTargetAttribute.setupAttributesMatchingStrategy(project.dependencies.attributesSchema)
|
||||
ProjectLocalConfigurations.setupAttributesMatchingStrategy(this)
|
||||
CInteropKlibLibraryElements.setupAttributesMatchingStrategy(this)
|
||||
CommonizerTargetAttribute.setupAttributesMatchingStrategy(this)
|
||||
CInteropCommonizerArtifactTypeAttribute.setupTransform(project)
|
||||
}
|
||||
|
||||
open fun whenBuildEvaluated(project: Project) {
|
||||
|
||||
-2
@@ -76,9 +76,7 @@ internal sealed class MetadataDependencyResolution(
|
||||
|
||||
abstract class ProjectMetadataProvider : MetadataProvider() {
|
||||
enum class MetadataConsumer { Ide, Cli }
|
||||
|
||||
abstract fun getSourceSetCompiledMetadata(sourceSetName: String): FileCollection?
|
||||
abstract fun getSourceSetCInteropMetadata(sourceSetName: String, consumer: MetadataConsumer): FileCollection?
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-36
@@ -27,13 +27,7 @@ internal fun ProjectMetadataProvider(
|
||||
|
||||
internal class SourceSetMetadataOutputs(
|
||||
val metadata: FileCollection?,
|
||||
val cinterop: CInterop?,
|
||||
) {
|
||||
class CInterop(
|
||||
val forCli: FileCollection,
|
||||
val forIde: FileCollection,
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
private class ProjectMetadataProviderImpl(
|
||||
private val sourceSetMetadataOutputs: Map<SourceSetName, SourceSetMetadataOutputs>,
|
||||
@@ -44,15 +38,6 @@ private class ProjectMetadataProviderImpl(
|
||||
return metadataOutputs.metadata
|
||||
}
|
||||
|
||||
|
||||
override fun getSourceSetCInteropMetadata(sourceSetName: String, consumer: MetadataConsumer): FileCollection? {
|
||||
val metadataOutputs = sourceSetMetadataOutputs[sourceSetName] ?: error("Unexpected source set '$sourceSetName'")
|
||||
val cinteropMetadata = metadataOutputs.cinterop ?: return null
|
||||
return when (consumer) {
|
||||
MetadataConsumer.Ide -> cinteropMetadata.forIde
|
||||
MetadataConsumer.Cli -> cinteropMetadata.forCli
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal suspend fun Project.collectSourceSetMetadataOutputs(): Map<SourceSetName, SourceSetMetadataOutputs> {
|
||||
@@ -72,14 +57,9 @@ internal suspend fun Project.collectSourceSetMetadataOutputs(): Map<SourceSetNam
|
||||
val multiplatformExtension = multiplatformExtensionOrNull ?: return emptyMap()
|
||||
|
||||
val sourceSetMetadata = multiplatformExtension.sourceSetsMetadataOutputs()
|
||||
val sourceSetCInteropMetadata = multiplatformExtension.cInteropMetadataOfSourceSets(sourceSetMetadata.keys)
|
||||
|
||||
return sourceSetMetadata.mapValues { (sourceSet, metadata) ->
|
||||
val cinteropMetadataOutput = sourceSetCInteropMetadata[sourceSet]
|
||||
SourceSetMetadataOutputs(
|
||||
metadata = metadata,
|
||||
cinterop = cinteropMetadataOutput
|
||||
)
|
||||
return sourceSetMetadata.mapValues { (_, metadata) ->
|
||||
SourceSetMetadataOutputs(metadata = metadata,)
|
||||
}.mapKeys { it.key.name }
|
||||
}
|
||||
|
||||
@@ -96,17 +76,4 @@ 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()
|
||||
|
||||
return sourceSets.associateWith { sourceSet ->
|
||||
val dependent = CInteropCommonizerDependent.from(sourceSet) ?: return@associateWith null
|
||||
SourceSetMetadataOutputs.CInterop(
|
||||
forCli = taskForCLI.get().commonizedOutputLibraries(dependent),
|
||||
forIde = taskForIde.get().commonizedOutputLibraries(dependent)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+1
-3
@@ -23,8 +23,6 @@ import java.io.File
|
||||
internal abstract class AbstractCInteropCommonizerTask : DefaultTask(), UsesBuildMetricsService {
|
||||
@get:OutputDirectory
|
||||
abstract val outputDirectory: File
|
||||
|
||||
internal abstract suspend fun findInteropsGroup(dependent: CInteropCommonizerDependent): CInteropCommonizerGroup?
|
||||
}
|
||||
|
||||
internal fun AbstractCInteropCommonizerTask.outputDirectory(group: CInteropCommonizerGroup): File {
|
||||
@@ -48,7 +46,7 @@ internal fun AbstractCInteropCommonizerTask.commonizedOutputLibraries(dependent:
|
||||
}
|
||||
|
||||
internal suspend fun AbstractCInteropCommonizerTask.commonizedOutputDirectory(dependent: CInteropCommonizerDependent): File? {
|
||||
val group = findInteropsGroup(dependent) ?: return null
|
||||
val group = project.findCInteropCommonizerGroup(dependent) ?: return null
|
||||
return CommonizerOutputFileLayout
|
||||
.resolveCommonizedDirectory(outputDirectory(group), dependent.target)
|
||||
}
|
||||
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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.artifacts.transform.InputArtifact
|
||||
import org.gradle.api.artifacts.transform.TransformAction
|
||||
import org.gradle.api.artifacts.transform.TransformOutputs
|
||||
import org.gradle.api.artifacts.transform.TransformParameters
|
||||
import org.gradle.api.attributes.Attribute
|
||||
import org.gradle.api.file.FileSystemLocation
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.api.tasks.PathSensitive
|
||||
import org.gradle.api.tasks.PathSensitivity
|
||||
import org.gradle.work.DisableCachingByDefault
|
||||
import org.jetbrains.kotlin.gradle.targets.native.internal.CInteropCommonizerArtifactTypeAttribute.KLIB
|
||||
import org.jetbrains.kotlin.gradle.targets.native.internal.CInteropCommonizerArtifactTypeAttribute.KLIB_COLLECTION_DIR
|
||||
|
||||
/**
|
||||
* Commonized CInterop artifacts can come in two shapes:
|
||||
* [KLIB]: Just a 'klib' directory or packaged file representing one commonized library
|
||||
* [KLIB_COLLECTION_DIR]: A flat directory containing several klib dirs or files representing the output of a commonizer task
|
||||
*/
|
||||
internal object CInteropCommonizerArtifactTypeAttribute {
|
||||
val attribute: Attribute<String> = Attribute.of("org.jetbrains.kotlin.cinteropCommonizerArtifactType", String::class.java)
|
||||
|
||||
/**
|
||||
* 'Regular'/'Typical' klib in the form of a 'directory' or '.klib' file representing a single library
|
||||
*/
|
||||
const val KLIB = "klib"
|
||||
|
||||
/**
|
||||
* Directory containing klibs as direct children:
|
||||
* e.g. output
|
||||
*
|
||||
* ```
|
||||
* build/some/output/
|
||||
* - foo.klib
|
||||
* - bar.klib
|
||||
* - ...
|
||||
* ```
|
||||
*
|
||||
* In this case the artifact will just be the 'output' directory that contains the collection of klibs
|
||||
*/
|
||||
const val KLIB_COLLECTION_DIR = "klib-collection-dir"
|
||||
|
||||
/**
|
||||
* Set up a transformation from artifacts of type 'collection dir' to a set of klibs.
|
||||
*/
|
||||
fun setupTransform(project: Project) {
|
||||
project.dependencies.artifactTypes.maybeCreate(KLIB_COLLECTION_DIR).also { artifactType ->
|
||||
artifactType.attributes.attribute(attribute, KLIB_COLLECTION_DIR)
|
||||
}
|
||||
|
||||
project.dependencies.artifactTypes.maybeCreate(KLIB).also { artifactType ->
|
||||
artifactType.attributes.attribute(attribute, KLIB)
|
||||
}
|
||||
|
||||
project.dependencies.registerTransform(KlibCollectionDirTransform::class.java) { transform ->
|
||||
transform.from.attribute(attribute, KLIB_COLLECTION_DIR)
|
||||
transform.to.attribute(attribute, KLIB)
|
||||
}
|
||||
}
|
||||
|
||||
@DisableCachingByDefault(because = "Trivial operation, building cache keys is presumably more expensive")
|
||||
abstract class KlibCollectionDirTransform : TransformAction<TransformParameters.None> {
|
||||
|
||||
@get:InputArtifact
|
||||
@get:PathSensitive(PathSensitivity.RELATIVE)
|
||||
abstract val inputArtifact: Provider<FileSystemLocation>
|
||||
|
||||
override fun transform(outputs: TransformOutputs) {
|
||||
val input = inputArtifact.get().asFile
|
||||
input.listFiles()?.forEach { klib ->
|
||||
if (klib.isDirectory) outputs.dir(klib)
|
||||
if (klib.isFile) outputs.file(klib)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
package org.jetbrains.kotlin.gradle.targets.native.internal
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.artifacts.Configuration
|
||||
import org.gradle.api.attributes.Category
|
||||
import org.gradle.api.attributes.LibraryElements
|
||||
import org.gradle.api.attributes.Usage
|
||||
import org.gradle.api.file.FileCollection
|
||||
import org.jetbrains.kotlin.commonizer.SharedCommonizerTarget
|
||||
import org.jetbrains.kotlin.commonizer.identityString
|
||||
import org.jetbrains.kotlin.gradle.plugin.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinUsages
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.resolvableMetadataConfiguration
|
||||
import org.jetbrains.kotlin.gradle.plugin.sources.internal
|
||||
import org.jetbrains.kotlin.gradle.targets.native.internal.CInteropKlibLibraryElements.cinteropKlibLibraryElements
|
||||
import org.jetbrains.kotlin.gradle.utils.markConsumable
|
||||
import org.jetbrains.kotlin.gradle.utils.markResolvable
|
||||
import org.jetbrains.kotlin.tooling.core.UnsafeApi
|
||||
|
||||
/* Elements configuration */
|
||||
|
||||
internal fun Project.locateOrCreateCommonizedCInteropApiElementsConfiguration(commonizerTarget: SharedCommonizerTarget): Configuration {
|
||||
val configurationName = commonizerTarget.identityString + "CInteropApiElements"
|
||||
configurations.findByName(configurationName)?.let { return it }
|
||||
|
||||
return configurations.create(configurationName) { configuration ->
|
||||
configuration.markConsumable()
|
||||
setupBasicCommonizedCInteropConfigurationAttributes(configuration, commonizerTarget)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Dependency configuration */
|
||||
|
||||
/**
|
||||
* Gives access the 'commonized cinterop dependency configuration' of the given [sourceSet].
|
||||
* The access is forced through this 'view' because the provided 'artifact view' is forced to be lenient as protective measure.
|
||||
*
|
||||
* If dependencies do not provide corresponding commonized cinterop element configurations then we should not fail the build!
|
||||
*/
|
||||
internal suspend fun Project.createCommonizedCInteropDependencyConfigurationView(sourceSet: KotlinSourceSet): FileCollection {
|
||||
@OptIn(UnsafeApi::class)
|
||||
val configuration = locateOrCreateCommonizedCInteropDependencyConfiguration(sourceSet) ?: return files()
|
||||
return configuration.incoming.artifactView { view -> view.isLenient = true }.files
|
||||
}
|
||||
|
||||
@UnsafeApi("Use createCommonizedCInteropDependencyConfigurationView instead")
|
||||
internal suspend fun Project.locateOrCreateCommonizedCInteropDependencyConfiguration(
|
||||
sourceSet: KotlinSourceSet,
|
||||
): Configuration? {
|
||||
val commonizerTarget = sourceSet.commonizerTarget.await() ?: return null
|
||||
if (commonizerTarget !is SharedCommonizerTarget) return null
|
||||
|
||||
val configurationName = commonizerTarget.identityString + "CInterop"
|
||||
configurations.findByName(configurationName)?.let { return it }
|
||||
|
||||
val configuration = configurations.create(configurationName) { configuration ->
|
||||
configuration.isVisible = false
|
||||
configuration.markResolvable()
|
||||
|
||||
// Extends from Metadata Configuration associated with given source set to ensure matching
|
||||
configuration.extendsFrom(sourceSet.internal.resolvableMetadataConfiguration)
|
||||
setupBasicCommonizedCInteropConfigurationAttributes(configuration, commonizerTarget)
|
||||
|
||||
/**
|
||||
* Dependencies require the [CInteropCommonizerArtifactTypeAttribute.KLIB].
|
||||
* If artifacts are added using [CInteropCommonizerArtifactTypeAttribute.KLIB_COLLECTION_DIR] then a transformation
|
||||
* has to happen that will resolve the exact list of klibs.
|
||||
*/
|
||||
configuration.attributes.attribute(CInteropCommonizerArtifactTypeAttribute.attribute, CInteropCommonizerArtifactTypeAttribute.KLIB)
|
||||
description = "Commonized CInterop dependencies for targets: '$commonizerTarget'."
|
||||
}
|
||||
|
||||
return configuration
|
||||
}
|
||||
|
||||
private fun Project.setupBasicCommonizedCInteropConfigurationAttributes(
|
||||
configuration: Configuration,
|
||||
commonizerTarget: SharedCommonizerTarget,
|
||||
) {
|
||||
configuration.attributes.attribute(KotlinPlatformType.attribute, KotlinPlatformType.native)
|
||||
configuration.attributes.attribute(CommonizerTargetAttribute.attribute, commonizerTarget.identityString)
|
||||
configuration.attributes.attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, cinteropKlibLibraryElements())
|
||||
configuration.attributes.attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage::class.java, KotlinUsages.KOTLIN_CINTEROP))
|
||||
configuration.attributes.attribute(Category.CATEGORY_ATTRIBUTE, project.categoryByName(Category.LIBRARY))
|
||||
}
|
||||
+79
-2
@@ -5,13 +5,24 @@
|
||||
|
||||
package org.jetbrains.kotlin.gradle.targets.native.internal
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.jetbrains.kotlin.commonizer.SharedCommonizerTarget
|
||||
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtensionOrNull
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinSharedNativeCompilation
|
||||
import org.jetbrains.kotlin.gradle.utils.Future
|
||||
import org.jetbrains.kotlin.gradle.utils.appendLine
|
||||
import org.jetbrains.kotlin.gradle.utils.getOrPut
|
||||
import org.jetbrains.kotlin.gradle.utils.lazyFuture
|
||||
|
||||
/**
|
||||
* Represents a group of cinterops and targets that can be passed to a single invocation to the commonizer.
|
||||
* CInterops are bundled into these groups to reduce the amount of commonizer invocations and therefore ensuring no duplicate work
|
||||
* has to be done.
|
||||
*/
|
||||
internal data class CInteropCommonizerGroup(
|
||||
@get:Input val targets: Set<SharedCommonizerTarget>,
|
||||
@get:Input val interops: Set<CInteropIdentifier>
|
||||
@get:Input val interops: Set<CInteropIdentifier>,
|
||||
) {
|
||||
override fun toString(): String {
|
||||
return buildString {
|
||||
@@ -28,4 +39,70 @@ internal data class CInteropCommonizerGroup(
|
||||
appendLine("}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Collection of cinterop groups present in the given project */
|
||||
|
||||
/**
|
||||
* Represents all collected [CInteropCommonizerGroup] gruops for the given project
|
||||
*/
|
||||
internal val Project.kotlinCInteropGroups: Future<Set<CInteropCommonizerGroup>>
|
||||
get() = extensions.extraProperties.getOrPut("org.jetbrains.kotlin.gradle.targets.native.internal.kotlinCInteropGroups") {
|
||||
lazyFuture { collectCInteropGroups() }
|
||||
}
|
||||
|
||||
private suspend fun Project.collectCInteropGroups(): Set<CInteropCommonizerGroup> {
|
||||
val dependents = allCinteropCommonizerDependents()
|
||||
|
||||
val allScopeSets = dependents.map { it.scopes }.toSet()
|
||||
val rootScopeSets = allScopeSets.filter { scopeSet ->
|
||||
allScopeSets.none { otherScopeSet -> otherScopeSet != scopeSet && otherScopeSet.containsAll(scopeSet) }
|
||||
}
|
||||
|
||||
return rootScopeSets.map { scopeSet ->
|
||||
val dependentsForScopes = dependents.filter { dependent ->
|
||||
scopeSet.containsAll(dependent.scopes)
|
||||
}
|
||||
|
||||
CInteropCommonizerGroup(
|
||||
targets = dependentsForScopes.map { it.target }.toSet(),
|
||||
interops = dependentsForScopes.flatMap { it.interops }.toSet()
|
||||
)
|
||||
}.toSet()
|
||||
|
||||
}
|
||||
|
||||
private suspend fun Project.allCinteropCommonizerDependents(): Set<CInteropCommonizerDependent> {
|
||||
val multiplatformExtension = project.multiplatformExtensionOrNull ?: return emptySet()
|
||||
|
||||
val fromSharedNativeCompilations = multiplatformExtension
|
||||
.targets.flatMap { target -> target.compilations }
|
||||
.filterIsInstance<KotlinSharedNativeCompilation>()
|
||||
.mapNotNull { compilation -> CInteropCommonizerDependent.from(compilation) }
|
||||
.toSet()
|
||||
|
||||
val fromSourceSets = multiplatformExtension.awaitSourceSets()
|
||||
.mapNotNull { sourceSet -> CInteropCommonizerDependent.from(sourceSet) }
|
||||
.toSet()
|
||||
|
||||
val fromSourceSetsAssociateCompilations = multiplatformExtension.awaitSourceSets()
|
||||
.mapNotNull { sourceSet -> CInteropCommonizerDependent.fromAssociateCompilations(sourceSet) }
|
||||
.toSet()
|
||||
|
||||
return (fromSharedNativeCompilations + fromSourceSets + fromSourceSetsAssociateCompilations)
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function that allows to find the corresponding [CInteropCommonizerGroup] for a given [CInteropCommonizerDependent]
|
||||
*/
|
||||
internal suspend fun Project.findCInteropCommonizerGroup(dependent: CInteropCommonizerDependent): CInteropCommonizerGroup? {
|
||||
val suitableGroups = kotlinCInteropGroups.await().filter { group ->
|
||||
group.interops.containsAll(dependent.interops) && group.targets.contains(dependent.target)
|
||||
}
|
||||
|
||||
assert(suitableGroups.size <= 1) {
|
||||
"CInteropCommonizerTask: Unnecessary work detected: More than one suitable group found for cinterop dependent."
|
||||
}
|
||||
|
||||
return suitableGroups.firstOrNull()
|
||||
}
|
||||
|
||||
+11
-46
@@ -42,7 +42,7 @@ internal abstract class CInteropCommonizerTask
|
||||
@Inject constructor(
|
||||
private val objectFactory: ObjectFactory,
|
||||
private val execOperations: ExecOperations,
|
||||
private val projectLayout: ProjectLayout
|
||||
private val projectLayout: ProjectLayout,
|
||||
) : AbstractCInteropCommonizerTask() {
|
||||
|
||||
internal class CInteropGist(
|
||||
@@ -54,7 +54,7 @@ internal abstract class CInteropCommonizerTask
|
||||
val libraryFile: Provider<File>,
|
||||
|
||||
@get:Classpath
|
||||
val dependencies: FileCollection
|
||||
val dependencies: FileCollection,
|
||||
) {
|
||||
@Suppress("unused") // Used for UP-TO-DATE check
|
||||
@get:Input
|
||||
@@ -91,10 +91,6 @@ internal abstract class CInteropCommonizerTask
|
||||
|
||||
override val outputDirectory: File get() = projectLayout.buildDirectory.get().asFile.resolve("classes/kotlin/commonizer")
|
||||
|
||||
internal fun outputDirectoriesOfCommonizerGroup(group: CInteropCommonizerGroup): Map<SharedCommonizerTarget, File> = group
|
||||
.targets
|
||||
.associateWith { target -> CommonizerOutputFileLayout.resolveCommonizedDirectory(outputDirectory, target) }
|
||||
|
||||
@get:Internal
|
||||
internal val kotlinPluginVersion: Property<String> = objectFactory
|
||||
.property<String>()
|
||||
@@ -123,7 +119,7 @@ internal abstract class CInteropCommonizerTask
|
||||
|
||||
data class CInteropCommonizerDependencies(
|
||||
val commonizerTarget: CommonizerTarget,
|
||||
val dependencies: FileCollection
|
||||
val dependencies: FileCollection,
|
||||
)
|
||||
|
||||
/**
|
||||
@@ -135,7 +131,7 @@ internal abstract class CInteropCommonizerTask
|
||||
|
||||
val sourceSetsByTarget = multiplatformExtension.sourceSets.groupBy { sourceSet -> sourceSet.commonizerTarget.getOrThrow() }
|
||||
val sourceSetsByGroup = multiplatformExtension.sourceSets.groupBy { sourceSet ->
|
||||
CInteropCommonizerDependent.from(sourceSet)?.let { findInteropsGroup(it) }
|
||||
CInteropCommonizerDependent.from(sourceSet)?.let { findCInteropCommonizerGroup(it) }
|
||||
}
|
||||
|
||||
allInteropGroups.await().associateWith { group ->
|
||||
@@ -158,19 +154,7 @@ internal abstract class CInteropCommonizerTask
|
||||
will provide the same dependencies (since cinterops are just based upon KonanTarget)
|
||||
*/
|
||||
.take(1)
|
||||
.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()
|
||||
}
|
||||
}
|
||||
.map { sourceSet -> project.createCommonizedCInteropDependencyConfigurationView(sourceSet) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,7 +198,8 @@ internal abstract class CInteropCommonizerTask
|
||||
|
||||
private fun commonize(group: CInteropCommonizerGroup) {
|
||||
val cinteropsForTarget = cinterops.get().filter { cinterop -> cinterop.identifier in group.interops }
|
||||
outputDirectory(group).deleteRecursively()
|
||||
val outputDirectory = outputDirectory(group)
|
||||
outputDirectory.deleteRecursively()
|
||||
if (cinteropsForTarget.isEmpty()) return
|
||||
|
||||
val commonizerRunner = KotlinNativeCommonizerToolRunner(
|
||||
@@ -228,7 +213,7 @@ internal abstract class CInteropCommonizerTask
|
||||
outputTargets = group.targets,
|
||||
inputLibraries = cinteropsForTarget.map { it.libraryFile.get() }.filter { it.exists() }.toSet(),
|
||||
dependencyLibraries = getCInteropCommonizerGroupDependencies(group),
|
||||
outputDirectory = outputDirectory(group),
|
||||
outputDirectory = outputDirectory,
|
||||
logLevel = commonizerLogLevel,
|
||||
additionalSettings = additionalCommonizerSettings,
|
||||
)
|
||||
@@ -239,14 +224,6 @@ 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()
|
||||
@@ -256,23 +233,11 @@ internal abstract class CInteropCommonizerTask
|
||||
}
|
||||
|
||||
@get:Internal
|
||||
internal val allInteropGroups: CompletableFuture<Set<CInteropCommonizerGroup>> = CompletableFuture()
|
||||
internal val allInteropGroups: Future<Set<CInteropCommonizerGroup>> = project.kotlinCInteropGroups
|
||||
|
||||
@get:Nested
|
||||
@Nested
|
||||
@Suppress("unused") // UP-TO-DATE check
|
||||
protected val allInteropGroupsOrThrow get() = allInteropGroups.getOrThrow()
|
||||
|
||||
override suspend fun findInteropsGroup(dependent: CInteropCommonizerDependent): CInteropCommonizerGroup? {
|
||||
val suitableGroups = allInteropGroups.await().filter { group ->
|
||||
group.interops.containsAll(dependent.interops) && group.targets.contains(dependent.target)
|
||||
}
|
||||
|
||||
assert(suitableGroups.size <= 1) {
|
||||
"CInteropCommonizerTask: Unnecessary work detected: More than one suitable group found for cinterop dependent."
|
||||
}
|
||||
|
||||
return suitableGroups.firstOrNull()
|
||||
}
|
||||
protected fun getAllInteropGroupsForUpToDateCheck() = allInteropGroups.getOrThrow()
|
||||
}
|
||||
|
||||
private fun CInteropProcess.toGist(): CInteropGist {
|
||||
|
||||
+4
-76
@@ -9,17 +9,17 @@ 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.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.sources.internal
|
||||
import org.jetbrains.kotlin.gradle.plugin.usesPlatformOf
|
||||
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(
|
||||
@@ -63,39 +63,9 @@ internal fun Project.locateOrCreateCInteropDependencyConfiguration(
|
||||
}
|
||||
}
|
||||
|
||||
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 }
|
||||
@@ -116,42 +86,10 @@ 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
|
||||
*/
|
||||
internal fun SharedCommonizerTarget.dashedIdentityString() = targets.map { it.name }.sorted().joinToString("-")
|
||||
private fun commonizedCInteropApiElementsConfigurationName(commonizerTarget: SharedCommonizerTarget): String {
|
||||
return commonizerTarget.dashedIdentityString() + "CInteropApiElements"
|
||||
}
|
||||
|
||||
private fun Configuration.applyAttributesForCommonizerTarget(commonizerTarget: SharedCommonizerTarget) {
|
||||
attributes.attribute(KotlinPlatformType.attribute, KotlinPlatformType.native)
|
||||
CommonizerTargetAttribute.apply(attributes, commonizerTarget)
|
||||
}
|
||||
|
||||
internal object CInteropKlibLibraryElements {
|
||||
const val CINTEROP_KLIB = "cinterop-klib"
|
||||
|
||||
@@ -173,13 +111,3 @@ private class CInteropLibraryElementsCompatibilityRule : AttributeCompatibilityR
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private object CommonizerTargetAttribute {
|
||||
private val attribute = Attribute.of("org.jetbrains.kotlin.native.commonizerTarget", String::class.java)
|
||||
|
||||
private val SharedCommonizerTarget.attributeValue get() = dashedIdentityString()
|
||||
|
||||
fun apply(attributeContainer: AttributeContainer, sharedCommonizerTarget: SharedCommonizerTarget) {
|
||||
attributeContainer.attribute(attribute, sharedCommonizerTarget.attributeValue)
|
||||
}
|
||||
}
|
||||
|
||||
+5
-50
@@ -8,13 +8,7 @@ package org.jetbrains.kotlin.gradle.targets.native.internal
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.file.FileCollection
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.MetadataDependencyResolution.ChooseVisibleSourceSets
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.MetadataDependencyResolution.ChooseVisibleSourceSets.MetadataProvider.ArtifactMetadataProvider
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.MetadataDependencyResolution.ChooseVisibleSourceSets.MetadataProvider.ProjectMetadataProvider
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.MetadataDependencyResolution.ChooseVisibleSourceSets.MetadataProvider.ProjectMetadataProvider.MetadataConsumer.Cli
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.MetadataDependencyResolution.ChooseVisibleSourceSets.MetadataProvider.ProjectMetadataProvider.MetadataConsumer.Ide
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.metadataDependencyResolutionsOrEmpty
|
||||
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
|
||||
@@ -37,54 +31,15 @@ internal suspend fun Project.createCInteropMetadataDependencyClasspath(sourceSet
|
||||
if (dependencyTransformationTask == null) return project.files()
|
||||
|
||||
val dependencyTransformationTaskOutputs = project.files(dependencyTransformationTask.map { it.outputLibraryFiles })
|
||||
return if (forIde) {
|
||||
/*
|
||||
For IDE Import the classpath will be assembled by three independent parts:
|
||||
1) C-Interop Metadata which will be downloaded Jar files that get transformed by the transformation task
|
||||
2) C-Interop Metadata directly provided by dependency projects (in the same build)
|
||||
3) C-Interop Metadata from 'associated compilations' / additionalVisible source sets
|
||||
(e.g. 'nativeTest' will be able to access the classpath from 'nativeMain')
|
||||
*/
|
||||
dependencyTransformationTaskOutputs +
|
||||
createCInteropMetadataDependencyClasspathFromProjectDependenciesForIde(sourceSet) +
|
||||
createCInteropMetadataDependencyClasspathFromAssociatedCompilations(sourceSet, true)
|
||||
} else {
|
||||
/*
|
||||
For CLI execution the classpath will be assembled from two parts:
|
||||
1) C-Interop metadata from the transformation task which transforms
|
||||
Project Dependencies and External Module Dependencies (e.g. the ones that downloaded from maven repo)
|
||||
2) C-Interop Metadata from 'associated compilations' / additionalVisible source sets
|
||||
(e.g. 'nativeTest' will be able to access the classpath from 'nativeMain')
|
||||
*/
|
||||
dependencyTransformationTaskOutputs +
|
||||
createCInteropMetadataDependencyClasspathFromAssociatedCompilations(sourceSet, false)
|
||||
}
|
||||
}
|
||||
|
||||
private fun Project.createCInteropMetadataDependencyClasspathFromProjectDependenciesForIde(
|
||||
sourceSet: DefaultKotlinSourceSet
|
||||
): FileCollection {
|
||||
return filesProvider {
|
||||
sourceSet.metadataTransformation
|
||||
.metadataDependencyResolutionsOrEmpty
|
||||
.filterIsInstance<ChooseVisibleSourceSets>()
|
||||
.mapNotNull { chooseVisibleSourceSets ->
|
||||
/* We only want to access resolutions that provide metadata from dependency projects */
|
||||
val projectMetadataProvider = when (chooseVisibleSourceSets.metadataProvider) {
|
||||
is ProjectMetadataProvider -> chooseVisibleSourceSets.metadataProvider
|
||||
is ArtifactMetadataProvider -> return@mapNotNull null
|
||||
}
|
||||
|
||||
chooseVisibleSourceSets.visibleSourceSetProvidingCInterops?.let { visibleSourceSetName ->
|
||||
projectMetadataProvider.getSourceSetCInteropMetadata(visibleSourceSetName, Ide)
|
||||
}
|
||||
}
|
||||
}
|
||||
return dependencyTransformationTaskOutputs +
|
||||
createCInteropMetadataDependencyClasspathFromAssociatedCompilations(sourceSet, forIde) +
|
||||
createCommonizedCInteropDependencyConfigurationView(sourceSet)
|
||||
}
|
||||
|
||||
private fun Project.createCInteropMetadataDependencyClasspathFromAssociatedCompilations(
|
||||
sourceSet: DefaultKotlinSourceSet,
|
||||
forIde: Boolean
|
||||
forIde: Boolean,
|
||||
): FileCollection {
|
||||
return filesProvider files@{
|
||||
val commonizerTarget = sourceSet.sharedCommonizerTarget.getOrThrow() ?: return@files emptySet<File>()
|
||||
@@ -96,7 +51,7 @@ private fun Project.createCInteropMetadataDependencyClasspathFromAssociatedCompi
|
||||
*/
|
||||
val (associatedSourceSet, _) = sourceSet.getAdditionalVisibleSourceSets()
|
||||
.filterIsInstance<DefaultKotlinSourceSet>()
|
||||
.mapNotNull { otherSourceSet -> otherSourceSet to (otherSourceSet.sharedCommonizerTarget.getOrThrow() ?: return@mapNotNull null) }
|
||||
.mapNotNull { other -> other to (other.sharedCommonizerTarget.getOrThrow() ?: return@mapNotNull null) }
|
||||
.filter { (_, otherCommonizerTarget) -> otherCommonizerTarget.targets.containsAll(commonizerTarget.targets) }
|
||||
.minByOrNull { (_, otherCommonizerTarget) -> otherCommonizerTarget.targets.size } ?: return@files emptySet<File>()
|
||||
|
||||
|
||||
+5
-21
@@ -55,8 +55,6 @@ internal suspend fun Project.locateOrRegisterCInteropMetadataDependencyTransform
|
||||
project.layout.kotlinTransformedCInteropMetadataLibraryDirectoryForBuild(sourceSet.name),
|
||||
/* cleaning = */
|
||||
CInteropMetadataDependencyTransformationTask.Cleaning.DeleteOutputDirectory,
|
||||
/* transformProjectDependencies = */
|
||||
true,
|
||||
),
|
||||
configureTask = { configureTaskOrder() }
|
||||
)
|
||||
@@ -84,8 +82,6 @@ internal suspend fun Project.locateOrRegisterCInteropMetadataDependencyTransform
|
||||
project.kotlinTransformedCInteropMetadataLibraryDirectoryForIde,
|
||||
/* cleaning = */
|
||||
CInteropMetadataDependencyTransformationTask.Cleaning.None,
|
||||
/* transformProjectDependencies = */
|
||||
false, // For IDE Project Dependencies will be transformed during configuration, see [createCInteropMetadataDependencyClasspath]
|
||||
),
|
||||
configureTask = { configureTaskOrder() }
|
||||
)
|
||||
@@ -114,9 +110,6 @@ internal open class CInteropMetadataDependencyTransformationTask @Inject constru
|
||||
@Transient @get:Internal val sourceSet: DefaultKotlinSourceSet,
|
||||
@get:OutputDirectory val outputDirectory: File,
|
||||
@get:Internal val cleaning: Cleaning,
|
||||
/** when false, project-to-project dependencies will not be transformed and listed in [outputLibraryFiles],
|
||||
* assuming they are added during gradle configuration, see [createCInteropMetadataDependencyClasspath] for details */
|
||||
private val transformProjectDependencies: Boolean,
|
||||
objectFactory: ObjectFactory,
|
||||
) : DefaultTask() {
|
||||
|
||||
@@ -139,7 +132,7 @@ internal open class CInteropMetadataDependencyTransformationTask @Inject constru
|
||||
}
|
||||
|
||||
@get:Nested
|
||||
internal val inputs = MetadataDependencyTransformationTaskInputs(project, sourceSet, transformProjectDependencies)
|
||||
internal val inputs = MetadataDependencyTransformationTaskInputs(project, sourceSet, keepProjectDependencies = false)
|
||||
|
||||
@get:OutputFile
|
||||
protected val outputLibrariesFileIndex: RegularFileProperty = objectFactory
|
||||
@@ -171,16 +164,9 @@ internal open class CInteropMetadataDependencyTransformationTask @Inject constru
|
||||
private fun materializeMetadata(
|
||||
chooseVisibleSourceSets: ChooseVisibleSourceSets,
|
||||
): Iterable<File> {
|
||||
val metadataProvider = chooseVisibleSourceSets.metadataProvider
|
||||
return when (metadataProvider) {
|
||||
is ProjectMetadataProvider -> {
|
||||
if (!transformProjectDependencies) return emptyList()
|
||||
val visibleSourceSetName = chooseVisibleSourceSets.visibleSourceSetProvidingCInterops ?: return emptyList()
|
||||
metadataProvider
|
||||
.getSourceSetCInteropMetadata(visibleSourceSetName, ProjectMetadataProvider.MetadataConsumer.Cli)
|
||||
?.files
|
||||
.orEmpty()
|
||||
}
|
||||
return when (val metadataProvider = chooseVisibleSourceSets.metadataProvider) {
|
||||
/* Project to Project commonized cinterops are shared using configurations */
|
||||
is ProjectMetadataProvider -> emptyList()
|
||||
|
||||
/* Extract/Materialize all cinterop files from composite jar file */
|
||||
is ArtifactMetadataProvider -> metadataProvider.read { artifactContent ->
|
||||
@@ -195,9 +181,7 @@ internal open class CInteropMetadataDependencyTransformationTask @Inject constru
|
||||
|
||||
private fun Iterable<MetadataDependencyResolution>.resolutionsToTransform(): List<ChooseVisibleSourceSets> {
|
||||
return filterIsInstance<ChooseVisibleSourceSets>()
|
||||
.applyIf(!transformProjectDependencies) {
|
||||
filterNot { it.dependency in currentBuild }
|
||||
}
|
||||
.filterNot { it.dependency in currentBuild }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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.attributes.*
|
||||
import org.jetbrains.kotlin.commonizer.SharedCommonizerTarget
|
||||
import org.jetbrains.kotlin.commonizer.parseCommonizerTarget
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.MetadataDependencyResolution
|
||||
|
||||
internal object CommonizerTargetAttribute {
|
||||
val attribute: Attribute<String> = Attribute.of("org.jetbrains.kotlin.native.commonizerTarget", String::class.java)
|
||||
|
||||
fun setupAttributesMatchingStrategy(attributesSchema: AttributesSchema) {
|
||||
attributesSchema.attribute(attribute) { strategy ->
|
||||
strategy.compatibilityRules.add(CommonizerTargetCompatibilityRule::class.java)
|
||||
strategy.disambiguationRules.add(CommonizerTargetDisambiguationRule::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generally all producers providing a superset of targets can be consumed!
|
||||
* e.g. a producer providing a commonizer target for ios and linux targets can be considered compatible
|
||||
* for consumers of just the ios targets
|
||||
*/
|
||||
class CommonizerTargetCompatibilityRule : AttributeCompatibilityRule<String> {
|
||||
override fun execute(details: CompatibilityCheckDetails<String>) {
|
||||
val producerValue = parseCommonizerTarget(details.producerValue ?: return) as? SharedCommonizerTarget ?: return
|
||||
val consumerValue = parseCommonizerTarget(details.consumerValue ?: return) as? SharedCommonizerTarget ?: return
|
||||
|
||||
if (producerValue.targets.containsAll(consumerValue.targets)) {
|
||||
details.compatible()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given the above [CommonizerTargetCompatibilityRule] we see that it is expected that every 'more common' cinterops
|
||||
* will be marked as compatible. However, in the commonizer output model we always just want to use a single
|
||||
* artifact to compile/analyze against. In the case of cinterops we always want the most specific cinterop
|
||||
* See [MetadataDependencyResolution.ChooseVisibleSourceSets.visibleSourceSetProvidingCInterops]
|
||||
*
|
||||
* e.g.
|
||||
* - given the consumer requests 'ios + linux'
|
||||
* - given the producer offers 'ios + macos + linux' and 'ios + macos + linux + windows'
|
||||
*
|
||||
* then: the more specific 'ios + macos + linux' (closer) shall be chosen
|
||||
*/
|
||||
class CommonizerTargetDisambiguationRule : AttributeDisambiguationRule<String> {
|
||||
override fun execute(details: MultipleCandidatesDetails<String>) {
|
||||
details.closestMatch(details.candidateValues.sorted().minByOrNull { it.length } ?: return)
|
||||
}
|
||||
}
|
||||
}
|
||||
-4
@@ -35,10 +35,6 @@ internal abstract class CopyCommonizeCInteropForIdeTask @Inject constructor(
|
||||
override val outputDirectory: File = project.rootDir.resolve(".gradle/kotlin/commonizer")
|
||||
.resolve(project.path.removePrefix(":").replace(":", "/"))
|
||||
|
||||
override suspend fun findInteropsGroup(dependent: CInteropCommonizerDependent): CInteropCommonizerGroup? {
|
||||
return commonizeCInteropTask.get().findInteropsGroup(dependent)
|
||||
}
|
||||
|
||||
@get:Internal
|
||||
val metrics: Property<BuildMetricsReporter<GradleBuildTime, GradleBuildPerformanceMetric>> = project.objects
|
||||
.property(GradleBuildMetricsReporter())
|
||||
|
||||
+6
-56
@@ -7,18 +7,11 @@ package org.jetbrains.kotlin.gradle.targets.native.internal
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.tasks.TaskProvider
|
||||
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtensionOrNull
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinSharedNativeCompilation
|
||||
import org.jetbrains.kotlin.commonizer.CommonizerOutputFileLayout
|
||||
|
||||
internal suspend fun Project.configureCInteropCommonizer() {
|
||||
val interopTask = commonizeCInteropTask() ?: return
|
||||
|
||||
val cinteropGroups = getCInteropGroups()
|
||||
configureCInteropCommonizerConsumableConfigurations(cinteropGroups, interopTask)
|
||||
|
||||
interopTask.configure {
|
||||
it.allInteropGroups.complete(cinteropGroups)
|
||||
}
|
||||
configureCInteropCommonizerConsumableConfigurations(kotlinCInteropGroups.await(), interopTask)
|
||||
}
|
||||
|
||||
private fun Project.configureCInteropCommonizerConsumableConfigurations(
|
||||
@@ -29,57 +22,14 @@ private fun Project.configureCInteropCommonizerConsumableConfigurations(
|
||||
for (sharedCommonizerTargets in commonizerGroup.targets) {
|
||||
val configuration = locateOrCreateCommonizedCInteropApiElementsConfiguration(sharedCommonizerTargets)
|
||||
val commonizerTargetOutputDir = interopTask.map { task ->
|
||||
task.outputDirectoriesOfCommonizerGroup(commonizerGroup)[sharedCommonizerTargets]
|
||||
?: error("Can't find output of Shared Commonizer Target $sharedCommonizerTargets")
|
||||
CommonizerOutputFileLayout.resolveCommonizedDirectory(task.outputDirectory(commonizerGroup), sharedCommonizerTargets)
|
||||
}
|
||||
|
||||
project.artifacts.add(configuration.name, commonizerTargetOutputDir) { artifact ->
|
||||
artifact.extension = "klib"
|
||||
artifact.type = "klib"
|
||||
artifact.classifier = "cinterop-" + sharedCommonizerTargets.dashedIdentityString()
|
||||
artifact.extension = CInteropCommonizerArtifactTypeAttribute.KLIB_COLLECTION_DIR
|
||||
artifact.type = CInteropCommonizerArtifactTypeAttribute.KLIB_COLLECTION_DIR
|
||||
artifact.builtBy(interopTask)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun Project.allCinteropCommonizerDependents(): Set<CInteropCommonizerDependent> {
|
||||
val multiplatformExtension = project.multiplatformExtensionOrNull ?: return emptySet()
|
||||
|
||||
val fromSharedNativeCompilations = multiplatformExtension
|
||||
.targets.flatMap { target -> target.compilations }
|
||||
.filterIsInstance<KotlinSharedNativeCompilation>()
|
||||
.mapNotNull { compilation -> CInteropCommonizerDependent.from(compilation) }
|
||||
.toSet()
|
||||
|
||||
val fromSourceSets = multiplatformExtension.awaitSourceSets()
|
||||
.mapNotNull { sourceSet -> CInteropCommonizerDependent.from(sourceSet) }
|
||||
.toSet()
|
||||
|
||||
val fromSourceSetsAssociateCompilations = multiplatformExtension.awaitSourceSets()
|
||||
.mapNotNull { sourceSet -> CInteropCommonizerDependent.fromAssociateCompilations(sourceSet) }
|
||||
.toSet()
|
||||
|
||||
return (fromSharedNativeCompilations + fromSourceSets + fromSourceSetsAssociateCompilations)
|
||||
}
|
||||
|
||||
private suspend fun Project.getCInteropGroups(): Set<CInteropCommonizerGroup> {
|
||||
val dependents = allCinteropCommonizerDependents()
|
||||
|
||||
val allScopeSets = dependents.map { it.scopes }.toSet()
|
||||
val rootScopeSets = allScopeSets.filter { scopeSet ->
|
||||
allScopeSets.none { otherScopeSet -> otherScopeSet != scopeSet && otherScopeSet.containsAll(scopeSet) }
|
||||
}
|
||||
|
||||
val result = rootScopeSets.map { scopeSet ->
|
||||
val dependentsForScopes = dependents.filter { dependent ->
|
||||
scopeSet.containsAll(dependent.scopes)
|
||||
}
|
||||
|
||||
CInteropCommonizerGroup(
|
||||
targets = dependentsForScopes.map { it.target }.toSet(),
|
||||
interops = dependentsForScopes.flatMap { it.interops }.toSet()
|
||||
)
|
||||
}.toSet()
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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.
|
||||
*/
|
||||
|
||||
@file:Suppress("FunctionName")
|
||||
|
||||
package org.jetbrains.kotlin.gradle.unitTests
|
||||
|
||||
import org.gradle.api.attributes.Usage
|
||||
import org.gradle.kotlin.dsl.dependencies
|
||||
import org.jetbrains.kotlin.gradle.targets.native.internal.CInteropCommonizerArtifactTypeAttribute
|
||||
import org.jetbrains.kotlin.gradle.util.buildProjectWithMPP
|
||||
import org.jetbrains.kotlin.gradle.utils.markConsumable
|
||||
import org.jetbrains.kotlin.gradle.utils.markResolvable
|
||||
import org.jetbrains.kotlin.gradle.utils.named
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class CInteropCommonizerArtifactTypeAttributeTest {
|
||||
|
||||
@Test
|
||||
fun `test - transformation from klib-collection-dir to klibs`() {
|
||||
val project = buildProjectWithMPP()
|
||||
|
||||
/* Create stub klibs collection directory */
|
||||
val klibCollectionDir = project.buildDir.resolve("testOutputDir")
|
||||
klibCollectionDir.mkdirs()
|
||||
|
||||
val klibs = listOf(
|
||||
klibCollectionDir.resolve("foo.klib").also { file -> file.writeText("stub") },
|
||||
klibCollectionDir.resolve("bar").also { file -> file.mkdirs() }
|
||||
)
|
||||
|
||||
/* Create consumable elements configuration */
|
||||
project.configurations.create("testElements") { configuration ->
|
||||
configuration.markConsumable()
|
||||
configuration.attributes.attribute(Usage.USAGE_ATTRIBUTE, project.objects.named("test"))
|
||||
|
||||
/* Add klibCollectionDir as artifact */
|
||||
configuration.outgoing.artifact(klibCollectionDir) { artifact ->
|
||||
artifact.type = CInteropCommonizerArtifactTypeAttribute.KLIB_COLLECTION_DIR
|
||||
artifact.extension = CInteropCommonizerArtifactTypeAttribute.KLIB_COLLECTION_DIR
|
||||
}
|
||||
}
|
||||
|
||||
/* Create resolvable configuration */
|
||||
val resolvable = project.configurations.create("testDependencies") { configuration ->
|
||||
configuration.markResolvable()
|
||||
configuration.attributes.attribute(Usage.USAGE_ATTRIBUTE, project.objects.named("test"))
|
||||
configuration.attributes.attribute(
|
||||
CInteropCommonizerArtifactTypeAttribute.attribute, CInteropCommonizerArtifactTypeAttribute.KLIB
|
||||
)
|
||||
}
|
||||
|
||||
project.dependencies { resolvable(project) }
|
||||
|
||||
assertEquals(klibs.toSet(), resolvable.incoming.artifactView { }.files.toSet())
|
||||
}
|
||||
}
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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.
|
||||
*/
|
||||
|
||||
@file:Suppress("FunctionName", "DuplicatedCode")
|
||||
|
||||
package org.jetbrains.kotlin.gradle.unitTests
|
||||
|
||||
import org.gradle.kotlin.dsl.dependencies
|
||||
import org.jetbrains.kotlin.commonizer.CommonizerTarget
|
||||
import org.jetbrains.kotlin.commonizer.identityString
|
||||
import org.jetbrains.kotlin.gradle.targets.native.internal.CommonizerTargetAttribute
|
||||
import org.jetbrains.kotlin.gradle.util.buildProjectWithMPP
|
||||
import org.jetbrains.kotlin.gradle.utils.markConsumable
|
||||
import org.jetbrains.kotlin.gradle.utils.markResolvable
|
||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.fail
|
||||
|
||||
class CInteropCommonizerConfigurationTests {
|
||||
|
||||
@Test
|
||||
fun `test - compatibility rule - superset is compatible`() {
|
||||
val project = buildProjectWithMPP()
|
||||
|
||||
val consumable = project.configurations.create("testElements") { configuration ->
|
||||
configuration.markConsumable()
|
||||
configuration.attributes.attribute(
|
||||
CommonizerTargetAttribute.attribute,
|
||||
CommonizerTarget(
|
||||
KonanTarget.LINUX_X64,
|
||||
KonanTarget.LINUX_ARM64,
|
||||
KonanTarget.IOS_ARM64,
|
||||
KonanTarget.IOS_X64
|
||||
).identityString
|
||||
)
|
||||
}
|
||||
|
||||
val resolvable = project.configurations.create("testDependencies") { configuration ->
|
||||
configuration.markResolvable()
|
||||
configuration.attributes.attribute(
|
||||
CommonizerTargetAttribute.attribute,
|
||||
CommonizerTarget(
|
||||
KonanTarget.IOS_ARM64,
|
||||
KonanTarget.IOS_X64
|
||||
).identityString
|
||||
)
|
||||
}
|
||||
|
||||
project.dependencies {
|
||||
resolvable(project)
|
||||
}
|
||||
|
||||
val resolvedDependencies = resolvable.resolvedConfiguration.lenientConfiguration.allModuleDependencies
|
||||
if (resolvedDependencies.isEmpty()) fail("Expected at least one dependency")
|
||||
if (resolvedDependencies.size > 1) fail("Expected exactly one dependency. Found: $resolvedDependencies")
|
||||
val resolved = resolvedDependencies.single()
|
||||
assertEquals(consumable.name, resolved.configuration)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - disambiguation rule - chooses most specific variant`() {
|
||||
val project = buildProjectWithMPP()
|
||||
|
||||
project.configurations.create("testConsumableAll") { configuration ->
|
||||
configuration.markConsumable()
|
||||
configuration.attributes.attribute(
|
||||
CommonizerTargetAttribute.attribute,
|
||||
CommonizerTarget(
|
||||
KonanTarget.LINUX_X64,
|
||||
KonanTarget.LINUX_ARM64,
|
||||
KonanTarget.IOS_ARM64,
|
||||
KonanTarget.IOS_X64,
|
||||
KonanTarget.MACOS_X64,
|
||||
KonanTarget.MACOS_ARM64
|
||||
).identityString
|
||||
)
|
||||
}
|
||||
|
||||
/* More specific as it does not offer macos parts */
|
||||
val consumableSpecific = project.configurations.create("testConsumableSpecific") { configuration ->
|
||||
configuration.markConsumable()
|
||||
configuration.attributes.attribute(
|
||||
CommonizerTargetAttribute.attribute,
|
||||
CommonizerTarget(
|
||||
KonanTarget.IOS_ARM64,
|
||||
KonanTarget.IOS_X64,
|
||||
KonanTarget.LINUX_X64,
|
||||
KonanTarget.LINUX_ARM64,
|
||||
).identityString
|
||||
)
|
||||
}
|
||||
|
||||
val resolvable = project.configurations.create("testDependencies") { configuration ->
|
||||
configuration.markResolvable()
|
||||
configuration.attributes.attribute(
|
||||
CommonizerTargetAttribute.attribute,
|
||||
CommonizerTarget(
|
||||
KonanTarget.IOS_ARM64,
|
||||
KonanTarget.IOS_X64
|
||||
).identityString
|
||||
)
|
||||
}
|
||||
|
||||
project.dependencies {
|
||||
resolvable(project)
|
||||
}
|
||||
|
||||
val resolvedDependencies = resolvable.resolvedConfiguration.lenientConfiguration.allModuleDependencies
|
||||
if (resolvedDependencies.isEmpty()) fail("Expected at least one dependency")
|
||||
if (resolvedDependencies.size > 1) fail("Expected exactly one dependency. Found: $resolvedDependencies")
|
||||
val resolved = resolvedDependencies.single()
|
||||
assertEquals(consumableSpecific.name, resolved.configuration)
|
||||
}
|
||||
}
|
||||
+14
-13
@@ -15,6 +15,7 @@ import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
|
||||
import org.jetbrains.kotlin.gradle.targets.native.internal.CInteropCommonizerTask
|
||||
import org.jetbrains.kotlin.gradle.targets.native.internal.CInteropCommonizerGroup
|
||||
import org.jetbrains.kotlin.gradle.targets.native.internal.commonizeCInteropTask
|
||||
import org.jetbrains.kotlin.gradle.targets.native.internal.findCInteropCommonizerGroup
|
||||
import org.jetbrains.kotlin.gradle.util.enableCInteropCommonization
|
||||
import org.jetbrains.kotlin.gradle.util.main
|
||||
import org.jetbrains.kotlin.gradle.util.runLifecycleAwareTest
|
||||
@@ -78,7 +79,7 @@ class CInteropCommonizerTaskTest : MultiplatformExtensionTest() {
|
||||
setOf(CommonizerTarget(LINUX_X64, MACOS_X64)),
|
||||
setOf(linuxInterop.identifier, macosInterop.identifier)
|
||||
),
|
||||
task.findInteropsGroup(expectCInteropCommonizerDependent(nativeMain))
|
||||
project.findCInteropCommonizerGroup(expectCInteropCommonizerDependent(nativeMain))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -148,8 +149,8 @@ class CInteropCommonizerTaskTest : MultiplatformExtensionTest() {
|
||||
assertCInteropDependentEqualsForSourceSetAndCompilation(nativeMain)
|
||||
assertCInteropDependentEqualsForSourceSetAndCompilation(iosMain)
|
||||
|
||||
assertEquals(group, task.findInteropsGroup(expectCInteropCommonizerDependent(nativeMain)))
|
||||
assertEquals(group, task.findInteropsGroup(expectCInteropCommonizerDependent(iosMain)))
|
||||
assertEquals(group, findCInteropCommonizerGroup(expectCInteropCommonizerDependent(nativeMain)))
|
||||
assertEquals(group, findCInteropCommonizerGroup(expectCInteropCommonizerDependent(iosMain)))
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -207,7 +208,7 @@ class CInteropCommonizerTaskTest : MultiplatformExtensionTest() {
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
group, task.findInteropsGroup(expectCInteropCommonizerDependent(nativeMain))
|
||||
group, findCInteropCommonizerGroup(expectCInteropCommonizerDependent(nativeMain))
|
||||
)
|
||||
|
||||
assertNull(
|
||||
@@ -267,11 +268,11 @@ class CInteropCommonizerTaskTest : MultiplatformExtensionTest() {
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
group, task.findInteropsGroup(expectCInteropCommonizerDependent(nativeMain))
|
||||
group, findCInteropCommonizerGroup(expectCInteropCommonizerDependent(nativeMain))
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
group, task.findInteropsGroup(expectCInteropCommonizerDependent(expectSharedNativeCompilation(nativeMain)))
|
||||
group, findCInteropCommonizerGroup(expectCInteropCommonizerDependent(expectSharedNativeCompilation(nativeMain)))
|
||||
)
|
||||
|
||||
assertNull(
|
||||
@@ -436,37 +437,37 @@ class CInteropCommonizerTaskTest : MultiplatformExtensionTest() {
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
mainGroup, task.findInteropsGroup(expectCInteropCommonizerDependent(nativeMain)),
|
||||
mainGroup, findCInteropCommonizerGroup(expectCInteropCommonizerDependent(nativeMain)),
|
||||
"Expected nativeMain being part of the mainGroup"
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
mainGroup, task.findInteropsGroup(expectCInteropCommonizerDependent(unixMain)),
|
||||
mainGroup, findCInteropCommonizerGroup(expectCInteropCommonizerDependent(unixMain)),
|
||||
"Expected unixMain being part of the mainGroup"
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
mainGroup, task.findInteropsGroup(expectCInteropCommonizerDependent(appleMain)),
|
||||
mainGroup, findCInteropCommonizerGroup(expectCInteropCommonizerDependent(appleMain)),
|
||||
"Expected appleMain being part of the mainGroup"
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
mainGroup, task.findInteropsGroup(expectCInteropCommonizerDependent(iosMain)),
|
||||
mainGroup, findCInteropCommonizerGroup(expectCInteropCommonizerDependent(iosMain)),
|
||||
"Expected iosMain being part of the mainGroup"
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
testGroup, task.findInteropsGroup(expectCInteropCommonizerDependent(nativeTest)),
|
||||
testGroup, findCInteropCommonizerGroup(expectCInteropCommonizerDependent(nativeTest)),
|
||||
"Expected nativeTest being part of the testGroup"
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
testGroup, task.findInteropsGroup(expectCInteropCommonizerDependent(appleTest)),
|
||||
testGroup, findCInteropCommonizerGroup(expectCInteropCommonizerDependent(appleTest)),
|
||||
"Expected appleTest being part of the testGroup"
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
testGroup, task.findInteropsGroup(expectCInteropCommonizerDependent(iosTest)),
|
||||
testGroup, findCInteropCommonizerGroup(expectCInteropCommonizerDependent(iosTest)),
|
||||
"Expected iosTest being part of the testGroup"
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user