[MPP] Initial support for cinterop import with KGP resolvers

KT-54975
This commit is contained in:
Pavel Kirpichenkov
2022-12-14 14:40:49 +02:00
committed by Space Team
parent 20e80401b2
commit 7e2a7579e8
9 changed files with 197 additions and 4 deletions
@@ -5,11 +5,14 @@
package org.jetbrains.kotlin.gradle.plugin.ide
import org.jetbrains.kotlin.commonizer.SharedCommonizerTarget
import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.Companion.kotlinPropertiesProvider
import org.jetbrains.kotlin.gradle.plugin.ide.IdeMultiplatformImport.SourceSetConstraint
import org.jetbrains.kotlin.gradle.plugin.ide.dependencyResolvers.*
import org.jetbrains.kotlin.gradle.plugin.ide.dependencyTransformers.IdePlatformStdlibCommonDependencyFilter
import org.jetbrains.kotlin.gradle.targets.native.internal.getCommonizerTarget
import org.jetbrains.kotlin.gradle.plugin.ide.dependencyTransformers.UnusedSourcesAndDocumentationFilter
internal fun IdeMultiplatformImport(extension: KotlinProjectExtension): IdeMultiplatformImport {
return IdeMultiplatformImportImpl(extension).apply {
@@ -84,6 +87,27 @@ internal fun IdeMultiplatformImport(extension: KotlinProjectExtension): IdeMulti
level = IdeMultiplatformImport.DependencyResolutionLevel.Default
)
registerDependencyResolver(
resolver = IdePlatformCinteropDependencyResolver,
constraint = { sourceSet -> SourceSetConstraint.isLeaf(sourceSet) && SourceSetConstraint.isNative(sourceSet) },
phase = IdeMultiplatformImport.DependencyResolutionPhase.BinaryDependencyResolution,
level = IdeMultiplatformImport.DependencyResolutionLevel.Default,
)
registerDependencyResolver(
resolver = IdeCommonizedCinteropDependencyResolver,
constraint = { sourceSet -> SourceSetConstraint.isNative(sourceSet) && getCommonizerTarget(sourceSet) is SharedCommonizerTarget },
phase = IdeMultiplatformImport.DependencyResolutionPhase.BinaryDependencyResolution,
level = IdeMultiplatformImport.DependencyResolutionLevel.Default,
)
registerDependencyResolver(
resolver = IdeCInteropMetadataDependencyClasspathResolver,
constraint = { SourceSetConstraint.isNative(it) && !SourceSetConstraint.isLeaf(it) },
phase = IdeMultiplatformImport.DependencyResolutionPhase.BinaryDependencyResolution,
level = IdeMultiplatformImport.DependencyResolutionLevel.Default,
)
/*
Register resolution of dependencies for jvm+android dependencies:
This resolver will resolve dependencies visible to the source set from a 'jvm' perspective.
@@ -0,0 +1,23 @@
/*
* Copyright 2010-2022 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.plugin.ide.dependencyResolvers
import org.jetbrains.kotlin.gradle.idea.tcs.IdeaKotlinDependency
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
import org.jetbrains.kotlin.gradle.plugin.ide.IdeDependencyResolver
import org.jetbrains.kotlin.gradle.plugin.sources.DefaultKotlinSourceSet
import org.jetbrains.kotlin.gradle.targets.native.internal.createCInteropMetadataDependencyClasspathForIde
object IdeCInteropMetadataDependencyClasspathResolver : IdeDependencyResolver {
override fun resolve(sourceSet: KotlinSourceSet): Set<IdeaKotlinDependency> {
if (sourceSet !is DefaultKotlinSourceSet) return emptySet()
val project = sourceSet.project
// TODO (kirpichenkov): sensitive to task being registered for the source set
val cinteropFiles = project.createCInteropMetadataDependencyClasspathForIde(sourceSet)
return resolveCinteropDependencies(project, cinteropFiles)
}
}
@@ -0,0 +1,22 @@
/*
* Copyright 2010-2022 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.plugin.ide.dependencyResolvers
import org.jetbrains.kotlin.gradle.idea.tcs.IdeaKotlinDependency
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
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
object IdeCommonizedCinteropDependencyResolver : IdeDependencyResolver {
override fun resolve(sourceSet: KotlinSourceSet): Set<IdeaKotlinDependency> {
if (sourceSet !is DefaultKotlinSourceSet) return emptySet()
val project = sourceSet.project
val cinteropFiles = project.cinteropCommonizerDependencies(sourceSet)
return resolveCinteropDependencies(project, cinteropFiles)
}
}
@@ -0,0 +1,27 @@
/*
* Copyright 2010-2022 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.plugin.ide.dependencyResolvers
import org.jetbrains.kotlin.gradle.idea.tcs.IdeaKotlinDependency
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
import org.jetbrains.kotlin.gradle.plugin.ide.IdeDependencyResolver
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeCompilation
import org.jetbrains.kotlin.gradle.plugin.sources.DefaultKotlinSourceSet
import org.jetbrains.kotlin.gradle.plugin.sources.internal
import org.jetbrains.kotlin.gradle.targets.native.internal.getAllCInteropOutputFiles
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
object IdePlatformCinteropDependencyResolver : IdeDependencyResolver {
override fun resolve(sourceSet: KotlinSourceSet): Set<IdeaKotlinDependency> {
if (sourceSet !is DefaultKotlinSourceSet) return emptySet()
val project = sourceSet.project
val nativeCompilation = sourceSet.internal.compilations.singleOrNull().safeAs<KotlinNativeCompilation>() ?: return emptySet()
val cinteropFiles = project.getAllCInteropOutputFiles(nativeCompilation)
return resolveCinteropDependencies(project, cinteropFiles)
}
}
@@ -0,0 +1,31 @@
/*
* Copyright 2010-2022 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.plugin.ide.dependencyResolvers
import org.gradle.api.artifacts.component.ProjectComponentIdentifier
import org.jetbrains.kotlin.gradle.idea.tcs.IdeaKotlinDependency
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
import org.jetbrains.kotlin.gradle.plugin.ide.IdeDependencyResolver
object IdeProjectToProjectCInteropDependencyResolver : IdeDependencyResolver {
private val platformDependencyResolver = IdePlatformDependencyResolver(
resolvedArtifactHandler = handler@{ artifact, context ->
val identifier = artifact.id.componentIdentifier as? ProjectComponentIdentifier
?: return@handler null
val projectArtifact = context.allArtifacts.filter { otherArtifact ->
val otherIdentifier = otherArtifact.id.componentIdentifier
otherIdentifier is ProjectComponentIdentifier && otherIdentifier.projectName == identifier.projectName
}
null
}
)
override fun resolve(sourceSet: KotlinSourceSet): Set<IdeaKotlinDependency> {
return platformDependencyResolver.resolve(sourceSet)
}
}
@@ -0,0 +1,52 @@
/*
* Copyright 2010-2022 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.plugin.ide.dependencyResolvers
import org.gradle.api.Project
import org.gradle.api.file.FileCollection
import org.jetbrains.kotlin.gradle.idea.tcs.IdeaKotlinBinaryCoordinates
import org.jetbrains.kotlin.gradle.idea.tcs.IdeaKotlinDependency
import org.jetbrains.kotlin.gradle.idea.tcs.IdeaKotlinResolvedBinaryDependency
import org.jetbrains.kotlin.gradle.idea.tcs.extras.klibExtra
import org.jetbrains.kotlin.gradle.plugin.ide.KlibExtra
import org.jetbrains.kotlin.konan.file.File
import org.jetbrains.kotlin.library.KLIB_FILE_EXTENSION
import org.jetbrains.kotlin.library.ToolingSingleFileKlibResolveStrategy
import org.jetbrains.kotlin.library.commonizerTarget
import org.jetbrains.kotlin.library.resolveSingleFileKlib
fun resolveCinteropDependencies(project: Project, cinteropFiles: FileCollection): Set<IdeaKotlinDependency> {
return cinteropFiles.files
.filter { it.isDirectory || it.extension == KLIB_FILE_EXTENSION }
.mapNotNullTo(mutableSetOf()) { libraryFile ->
createCinteropLibraryDependency(project, libraryFile)
}
}
private fun createCinteropLibraryDependency(project: Project, libraryFile: java.io.File): IdeaKotlinResolvedBinaryDependency? {
val library = try {
resolveSingleFileKlib(
libraryFile = File(libraryFile.absolutePath),
strategy = ToolingSingleFileKlibResolveStrategy
)
} catch (error: Throwable) {
project.logger.error("Failed to resolve library ${libraryFile.path}", error)
return null
}
return IdeaKotlinResolvedBinaryDependency(
binaryType = IdeaKotlinDependency.CLASSPATH_BINARY_TYPE,
binaryFile = libraryFile,
coordinates = IdeaKotlinBinaryCoordinates(
group = project.group.toString(),
module = library.moduleDisplayName,
version = null,
sourceSetName = library.commonizerTarget
),
).apply {
klibExtra = KlibExtra(library)
}
}
@@ -9,8 +9,17 @@ import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
import org.jetbrains.kotlin.gradle.plugin.mpp.MetadataDependencyResolution
import org.jetbrains.kotlin.gradle.plugin.mpp.metadataDependencyResolutionsOrEmpty
import org.jetbrains.kotlin.gradle.plugin.sources.DefaultKotlinSourceSet
import org.jetbrains.kotlin.gradle.plugin.sources.KotlinDependencyScope
import org.jetbrains.kotlin.gradle.plugin.sources.KotlinDependencyScope.*
import org.jetbrains.kotlin.library.KotlinLibrary
import org.jetbrains.kotlin.library.packageFqName
import org.jetbrains.kotlin.library.shortName
import org.jetbrains.kotlin.library.uniqueName
internal inline fun <reified T : MetadataDependencyResolution> KotlinSourceSet.resolveMetadata(): List<T> {
if (this !is DefaultKotlinSourceSet) return emptyList()
return compileDependenciesTransformation.metadataDependencyResolutionsOrEmpty.filterIsInstance<T>()
}
internal val KotlinLibrary.moduleDisplayName: String
get() = packageFqName ?: shortName ?: uniqueName
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.gradle.targets.native.internal
import org.gradle.api.Project
import org.gradle.api.file.FileCollection
import org.jetbrains.kotlin.commonizer.SharedCommonizerTarget
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtensionOrNull
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinSharedNativeCompilation
@@ -42,16 +43,20 @@ private fun Project.setupCInteropCommonizerDependenciesForCompilation(compilatio
* The copying task prevent red code within the IDE after cleaning the build output.
*/
private fun Project.setupCInteropCommonizerDependenciesForIde(sourceSet: DefaultKotlinSourceSet) {
val cinteropCommonizerTask = project.copyCommonizeCInteropForIdeTask ?: return
addIntransitiveMetadataDependencyIfPossible(sourceSet, cinteropCommonizerDependencies(sourceSet))
}
addIntransitiveMetadataDependencyIfPossible(sourceSet, filesProvider files@{
internal fun Project.cinteropCommonizerDependencies(sourceSet: DefaultKotlinSourceSet): FileCollection {
val cinteropCommonizerTask = project.copyCommonizeCInteropForIdeTask ?: return project.files()
return filesProvider {
val directlyDependent = CInteropCommonizerDependent.from(sourceSet)
val associateDependent = CInteropCommonizerDependent.fromAssociateCompilations(sourceSet)
listOfNotNull(directlyDependent, associateDependent).map { cinteropCommonizerDependent ->
cinteropCommonizerTask.get().commonizedOutputLibraries(cinteropCommonizerDependent)
}
})
}
}
private fun Project.setupCInteropTransformCompositeMetadataDependenciesForIde(sourceSet: DefaultKotlinSourceSet) {
@@ -81,7 +81,7 @@ private fun Project.getPropagatedCInteropDependenciesOrEmpty(compilation: Kotlin
getAllCInteropOutputFiles(platformCompilation)
}
private fun Project.getAllCInteropOutputFiles(compilation: KotlinNativeCompilation): FileCollection {
internal fun Project.getAllCInteropOutputFiles(compilation: KotlinNativeCompilation): FileCollection {
val cinteropTasks = compilation.cinterops.map { interop -> interop.interopProcessingTaskName }
.mapNotNull { taskName -> tasks.findByName(taskName) as? CInteropProcess }