[Gradle] Ide Dependency Resolution: Filter 'loop' or 'self' dependencies
Those are dependencies that point back to the original project.
This can happen with two projects like
```
// project a
kotlin {
commonMain.get().dependencies {
api(project(":b"))
}
}
// project b
kotlin {
commonTest.get().dependencies {
implementation(project(":a"))
}
}
```
Where b(test) -> a -> b
^KT-59020 Verification Pending
This commit is contained in:
committed by
Space Team
parent
19073b96a7
commit
a80d6a7b74
@@ -154,7 +154,8 @@ public final class org/jetbrains/kotlin/gradle/plugin/ide/dependencyResolvers/Id
|
||||
public fun <init> (Lkotlin/jvm/functions/Function1;)V
|
||||
public fun <init> (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)V
|
||||
public fun <init> (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;)V
|
||||
public synthetic fun <init> (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||
public fun <init> (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)V
|
||||
public synthetic fun <init> (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||
}
|
||||
|
||||
public final class org/jetbrains/kotlin/gradle/plugin/ide/dependencyResolvers/IdeBinaryDependencyResolver$ArtifactResolutionStrategy$PlatformLikeSourceSet : org/jetbrains/kotlin/gradle/plugin/ide/dependencyResolvers/IdeBinaryDependencyResolver$ArtifactResolutionStrategy {
|
||||
@@ -162,14 +163,16 @@ public final class org/jetbrains/kotlin/gradle/plugin/ide/dependencyResolvers/Id
|
||||
public fun <init> (Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;)V
|
||||
public fun <init> (Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;)V
|
||||
public fun <init> (Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)V
|
||||
public synthetic fun <init> (Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||
public fun <init> (Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)V
|
||||
public synthetic fun <init> (Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||
}
|
||||
|
||||
public final class org/jetbrains/kotlin/gradle/plugin/ide/dependencyResolvers/IdeBinaryDependencyResolver$ArtifactResolutionStrategy$ResolvableConfiguration : org/jetbrains/kotlin/gradle/plugin/ide/dependencyResolvers/IdeBinaryDependencyResolver$ArtifactResolutionStrategy {
|
||||
public fun <init> (Lkotlin/jvm/functions/Function1;)V
|
||||
public fun <init> (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)V
|
||||
public fun <init> (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;)V
|
||||
public synthetic fun <init> (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||
public fun <init> (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)V
|
||||
public synthetic fun <init> (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||
}
|
||||
|
||||
public final class org/jetbrains/kotlin/gradle/plugin/mpp/external/CreateExternalKotlinCompilationKt {
|
||||
|
||||
+32
-28
@@ -5,9 +5,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.gradle.plugin.ide.dependencyResolvers
|
||||
|
||||
import org.gradle.api.artifacts.ArtifactView
|
||||
import org.gradle.api.artifacts.Configuration
|
||||
import org.gradle.api.artifacts.DependencySubstitutions
|
||||
import org.gradle.api.artifacts.*
|
||||
import org.gradle.api.artifacts.component.*
|
||||
import org.gradle.api.attributes.AttributeContainer
|
||||
import org.gradle.api.logging.Logger
|
||||
@@ -24,6 +22,7 @@ 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.IdeaKotlinBinaryCoordinates
|
||||
import org.jetbrains.kotlin.gradle.plugin.ide.IdeaKotlinProjectCoordinates
|
||||
import org.jetbrains.kotlin.gradle.plugin.ide.dependencyResolvers.IdeBinaryDependencyResolver.ArtifactResolutionStrategy
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinMetadataCompilation
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.KotlinCompilationConfigurationsContainer
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.internal
|
||||
@@ -34,7 +33,6 @@ import org.jetbrains.kotlin.gradle.plugin.sources.DefaultKotlinSourceSet
|
||||
import org.jetbrains.kotlin.gradle.plugin.sources.InternalKotlinSourceSet
|
||||
import org.jetbrains.kotlin.gradle.plugin.sources.internal
|
||||
import org.jetbrains.kotlin.gradle.utils.markResolvable
|
||||
import org.gradle.api.artifacts.ResolutionStrategy
|
||||
import org.jetbrains.kotlin.tooling.core.mutableExtrasOf
|
||||
|
||||
/**
|
||||
@@ -57,11 +55,14 @@ import org.jetbrains.kotlin.tooling.core.mutableExtrasOf
|
||||
@ExternalKotlinTargetApi
|
||||
class IdeBinaryDependencyResolver @JvmOverloads constructor(
|
||||
private val binaryType: String = IdeaKotlinBinaryDependency.KOTLIN_COMPILE_BINARY_TYPE,
|
||||
private val artifactResolutionStrategy: ArtifactResolutionStrategy = ArtifactResolutionStrategy.Compilation()
|
||||
private val artifactResolutionStrategy: ArtifactResolutionStrategy = ArtifactResolutionStrategy.Compilation(),
|
||||
) : IdeDependencyResolver {
|
||||
|
||||
@ExternalKotlinTargetApi
|
||||
sealed class ArtifactResolutionStrategy {
|
||||
internal abstract val setupArtifactViewAttributes: AttributeContainer.(sourceSet: KotlinSourceSet) -> Unit
|
||||
internal abstract val componentFilter: ((ComponentIdentifier) -> Boolean)?
|
||||
internal abstract val dependencyFilter: ((Dependency) -> Boolean)?
|
||||
|
||||
/**
|
||||
* Resolve the artifacts from a [KotlinSourceSet] using its [KotlinCompilation.compileDependencyConfigurationName],
|
||||
@@ -74,9 +75,11 @@ class IdeBinaryDependencyResolver @JvmOverloads constructor(
|
||||
*/
|
||||
@ExternalKotlinTargetApi
|
||||
class Compilation @JvmOverloads constructor(
|
||||
internal val compilationSelector: (KotlinSourceSet) -> KotlinCompilation<*>? = { sourceSet -> sourceSet.internal.compilations.singleOrNull { it.platformType != KotlinPlatformType.common } },
|
||||
internal val setupArtifactViewAttributes: AttributeContainer.(sourceSet: KotlinSourceSet) -> Unit = {},
|
||||
internal val componentFilter: ((ComponentIdentifier) -> Boolean)? = null
|
||||
internal val compilationSelector: (KotlinSourceSet) -> KotlinCompilation<*>? =
|
||||
{ sourceSet -> sourceSet.internal.compilations.singleOrNull { it.platformType != KotlinPlatformType.common } },
|
||||
override val setupArtifactViewAttributes: AttributeContainer.(sourceSet: KotlinSourceSet) -> Unit = {},
|
||||
override val componentFilter: ((ComponentIdentifier) -> Boolean)? = null,
|
||||
override val dependencyFilter: ((Dependency) -> Boolean)? = null,
|
||||
) : ArtifactResolutionStrategy()
|
||||
|
||||
/**
|
||||
@@ -84,12 +87,14 @@ class IdeBinaryDependencyResolver @JvmOverloads constructor(
|
||||
* @param configurationSelector Returns the configuration that shall be resolved for the given [KotlinSourceSet]
|
||||
* @param setupArtifactViewAttributes: Additional attributes that will be used to create an [ArtifactView] for resolving the dependencies.
|
||||
* @param componentFilter Filter added to the artifactView: Only components passing the filter will be resolved
|
||||
* @param dependencyFilter Filter added to the [ResolvableDependencies]: Only dependencies passing the filter will be resolved
|
||||
*/
|
||||
@ExternalKotlinTargetApi
|
||||
class ResolvableConfiguration @JvmOverloads constructor(
|
||||
internal val configurationSelector: (KotlinSourceSet) -> Configuration?,
|
||||
internal val setupArtifactViewAttributes: AttributeContainer.(sourceSet: KotlinSourceSet) -> Unit = {},
|
||||
internal val componentFilter: ((ComponentIdentifier) -> Boolean)? = null
|
||||
override val setupArtifactViewAttributes: AttributeContainer.(sourceSet: KotlinSourceSet) -> Unit = {},
|
||||
override val componentFilter: ((ComponentIdentifier) -> Boolean)? = null,
|
||||
override val dependencyFilter: ((Dependency) -> Boolean)? = null,
|
||||
) : ArtifactResolutionStrategy()
|
||||
|
||||
/**
|
||||
@@ -101,13 +106,15 @@ class IdeBinaryDependencyResolver @JvmOverloads constructor(
|
||||
* @param componentFilter Filter added to the artifactView: Only components passing the filter will be resolved
|
||||
* @param dependencySubstitution Dependency substitution added to the adhoc configuration created for this resolution.
|
||||
* see [ResolutionStrategy.dependencySubstitution]
|
||||
* @param dependencyFilter Filter added to the [ResolvableDependencies]: Only dependencies passing the filter will be resolved
|
||||
*/
|
||||
@ExternalKotlinTargetApi
|
||||
class PlatformLikeSourceSet @JvmOverloads constructor(
|
||||
internal val setupPlatformResolutionAttributes: AttributeContainer.(sourceSet: KotlinSourceSet) -> Unit,
|
||||
internal val setupArtifactViewAttributes: AttributeContainer.(sourceSet: KotlinSourceSet) -> Unit = {},
|
||||
internal val componentFilter: ((ComponentIdentifier) -> Boolean)? = null,
|
||||
override val setupArtifactViewAttributes: AttributeContainer.(sourceSet: KotlinSourceSet) -> Unit = {},
|
||||
override val componentFilter: ((ComponentIdentifier) -> Boolean)? = null,
|
||||
internal val dependencySubstitution: ((DependencySubstitutions) -> Unit)? = null,
|
||||
override val dependencyFilter: ((Dependency) -> Boolean)? = null,
|
||||
) : ArtifactResolutionStrategy()
|
||||
}
|
||||
|
||||
@@ -194,24 +201,12 @@ class IdeBinaryDependencyResolver @JvmOverloads constructor(
|
||||
return null
|
||||
}
|
||||
|
||||
return compilation.internal.configurations.compileDependencyConfiguration.incoming.artifactView { view ->
|
||||
view.isLenient = true
|
||||
view.attributes.setupArtifactViewAttributes(sourceSet)
|
||||
if (componentFilter != null) {
|
||||
view.componentFilter(componentFilter)
|
||||
}
|
||||
}
|
||||
return createArtifactViewFromConfiguration(sourceSet, compilation.internal.configurations.compileDependencyConfiguration)
|
||||
}
|
||||
|
||||
private fun ArtifactResolutionStrategy.ResolvableConfiguration.createArtifactView(sourceSet: InternalKotlinSourceSet): ArtifactView? {
|
||||
val configuration = configurationSelector(sourceSet) ?: return null
|
||||
return configuration.incoming.artifactView { view ->
|
||||
view.isLenient = true
|
||||
view.attributes.setupArtifactViewAttributes(sourceSet)
|
||||
if (componentFilter != null) {
|
||||
view.componentFilter(componentFilter)
|
||||
}
|
||||
}
|
||||
return createArtifactViewFromConfiguration(sourceSet, configuration)
|
||||
}
|
||||
|
||||
private fun ArtifactResolutionStrategy.PlatformLikeSourceSet.createArtifactView(sourceSet: InternalKotlinSourceSet): ArtifactView? {
|
||||
@@ -227,14 +222,23 @@ class IdeBinaryDependencyResolver @JvmOverloads constructor(
|
||||
platformLikeCompileDependenciesConfiguration.resolutionStrategy.dependencySubstitution(dependencySubstitution)
|
||||
}
|
||||
|
||||
return platformLikeCompileDependenciesConfiguration.incoming.artifactView { view ->
|
||||
return createArtifactViewFromConfiguration(sourceSet, platformLikeCompileDependenciesConfiguration)
|
||||
}
|
||||
|
||||
private fun ArtifactResolutionStrategy.createArtifactViewFromConfiguration(
|
||||
sourceSet: KotlinSourceSet, configuration: Configuration,
|
||||
): ArtifactView = configuration.incoming
|
||||
.apply {
|
||||
val dependencyFilter = dependencyFilter
|
||||
if (dependencyFilter != null) dependencies.removeIf { dependency -> !dependencyFilter.invoke(dependency) }
|
||||
}
|
||||
.artifactView { view ->
|
||||
view.isLenient = true
|
||||
view.attributes.setupArtifactViewAttributes(sourceSet)
|
||||
if (componentFilter != null) {
|
||||
view.componentFilter(componentFilter)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
val logger: Logger = Logging.getLogger(IdeBinaryDependencyResolver::class.java)
|
||||
|
||||
+3
-1
@@ -9,6 +9,7 @@ package org.jetbrains.kotlin.gradle.plugin.ide.dependencyResolvers
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.artifacts.DependencySubstitutions
|
||||
import org.gradle.api.artifacts.ProjectDependency
|
||||
import org.gradle.api.artifacts.component.ModuleComponentSelector
|
||||
import org.gradle.api.artifacts.component.ProjectComponentIdentifier
|
||||
import org.gradle.api.attributes.Category
|
||||
@@ -44,7 +45,8 @@ internal fun IdeJvmAndAndroidPlatformBinaryDependencyResolver(project: Project):
|
||||
Otherwise we would match the -jvm.jar from the dependency project which will result in
|
||||
matching the jvmMain source set as well (which is undesired)
|
||||
*/
|
||||
componentFilter = { identifier -> identifier !is ProjectComponentIdentifier },
|
||||
componentFilter = { identifier -> identifier !is ProjectComponentIdentifier }, // paranoia: Only dependencyFilter should be OK
|
||||
dependencyFilter = { dependency -> dependency !is ProjectDependency },
|
||||
dependencySubstitution = ::substituteStdlibCommonWithAndroidJvm,
|
||||
)
|
||||
)
|
||||
|
||||
+4
@@ -29,6 +29,10 @@ internal object IdeJvmAndAndroidSourceDependencyResolver : IdeDependencyResolver
|
||||
if (!isJvmAndAndroid(sourceSet)) return emptySet()
|
||||
if (sourceSet !is DefaultKotlinSourceSet) return emptySet()
|
||||
return sourceSet.resolveMetadata<MetadataDependencyResolution.ChooseVisibleSourceSets>()
|
||||
/**
|
||||
* See [IdeVisibleMultiplatformSourceDependencyResolver] on why this could happen
|
||||
*/
|
||||
.filter { chooseVisibleSourceSets -> chooseVisibleSourceSets.projectDependency(sourceSet.project) != sourceSet.project }
|
||||
.flatMap { chooseVisibleSourceSets ->
|
||||
val projectDependency = chooseVisibleSourceSets.projectDependency(sourceSet.project)
|
||||
?: return@flatMap emptyList<IdeaKotlinDependency>()
|
||||
|
||||
+11
-1
@@ -13,18 +13,28 @@ import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
|
||||
import org.jetbrains.kotlin.gradle.plugin.ide.IdeDependencyResolver
|
||||
import org.jetbrains.kotlin.gradle.plugin.ide.IdeaKotlinProjectCoordinates
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.MetadataDependencyResolution
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.projectDependency
|
||||
import org.jetbrains.kotlin.gradle.plugin.sources.DefaultKotlinSourceSet
|
||||
|
||||
internal object IdeVisibleMultiplatformSourceDependencyResolver : IdeDependencyResolver {
|
||||
override fun resolve(sourceSet: KotlinSourceSet): Set<IdeaKotlinDependency> {
|
||||
if (sourceSet !is DefaultKotlinSourceSet) return emptySet()
|
||||
return sourceSet.resolveMetadata<MetadataDependencyResolution.ChooseVisibleSourceSets>()
|
||||
/*
|
||||
Check if resolved dependency is pointing to the project that contains the Source Set
|
||||
Such 'loop' or 'self' dependencies can happen when e.g.
|
||||
1) 'project A' depends on 'project B'
|
||||
2) 'project B / test' depends on project A
|
||||
|
||||
In this case project B tests will get a project dependency pointing to project B's main variant
|
||||
*/
|
||||
.filter { resolution -> resolution.projectDependency(sourceSet.project) != sourceSet.project }
|
||||
.flatMap { resolution -> resolveSourceDependencies(resolution) }
|
||||
.toSet()
|
||||
}
|
||||
|
||||
private fun resolveSourceDependencies(
|
||||
resolution: MetadataDependencyResolution.ChooseVisibleSourceSets
|
||||
resolution: MetadataDependencyResolution.ChooseVisibleSourceSets,
|
||||
): Iterable<IdeaKotlinDependency> {
|
||||
val projectComponentIdentifier = resolution.dependency.id as? ProjectComponentIdentifier ?: return emptyList()
|
||||
return resolution.allVisibleSourceSetNames.map { visibleSourceSetName ->
|
||||
|
||||
+27
@@ -112,6 +112,33 @@ class IdeJvmAndAndroidDependencyResolutionTest {
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - KT-59020 - transitive project dependency to self`() {
|
||||
val root = buildProject { setMultiplatformAndroidSourceSetLayoutVersion(2) }
|
||||
val a = buildProject({ withParent(root).withName("a") }) { configureAndroidAndMultiplatform() }
|
||||
val b = buildProject({ withParent(root).withName("b") }) { configureAndroidAndMultiplatform() }
|
||||
|
||||
b.multiplatformExtension.commonMain.dependencies {
|
||||
api(project(":a"))
|
||||
}
|
||||
|
||||
a.multiplatformExtension.commonTest.dependencies {
|
||||
api(project(":b"))
|
||||
}
|
||||
|
||||
root.evaluate()
|
||||
a.evaluate()
|
||||
b.evaluate()
|
||||
|
||||
a.kotlinIdeMultiplatformImport.resolveDependencies("jvmAndAndroidTest").assertMatches(
|
||||
friendSourceDependency(":a/commonMain"),
|
||||
friendSourceDependency(":a/jvmAndAndroidMain"),
|
||||
dependsOnDependency(":a/commonTest"),
|
||||
regularSourceDependency(":b/commonMain"),
|
||||
regularSourceDependency(":b/jvmAndAndroidMain"),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - default stdlib with no other dependencies`() {
|
||||
val project = buildProject { configureAndroidAndMultiplatform(enableDefaultStdlib = true) }
|
||||
|
||||
Reference in New Issue
Block a user