diff --git a/libraries/tools/kotlin-gradle-plugin/api/kotlin-gradle-plugin.api b/libraries/tools/kotlin-gradle-plugin/api/kotlin-gradle-plugin.api index 810805de408..62a9c264da6 100644 --- a/libraries/tools/kotlin-gradle-plugin/api/kotlin-gradle-plugin.api +++ b/libraries/tools/kotlin-gradle-plugin/api/kotlin-gradle-plugin.api @@ -154,7 +154,8 @@ public final class org/jetbrains/kotlin/gradle/plugin/ide/dependencyResolvers/Id public fun (Lkotlin/jvm/functions/Function1;)V public fun (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)V public fun (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;)V - public synthetic fun (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)V + public synthetic fun (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 (Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;)V public fun (Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;)V public fun (Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)V - public synthetic fun (Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)V + public synthetic fun (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 (Lkotlin/jvm/functions/Function1;)V public fun (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)V public fun (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;)V - public synthetic fun (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)V + public synthetic fun (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 { diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/ide/dependencyResolvers/IdeBinaryDependencyResolver.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/ide/dependencyResolvers/IdeBinaryDependencyResolver.kt index 30fa573108a..de8def0eb53 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/ide/dependencyResolvers/IdeBinaryDependencyResolver.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/ide/dependencyResolvers/IdeBinaryDependencyResolver.kt @@ -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) diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/ide/dependencyResolvers/IdeJvmAndAndroidPlatformDependencyResolver.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/ide/dependencyResolvers/IdeJvmAndAndroidPlatformDependencyResolver.kt index 2021fa97bf8..d3b5f60790c 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/ide/dependencyResolvers/IdeJvmAndAndroidPlatformDependencyResolver.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/ide/dependencyResolvers/IdeJvmAndAndroidPlatformDependencyResolver.kt @@ -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, ) ) diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/ide/dependencyResolvers/IdeJvmAndAndroidSourceDependencyResolver.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/ide/dependencyResolvers/IdeJvmAndAndroidSourceDependencyResolver.kt index 33489026600..363faefa0a3 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/ide/dependencyResolvers/IdeJvmAndAndroidSourceDependencyResolver.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/ide/dependencyResolvers/IdeJvmAndAndroidSourceDependencyResolver.kt @@ -29,6 +29,10 @@ internal object IdeJvmAndAndroidSourceDependencyResolver : IdeDependencyResolver if (!isJvmAndAndroid(sourceSet)) return emptySet() if (sourceSet !is DefaultKotlinSourceSet) return emptySet() return sourceSet.resolveMetadata() + /** + * 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() diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/ide/dependencyResolvers/IdeVisibleMultiplatformSourceDependencyResolver.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/ide/dependencyResolvers/IdeVisibleMultiplatformSourceDependencyResolver.kt index 8ed0c919c02..ac6559f78d4 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/ide/dependencyResolvers/IdeVisibleMultiplatformSourceDependencyResolver.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/ide/dependencyResolvers/IdeVisibleMultiplatformSourceDependencyResolver.kt @@ -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 { if (sourceSet !is DefaultKotlinSourceSet) return emptySet() return sourceSet.resolveMetadata() + /* + 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 { val projectComponentIdentifier = resolution.dependency.id as? ProjectComponentIdentifier ?: return emptyList() return resolution.allVisibleSourceSetNames.map { visibleSourceSetName -> diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/dependencyResolutionTests/tcs/IdeJvmAndAndroidDependencyResolutionTest.kt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/dependencyResolutionTests/tcs/IdeJvmAndAndroidDependencyResolutionTest.kt index bb3d7db1591..1dbc02853dc 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/dependencyResolutionTests/tcs/IdeJvmAndAndroidDependencyResolutionTest.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/dependencyResolutionTests/tcs/IdeJvmAndAndroidDependencyResolutionTest.kt @@ -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) }