[Gradle] Initial port of IdeaKpmProjectModelBuilder into TCS IdeMultiplatformImport
KT-54825
This commit is contained in:
committed by
Space Team
parent
c727dbb8ca
commit
405c94e0bb
+55
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import org.jetbrains.kotlin.tooling.core.Extras
|
||||
import java.io.File
|
||||
import java.io.Serializable
|
||||
|
||||
sealed class IdeDependency {
|
||||
abstract val coordinates: IdeDependencyCoordinates?
|
||||
abstract val extras: Extras
|
||||
|
||||
/* Reflection helper */
|
||||
|
||||
val isSourceDependency: Boolean get() = this is IdeSourceDependency
|
||||
val isBinaryDependency: Boolean get() = this is IdeBinaryDependency
|
||||
val isResolvedBinaryDependency: Boolean get() = this is IdeResolvedBinaryDependency
|
||||
val isUnresolvedBinaryDependency: Boolean get() = this is IdeUnresolvedBinaryDependency
|
||||
|
||||
companion object {
|
||||
const val CLASSPATH_BINARY_TYPE = "org.jetbrains.kotlin.gradle.plugin.ide.IdeDependency.classpathBinaryType"
|
||||
const val SOURCES_BINARY_TYPE = "org.jetbrains.kotlin.gradle.plugin.ide.IdeDependency.sourcesBinaryType"
|
||||
const val DOCUMENTATION_BINARY_TYPE = "org.jetbrains.kotlin.gradle.plugin.ide.IdeDependency.documentationBinaryType"
|
||||
}
|
||||
}
|
||||
|
||||
data class IdeSourceDependency(
|
||||
val type: Type,
|
||||
override val coordinates: IdeSourceCoordinates,
|
||||
override val extras: Extras
|
||||
) : IdeDependency() {
|
||||
enum class Type : Serializable {
|
||||
Regular, Friend, DependsOn
|
||||
}
|
||||
}
|
||||
|
||||
sealed class IdeBinaryDependency : IdeDependency() {
|
||||
abstract override val coordinates: IdeBinaryCoordinates?
|
||||
}
|
||||
|
||||
data class IdeUnresolvedBinaryDependency(
|
||||
val cause: String?,
|
||||
override val coordinates: IdeBinaryCoordinates?,
|
||||
override val extras: Extras
|
||||
) : IdeBinaryDependency()
|
||||
|
||||
data class IdeResolvedBinaryDependency(
|
||||
val binaryType: String,
|
||||
val binaryFile: File,
|
||||
override val extras: Extras,
|
||||
override val coordinates: IdeBinaryCoordinates?
|
||||
) : IdeBinaryDependency()
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
sealed class IdeDependencyCoordinates {
|
||||
/* Reflection helpers */
|
||||
val isBinaryCoordinates get() = this is IdeBinaryCoordinates
|
||||
val isSourceCoordinates get() = this is IdeSourceCoordinates
|
||||
}
|
||||
|
||||
data class IdeBinaryCoordinates(
|
||||
val group: String,
|
||||
val module: String,
|
||||
val version: String?,
|
||||
val sourceSetName: String?
|
||||
) : IdeDependencyCoordinates()
|
||||
|
||||
data class IdeSourceCoordinates(
|
||||
val buildId: String,
|
||||
val projectPath: String,
|
||||
val projectName: String,
|
||||
val sourceSetName: String
|
||||
) : IdeDependencyCoordinates()
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
|
||||
|
||||
/**
|
||||
* An [IdeDependencyEffect] is invoked last in the dependency resolution pipeline.
|
||||
* It is not intended/capable of changing the resolution result, but can trigger side-effects, being sure
|
||||
* that the dependency resolution result is final.
|
||||
*/
|
||||
fun interface IdeDependencyEffect {
|
||||
operator fun invoke(sourceSet: KotlinSourceSet, dependencies: Set<IdeDependency>)
|
||||
}
|
||||
|
||||
fun IdeDependencyResolver.withEffect(effect: IdeDependencyEffect) = IdeDependencyResolver { sourceSet ->
|
||||
this@withEffect.resolve(sourceSet).also { dependencies -> effect(sourceSet, dependencies) }
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
|
||||
|
||||
fun interface IdeDependencyResolver {
|
||||
fun resolve(sourceSet: KotlinSourceSet): Set<IdeDependency>
|
||||
|
||||
object Empty : IdeDependencyResolver {
|
||||
override fun resolve(sourceSet: KotlinSourceSet): Set<IdeDependency> = emptySet()
|
||||
}
|
||||
}
|
||||
|
||||
fun IdeDependencyResolver(
|
||||
resolvers: Iterable<IdeDependencyResolver?>
|
||||
): IdeDependencyResolver {
|
||||
val resolversList = resolvers.filterNotNull()
|
||||
if (resolversList.isEmpty()) return IdeDependencyResolver.Empty
|
||||
return IdeCompositeDependencyResolver(resolversList)
|
||||
}
|
||||
|
||||
fun IdeDependencyResolver(
|
||||
vararg resolvers: IdeDependencyResolver?
|
||||
): IdeDependencyResolver = IdeDependencyResolver(resolvers.toList())
|
||||
|
||||
|
||||
operator fun IdeDependencyResolver.plus(other: IdeDependencyResolver): IdeDependencyResolver {
|
||||
if (this is IdeCompositeDependencyResolver && other is IdeCompositeDependencyResolver)
|
||||
return IdeCompositeDependencyResolver(this.children + other.children)
|
||||
|
||||
if (this is IdeCompositeDependencyResolver) {
|
||||
return IdeCompositeDependencyResolver(this.children + other)
|
||||
}
|
||||
|
||||
if (other is IdeCompositeDependencyResolver) {
|
||||
return IdeCompositeDependencyResolver(listOf(this) + other.children)
|
||||
}
|
||||
return IdeCompositeDependencyResolver(listOf(this, other))
|
||||
}
|
||||
|
||||
private class IdeCompositeDependencyResolver(
|
||||
val children: List<IdeDependencyResolver>
|
||||
) : IdeDependencyResolver {
|
||||
override fun resolve(sourceSet: KotlinSourceSet): Set<IdeDependency> {
|
||||
return children.flatMap { child -> child.resolve(sourceSet) }.toSet()
|
||||
}
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
|
||||
|
||||
/**
|
||||
* An [IdeDependencyTransformer] will be invoked after all [IdeDependencyResolver] finished.
|
||||
* The transformation step is allowed to entirely modify the resolution result. Typical applications would be dependency filtering,
|
||||
* or grouping. Adding additional dependencies might be harmful, since other transformers might not be able to see this additional
|
||||
* dependency, running before the addition.
|
||||
*
|
||||
* Transformations can be scheduled in different phases see: [IdeMultiplatformImport.DependencyTransformationPhase]
|
||||
*/
|
||||
fun interface IdeDependencyTransformer {
|
||||
fun transform(sourceSet: KotlinSourceSet, dependencies: Set<IdeDependency>): Set<IdeDependency>
|
||||
}
|
||||
|
||||
fun IdeDependencyResolver.withTransformer(transformer: IdeDependencyTransformer) = IdeDependencyResolver { sourceSet ->
|
||||
transformer.transform(sourceSet, this@withTransformer.resolve(sourceSet))
|
||||
}
|
||||
|
||||
fun IdeDependencyTransformer(
|
||||
transformers: List<IdeDependencyTransformer?>
|
||||
): IdeDependencyTransformer = IdeCompositeDependencyTransformer(transformers.filterNotNull())
|
||||
|
||||
fun IdeDependencyTransformer(
|
||||
vararg transformers: IdeDependencyTransformer?
|
||||
): IdeDependencyTransformer = IdeDependencyTransformer(transformers.toList())
|
||||
|
||||
operator fun IdeDependencyTransformer.plus(other: IdeDependencyTransformer):
|
||||
IdeDependencyTransformer {
|
||||
if (this is IdeCompositeDependencyTransformer && other is IdeCompositeDependencyTransformer) {
|
||||
return IdeCompositeDependencyTransformer(this.transformers + other.transformers)
|
||||
}
|
||||
|
||||
if (this is IdeCompositeDependencyTransformer) {
|
||||
return IdeCompositeDependencyTransformer(this.transformers + other)
|
||||
}
|
||||
|
||||
if (other is IdeCompositeDependencyTransformer) {
|
||||
return IdeCompositeDependencyTransformer(listOf(this) + other.transformers)
|
||||
}
|
||||
|
||||
return IdeCompositeDependencyTransformer(listOf(this, other))
|
||||
}
|
||||
|
||||
private class IdeCompositeDependencyTransformer(
|
||||
val transformers: List<IdeDependencyTransformer>
|
||||
) : IdeDependencyTransformer {
|
||||
override fun transform(
|
||||
sourceSet: KotlinSourceSet, dependencies: Set<IdeDependency>
|
||||
): Set<IdeDependency> {
|
||||
return transformers.fold(dependencies) { currentDependencies, transformer -> transformer.transform(sourceSet, currentDependencies) }
|
||||
}
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import org.jetbrains.kotlin.tooling.core.Extras
|
||||
|
||||
interface IdeExtrasSerializationExtension {
|
||||
fun <T : Any> serializer(key: Extras.Key<T>): IdeExtrasSerializer<T>?
|
||||
|
||||
object Empty : IdeExtrasSerializationExtension {
|
||||
override fun <T : Any> serializer(key: Extras.Key<T>): IdeExtrasSerializer<T>? = null
|
||||
}
|
||||
}
|
||||
|
||||
interface IdeExtrasSerializationExtensionBuilder {
|
||||
fun <T : Any> register(key: Extras.Key<T>, serializer: IdeExtrasSerializer<T>)
|
||||
}
|
||||
|
||||
fun IdeExtrasSerializationExtension(
|
||||
builder: IdeExtrasSerializationExtensionBuilder.() -> Unit
|
||||
): IdeExtrasSerializationExtension {
|
||||
return IdeaKpmExtrasSerializationExtensionImpl(
|
||||
IdeaExtrasSerializationExtensionBuilderImpl().apply(builder).serializers
|
||||
)
|
||||
}
|
||||
|
||||
private class IdeaExtrasSerializationExtensionBuilderImpl : IdeExtrasSerializationExtensionBuilder {
|
||||
val serializers = mutableMapOf<Extras.Key<*>, IdeExtrasSerializer<*>>()
|
||||
|
||||
override fun <T : Any> register(key: Extras.Key<T>, serializer: IdeExtrasSerializer<T>) {
|
||||
serializers[key] = serializer
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class IdeaKpmExtrasSerializationExtensionImpl(
|
||||
private val map: Map<Extras.Key<*>, IdeExtrasSerializer<*>>
|
||||
) : IdeExtrasSerializationExtension {
|
||||
override fun <T : Any> serializer(key: Extras.Key<T>): IdeExtrasSerializer<T>? {
|
||||
@Suppress("unchecked_cast")
|
||||
return map[key] as? IdeExtrasSerializer<T>
|
||||
}
|
||||
}
|
||||
+27
@@ -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
|
||||
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
|
||||
interface IdeExtrasSerializer<T : Any> {
|
||||
fun serialize(context: IdeSerializationContext, value: T): ByteArray?
|
||||
fun deserialize(context: IdeSerializationContext, data: ByteArray): T?
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Returns a [IdeExtrasSerializer] based upon [java.io.Serializable]
|
||||
*/
|
||||
fun <T : Any> javaIoSerializable(clazz: KClass<T>): IdeExtrasSerializer<T> {
|
||||
return IdeJavaIoSerializableExtrasSerializer(clazz)
|
||||
}
|
||||
|
||||
inline fun <reified T : Any> javaIoSerializable(): IdeExtrasSerializer<T> {
|
||||
return javaIoSerializable(T::class)
|
||||
}
|
||||
}
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.ObjectInputStream
|
||||
import java.io.ObjectOutputStream
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
internal class IdeJavaIoSerializableExtrasSerializer<T : Any>(
|
||||
private val clazz: KClass<T>
|
||||
) : IdeExtrasSerializer<T> {
|
||||
|
||||
override fun serialize(context: IdeSerializationContext, value: T): ByteArray? {
|
||||
return try {
|
||||
ByteArrayOutputStream().use { byteArrayOutputStream ->
|
||||
ObjectOutputStream(byteArrayOutputStream).use { oos -> oos.writeObject(value) }
|
||||
byteArrayOutputStream.toByteArray()
|
||||
}
|
||||
} catch (t: Throwable) {
|
||||
context.logger.error("${ErrorMessages.SERIALIZATION_FAILURE} $value", t)
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
override fun deserialize(context: IdeSerializationContext, data: ByteArray): T? {
|
||||
return try {
|
||||
ObjectInputStream(ByteArrayInputStream(data)).use { stream -> clazz.java.cast(stream.readObject()) }
|
||||
} catch (t: Throwable) {
|
||||
context.logger.error("${ErrorMessages.DESERIALIZATION_FAILURE} ${clazz.java}", t)
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
object ErrorMessages {
|
||||
const val SERIALIZATION_FAILURE = "Failed to serialize"
|
||||
const val DESERIALIZATION_FAILURE = "Failed to deserialize"
|
||||
}
|
||||
}
|
||||
+150
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.jetbrains.kotlin.gradle.ExternalKotlinTargetApi
|
||||
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
|
||||
import org.jetbrains.kotlin.gradle.plugin.extraProperties
|
||||
import org.jetbrains.kotlin.gradle.plugin.ide.IdeMultiplatformImport.DependencyResolutionLevel.Default
|
||||
import org.jetbrains.kotlin.gradle.plugin.ide.IdeMultiplatformImport.DependencyResolutionLevel.Overwrite
|
||||
import org.jetbrains.kotlin.gradle.plugin.ide.IdeMultiplatformImport.SourceSetConstraint
|
||||
import org.jetbrains.kotlin.gradle.targets.metadata.isNativeSourceSet
|
||||
import org.jetbrains.kotlin.gradle.utils.getOrPut
|
||||
import org.jetbrains.kotlin.tooling.core.Extras
|
||||
|
||||
interface IdeMultiplatformImport {
|
||||
|
||||
fun resolveDependencies(sourceSet: KotlinSourceSet): Set<IdeDependency>
|
||||
|
||||
fun <T : Any> serialize(key: Extras.Key<T>, value: T): ByteArray?
|
||||
|
||||
@ExternalKotlinTargetApi
|
||||
fun registerDependencyResolver(
|
||||
resolver: IdeDependencyResolver,
|
||||
constraint: SourceSetConstraint,
|
||||
phase: DependencyResolutionPhase,
|
||||
level: DependencyResolutionLevel = Default,
|
||||
)
|
||||
|
||||
@ExternalKotlinTargetApi
|
||||
fun registerDependencyTransformer(
|
||||
transformer: IdeDependencyTransformer,
|
||||
constraint: SourceSetConstraint,
|
||||
phase: DependencyTransformationPhase
|
||||
)
|
||||
|
||||
@ExternalKotlinTargetApi
|
||||
fun registerDependencyEffect(
|
||||
effect: IdeDependencyEffect,
|
||||
constraint: SourceSetConstraint
|
||||
)
|
||||
|
||||
@ExternalKotlinTargetApi
|
||||
fun registerExtrasSerializationExtension(
|
||||
extension: IdeExtrasSerializationExtension
|
||||
)
|
||||
|
||||
/**
|
||||
* Any [IdeDependencyResolver] has to be registered for a given dependency resolution phase in which it participates
|
||||
* The resolution phases will be executed in the order of their enum's ordinal.
|
||||
*/
|
||||
enum class DependencyResolutionPhase {
|
||||
/**
|
||||
* Generic phase intended to run before all other resolvers
|
||||
*/
|
||||
PreDependencyResolution,
|
||||
|
||||
/**
|
||||
* Resolution phase intended to resolve project to project (source) dependencies
|
||||
*/
|
||||
SourceDependencyResolution,
|
||||
|
||||
/**
|
||||
* Resolution phase intended to resolve binary dependencies (downloading and transforming from repositories)
|
||||
*/
|
||||
BinaryDependencyResolution,
|
||||
|
||||
/**
|
||||
* Generic phase intended to run after all other resolvers.
|
||||
*/
|
||||
PostDependencyResolution
|
||||
}
|
||||
|
||||
/**
|
||||
* Any [IdeDependencyResolver] has to be registered specifying a certain resolution level.
|
||||
* Generally, all resolvers registered in a given resolution level will work collaboratively, meaning the dependency resolution
|
||||
* result is the aggregation of all resolvers running.
|
||||
*
|
||||
* However, only the resolvers in the highest resolution result will run e.g.
|
||||
* If resolvers with level [Overwrite] are found, then only those will contribute to the dependency resolution.
|
||||
* Otherwise, all [Default] resolvers will run.
|
||||
*/
|
||||
enum class DependencyResolutionLevel {
|
||||
Default, Overwrite
|
||||
}
|
||||
|
||||
/**
|
||||
* Any [IdeDependencyResolver] has to be registered for a given transformation phase.
|
||||
* The phases will be executed in the order of this enums ordinal.
|
||||
*/
|
||||
enum class DependencyTransformationPhase {
|
||||
/**
|
||||
* Generic dependency transformation phase, intended to run a transformation before all other transformers.
|
||||
*/
|
||||
PreDependencyTransformationPhase,
|
||||
|
||||
/**
|
||||
* Dependency transformation phase that is free entirely free in its transformation type.
|
||||
* Note: Adding dependencies to the resolution result might most likely better be modelled as [IdeDependencyResolver]
|
||||
*/
|
||||
FreeDependencyTransformationPhase,
|
||||
|
||||
/**
|
||||
* Special dependency transformation phase intended for filtering dependencies.
|
||||
* This phase is guaranteed to run after the [FreeDependencyTransformationPhase] alongside other dependency filtering
|
||||
* transformations. Adding filters here is the safest.
|
||||
*/
|
||||
DependencyFilteringPhase,
|
||||
|
||||
/**
|
||||
* Generic dependency transformation phase, intended to run a transformation after all other transformers
|
||||
*/
|
||||
PostDependencyTransformationPhase
|
||||
}
|
||||
|
||||
/**
|
||||
* Used for scoping [IdeDependencyResolver], [IdeDependencyResolver] and [IdeDependencyEffect]
|
||||
*/
|
||||
fun interface SourceSetConstraint {
|
||||
operator fun invoke(sourceSet: KotlinSourceSet): Boolean
|
||||
|
||||
companion object {
|
||||
val unconstrained = SourceSetConstraint { true }
|
||||
val isNative = SourceSetConstraint { sourceSet -> isNativeSourceSet(sourceSet) }
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun instance(project: Project): IdeMultiplatformImport {
|
||||
return project.extraProperties.getOrPut(IdeMultiplatformImport::class.java.name) {
|
||||
IdeMultiplatformImport(project.multiplatformExtension)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal val Project.kotlinIdeMultiplatformImport: IdeMultiplatformImport get() = IdeMultiplatformImport.instance(project)
|
||||
|
||||
@ExternalKotlinTargetApi
|
||||
fun IdeMultiplatformImport.registerExtrasSerializationExtension(
|
||||
builder: IdeExtrasSerializationExtensionBuilder.() -> Unit
|
||||
) {
|
||||
registerExtrasSerializationExtension(IdeExtrasSerializationExtension(builder))
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
|
||||
import org.jetbrains.kotlin.gradle.kpm.idea.kotlinDebugKey
|
||||
import org.jetbrains.kotlin.gradle.plugin.ide.dependencyResolvers.IdeDependsOnDependencyResolver
|
||||
|
||||
fun IdeMultiplatformImport(extension: KotlinMultiplatformExtension): IdeMultiplatformImport {
|
||||
return IdeMultiplatformImportImpl(extension).apply {
|
||||
|
||||
registerDependencyResolver(
|
||||
resolver = IdeDependsOnDependencyResolver,
|
||||
constraint = IdeMultiplatformImport.SourceSetConstraint.unconstrained,
|
||||
phase = IdeMultiplatformImport.DependencyResolutionPhase.SourceDependencyResolution,
|
||||
level = IdeMultiplatformImport.DependencyResolutionLevel.Default
|
||||
)
|
||||
|
||||
registerExtrasSerializationExtension {
|
||||
register(kotlinDebugKey, IdeExtrasSerializer.javaIoSerializable())
|
||||
}
|
||||
}
|
||||
}
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import org.jetbrains.kotlin.gradle.ExternalKotlinTargetApi
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
|
||||
import org.jetbrains.kotlin.gradle.plugin.ide.IdeMultiplatformImport.*
|
||||
import org.jetbrains.kotlin.tooling.core.Extras
|
||||
|
||||
|
||||
internal class IdeMultiplatformImportImpl(
|
||||
private val extension: KotlinMultiplatformExtension
|
||||
) : IdeMultiplatformImport {
|
||||
|
||||
override fun resolveDependencies(sourceSet: KotlinSourceSet): Set<IdeDependency> {
|
||||
return createDependencyResolver().resolve(sourceSet)
|
||||
}
|
||||
|
||||
override fun <T : Any> serialize(key: Extras.Key<T>, value: T): ByteArray? {
|
||||
return null
|
||||
}
|
||||
|
||||
private data class RegisteredDependencyResolver(
|
||||
val resolver: IdeDependencyResolver,
|
||||
val constraint: SourceSetConstraint,
|
||||
val phase: DependencyResolutionPhase,
|
||||
val level: DependencyResolutionLevel,
|
||||
)
|
||||
|
||||
private data class RegisteredDependencyTransformer(
|
||||
val transformer: IdeDependencyTransformer,
|
||||
val constraint: SourceSetConstraint,
|
||||
val phase: DependencyTransformationPhase
|
||||
)
|
||||
|
||||
private data class RegisteredDependencyEffect(
|
||||
val effect: IdeDependencyEffect,
|
||||
val constraint: SourceSetConstraint,
|
||||
)
|
||||
|
||||
private val registeredDependencyResolvers = mutableListOf<RegisteredDependencyResolver>()
|
||||
private val registeredDependencyTransformers = mutableListOf<RegisteredDependencyTransformer>()
|
||||
private val registeredDependencyEffects = mutableListOf<RegisteredDependencyEffect>()
|
||||
private val registeredExtrasSerializationExtensions = mutableListOf<IdeExtrasSerializationExtension>()
|
||||
|
||||
@ExternalKotlinTargetApi
|
||||
override fun registerDependencyResolver(
|
||||
resolver: IdeDependencyResolver,
|
||||
constraint: SourceSetConstraint,
|
||||
phase: DependencyResolutionPhase,
|
||||
level: DependencyResolutionLevel
|
||||
) {
|
||||
registeredDependencyResolvers.add(
|
||||
RegisteredDependencyResolver(resolver, constraint, phase, level)
|
||||
)
|
||||
}
|
||||
|
||||
@ExternalKotlinTargetApi
|
||||
override fun registerDependencyTransformer(
|
||||
transformer: IdeDependencyTransformer,
|
||||
constraint: SourceSetConstraint,
|
||||
phase: DependencyTransformationPhase
|
||||
) {
|
||||
registeredDependencyTransformers.add(
|
||||
RegisteredDependencyTransformer(transformer, constraint, phase)
|
||||
)
|
||||
}
|
||||
|
||||
@ExternalKotlinTargetApi
|
||||
override fun registerDependencyEffect(effect: IdeDependencyEffect, constraint: SourceSetConstraint) {
|
||||
registeredDependencyEffects.add(
|
||||
RegisteredDependencyEffect(effect, constraint)
|
||||
)
|
||||
}
|
||||
|
||||
@ExternalKotlinTargetApi
|
||||
override fun registerExtrasSerializationExtension(extension: IdeExtrasSerializationExtension) {
|
||||
registeredExtrasSerializationExtensions.add(extension)
|
||||
}
|
||||
|
||||
private fun createDependencyResolver(): IdeDependencyResolver {
|
||||
return IdeDependencyResolver(DependencyResolutionPhase.values().map { phase ->
|
||||
createDependencyResolver(phase)
|
||||
}).withTransformer(createDependencyTransformer())
|
||||
.withEffect(createDependencyEffect())
|
||||
}
|
||||
|
||||
private fun createDependencyResolver(phase: DependencyResolutionPhase) = IdeDependencyResolver resolve@{ sourceSet ->
|
||||
val applicableResolvers = registeredDependencyResolvers
|
||||
.filter { it.phase == phase }
|
||||
.filter { it.constraint(sourceSet) }
|
||||
.groupBy { it.level }
|
||||
|
||||
/* Find resolvers in the highest resolution level and only consider those */
|
||||
DependencyResolutionLevel.values().reversed().forEach { level ->
|
||||
val resolvers = applicableResolvers[level].orEmpty().map { it.resolver }
|
||||
if (resolvers.isNotEmpty()) {
|
||||
return@resolve IdeDependencyResolver(resolvers).resolve(sourceSet)
|
||||
}
|
||||
}
|
||||
|
||||
/* No resolvers found */
|
||||
emptySet()
|
||||
}
|
||||
|
||||
private fun createDependencyTransformer(): IdeDependencyTransformer {
|
||||
return IdeDependencyTransformer(DependencyTransformationPhase.values().map { phase ->
|
||||
createDependencyTransformer(phase)
|
||||
})
|
||||
}
|
||||
|
||||
private fun createDependencyTransformer(phase: DependencyTransformationPhase): IdeDependencyTransformer {
|
||||
return IdeDependencyTransformer { sourceSet, dependencies ->
|
||||
IdeDependencyTransformer(
|
||||
registeredDependencyTransformers
|
||||
.filter { it.phase == phase }
|
||||
.filter { it.constraint(sourceSet) }
|
||||
.map { it.transformer }
|
||||
).transform(sourceSet, dependencies)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createDependencyEffect(): IdeDependencyEffect = IdeDependencyEffect { sourceSet, dependencies ->
|
||||
registeredDependencyEffects
|
||||
.filter { it.constraint(sourceSet) }
|
||||
.forEach { it.effect(sourceSet, dependencies) }
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
interface IdeSerializationContext {
|
||||
val logger: IdeSerializationLogger
|
||||
val extrasSerializationExtension: IdeExtrasSerializationExtension
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
interface IdeSerializationLogger {
|
||||
enum class Severity {
|
||||
WARNING,
|
||||
ERROR,
|
||||
}
|
||||
|
||||
fun report(severity: Severity, message: String, cause: Throwable? = null)
|
||||
fun warn(message: String, cause: Throwable? = null) = report(Severity.WARNING, message, cause)
|
||||
fun error(message: String, cause: Throwable? = null) = report(Severity.ERROR, message, cause)
|
||||
|
||||
object None : IdeSerializationLogger {
|
||||
override fun report(severity: Severity, message: String, cause: Throwable?) = Unit
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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.plugin.KotlinSourceSet
|
||||
import org.jetbrains.kotlin.gradle.plugin.ide.IdeDependency
|
||||
import org.jetbrains.kotlin.gradle.plugin.ide.IdeDependencyResolver
|
||||
import org.jetbrains.kotlin.gradle.plugin.ide.IdeSourceCoordinates
|
||||
import org.jetbrains.kotlin.gradle.plugin.ide.IdeSourceDependency
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.util.currentBuildId
|
||||
import org.jetbrains.kotlin.gradle.plugin.sources.internal
|
||||
import org.jetbrains.kotlin.tooling.core.emptyExtras
|
||||
|
||||
internal object IdeDependsOnDependencyResolver : IdeDependencyResolver {
|
||||
override fun resolve(sourceSet: KotlinSourceSet): Set<IdeDependency> {
|
||||
return sourceSet.internal.dependsOnClosure.map { dependsOnSourceSet ->
|
||||
IdeSourceDependency(
|
||||
type = IdeSourceDependency.Type.DependsOn,
|
||||
coordinates = IdeSourceCoordinates(
|
||||
buildId = dependsOnSourceSet.internal.project.currentBuildId().name,
|
||||
projectPath = dependsOnSourceSet.internal.project.path,
|
||||
projectName = dependsOnSourceSet.internal.project.name,
|
||||
sourceSetName = dependsOnSourceSet.name
|
||||
),
|
||||
extras = emptyExtras()
|
||||
)
|
||||
}.toSet()
|
||||
}
|
||||
}
|
||||
+3
-1
@@ -11,6 +11,7 @@ import org.gradle.api.Action
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.file.SourceDirectorySet
|
||||
import org.jetbrains.kotlin.build.DEFAULT_KOTLIN_SOURCE_FILES_EXTENSIONS
|
||||
import org.jetbrains.kotlin.gradle.InternalKotlinGradlePluginApi
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinDependencyHandler
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
|
||||
@@ -24,8 +25,9 @@ import javax.inject.Inject
|
||||
|
||||
const val METADATA_CONFIGURATION_NAME_SUFFIX = "DependenciesMetadata"
|
||||
|
||||
@InternalKotlinGradlePluginApi
|
||||
abstract class DefaultKotlinSourceSet @Inject constructor(
|
||||
private val project: Project,
|
||||
final override val project: Project,
|
||||
val displayName: String
|
||||
) : AbstractKotlinSourceSet() {
|
||||
|
||||
|
||||
+2
@@ -5,6 +5,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.gradle.plugin.sources
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
|
||||
import org.jetbrains.kotlin.gradle.utils.MutableObservableSet
|
||||
@@ -16,6 +17,7 @@ internal val KotlinSourceSet.internal: InternalKotlinSourceSet
|
||||
)
|
||||
|
||||
internal interface InternalKotlinSourceSet : KotlinSourceSet {
|
||||
val project: Project
|
||||
override val dependsOn: ObservableSet<KotlinSourceSet>
|
||||
val dependsOnClosure: ObservableSet<KotlinSourceSet>
|
||||
val withDependsOnClosure: ObservableSet<KotlinSourceSet>
|
||||
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
@file:Suppress("FunctionName")
|
||||
|
||||
package org.jetbrains.kotlin.gradle.ide
|
||||
|
||||
import org.jetbrains.kotlin.gradle.buildProjectWithMPP
|
||||
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension
|
||||
import org.jetbrains.kotlin.gradle.plugin.ide.IdeSourceDependency
|
||||
import org.jetbrains.kotlin.gradle.plugin.ide.kotlinIdeMultiplatformImport
|
||||
import kotlin.test.Test
|
||||
|
||||
class IdeMultiplatformResolveDependsOnDependenciesTest {
|
||||
|
||||
@Test
|
||||
fun `test - sample 0 - default dependsOn to commonMain and commonTest`() {
|
||||
val project = buildProjectWithMPP()
|
||||
val kotlin = project.multiplatformExtension
|
||||
kotlin.jvm()
|
||||
|
||||
val commonMain = kotlin.sourceSets.getByName("commonMain")
|
||||
val commonTest = kotlin.sourceSets.getByName("commonTest")
|
||||
val jvmMain = kotlin.sourceSets.getByName("jvmMain")
|
||||
val jvmTest = kotlin.sourceSets.getByName("jvmTest")
|
||||
|
||||
|
||||
project.kotlinIdeMultiplatformImport.resolveDependencies(jvmMain)
|
||||
.filterIsInstance<IdeSourceDependency>()
|
||||
.assertMatches(dependsOnDependency(commonMain))
|
||||
|
||||
|
||||
project.kotlinIdeMultiplatformImport.resolveDependencies(jvmTest)
|
||||
.filterIsInstance<IdeSourceDependency>()
|
||||
.assertMatches(dependsOnDependency(commonTest))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - sample 1 - custom dependsOn edge`() {
|
||||
val project = buildProjectWithMPP()
|
||||
val kotlin = project.multiplatformExtension
|
||||
kotlin.jvm()
|
||||
val commonMain = kotlin.sourceSets.getByName("commonMain")
|
||||
val customMain = kotlin.sourceSets.create("customMain")
|
||||
val jvmMain = kotlin.sourceSets.getByName("jvmMain")
|
||||
|
||||
jvmMain.dependsOn(customMain)
|
||||
customMain.dependsOn(commonMain)
|
||||
|
||||
project.kotlinIdeMultiplatformImport.resolveDependencies(jvmMain)
|
||||
.filterIsInstance<IdeSourceDependency>()
|
||||
.assertMatches(
|
||||
dependsOnDependency(commonMain),
|
||||
dependsOnDependency(customMain)
|
||||
)
|
||||
|
||||
project.kotlinIdeMultiplatformImport.resolveDependencies(customMain)
|
||||
.filterIsInstance<IdeSourceDependency>()
|
||||
.assertMatches(dependsOnDependency(commonMain))
|
||||
}
|
||||
}
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* 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.ide
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
|
||||
import org.jetbrains.kotlin.gradle.plugin.ide.IdeDependency
|
||||
import org.jetbrains.kotlin.gradle.plugin.ide.IdeSourceDependency
|
||||
import org.jetbrains.kotlin.gradle.plugin.sources.internal
|
||||
import org.junit.jupiter.api.fail
|
||||
|
||||
fun buildIdeDependencyMatchers(notation: Any?): List<IdeDependencyMatcher> {
|
||||
return when (notation) {
|
||||
null -> return emptyList()
|
||||
is IdeDependencyMatcher -> listOf(notation)
|
||||
is Iterable<*> -> notation.flatMap { buildIdeDependencyMatchers(it) }
|
||||
else -> error("Can't build ${IdeSourceDependencyMatcher::class.java.simpleName} from $notation")
|
||||
}
|
||||
}
|
||||
|
||||
fun ideSourceDependency(type: IdeSourceDependency.Type, project: Project, sourceSetName: String): IdeDependencyMatcher {
|
||||
return IdeSourceDependencyMatcher(type, project.path, sourceSetName)
|
||||
}
|
||||
|
||||
fun dependsOnDependency(project: Project, sourceSetName: String) =
|
||||
ideSourceDependency(IdeSourceDependency.Type.DependsOn, project, sourceSetName)
|
||||
|
||||
fun dependsOnDependency(sourceSet: KotlinSourceSet) =
|
||||
dependsOnDependency(sourceSet.internal.project, sourceSet.name)
|
||||
|
||||
interface IdeDependencyMatcher {
|
||||
val description: String
|
||||
fun matches(dependency: IdeDependency): Boolean
|
||||
}
|
||||
|
||||
private class IdeSourceDependencyMatcher(
|
||||
val type: IdeSourceDependency.Type,
|
||||
val projectPath: String,
|
||||
val sourceSetName: String
|
||||
) : IdeDependencyMatcher {
|
||||
override val description: String =
|
||||
"source($type)::$projectPath/$sourceSetName"
|
||||
|
||||
override fun matches(dependency: IdeDependency): Boolean {
|
||||
if (dependency !is IdeSourceDependency) return false
|
||||
return dependency.type == type &&
|
||||
dependency.coordinates.projectPath == projectPath &&
|
||||
dependency.coordinates.sourceSetName == sourceSetName
|
||||
}
|
||||
}
|
||||
|
||||
fun Iterable<IdeDependency>.assertMatches(vararg notation: Any?) {
|
||||
val thisList = toList()
|
||||
val matchers = notation.flatMap { buildIdeDependencyMatchers(it) }
|
||||
|
||||
val unexpectedDependencies = thisList.filter { dependency -> matchers.none { matcher -> matcher.matches(dependency) } }
|
||||
val missingDependencies = matchers.filter { matcher -> thisList.none { dependency -> matcher.matches(dependency) } }
|
||||
|
||||
if (unexpectedDependencies.isEmpty() && missingDependencies.isEmpty()) {
|
||||
return
|
||||
}
|
||||
|
||||
fail(
|
||||
buildString {
|
||||
if (unexpectedDependencies.isNotEmpty()) {
|
||||
appendLine()
|
||||
appendLine("Unexpected dependency found:")
|
||||
unexpectedDependencies.forEach { unexpectedDependency ->
|
||||
appendLine("\"${unexpectedDependency}\",")
|
||||
}
|
||||
}
|
||||
|
||||
if (missingDependencies.isNotEmpty()) {
|
||||
appendLine()
|
||||
appendLine("Missing dependencies:")
|
||||
missingDependencies.forEach { missingDependency ->
|
||||
appendLine(missingDependency.description)
|
||||
}
|
||||
}
|
||||
|
||||
appendLine()
|
||||
appendLine("Dependencies:")
|
||||
thisList.forEach { dependency ->
|
||||
appendLine("\"${dependency}\",")
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user