[Gradle] Fine-tune Jvm and Android ide dependency resolution

The binary dependency resolver shall only run for remote
artifacts. Project to project dependencies shall be handled
by the IdeJvmAndAndroidSourceDependencyResolver

^KT-55475 Verification Pending
This commit is contained in:
Sebastian Sellmair
2022-12-14 12:17:30 +01:00
committed by Space Team
parent 5dd1777624
commit 9f810116d3
4 changed files with 76 additions and 24 deletions
@@ -7,10 +7,7 @@ package org.jetbrains.kotlin.gradle.plugin.ide.dependencyResolvers
import org.gradle.api.artifacts.ArtifactView import org.gradle.api.artifacts.ArtifactView
import org.gradle.api.artifacts.Configuration import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.component.LibraryBinaryIdentifier import org.gradle.api.artifacts.component.*
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.attributes.AttributeContainer
import org.gradle.api.logging.Logger import org.gradle.api.logging.Logger
import org.gradle.api.logging.Logging import org.gradle.api.logging.Logging
@@ -18,7 +15,6 @@ import org.gradle.internal.component.local.model.OpaqueComponentArtifactIdentifi
import org.gradle.internal.resolve.ModuleVersionResolveException import org.gradle.internal.resolve.ModuleVersionResolveException
import org.jetbrains.kotlin.gradle.ExternalKotlinTargetApi import org.jetbrains.kotlin.gradle.ExternalKotlinTargetApi
import org.jetbrains.kotlin.gradle.idea.tcs.* import org.jetbrains.kotlin.gradle.idea.tcs.*
import org.jetbrains.kotlin.gradle.kpm.external.ExternalVariantApi
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
@@ -76,6 +72,7 @@ class IdeBinaryDependencyResolver(
data class PlatformLikeSourceSet( data class PlatformLikeSourceSet(
internal val setupPlatformResolutionAttributes: AttributeContainer.(sourceSet: KotlinSourceSet) -> Unit, internal val setupPlatformResolutionAttributes: AttributeContainer.(sourceSet: KotlinSourceSet) -> Unit,
internal val setupArtifactViewAttributes: AttributeContainer.(sourceSet: KotlinSourceSet) -> Unit = {}, internal val setupArtifactViewAttributes: AttributeContainer.(sourceSet: KotlinSourceSet) -> Unit = {},
internal val componentFilter: ((ComponentIdentifier) -> Boolean)? = null
) : ArtifactResolutionStrategy() ) : ArtifactResolutionStrategy()
} }
@@ -192,6 +189,9 @@ class IdeBinaryDependencyResolver(
return platformLikeCompileDependenciesConfiguration.incoming.artifactView { view -> return platformLikeCompileDependenciesConfiguration.incoming.artifactView { view ->
view.isLenient = true view.isLenient = true
view.attributes.setupArtifactViewAttributes(sourceSet) view.attributes.setupArtifactViewAttributes(sourceSet)
if (componentFilter != null) {
view.componentFilter(componentFilter)
}
} }
} }
@@ -8,6 +8,7 @@
package org.jetbrains.kotlin.gradle.plugin.ide.dependencyResolvers package org.jetbrains.kotlin.gradle.plugin.ide.dependencyResolvers
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.artifacts.component.ProjectComponentIdentifier
import org.gradle.api.attributes.Category import org.gradle.api.attributes.Category
import org.gradle.api.attributes.Usage import org.gradle.api.attributes.Usage
import org.gradle.api.attributes.java.TargetJvmEnvironment import org.gradle.api.attributes.java.TargetJvmEnvironment
@@ -33,5 +34,11 @@ internal fun IdeJvmAndAndroidPlatformBinaryDependencyResolver(project: Project):
project.objects.named(TargetJvmEnvironment.STANDARD_JVM) project.objects.named(TargetJvmEnvironment.STANDARD_JVM)
) )
}, },
/*
Prevent this resolver from running against project dependencies:
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 }
) )
) )
@@ -9,29 +9,47 @@ import org.jetbrains.kotlin.gradle.dsl.multiplatformExtensionOrNull
import org.jetbrains.kotlin.gradle.idea.tcs.IdeaKotlinDependency import org.jetbrains.kotlin.gradle.idea.tcs.IdeaKotlinDependency
import org.jetbrains.kotlin.gradle.idea.tcs.IdeaKotlinSourceDependency import org.jetbrains.kotlin.gradle.idea.tcs.IdeaKotlinSourceDependency
import org.jetbrains.kotlin.gradle.idea.tcs.IdeaKotlinSourceDependency.Type.Regular import org.jetbrains.kotlin.gradle.idea.tcs.IdeaKotlinSourceDependency.Type.Regular
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
import org.jetbrains.kotlin.gradle.plugin.ide.IdeDependencyResolver import org.jetbrains.kotlin.gradle.plugin.ide.IdeDependencyResolver
import org.jetbrains.kotlin.gradle.plugin.ide.IdeMultiplatformImport.SourceSetConstraint.Companion.isJvmAndAndroid
import org.jetbrains.kotlin.gradle.plugin.ide.IdeaKotlinSourceCoordinates import org.jetbrains.kotlin.gradle.plugin.ide.IdeaKotlinSourceCoordinates
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJvmAndroidCompilation
import org.jetbrains.kotlin.gradle.plugin.mpp.MetadataDependencyResolution import org.jetbrains.kotlin.gradle.plugin.mpp.MetadataDependencyResolution
import org.jetbrains.kotlin.gradle.plugin.mpp.isMain
import org.jetbrains.kotlin.gradle.plugin.sources.DefaultKotlinSourceSet import org.jetbrains.kotlin.gradle.plugin.sources.DefaultKotlinSourceSet
import org.jetbrains.kotlin.gradle.plugin.sources.android.AndroidVariantType
import org.jetbrains.kotlin.gradle.plugin.sources.android.type
import org.jetbrains.kotlin.gradle.plugin.sources.internal import org.jetbrains.kotlin.gradle.plugin.sources.internal
internal object IdeJvmAndAndroidSourceDependencyResolver : IdeDependencyResolver { internal object IdeJvmAndAndroidSourceDependencyResolver : IdeDependencyResolver {
override fun resolve(sourceSet: KotlinSourceSet): Set<IdeaKotlinDependency> { override fun resolve(sourceSet: KotlinSourceSet): Set<IdeaKotlinDependency> {
if (!sourceSet.isJvmAndAndroid) return emptySet() if (!isJvmAndAndroid(sourceSet)) return emptySet()
if (sourceSet !is DefaultKotlinSourceSet) return emptySet() if (sourceSet !is DefaultKotlinSourceSet) return emptySet()
return sourceSet.resolveMetadata<MetadataDependencyResolution.ChooseVisibleSourceSets>() return sourceSet.resolveMetadata<MetadataDependencyResolution.ChooseVisibleSourceSets>()
.flatMap { chooseVisibleSourceSets -> .flatMap { chooseVisibleSourceSets ->
val projectDependency = chooseVisibleSourceSets.projectDependency ?: return@flatMap emptyList<IdeaKotlinDependency>() val projectDependency = chooseVisibleSourceSets.projectDependency ?: return@flatMap emptyList<IdeaKotlinDependency>()
val kotlin = projectDependency.multiplatformExtensionOrNull ?: return@flatMap emptyList<IdeaKotlinDependency>() val kotlin = projectDependency.multiplatformExtensionOrNull ?: return@flatMap emptyList<IdeaKotlinDependency>()
kotlin.sourceSets kotlin.sourceSets
.filter { sourceSet -> sourceSet.isJvmAndAndroid } .filter { sourceSet -> isJvmAndAndroidMain(sourceSet) }
.map { sourceSet -> IdeaKotlinSourceDependency(type = Regular, coordinates = IdeaKotlinSourceCoordinates(sourceSet)) } .map { sourceSet -> IdeaKotlinSourceDependency(type = Regular, coordinates = IdeaKotlinSourceCoordinates(sourceSet)) }
} }
.toSet() .toSet()
} }
private val KotlinSourceSet.isJvmAndAndroid: Boolean private fun isJvmAndAndroidMain(sourceSet: KotlinSourceSet): Boolean {
get() = internal.compilations.map { it.platformType }.toSet() == setOf(KotlinPlatformType.androidJvm, KotlinPlatformType.jvm) if (!isJvmAndAndroid(sourceSet)) return false
return sourceSet.internal.compilations.filter { it.platformType != KotlinPlatformType.common }.all { compilation ->
isJvmMain(compilation) || isAndroidMain(compilation)
}
}
private fun isJvmMain(compilation: KotlinCompilation<*>): Boolean {
return compilation.platformType == KotlinPlatformType.jvm && compilation.isMain()
}
private fun isAndroidMain(compilation: KotlinCompilation<*>): Boolean {
return compilation is KotlinJvmAndroidCompilation && compilation.androidVariant.type == AndroidVariantType.Main
}
} }
@@ -7,36 +7,36 @@
package org.jetbrains.kotlin.gradle.ide package org.jetbrains.kotlin.gradle.ide
import org.gradle.api.Project
import org.jetbrains.kotlin.gradle.* import org.jetbrains.kotlin.gradle.*
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension
import org.jetbrains.kotlin.gradle.idea.testFixtures.tcs.assertMatches 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.binaryCoordinates
import org.jetbrains.kotlin.gradle.idea.testFixtures.tcs.dependsOnDependency
import org.jetbrains.kotlin.gradle.idea.testFixtures.tcs.regularSourceDependency
import org.jetbrains.kotlin.gradle.kpm.idea.mavenCentralCacheRedirector import org.jetbrains.kotlin.gradle.kpm.idea.mavenCentralCacheRedirector
import org.jetbrains.kotlin.gradle.plugin.ide.dependencyResolvers.IdeJvmAndAndroidPlatformBinaryDependencyResolver import org.jetbrains.kotlin.gradle.plugin.ide.dependencyResolvers.IdeJvmAndAndroidPlatformBinaryDependencyResolver
import org.jetbrains.kotlin.gradle.plugin.ide.kotlinIdeMultiplatformImport
import org.jetbrains.kotlin.gradle.utils.androidExtension import org.jetbrains.kotlin.gradle.utils.androidExtension
import kotlin.test.BeforeTest import kotlin.test.BeforeTest
import kotlin.test.Test import kotlin.test.Test
class IdeJvmAndAndroidPlatformDependencyResolverTest { class IdeJvmAndAndroidDependencyResolutionTest {
@BeforeTest @BeforeTest
fun checkEnvironment() { fun checkEnvironment() {
assumeAndroidSdkAvailable() assumeAndroidSdkAvailable()
} }
@Test private fun Project.configureAndroidAndMultiplatform() {
fun `test - MVIKotlin - on jvmAndAndroidMain`() { enableDefaultStdlibDependency(false)
val project = buildProject { enableDependencyVerification(false)
enableDefaultStdlibDependency(false) applyMultiplatformPlugin()
enableDependencyVerification(false) plugins.apply("com.android.library")
applyMultiplatformPlugin() androidExtension.compileSdkVersion(33)
plugins.apply("com.android.library") repositories.mavenCentralCacheRedirector()
androidExtension.compileSdkVersion(33)
repositories.mavenCentralCacheRedirector()
}
val kotlin = project.multiplatformExtension project.multiplatformExtension.targetHierarchy.custom {
kotlin.targetHierarchy.custom {
common { common {
group("jvmAndAndroid") { group("jvmAndAndroid") {
anyJvm() anyJvm()
@@ -45,9 +45,15 @@ class IdeJvmAndAndroidPlatformDependencyResolverTest {
} }
} }
kotlin.jvm() project.multiplatformExtension.jvm()
kotlin.android() project.multiplatformExtension.android()
}
@Test
fun `test - MVIKotlin - on jvmAndAndroidMain`() {
val project = buildProject { configureAndroidAndMultiplatform() }
val kotlin = project.multiplatformExtension
kotlin.sourceSets.getByName("commonMain").dependencies { kotlin.sourceSets.getByName("commonMain").dependencies {
implementation("com.arkivanov.mvikotlin:mvikotlin:3.0.2") implementation("com.arkivanov.mvikotlin:mvikotlin:3.0.2")
} }
@@ -72,4 +78,25 @@ class IdeJvmAndAndroidPlatformDependencyResolverTest {
IdeJvmAndAndroidPlatformBinaryDependencyResolver(project).resolve(kotlin.sourceSets.getByName("jvmAndAndroidTest")) IdeJvmAndAndroidPlatformBinaryDependencyResolver(project).resolve(kotlin.sourceSets.getByName("jvmAndAndroidTest"))
.assertMatches(jvmAndAndroidDependencies) .assertMatches(jvmAndAndroidDependencies)
} }
@Test
fun `test - project to project dependency`() {
val root = buildProject()
val producer = buildProject({ withParent(root).withName("producer") }) { configureAndroidAndMultiplatform() }
val consumer = buildProject({ withParent(root).withName("consumer") }) { configureAndroidAndMultiplatform() }
root.evaluate()
producer.evaluate()
consumer.evaluate()
consumer.multiplatformExtension.sourceSets.getByName("commonMain").dependencies {
implementation(project(":producer"))
}
consumer.kotlinIdeMultiplatformImport.resolveDependencies("jvmAndAndroidMain").assertMatches(
dependsOnDependency(":consumer/commonMain"),
regularSourceDependency(":producer/commonMain"),
regularSourceDependency(":producer/jvmAndAndroidMain"),
)
}
} }