[Gradle] Implement IdePlatformDependencyResolver
(ported from IdeaKpmPlatformDependencyResolver) ^KT-54948 Verification Pending
This commit is contained in:
committed by
Space Team
parent
e15812f196
commit
bc9296d646
+1
-1
@@ -13,7 +13,7 @@ data class IdeaKotlinBinaryCoordinates(
|
||||
val group: String,
|
||||
val module: String,
|
||||
val version: String?,
|
||||
val sourceSetName: String?
|
||||
val sourceSetName: String? = null,
|
||||
) : IdeaKotlinDependencyCoordinates {
|
||||
override fun toString(): String {
|
||||
return "$group:$module:$version${sourceSetName?.let { ":$it" }.orEmpty()}"
|
||||
|
||||
+22
@@ -12,6 +12,8 @@ import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension
|
||||
import org.jetbrains.kotlin.gradle.idea.serialize.IdeaKotlinExtrasSerializationExtension
|
||||
import org.jetbrains.kotlin.gradle.idea.serialize.IdeaKotlinExtrasSerializationExtensionBuilder
|
||||
import org.jetbrains.kotlin.gradle.idea.tcs.IdeaKotlinDependency
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType.androidJvm
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType.jvm
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
|
||||
import org.jetbrains.kotlin.gradle.plugin.extraProperties
|
||||
import org.jetbrains.kotlin.gradle.plugin.ide.IdeMultiplatformImport.DependencyResolutionLevel.Default
|
||||
@@ -139,6 +141,10 @@ interface IdeMultiplatformImport {
|
||||
val unconstrained = SourceSetConstraint { true }
|
||||
val isNative = SourceSetConstraint { sourceSet -> isNativeSourceSet(sourceSet) }
|
||||
val isMetadata = SourceSetConstraint { sourceSet -> sourceSet.internal.compilations.any { it is KotlinMetadataCompilation } }
|
||||
val isPlatform = !isMetadata and SourceSetConstraint { sourceSet -> sourceSet.internal.compilations.any() }
|
||||
val isJvmAndAndroid = SourceSetConstraint { sourceSet ->
|
||||
sourceSet.internal.compilations.map { it.platformType }.toSet() == setOf(jvm, androidJvm)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,3 +166,19 @@ fun IdeMultiplatformImport.registerExtrasSerializationExtension(
|
||||
) {
|
||||
registerExtrasSerializationExtension(IdeaKotlinExtrasSerializationExtension(builder))
|
||||
}
|
||||
|
||||
infix fun SourceSetConstraint.or(
|
||||
other: SourceSetConstraint
|
||||
) = SourceSetConstraint { sourceSet ->
|
||||
this@or(sourceSet) || other(sourceSet)
|
||||
}
|
||||
|
||||
infix fun SourceSetConstraint.and(
|
||||
other: SourceSetConstraint
|
||||
) = SourceSetConstraint { sourceSet ->
|
||||
this@and(sourceSet) && other(sourceSet)
|
||||
}
|
||||
|
||||
operator fun SourceSetConstraint.not() = SourceSetConstraint { sourceSet ->
|
||||
this@not(sourceSet).not()
|
||||
}
|
||||
|
||||
+20
@@ -9,6 +9,8 @@ import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
|
||||
import org.jetbrains.kotlin.gradle.idea.serialize.IdeaKotlinExtrasSerializer
|
||||
import org.jetbrains.kotlin.gradle.kpm.idea.kotlinDebugKey
|
||||
import org.jetbrains.kotlin.gradle.plugin.ide.dependencyResolvers.IdeDependsOnDependencyResolver
|
||||
import org.jetbrains.kotlin.gradle.plugin.ide.dependencyResolvers.IdeJvmAndAndroidPlatformDependencyResolver
|
||||
import org.jetbrains.kotlin.gradle.plugin.ide.dependencyResolvers.IdePlatformDependencyResolver
|
||||
import org.jetbrains.kotlin.gradle.plugin.ide.dependencyResolvers.IdeTransformedMetadataDependencyResolver
|
||||
|
||||
fun IdeMultiplatformImport(extension: KotlinMultiplatformExtension): IdeMultiplatformImport {
|
||||
@@ -28,6 +30,24 @@ fun IdeMultiplatformImport(extension: KotlinMultiplatformExtension): IdeMultipla
|
||||
level = IdeMultiplatformImport.DependencyResolutionLevel.Default
|
||||
)
|
||||
|
||||
registerDependencyResolver(
|
||||
resolver = IdePlatformDependencyResolver(),
|
||||
constraint = IdeMultiplatformImport.SourceSetConstraint.isPlatform,
|
||||
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.
|
||||
*/
|
||||
registerDependencyResolver(
|
||||
resolver = IdeJvmAndAndroidPlatformDependencyResolver(extension.project),
|
||||
constraint = IdeMultiplatformImport.SourceSetConstraint.isJvmAndAndroid,
|
||||
phase = IdeMultiplatformImport.DependencyResolutionPhase.BinaryDependencyResolution,
|
||||
level = IdeMultiplatformImport.DependencyResolutionLevel.Default
|
||||
)
|
||||
|
||||
registerExtrasSerializationExtension {
|
||||
register(kotlinDebugKey, IdeaKotlinExtrasSerializer.javaIoSerializable())
|
||||
}
|
||||
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.plugin.ide.dependencyResolvers
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.attributes.Category
|
||||
import org.gradle.api.attributes.Usage
|
||||
import org.gradle.api.attributes.java.TargetJvmEnvironment
|
||||
import org.jetbrains.kotlin.gradle.idea.tcs.IdeaKotlinDependency
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
|
||||
import org.jetbrains.kotlin.gradle.plugin.ide.IdeDependencyResolver
|
||||
import org.jetbrains.kotlin.gradle.plugin.usageByName
|
||||
import org.jetbrains.kotlin.gradle.utils.named
|
||||
|
||||
/**
|
||||
* Resolves dependencies of jvm and Android source sets from the perspective jvm
|
||||
*/
|
||||
internal fun IdeJvmAndAndroidPlatformDependencyResolver(project: Project): IdeDependencyResolver = IdePlatformDependencyResolver(
|
||||
binaryType = IdeaKotlinDependency.CLASSPATH_BINARY_TYPE,
|
||||
artifactResolutionStrategy = IdePlatformDependencyResolver.ArtifactResolutionStrategy.PlatformLikeSourceSet(
|
||||
setupPlatformResolutionAttributes = {
|
||||
attributes.attribute(Usage.USAGE_ATTRIBUTE, project.usageByName(Usage.JAVA_API))
|
||||
attributes.attribute(Category.CATEGORY_ATTRIBUTE, project.objects.named(Category.LIBRARY))
|
||||
attributes.attribute(KotlinPlatformType.attribute, KotlinPlatformType.jvm)
|
||||
attributes.attribute(
|
||||
TargetJvmEnvironment.TARGET_JVM_ENVIRONMENT_ATTRIBUTE,
|
||||
project.objects.named(TargetJvmEnvironment.STANDARD_JVM)
|
||||
)
|
||||
},
|
||||
)
|
||||
)
|
||||
+158
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* 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.ArtifactView
|
||||
import org.gradle.api.artifacts.component.ComponentIdentifier
|
||||
import org.gradle.api.artifacts.component.ModuleComponentIdentifier
|
||||
import org.gradle.api.artifacts.component.ModuleComponentSelector
|
||||
import org.gradle.api.artifacts.component.ProjectComponentIdentifier
|
||||
import org.gradle.api.attributes.AttributeContainer
|
||||
import org.gradle.api.logging.Logging
|
||||
import org.gradle.internal.resolve.ModuleVersionResolveException
|
||||
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.IdeaKotlinUnresolvedBinaryDependency
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
|
||||
import org.jetbrains.kotlin.gradle.plugin.ide.IdeDependencyResolver
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinMetadataCompilation
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.internal
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.GradleKpmFragment
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.GradleKpmVariant
|
||||
import org.jetbrains.kotlin.gradle.plugin.sources.*
|
||||
import org.jetbrains.kotlin.gradle.targets.metadata.ALL_COMPILE_METADATA_CONFIGURATION_NAME
|
||||
import org.jetbrains.kotlin.tooling.core.mutableExtrasOf
|
||||
|
||||
internal class IdePlatformDependencyResolver(
|
||||
private val binaryType: String = IdeaKotlinDependency.CLASSPATH_BINARY_TYPE,
|
||||
private val artifactResolutionStrategy: ArtifactResolutionStrategy = ArtifactResolutionStrategy.Compilation()
|
||||
) : IdeDependencyResolver {
|
||||
|
||||
sealed class ArtifactResolutionStrategy {
|
||||
/**
|
||||
* Resolve the artifacts from a [GradleKpmVariant] using its [GradleKpmVariant.compileDependenciesConfiguration],
|
||||
* which already knows how to resolve platform artifacts.
|
||||
* @param setupArtifactViewAttributes: Additional attributes that will be used to create an [ArtifactView] for resolving the dependencies.
|
||||
*/
|
||||
data class Compilation(
|
||||
internal val compilationSelector: (Set<KotlinCompilation<*>>) -> KotlinCompilation<*>? = { it.singleOrNull() },
|
||||
internal val setupArtifactViewAttributes: AttributeContainer.(sourceSet: KotlinSourceSet) -> Unit = {}
|
||||
) : ArtifactResolutionStrategy()
|
||||
|
||||
/**
|
||||
* Capable of resolving artifacts from a plain [GradleKpmFragment] which does not have to implement [GradleKpmVariant].
|
||||
* Such fragments are called 'platform-like', since they still resolve the linkable platform dependencies.
|
||||
* @param setupPlatformResolutionAttributes: Attributes describing how to resolve platform artifacts in general.
|
||||
* @param setupArtifactViewAttributes: Additional attributes that will be used to create an [ArtifactView] for
|
||||
* resolving the dependencies
|
||||
*/
|
||||
data class PlatformLikeSourceSet(
|
||||
internal val setupPlatformResolutionAttributes: AttributeContainer.(sourceSet: KotlinSourceSet) -> Unit,
|
||||
internal val setupArtifactViewAttributes: AttributeContainer.(sourceSet: KotlinSourceSet) -> Unit = {},
|
||||
) : ArtifactResolutionStrategy()
|
||||
}
|
||||
|
||||
override fun resolve(sourceSet: KotlinSourceSet): Set<IdeaKotlinDependency> {
|
||||
val artifacts = artifactResolutionStrategy.createArtifactView(sourceSet.internal)?.artifacts ?: return emptySet()
|
||||
|
||||
val unresolvedDependencies = artifacts.failures
|
||||
.onEach { reason -> sourceSet.project.logger.error("Failed to resolve dependency", reason) }
|
||||
.map { reason ->
|
||||
val selector = (reason as? ModuleVersionResolveException)?.selector as? ModuleComponentSelector
|
||||
/* Can't figure out the dependency here :( */
|
||||
?: return@map IdeaKotlinUnresolvedBinaryDependency(
|
||||
coordinates = null, cause = reason.message?.takeIf { it.isNotBlank() }, extras = mutableExtrasOf()
|
||||
)
|
||||
|
||||
IdeaKotlinUnresolvedBinaryDependency(
|
||||
coordinates = IdeaKotlinBinaryCoordinates(selector.group, selector.module, selector.version, null),
|
||||
cause = reason.message?.takeIf { it.isNotBlank() },
|
||||
extras = mutableExtrasOf()
|
||||
)
|
||||
}.toSet()
|
||||
|
||||
val resolvedDependencies = artifacts.artifacts.mapNotNull { artifact ->
|
||||
IdeaKotlinResolvedBinaryDependency(
|
||||
coordinates = artifact.variant.owner.toIdeaKotlinBinaryCoordinatesOrNull(),
|
||||
binaryType = binaryType,
|
||||
binaryFile = artifact.file,
|
||||
extras = mutableExtrasOf()
|
||||
)
|
||||
}.toSet()
|
||||
|
||||
return resolvedDependencies + unresolvedDependencies
|
||||
}
|
||||
|
||||
private fun ArtifactResolutionStrategy.createArtifactView(sourceSet: InternalKotlinSourceSet): ArtifactView? {
|
||||
return when (this) {
|
||||
is ArtifactResolutionStrategy.Compilation -> createArtifactView(sourceSet)
|
||||
is ArtifactResolutionStrategy.PlatformLikeSourceSet -> createArtifactView(sourceSet)
|
||||
}
|
||||
}
|
||||
|
||||
private fun ArtifactResolutionStrategy.Compilation.createArtifactView(sourceSet: InternalKotlinSourceSet): ArtifactView? {
|
||||
val compilation = compilationSelector(sourceSet.compilations) ?: return null
|
||||
|
||||
/*
|
||||
Prevent case where this resolver was configured to resolve dependencies for a metadata compilation:
|
||||
Refuse resolution. Write your own code if you really want to do this!
|
||||
*/
|
||||
if (compilation is KotlinMetadataCompilation<*>) {
|
||||
logger.warn("Unexpected ${KotlinMetadataCompilation::class.java}(${compilation.name}) for $sourceSet")
|
||||
return null
|
||||
}
|
||||
|
||||
return compilation.internal.configurations.compileDependencyConfiguration.incoming.artifactView { view ->
|
||||
view.isLenient = true
|
||||
view.componentFilter { id -> id !is ProjectComponentIdentifier }
|
||||
view.attributes.setupArtifactViewAttributes(sourceSet)
|
||||
}
|
||||
}
|
||||
|
||||
private fun ArtifactResolutionStrategy.PlatformLikeSourceSet.createArtifactView(sourceSet: InternalKotlinSourceSet): ArtifactView? {
|
||||
if (sourceSet !is DefaultKotlinSourceSet) return null
|
||||
val sourceSetCompileDependencies = sourceSet.project.configurations.detachedConfiguration()
|
||||
sourceSetCompileDependencies.attributes.setupPlatformResolutionAttributes(sourceSet)
|
||||
|
||||
((sourceSet.getAdditionalVisibleSourceSets() + sourceSet).withDependsOnClosure).forEach { visibleSourceSet ->
|
||||
sourceSetCompileDependencies.dependencies.addAll(
|
||||
sourceSet.project.configurations.getByName(visibleSourceSet.apiConfigurationName).allDependencies
|
||||
)
|
||||
|
||||
sourceSetCompileDependencies.dependencies.addAll(
|
||||
sourceSet.project.configurations.getByName(visibleSourceSet.implementationConfigurationName).allDependencies
|
||||
)
|
||||
|
||||
sourceSetCompileDependencies.dependencies.addAll(
|
||||
sourceSet.project.configurations.getByName(visibleSourceSet.compileOnlyConfigurationName).allDependencies
|
||||
)
|
||||
}
|
||||
|
||||
/* Ensure consistent dependency resolution result within the whole module */
|
||||
sourceSetCompileDependencies.shouldResolveConsistentlyWith(
|
||||
sourceSet.project.configurations.getByName(ALL_COMPILE_METADATA_CONFIGURATION_NAME)
|
||||
)
|
||||
|
||||
return sourceSetCompileDependencies.incoming.artifactView { view ->
|
||||
view.isLenient = true
|
||||
view.componentFilter { id -> id !is ProjectComponentIdentifier }
|
||||
view.attributes.setupArtifactViewAttributes(sourceSet)
|
||||
}
|
||||
}
|
||||
|
||||
private fun ComponentIdentifier.toIdeaKotlinBinaryCoordinatesOrNull(): IdeaKotlinBinaryCoordinates? {
|
||||
return when (this) {
|
||||
is ModuleComponentIdentifier -> IdeaKotlinBinaryCoordinates(group, module, version)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
val logger = Logging.getLogger(IdePlatformDependencyResolver::class.java)
|
||||
}
|
||||
}
|
||||
+2
@@ -23,3 +23,5 @@ internal interface InternalKotlinSourceSet : KotlinSourceSet {
|
||||
val withDependsOnClosure: ObservableSet<KotlinSourceSet>
|
||||
val compilations: MutableObservableSet<KotlinCompilation<*>>
|
||||
}
|
||||
|
||||
internal val KotlinSourceSet.project: Project get() = this.internal.project
|
||||
+8
@@ -34,6 +34,7 @@ class IdeTransformedMetadataDependencyResolverTest {
|
||||
kotlin.linuxArm64()
|
||||
|
||||
val commonMain = kotlin.sourceSets.getByName("commonMain")
|
||||
val commonTest = kotlin.sourceSets.getByName("commonTest")
|
||||
val linuxMain = kotlin.sourceSets.getByName("linuxMain")
|
||||
|
||||
commonMain.dependencies {
|
||||
@@ -51,6 +52,13 @@ class IdeTransformedMetadataDependencyResolverTest {
|
||||
binaryCoordinates("com.arkivanov.essenty:instance-keeper:0.4.2:commonMain")
|
||||
)
|
||||
|
||||
IdeTransformedMetadataDependencyResolver.resolve(commonTest)
|
||||
.assertMatches(
|
||||
binaryCoordinates("com.arkivanov.mvikotlin:mvikotlin:3.0.2:commonMain"),
|
||||
binaryCoordinates("com.arkivanov.essenty:lifecycle:0.4.2:commonMain"),
|
||||
binaryCoordinates("com.arkivanov.essenty:instance-keeper:0.4.2:commonMain")
|
||||
)
|
||||
|
||||
IdeTransformedMetadataDependencyResolver.resolve(linuxMain)
|
||||
.assertMatches(
|
||||
binaryCoordinates("com.arkivanov.mvikotlin:mvikotlin:3.0.2:commonMain"),
|
||||
|
||||
Reference in New Issue
Block a user