ModuleOrigin -> ModuleIdentifier

This commit is contained in:
Sergey Igushkin
2020-12-01 14:59:40 +03:00
committed by TeamCityServer
parent 66b8a444c2
commit 7103aa3100
7 changed files with 77 additions and 63 deletions
@@ -18,18 +18,18 @@ import org.jetbrains.kotlin.gradle.dsl.multiplatformExtensionOrNull
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.currentBuildId
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.matchesModule
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.representsProject
import org.jetbrains.kotlin.gradle.plugin.sources.getVisibleSourceSetsFromAssociateCompilations
import org.jetbrains.kotlin.project.model.*
class ProjectStructureMetadataModuleBuilder {
private val modulesCache = mutableMapOf<String, KotlinModule>()
private val modulesCache = mutableMapOf<ModuleIdentifier, KotlinModule>()
private fun buildModuleFromProjectStructureMetadata(
moduleName: String,
moduleOrigin: ModuleOrigin,
moduleIdentifier: ModuleIdentifier,
metadata: KotlinProjectStructureMetadata
): KotlinModule =
BasicKotlinModule(moduleName, moduleOrigin).apply {
BasicKotlinModule(moduleIdentifier).apply {
metadata.sourceSetNamesByVariantName.keys.forEach { variantName ->
fragments.add(BasicKotlinModuleVariant(this@apply, variantName))
}
@@ -53,11 +53,10 @@ class ProjectStructureMetadataModuleBuilder {
}
}
fun getModule(name: String, moduleOrigin: ModuleOrigin, projectStructureMetadata: KotlinProjectStructureMetadata): KotlinModule {
return modulesCache.getOrPut(name) {
fun getModule(moduleIdentifier: ModuleIdentifier, projectStructureMetadata: KotlinProjectStructureMetadata): KotlinModule {
return modulesCache.getOrPut(moduleIdentifier) {
buildModuleFromProjectStructureMetadata(
name,
moduleOrigin,
moduleIdentifier,
projectStructureMetadata
)
}
@@ -77,7 +76,7 @@ class GradleProjectModuleBuilder(private val addInferredSourceSetVisibilityAsExp
else -> return null
}
return BasicKotlinModule(project.path, LocalBuild(project.currentBuildId().name)).apply {
return BasicKotlinModule(LocalModuleIdentifier(project.currentBuildId().name, project.path)).apply {
val variantToCompilation = mutableMapOf<KotlinModuleFragment, KotlinCompilation<*>>()
targets.forEach { target ->
@@ -109,7 +108,7 @@ class GradleProjectModuleBuilder(private val addInferredSourceSetVisibilityAsExp
extension.sourceSets.forEach { sourceSet ->
val existingVariant = fragments.filterIsInstance<BasicKotlinModuleVariant>().find { it.fragmentName == sourceSet.name }
val fragment = existingVariant ?: BasicKotlinModuleFragment(this@apply, sourceSet.name).also { fragments.add(it) }
fragment.kotlinSourceRoots = sourceSet.kotlin.sourceDirectories.toList()
fragment.kotlinSourceDirectories = sourceSet.kotlin.sourceDirectories.toList()
// FIXME: Kotlin/Native implementation-effective-api dependencies are missing here. Introduce dependency scopes
project.configurations.getByName(sourceSet.apiConfigurationName).allDependencies.forEach {
@@ -163,9 +162,9 @@ internal fun Dependency.toModuleDependency(
project: Project
) = when (val dependency = this) {
is ProjectDependency ->
LocalModuleDependency(LocalBuild(project.currentBuildId().name), dependency.dependencyProject.path)
ModuleDependency(LocalModuleIdentifier(project.currentBuildId().name, dependency.dependencyProject.path))
else ->
ExternalModuleDependency(ExternalOrigin(listOfNotNull(dependency.group, dependency.name)))
ModuleDependency(MavenModuleIdentifier(dependency.group.orEmpty(), dependency.name))
}
private fun BasicKotlinModule.fragmentByName(name: String) =
@@ -184,8 +183,7 @@ class GradleModuleVariantResolver(val project: Project) : ModuleVariantResolver
val module = requestingVariant.containingModule
// This implementation can only resolve variants for the current project's KotlinModule
require(module.moduleName == project.path)
require(module.moduleOrigin.let { it is LocalBuild && it.buildId == project.currentBuildId().name })
require(module.representsProject(project))
val targets =
project.multiplatformExtensionOrNull?.targets ?: listOf((project.kotlinExtension as KotlinSingleTargetExtension).target)
@@ -13,7 +13,6 @@ import org.gradle.api.artifacts.component.ProjectComponentIdentifier
import org.gradle.api.artifacts.result.ResolvedComponentResult
import org.gradle.api.artifacts.result.ResolvedDependencyResult
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
import org.jetbrains.kotlin.gradle.plugin.mpp.GradleModuleVariantResolver
import org.jetbrains.kotlin.gradle.plugin.mpp.ProjectStructureMetadataModuleBuilder
import org.jetbrains.kotlin.gradle.plugin.mpp.GradleProjectModuleBuilder
import org.jetbrains.kotlin.gradle.plugin.mpp.getProjectStructureMetadata
@@ -41,7 +40,7 @@ class GradleModuleDependencyResolver(
projectModuleBuilder.buildModuleFromProject(project.project(id.projectPath))
id is ModuleComponentIdentifier -> {
val metadata = getProjectStructureMetadata(project, component, configurationToResolve) ?: return null
projectStructureMetadataModuleBuilder.getModule(id.displayName, id.toModuleOrigin(), metadata)
projectStructureMetadataModuleBuilder.getModule(id.toModuleIdentifier(), metadata)
}
else -> null
}
@@ -110,11 +109,7 @@ class FragmentDependenciesDiscovery(
// TODO think about multi-variant stub modules for non-Kotlin modules which got more than one chosen variant
private fun buildStubModule(resolvedComponentResult: ResolvedComponentResult, singleVariantName: String): KotlinModule {
val moduleDependency = resolvedComponentResult.toModuleDependency()
val moduleName = when (val id = resolvedComponentResult.id) {
is ProjectComponentIdentifier -> id.projectPath
else -> id.displayName
}
return BasicKotlinModule(moduleName, moduleDependency.moduleOrigin).apply {
return BasicKotlinModule(moduleDependency.moduleIdentifier).apply {
BasicKotlinModuleVariant(this@apply, singleVariantName).apply {
fragments.add(this)
this.declaredModuleDependencies.addAll(
@@ -140,6 +135,7 @@ class VariantDependencyDiscovery(
.find { it.defaultSourceSetName == fragment.fragmentName }
requireNotNull(compilation)
// TODO distinguish between compile and runtime dependencies?
val configuration = project.configurations.getByName(compilation.compileDependencyConfigurationName)
return configuration.incoming.resolutionResult.allComponents
}
@@ -150,45 +146,50 @@ class VariantDependencyDiscovery(
val components = resolvedComponentResults(fragment)
return components.mapNotNull { component ->
when (val id = component.id) {
is ProjectComponentIdentifier -> LocalModuleDependency(LocalBuild(id.build.name), id.projectPath)
is ModuleComponentIdentifier -> ExternalModuleDependency(id.toModuleOrigin())
else -> null // TODO check that no other options are possible, throw errors
val moduleIdentifier = when (val id = component.id) {
is ProjectComponentIdentifier -> LocalModuleIdentifier(id.build.name, id.projectPath)
is ModuleComponentIdentifier -> id.toModuleIdentifier()
else -> return@mapNotNull null // TODO check that no other options are possible, throw errors?
}
ModuleDependency(moduleIdentifier)
}
}
}
private fun ModuleComponentIdentifier.toModuleOrigin(): ExternalOrigin =
ExternalOrigin(listOf(moduleIdentifier.group, moduleIdentifier.name))
private fun ModuleComponentIdentifier.toModuleIdentifier(): MavenModuleIdentifier =
MavenModuleIdentifier(moduleIdentifier.group, moduleIdentifier.name)
internal fun ComponentIdentifier.matchesModule(module: KotlinModule): Boolean {
return when (val moduleSource = module.moduleOrigin) {
is LocalBuild -> {
return when (val moduleId = module.moduleIdentifier) {
is LocalModuleIdentifier -> {
val projectId = this as? ProjectComponentIdentifier
projectId?.build?.name == moduleSource.buildId && projectId.projectPath == module.moduleName
projectId?.build?.name == moduleId.buildId && projectId.projectPath == moduleId.projectId
}
is ExternalOrigin -> {
val moduleId = this as? ModuleComponentIdentifier
moduleId?.toModuleOrigin() == moduleSource
is MavenModuleIdentifier -> {
val componentId = this as? ModuleComponentIdentifier
componentId?.toModuleIdentifier() == moduleId
}
else -> false
}
}
internal fun ResolvedComponentResult.toModuleDependency(): ModuleDependency = when (val id = id) {
is ProjectComponentIdentifier -> LocalModuleDependency(LocalBuild(id.build.name), id.projectPath)
is ModuleComponentIdentifier -> ExternalModuleDependency(id.toModuleOrigin())
else -> ExternalModuleDependency(ExternalOrigin(listOf(moduleVersion?.group.orEmpty(), moduleVersion?.name.orEmpty())))
}
internal fun ResolvedComponentResult.toModuleDependency(): ModuleDependency = ModuleDependency(
when (val id = id) {
is ProjectComponentIdentifier -> LocalModuleIdentifier(id.build.name, id.projectPath)
is ModuleComponentIdentifier -> id.toModuleIdentifier()
else -> MavenModuleIdentifier(moduleVersion?.group.orEmpty(), moduleVersion?.name.orEmpty())
}
)
internal fun ComponentIdentifier.matchesModuleDependency(moduleDependency: ModuleDependency) =
when (moduleDependency) {
is LocalModuleDependency -> {
when (val id = moduleDependency.moduleIdentifier) {
is LocalModuleIdentifier -> {
val projectId = this as? ProjectComponentIdentifier
projectId?.build?.name == moduleDependency.moduleOrigin.buildId && projectId.projectPath == moduleDependency.moduleName
projectId?.build?.name == id.buildId && projectId.projectPath == id.projectId
}
is ExternalModuleDependency -> {
val moduleId = this as? ModuleComponentIdentifier
moduleId?.toModuleOrigin() == moduleDependency.moduleOrigin
is MavenModuleIdentifier -> {
val componentId = this as? ModuleComponentIdentifier
componentId?.toModuleIdentifier() == id
}
else -> false
}
@@ -14,7 +14,7 @@ import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension
import org.jetbrains.kotlin.gradle.dsl.KotlinSingleTargetExtension
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
import org.jetbrains.kotlin.project.model.KotlinModule
import org.jetbrains.kotlin.project.model.LocalBuild
import org.jetbrains.kotlin.project.model.LocalModuleIdentifier
val KotlinProjectExtension.targets: Iterable<KotlinTarget>
get() = when (this) {
@@ -24,7 +24,7 @@ val KotlinProjectExtension.targets: Iterable<KotlinTarget>
}
fun KotlinModule.representsProject(project: Project): Boolean =
moduleOrigin.let { it is LocalBuild && it.buildId == project.currentBuildId().name } && moduleName == project.path
moduleIdentifier.let { it is LocalModuleIdentifier && it.buildId == project.currentBuildId().name && it.projectId == project.path }
// FIXME internal API?
fun Project.currentBuildId(): BuildIdentifier =
@@ -5,10 +5,15 @@
package org.jetbrains.kotlin.project.model
// TODO ensure that resolvers are pluggable + custom dependency kinds (& result kinds?)
// TODO think about state management: unresolved -> (known dependency graph?) ... -> completely resolved
// it seems to be important to learn whether or not the model is final
interface ModuleDependencyResolver {
fun resolveDependency(moduleDependency: ModuleDependency): KotlinModule?
}
// TODO merge with ModuleDependencyResolver?
// Semantically, they are close and may use shared caches and other shared state in the implementations
interface DependencyDiscovery {
// TODO return dependency graph rather than just iterable?
// TODO make this a partial function, too
@@ -5,13 +5,26 @@
package org.jetbrains.kotlin.project.model
sealed class ModuleOrigin
data class LocalBuild(val buildId: String) : ModuleOrigin() // TODO add project ID?
data class ExternalOrigin(val dependencyIdParts: List<String>) : ModuleOrigin()
// TODO sealed with an abstract subclass? this will make exhaustive checks work
open class ModuleIdentifier
// TODO consider id: Any, to allow IDs with custom equality?
data class LocalModuleIdentifier(val buildId: String, val projectId: String) : ModuleIdentifier() {
companion object {
private const val SINGLE_BUILD_ID = ":"
}
override fun toString(): String = "project '$projectId'" + buildId.takeIf { it != SINGLE_BUILD_ID }?.let { "(build '$it')" }.orEmpty()
}
data class MavenModuleIdentifier(val group: String, val name: String) : ModuleIdentifier() {
override fun toString(): String = "$group:$name"
}
// TODO Gradle allows having multiple capabilities in a published module, we need to figure out how we can include them in the module IDs
interface KotlinModule {
val moduleName: String
val moduleOrigin: ModuleOrigin
val moduleIdentifier: ModuleIdentifier
val fragments: Iterable<KotlinModuleFragment>
@@ -20,10 +33,9 @@ interface KotlinModule {
}
class BasicKotlinModule(
override val moduleName: String,
override val moduleOrigin: ModuleOrigin
override val moduleIdentifier: ModuleIdentifier
) : KotlinModule {
override val fragments = mutableListOf<BasicKotlinModuleFragment>()
override fun toString(): String = "module '$moduleName'"
override fun toString(): String = "module $moduleIdentifier"
}
@@ -5,13 +5,11 @@
package org.jetbrains.kotlin.project.model
sealed class ModuleDependency(open val moduleOrigin: ModuleOrigin)
data class ExternalModuleDependency(override val moduleOrigin: ExternalOrigin) : ModuleDependency(moduleOrigin) {
override fun toString() = "external dependency ${moduleOrigin.dependencyIdParts.joinToString(":")}"
data class ModuleDependency(val moduleIdentifier: ModuleIdentifier) {
override fun toString(): String = "dependency $moduleIdentifier"
}
data class LocalModuleDependency(override val moduleOrigin: LocalBuild, val moduleName: String) :
ModuleDependency(moduleOrigin) {
override fun toString() = "local module $moduleName (build ${moduleOrigin.buildId})"
}
/**
* TODO other kinds of dependencies: non-Kotlin: cinterop, CocoaPods, NPM dependencies?
* support with different moduleIdentifiers? Introduce other kinds of dependencies than ModuleDependency?
*/
@@ -5,7 +5,7 @@
package org.jetbrains.kotlin.project.model
fun module(name: String) = BasicKotlinModule(name, LocalBuild("current"))
fun module(name: String) = BasicKotlinModule(LocalModuleIdentifier("current", name))
fun BasicKotlinModule.fragment(vararg nameParts: String): BasicKotlinModuleFragment =
fragment(nameParts.drop(1).joinToString("", nameParts.first()) { it.capitalize() })