[Gradle] Ide Dependency Resolution: Provide capabilities for binary coordinates

^KT-60053 Verification Pending
This commit is contained in:
Sebastian Sellmair
2023-07-10 20:20:52 +02:00
committed by Space Team
parent 7af254fbf2
commit 041b424bab
11 changed files with 148 additions and 16 deletions
@@ -8,7 +8,7 @@ package org.jetbrains.kotlin.gradle.idea.testFixtures.tcs
import org.jetbrains.kotlin.gradle.idea.tcs.IdeaKotlinDependency
import kotlin.test.fail
fun Iterable<IdeaKotlinDependency>.assertMatches(vararg notation: Any?): Iterable<IdeaKotlinDependency> {
fun <T : IdeaKotlinDependency> Iterable<T>.assertMatches(vararg notation: Any?): Iterable<T> {
val thisList = toList()
val matchers = notation.flatMap { buildIdeaKotlinDependencyMatchers(it) }
@@ -51,3 +51,10 @@ fun Iterable<IdeaKotlinDependency>.assertMatches(vararg notation: Any?): Iterabl
}
)
}
fun <T : IdeaKotlinDependency> Iterable<T>.getOrFail(matcher: IdeaKotlinDependencyMatcher): T {
val candidates = filter { matcher.matches(it) }
if (candidates.isEmpty()) fail("No dependency matching '$matcher' found")
if (candidates.size > 1) fail("Multiple dependencies matching '$matcher' found: ${candidates.map { it.coordinates }}")
return candidates.single()
}
@@ -16,15 +16,15 @@ import org.jetbrains.kotlin.gradle.idea.tcs.extras.*
import org.jetbrains.kotlin.gradle.idea.testFixtures.tcs.IdeaKotlinDependencyMatcher
import org.jetbrains.kotlin.gradle.idea.testFixtures.tcs.assertMatches
import org.jetbrains.kotlin.gradle.idea.testFixtures.tcs.binaryCoordinates
import org.jetbrains.kotlin.gradle.idea.testFixtures.tcs.getOrFail
import org.jetbrains.kotlin.gradle.testbase.*
import org.jetbrains.kotlin.gradle.util.kotlinNativeDistributionDependencies
import org.jetbrains.kotlin.gradle.util.replaceText
import org.jetbrains.kotlin.gradle.util.resolveIdeDependencies
import org.jetbrains.kotlin.gradle.util.*
import org.jetbrains.kotlin.konan.target.HostManager
import org.jetbrains.kotlin.konan.target.KonanTarget.*
import org.junit.AssumptionViolatedException
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.io.TempDir
import java.io.File
import java.nio.ByteBuffer
import java.nio.file.Path
import java.util.*
@@ -277,8 +277,46 @@ class MppIdeDependencyResolutionIT : KGPBaseTest() {
}
}
@GradleTestVersions(minVersion = TestVersions.Gradle.G_7_6)
@GradleTest
fun `test dependency on java testFixtures and feature source sets`(gradleVersion: GradleVersion) {
project("kt-60053-dependencyOn-testFixtures", gradleVersion) {
build("publish")
resolveIdeDependencies(":consumer") { dependencies ->
val jvmMainDependencies = dependencies["jvmMain"].filterIsInstance<IdeaKotlinBinaryDependency>().assertMatches(
kotlinStdlibDependencies,
jetbrainsAnnotationDependencies,
binaryCoordinates("org.jetbrains.sample:producer:1.0.0"),
binaryCoordinates("org.jetbrains.sample:producer-foo:1.0.0"),
)
val jvmTestDependencies = dependencies["jvmTest"].filterIsInstance<IdeaKotlinBinaryDependency>().assertMatches(
kotlinStdlibDependencies,
jetbrainsAnnotationDependencies,
binaryCoordinates("org.jetbrains.sample:producer:1.0.0"),
binaryCoordinates("org.jetbrains.sample:producer-foo:1.0.0"),
binaryCoordinates("org.jetbrains.sample:producer-test-fixtures:1.0.0"),
)
jvmMainDependencies.getOrFail(binaryCoordinates("org.jetbrains.sample:producer:1.0.0")).assertSingleSourcesJar()
jvmTestDependencies.getOrFail(binaryCoordinates("org.jetbrains.sample:producer:1.0.0")).assertSingleSourcesJar()
}
}
}
private fun Iterable<IdeaKotlinDependency>.cinteropDependencies() =
this.filterIsInstance<IdeaKotlinBinaryDependency>().filter {
it.klibExtra?.isInterop == true && !it.isNativeStdlib && !it.isNativeDistribution
}
private fun IdeaKotlinBinaryDependency.assertSingleSourcesJar(): File {
val sources = sourcesClasspath.toList()
if (sources.isEmpty()) fail("Missing -sources.jar")
if (sources.size > 1) fail("Multiple -sources.jar: $sources")
return sources.single().also { sourcesFile ->
if (!sourcesFile.name.endsWith("-sources.jar")) fail("-sources.jar suffix expected. Found: ${sourcesFile.name}")
}
}
}
@@ -0,0 +1,26 @@
plugins {
kotlin("multiplatform")
}
repositories {
maven(rootDir.resolve("repo"))
mavenLocal()
mavenCentral()
}
kotlin {
jvm()
sourceSets.jvmMain.dependencies {
implementation("org.jetbrains.sample:producer:1.0.0")
implementation("org.jetbrains.sample:producer:1.0.0") {
capabilities {
requireCapability("org.jetbrains.sample:producer-foo")
}
}
}
sourceSets.jvmTest.dependencies {
implementation(project.dependencies.testFixtures("org.jetbrains.sample:producer:1.0.0"))
}
}
@@ -0,0 +1,28 @@
plugins {
`java-library`
`java-test-fixtures`
`maven-publish`
}
group = "org.jetbrains.sample"
version = "1.0.0"
publishing {
publications {
create("myLibrary", MavenPublication::class.java) {
from(components["java"])
}
}
repositories {
maven(rootDir.resolve("repo"))
}
}
java {
withSourcesJar()
registerFeature("foo") {
usingSourceSet(sourceSets.create("foo"))
}
}
@@ -11,6 +11,7 @@ import org.gradle.api.artifacts.result.ResolvedArtifactResult
import org.gradle.jvm.JvmLibrary
import org.gradle.language.base.artifact.SourcesArtifact
import org.gradle.language.java.artifact.JavadocArtifact
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.documentationClasspath
@@ -19,7 +20,6 @@ import org.jetbrains.kotlin.gradle.idea.tcs.isKotlinCompileBinaryType
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
import org.jetbrains.kotlin.gradle.plugin.ide.IdeAdditionalArtifactResolver
import org.jetbrains.kotlin.gradle.plugin.ide.IdeaKotlinBinaryCoordinates
import org.jetbrains.kotlin.gradle.plugin.mpp.internal
import org.jetbrains.kotlin.gradle.plugin.mpp.resolvableMetadataConfiguration
import org.jetbrains.kotlin.gradle.plugin.sources.internal
@@ -33,10 +33,11 @@ import org.jetbrains.kotlin.gradle.plugin.sources.internal
* cc Anton Lakotka, Sebastian Sellmair
*/
internal object IdeArtifactResolutionQuerySourcesAndDocumentationResolver : IdeAdditionalArtifactResolver {
override fun resolve(sourceSet: KotlinSourceSet, dependencies: Set<IdeaKotlinDependency>) {
val binaryDependencies = dependencies.filterIsInstance<IdeaKotlinResolvedBinaryDependency>()
.filter { dependency -> dependency.isKotlinCompileBinaryType }
.groupBy { dependency -> dependency.coordinates?.copy(sourceSetName = null) }
.groupBy { dependency -> Coordinates(dependency.coordinates ?: return@groupBy null) }
val project = sourceSet.project
val configuration = selectConfiguration(sourceSet)
@@ -50,9 +51,7 @@ internal object IdeArtifactResolutionQuerySourcesAndDocumentationResolver : IdeA
}
sourcesArtifacts.forEach { artifact ->
val artifactId = artifact.id.componentIdentifier as? ModuleComponentIdentifier ?: return@forEach
val artifactCoordinates = IdeaKotlinBinaryCoordinates(artifactId)
binaryDependencies[artifactCoordinates]?.forEach { dependency ->
binaryDependencies[Coordinates(artifact)]?.forEach { dependency ->
dependency.sourcesClasspath.add(artifact.file)
}
}
@@ -62,9 +61,7 @@ internal object IdeArtifactResolutionQuerySourcesAndDocumentationResolver : IdeA
}
javadocArtifacts.forEach { artifact ->
val artifactId = artifact.id.componentIdentifier as? ModuleComponentIdentifier ?: return@forEach
val artifactCoordinates = IdeaKotlinBinaryCoordinates(artifactId)
binaryDependencies[artifactCoordinates]?.forEach { dependency ->
binaryDependencies[Coordinates(artifact)]?.forEach { dependency ->
dependency.documentationClasspath.add(artifact.file)
}
}
@@ -75,4 +72,26 @@ internal object IdeArtifactResolutionQuerySourcesAndDocumentationResolver : IdeA
return platformCompilation?.internal?.configurations?.compileDependencyConfiguration
?: sourceSet.internal.resolvableMetadataConfiguration
}
/**
* Specific 'Coordinates' type used to match previously resolved dependencies with their
* sources and javadoc artifact counterparts
*/
private data class Coordinates(private val coordinates: String)
private fun Coordinates(coordinates: IdeaKotlinBinaryCoordinates): Coordinates? = when {
coordinates.capabilities.isEmpty() -> Coordinates("${coordinates.group}:${coordinates.module}:${coordinates.version}")
coordinates.capabilities.size == 1 -> coordinates.capabilities.single().run { Coordinates("$group:$name:$version") }
/*
We do have a dependency that declares multiple capabilities. In this case we cannot use this resolver
to find the sources as we can only specify the componentId and not the explicit artifact
*/
else -> null
}
private fun Coordinates(artifact: ResolvedArtifactResult): Coordinates? {
val id = artifact.id.componentIdentifier as? ModuleComponentIdentifier ?: return null
return Coordinates("${id.group}:${id.module}:${id.version}")
}
}
@@ -15,11 +15,13 @@ import org.gradle.internal.resolve.ModuleVersionResolveException
import org.jetbrains.kotlin.gradle.ExternalKotlinTargetApi
import org.jetbrains.kotlin.gradle.idea.tcs.*
import org.jetbrains.kotlin.gradle.idea.tcs.extras.artifactsClasspath
import org.jetbrains.kotlin.gradle.idea.tcs.extras.isIdeaProjectLevel
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
import org.jetbrains.kotlin.gradle.plugin.ide.IdeDependencyResolver
import org.jetbrains.kotlin.gradle.plugin.ide.IdeDependencyResolver.Companion.gradleArtifact
import org.jetbrains.kotlin.gradle.plugin.ide.IdeaKotlinBinaryCapability
import org.jetbrains.kotlin.gradle.plugin.ide.IdeaKotlinBinaryCoordinates
import org.jetbrains.kotlin.gradle.plugin.ide.IdeaKotlinProjectCoordinates
import org.jetbrains.kotlin.gradle.plugin.ide.dependencyResolvers.IdeBinaryDependencyResolver.ArtifactResolutionStrategy
@@ -124,6 +126,7 @@ class IdeBinaryDependencyResolver @JvmOverloads constructor(
val unresolvedDependencies = artifacts.failures
.onEach { reason -> sourceSet.project.logger.info("Failed to resolve platform dependency on ${sourceSet.name}", reason) }
.map { reason ->
val selector = (reason as? ModuleVersionResolveException)?.selector as? ModuleComponentSelector
/* Can't figure out the dependency here :( */
?: return@map IdeaKotlinUnresolvedBinaryDependency(
@@ -141,7 +144,8 @@ class IdeBinaryDependencyResolver @JvmOverloads constructor(
when (val componentId = artifact.id.componentIdentifier) {
is ProjectComponentIdentifier -> {
IdeaKotlinProjectArtifactDependency(
type = IdeaKotlinSourceDependency.Type.Regular, coordinates = IdeaKotlinProjectCoordinates(componentId)
type = IdeaKotlinSourceDependency.Type.Regular,
coordinates = IdeaKotlinProjectCoordinates(componentId)
).apply {
artifactsClasspath.add(artifact.file)
}
@@ -149,7 +153,7 @@ class IdeBinaryDependencyResolver @JvmOverloads constructor(
is ModuleComponentIdentifier -> {
IdeaKotlinResolvedBinaryDependency(
coordinates = IdeaKotlinBinaryCoordinates(componentId),
coordinates = IdeaKotlinBinaryCoordinates(componentId, artifact.variant.capabilities),
binaryType = binaryType,
classpath = IdeaKotlinClasspath(artifact.file),
)
@@ -161,7 +165,8 @@ class IdeBinaryDependencyResolver @JvmOverloads constructor(
group = componentId.projectPath + "(${componentId.variant})",
module = componentId.libraryName,
version = null,
sourceSetName = null
sourceSetName = null,
capabilities = artifact.variant.capabilities.map(::IdeaKotlinBinaryCapability).toSet()
), classpath = IdeaKotlinClasspath(artifact.file)
)
}
@@ -53,4 +53,4 @@ internal object IdeTransformedMetadataDependencyResolver : IdeDependencyResolver
}
}
}
}
}