[KPM] Integrate refines dependencies into IdeaKotlinSourceDependency

KT-51386
This commit is contained in:
sebastian.sellmair
2022-03-15 14:28:47 +01:00
committed by Space
parent 0f8f61c373
commit e1ead6b8bf
12 changed files with 168 additions and 82 deletions
@@ -101,7 +101,6 @@ public final class org/jetbrains/kotlin/gradle/kpm/idea/IdeaKotlinDependencyKt {
public abstract interface class org/jetbrains/kotlin/gradle/kpm/idea/IdeaKotlinFragment : java/io/Serializable {
public abstract fun getDependencies ()Ljava/util/List;
public abstract fun getDirectRefinesDependencies ()Ljava/util/List;
public abstract fun getExternal ()Lorg/jetbrains/kotlin/gradle/kpm/KotlinExternalModelContainer;
public abstract fun getLanguageSettings ()Lorg/jetbrains/kotlin/gradle/kpm/idea/IdeaKotlinLanguageSettings;
public abstract fun getModuleIdentifier ()Lorg/jetbrains/kotlin/gradle/kpm/idea/IdeaKotlinModuleIdentifier;
@@ -216,6 +215,16 @@ public final class org/jetbrains/kotlin/gradle/kpm/idea/IdeaKotlinSourceCoordina
public abstract interface class org/jetbrains/kotlin/gradle/kpm/idea/IdeaKotlinSourceDependency : org/jetbrains/kotlin/gradle/kpm/idea/IdeaKotlinDependency {
public abstract fun getCoordinates ()Lorg/jetbrains/kotlin/gradle/kpm/idea/IdeaKotlinSourceCoordinates;
public abstract fun getType ()Lorg/jetbrains/kotlin/gradle/kpm/idea/IdeaKotlinSourceDependency$Type;
}
public final class org/jetbrains/kotlin/gradle/kpm/idea/IdeaKotlinSourceDependency$Type : java/lang/Enum, java/io/Serializable {
public static final field Companion Lorg/jetbrains/kotlin/gradle/kpm/idea/IdeaKotlinSourceDependency$Type$Companion;
public static final field Friend Lorg/jetbrains/kotlin/gradle/kpm/idea/IdeaKotlinSourceDependency$Type;
public static final field Refines Lorg/jetbrains/kotlin/gradle/kpm/idea/IdeaKotlinSourceDependency$Type;
public static final field Regular Lorg/jetbrains/kotlin/gradle/kpm/idea/IdeaKotlinSourceDependency$Type;
public static fun valueOf (Ljava/lang/String;)Lorg/jetbrains/kotlin/gradle/kpm/idea/IdeaKotlinSourceDependency$Type;
public static fun values ()[Lorg/jetbrains/kotlin/gradle/kpm/idea/IdeaKotlinSourceDependency$Type;
}
public abstract interface class org/jetbrains/kotlin/gradle/kpm/idea/IdeaKotlinSourceDirectory : java/io/Serializable {
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.gradle.kpm.idea.IdeaKotlinDependency.Companion.DOCUM
import org.jetbrains.kotlin.gradle.kpm.idea.IdeaKotlinDependency.Companion.SOURCES_BINARY_TYPE
import java.io.File
import java.io.Serializable
import java.util.*
sealed interface IdeaKotlinDependency : Serializable {
val coordinates: IdeaKotlinDependencyCoordinates?
@@ -37,6 +38,16 @@ sealed interface IdeaKotlinSourceCoordinates : IdeaKotlinDependencyCoordinates {
}
sealed interface IdeaKotlinSourceDependency : IdeaKotlinDependency {
enum class Type : Serializable {
Regular, Friend, Refines;
@InternalKotlinGradlePluginApi
companion object {
private const val serialVersionUID = 0L
}
}
val type: Type
override val coordinates: IdeaKotlinSourceCoordinates
}
@@ -67,12 +78,14 @@ val IdeaKotlinResolvedBinaryDependency.isClasspathType get() = binaryType == CLA
@InternalKotlinGradlePluginApi
data class IdeaKotlinSourceDependencyImpl(
override val type: IdeaKotlinSourceDependency.Type,
override val coordinates: IdeaKotlinSourceCoordinates,
override val external: KotlinExternalModelContainer = KotlinExternalModelContainer.Empty
override val external: KotlinExternalModelContainer = KotlinExternalModelContainer.Empty,
) : IdeaKotlinSourceDependency {
override fun toString(): String {
return "source: $coordinates"
@Suppress("DEPRECATION")
return "${type.name.toLowerCase(Locale.ROOT)}:$coordinates"
}
@InternalKotlinGradlePluginApi
@@ -14,7 +14,6 @@ interface IdeaKotlinFragment : Serializable {
val platforms: Set<IdeaKotlinPlatform>
val languageSettings: IdeaKotlinLanguageSettings?
val dependencies: List<IdeaKotlinDependency>
val directRefinesDependencies: List<IdeaKotlinFragment>
val sourceDirectories: List<IdeaKotlinSourceDirectory>
val resourceDirectories: List<IdeaKotlinResourceDirectory>
val external: KotlinExternalModelContainer
@@ -27,7 +26,6 @@ data class IdeaKotlinFragmentImpl(
override val platforms: Set<IdeaKotlinPlatform>,
override val languageSettings: IdeaKotlinLanguageSettings?,
override val dependencies: List<IdeaKotlinDependency>,
override val directRefinesDependencies: List<IdeaKotlinFragment>,
override val sourceDirectories: List<IdeaKotlinSourceDirectory>,
override val resourceDirectories: List<IdeaKotlinResourceDirectory>,
override val external: KotlinExternalModelContainer
@@ -1,7 +1,6 @@
package org.jetbrains.kotlin.gradle.kpm.idea.testFixtures
import org.jetbrains.kotlin.gradle.kpm.idea.IdeaKotlinSourceDependency
import org.jetbrains.kotlin.gradle.kpm.idea.path
fun buildIdeaKotlinSourceDependencyMatchers(notation: Any?): List<IdeaKotlinSourceDependencyMatcher> {
return when (notation) {
@@ -14,19 +13,19 @@ fun buildIdeaKotlinSourceDependencyMatchers(notation: Any?): List<IdeaKotlinSour
}
interface IdeaKotlinSourceDependencyMatcher : IdeaKotlinDependencyMatcher<IdeaKotlinSourceDependency> {
class FragmentPath(private val path: String) : IdeaKotlinSourceDependencyMatcher {
override val description: String = path
class FragmentPath(private val dependency: String) : IdeaKotlinSourceDependencyMatcher {
override val description: String = dependency
override fun matches(dependency: IdeaKotlinSourceDependency): Boolean {
return path == dependency.coordinates.path
return this.dependency == dependency.toString()
}
}
class FragmentPathRegex(private val pathRegex: Regex) : IdeaKotlinSourceDependencyMatcher {
override val description: String = pathRegex.pattern
class FragmentPathRegex(private val dependencyRegex: Regex) : IdeaKotlinSourceDependencyMatcher {
override val description: String = dependencyRegex.pattern
override fun matches(dependency: IdeaKotlinSourceDependency): Boolean {
return pathRegex.matches(dependency.coordinates.path)
return dependencyRegex.matches(dependency.toString())
}
}
}
@@ -105,33 +105,30 @@ fun IdeaKotlinFragment.assertSourceDependencies(matchers: Set<IdeaKotlinSourceDe
return sourceDependencies
}
val fragmentIdentifier = "${moduleIdentifier.moduleClassifier?.plus("/").orEmpty()}${name}"
fail(
buildString {
if (unexpectedDependencies.isNotEmpty()) {
appendLine("${name}: Unexpected source dependencies found:")
unexpectedDependencies.forEach { unexpectedDependency ->
appendLine(unexpectedDependency)
}
appendLine()
appendLine("${name}: Unexpected source dependency paths:")
appendLine("${fragmentIdentifier}: Unexpected source dependency found:")
unexpectedDependencies.forEach { unexpectedDependency ->
appendLine("\"${unexpectedDependency.coordinates.path}\",")
appendLine("\"${unexpectedDependency}\",")
}
}
if (missingDependencies.isNotEmpty()) {
appendLine()
appendLine("${name}: Missing fragment dependencies:")
appendLine("${fragmentIdentifier}: Missing fragment dependencies:")
missingDependencies.forEach { missingDependency ->
appendLine(missingDependency.description)
}
}
appendLine()
appendLine("${name}: Resolved source dependency paths:")
appendLine("${fragmentIdentifier}: Resolved source dependency paths:")
sourceDependencies.forEach { dependency ->
appendLine("\"${dependency.coordinates.path}\",")
appendLine("\"${dependency}\",")
}
}
)
@@ -82,75 +82,114 @@ class SimpleProjectToProjectDependencyResolutionTest : AbstractLightweightIdeaDe
fun ifTestModule(vararg any: Any?) =
listOf(*any).takeIf { module.name == KotlinGradleModule.TEST_MODULE_NAME }
fun ifMainModule(vararg any: Any?) =
listOf(*any).takeIf { module.name == KotlinGradleModule.MAIN_MODULE_NAME }
module.assertContainsFragment("common").assertSourceDependencies(
":producer/main/common",
ifTestModule(":consumer/main/common")
"regular::producer/main/common",
ifTestModule("friend::consumer/main/common")
)
module.assertContainsFragment("jvm").assertSourceDependencies(
":producer/main/jvm",
":producer/main/common",
"regular::producer/main/jvm",
"regular::producer/main/common",
ifMainModule(
"refines::consumer/main/common",
),
ifTestModule(
":consumer/main/common",
":consumer/main/jvm"
"friend::consumer/main/common",
"friend::consumer/main/jvm",
"refines::consumer/test/common",
)
)
module.assertContainsFragment("nativeCommon").assertSourceDependencies(
":producer/main/common",
":producer/main/nativeCommon",
"regular::producer/main/common",
"regular::producer/main/nativeCommon",
ifMainModule(
"refines::consumer/main/common"
),
ifTestModule(
":consumer/main/common",
":consumer/main/nativeCommon"
"friend::consumer/main/common",
"friend::consumer/main/nativeCommon",
"refines::consumer/test/common",
)
)
module.assertContainsFragment("appleCommon").assertSourceDependencies(
":producer/main/common",
":producer/main/appleCommon",
":producer/main/nativeCommon",
"regular::producer/main/common",
"regular::producer/main/appleCommon",
"regular::producer/main/nativeCommon",
ifMainModule(
"refines::consumer/main/common",
"refines::consumer/main/nativeCommon"
),
ifTestModule(
":consumer/main/common",
":consumer/main/nativeCommon",
":consumer/main/appleCommon"
"friend::consumer/main/common",
"friend::consumer/main/nativeCommon",
"friend::consumer/main/appleCommon",
"refines::consumer/test/common",
"refines::consumer/test/nativeCommon"
)
)
module.assertContainsFragment("linuxX64").assertSourceDependencies(
":producer/main/common",
":producer/main/nativeCommon",
":producer/main/linuxX64",
"regular::producer/main/common",
"regular::producer/main/nativeCommon",
"regular::producer/main/linuxX64",
ifMainModule(
"refines::consumer/main/common",
"refines::consumer/main/nativeCommon",
),
ifTestModule(
":consumer/main/common",
":consumer/main/nativeCommon",
":consumer/main/linuxX64"
"friend::consumer/main/common",
"friend::consumer/main/nativeCommon",
"friend::consumer/main/linuxX64",
"refines::consumer/test/common",
"refines::consumer/test/nativeCommon"
)
)
module.assertContainsFragment("macosX64").assertSourceDependencies(
":producer/main/macosX64",
":producer/main/common",
":producer/main/appleCommon",
":producer/main/nativeCommon",
"regular::producer/main/macosX64",
"regular::producer/main/common",
"regular::producer/main/appleCommon",
"regular::producer/main/nativeCommon",
ifMainModule(
"refines::consumer/main/common",
"refines::consumer/main/nativeCommon",
"refines::consumer/main/appleCommon"
),
ifTestModule(
":consumer/main/common",
":consumer/main/nativeCommon",
":consumer/main/appleCommon",
":consumer/main/macosX64"
"friend::consumer/main/common",
"friend::consumer/main/nativeCommon",
"friend::consumer/main/appleCommon",
"friend::consumer/main/macosX64",
"refines::consumer/test/common",
"refines::consumer/test/nativeCommon",
"refines::consumer/test/appleCommon"
)
)
module.assertContainsFragment("iosX64").assertSourceDependencies(
":producer/main/iosX64",
":producer/main/common",
":producer/main/iosMain",
":producer/main/appleCommon",
":producer/main/nativeCommon",
"regular::producer/main/iosX64",
"regular::producer/main/common",
"regular::producer/main/iosMain",
"regular::producer/main/appleCommon",
"regular::producer/main/nativeCommon",
ifMainModule(
"refines::consumer/main/common",
"refines::consumer/main/nativeCommon",
"refines::consumer/main/appleCommon"
),
ifTestModule(
":consumer/main/common",
":consumer/main/nativeCommon",
":consumer/main/appleCommon",
":consumer/main/iosX64"
"friend::consumer/main/common",
"friend::consumer/main/nativeCommon",
"friend::consumer/main/appleCommon",
"friend::consumer/main/iosX64",
"refines::consumer/test/common",
"refines::consumer/test/nativeCommon",
"refines::consumer/test/appleCommon"
)
)
}
@@ -8,35 +8,25 @@ package org.jetbrains.kotlin.gradle.kpm.idea
import org.jetbrains.kotlin.gradle.kpm.KotlinExternalModelContainer
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.*
internal class IdeaKotlinFragmentBuildingContext(
private val parent: IdeaKotlinProjectModelBuildingContext
) : IdeaKotlinProjectModelBuildingContext by parent {
private val fragmentCache = mutableMapOf<KotlinGradleFragment, IdeaKotlinFragment>()
fun getOrPut(source: KotlinGradleFragment, builder: () -> IdeaKotlinFragment) = fragmentCache.getOrPut(source, builder)
internal fun IdeaKotlinProjectModelBuildingContext.toIdeaKotlinFragment(fragment: KotlinGradleFragment): IdeaKotlinFragment {
return if (fragment is KotlinGradleVariant) buildIdeaKotlinVariant(fragment)
else buildIdeaKotlinFragment(fragment)
}
internal fun IdeaKotlinFragmentBuildingContext.toIdeaKotlinFragment(fragment: KotlinGradleFragment): IdeaKotlinFragment {
return getOrPut(fragment) {
if (fragment is KotlinGradleVariant) buildIdeaKotlinVariant(fragment)
else buildIdeaKotlinFragment(fragment)
}
}
private fun IdeaKotlinFragmentBuildingContext.buildIdeaKotlinFragment(fragment: KotlinGradleFragment): IdeaKotlinFragment {
private fun IdeaKotlinProjectModelBuildingContext.buildIdeaKotlinFragment(fragment: KotlinGradleFragment): IdeaKotlinFragment {
return IdeaKotlinFragmentImpl(
name = fragment.name,
moduleIdentifier = fragment.containingModule.moduleIdentifier.toIdeaKotlinModuleIdentifier(),
platforms = fragment.containingVariants.map { variant -> buildIdeaKotlinPlatform(variant) }.toSet(),
languageSettings = fragment.languageSettings.toIdeaKotlinLanguageSettings(),
dependencies = dependencyResolver.resolve(fragment).toList(),
directRefinesDependencies = fragment.directRefinesDependencies.map { refinesFragment -> toIdeaKotlinFragment(refinesFragment) },
sourceDirectories = fragment.kotlinSourceRoots.sourceDirectories.files.toList().map { file -> IdeaKotlinSourceDirectoryImpl(file) },
resourceDirectories = emptyList(),
external = (fragment as? KotlinGradleFragmentInternal)?.external ?: KotlinExternalModelContainer.Empty
)
}
private fun IdeaKotlinFragmentBuildingContext.buildIdeaKotlinVariant(variant: KotlinGradleVariant): IdeaKotlinVariant {
private fun IdeaKotlinProjectModelBuildingContext.buildIdeaKotlinVariant(variant: KotlinGradleVariant): IdeaKotlinVariant {
return IdeaKotlinVariantImpl(
fragment = buildIdeaKotlinFragment(variant),
platform = buildIdeaKotlinPlatform(variant),
@@ -8,10 +8,9 @@ package org.jetbrains.kotlin.gradle.kpm.idea
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinGradleModule
internal fun IdeaKotlinProjectModelBuildingContext.toIdeaKotlinModule(module: KotlinGradleModule): IdeaKotlinModule {
val fragmentBuildingContext = IdeaKotlinFragmentBuildingContext(this)
return IdeaKotlinModuleImpl(
name = module.name,
moduleIdentifier = module.moduleIdentifier.toIdeaKotlinModuleIdentifier(),
fragments = module.fragments.toList().map { fragment -> fragmentBuildingContext.toIdeaKotlinFragment(fragment) }
fragments = module.fragments.toList().map { fragment -> toIdeaKotlinFragment(fragment) }
)
}
@@ -14,7 +14,7 @@ import org.jetbrains.kotlin.konan.target.HostManager
@Suppress("unused")
/* Receiver acts as scope, or key to that function */
internal fun IdeaKotlinFragmentBuildingContext.buildIdeaKotlinPlatform(variant: KotlinGradleVariant): IdeaKotlinPlatform {
internal fun IdeaKotlinProjectModelBuildingContext.buildIdeaKotlinPlatform(variant: KotlinGradleVariant): IdeaKotlinPlatform {
when (variant) {
is KotlinJvmVariant -> return IdeaKotlinPlatform.jvm(variant.compilationData.kotlinOptions.jvmTarget ?: JvmTarget.DEFAULT.name)
is KotlinNativeVariantInternal -> return IdeaKotlinPlatform.native(variant.konanTarget.name)
@@ -18,6 +18,13 @@ internal fun IdeaKotlinProjectModelBuilder.Companion.default(
) = IdeaKotlinProjectModelBuilderImpl(extension).apply {
val fragmentMetadataResolverFactory = FragmentGranularMetadataResolverFactory()
registerDependencyResolver(
resolver = IdeaKotlinRefinesDependencyResolver,
constraint = IdeaKotlinProjectModelBuilder.FragmentConstraint.unconstrained,
phase = IdeaKotlinProjectModelBuilder.DependencyResolutionPhase.SourceDependencyResolution,
level = IdeaKotlinProjectModelBuilder.DependencyResolutionLevel.Default
)
registerDependencyResolver(
resolver = IdeaKotlinSourceDependencyResolver(fragmentMetadataResolverFactory),
constraint = IdeaKotlinProjectModelBuilder.FragmentConstraint.unconstrained,
@@ -0,0 +1,29 @@
/*
* 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.kpm.idea
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinGradleFragment
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.currentBuildId
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.refinesClosure
internal object IdeaKotlinRefinesDependencyResolver : IdeaKotlinDependencyResolver {
override fun resolve(fragment: KotlinGradleFragment): Set<IdeaKotlinDependency> = fragment.refinesClosure
.map { refinesDependencyFragment -> createRefinesDependency(refinesDependencyFragment) }.toSet()
private fun createRefinesDependency(fragment: KotlinGradleFragment): IdeaKotlinDependency {
return IdeaKotlinSourceDependencyImpl(
type = IdeaKotlinSourceDependency.Type.Refines,
coordinates = IdeaKotlinSourceCoordinatesImpl(
buildId = fragment.project.currentBuildId().name,
projectPath = fragment.project.path,
projectName = fragment.project.name,
kotlinModuleName = fragment.containingModule.name,
kotlinModuleClassifier = fragment.containingModule.moduleClassifier,
kotlinFragmentName = fragment.name
)
)
}
}
@@ -10,6 +10,7 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.MetadataDependencyResolution.Choos
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.FragmentGranularMetadataResolverFactory
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinGradleFragment
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinGradleModule.Companion.moduleName
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.currentBuildId
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.toModuleDependency
internal class IdeaKotlinSourceDependencyResolver(
@@ -18,16 +19,21 @@ internal class IdeaKotlinSourceDependencyResolver(
override fun resolve(fragment: KotlinGradleFragment): Set<IdeaKotlinDependency> {
return fragmentGranularMetadataResolverFactory.getOrCreate(fragment).resolutions
.filterIsInstance<ChooseVisibleSourceSets>()
.flatMap { resolution -> resolve(resolution) }
.flatMap { resolution -> resolve(fragment, resolution) }
.toSet()
}
private fun resolve(resolution: ChooseVisibleSourceSets): Iterable<IdeaKotlinDependency> {
private fun resolve(fragment: KotlinGradleFragment, resolution: ChooseVisibleSourceSets): Iterable<IdeaKotlinDependency> {
val gradleProjectIdentifier = resolution.dependency.id as? ProjectComponentIdentifier ?: return emptyList()
val kotlinModuleIdentifier = resolution.dependency.toModuleDependency().moduleIdentifier
return resolution.allVisibleSourceSetNames.map { visibleFragmentName ->
IdeaKotlinSourceDependencyImpl(
IdeaKotlinSourceCoordinatesImpl(
type = if (
gradleProjectIdentifier.build == fragment.project.currentBuildId() &&
gradleProjectIdentifier.projectPath == fragment.project.path
) IdeaKotlinSourceDependency.Type.Friend
else IdeaKotlinSourceDependency.Type.Regular,
coordinates = IdeaKotlinSourceCoordinatesImpl(
buildId = gradleProjectIdentifier.build.name,
projectPath = gradleProjectIdentifier.projectPath,
projectName = gradleProjectIdentifier.projectName,