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.KotlinCompilation
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.currentBuildId 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.matchesModule
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.representsProject
import org.jetbrains.kotlin.gradle.plugin.sources.getVisibleSourceSetsFromAssociateCompilations import org.jetbrains.kotlin.gradle.plugin.sources.getVisibleSourceSetsFromAssociateCompilations
import org.jetbrains.kotlin.project.model.* import org.jetbrains.kotlin.project.model.*
class ProjectStructureMetadataModuleBuilder { class ProjectStructureMetadataModuleBuilder {
private val modulesCache = mutableMapOf<String, KotlinModule>() private val modulesCache = mutableMapOf<ModuleIdentifier, KotlinModule>()
private fun buildModuleFromProjectStructureMetadata( private fun buildModuleFromProjectStructureMetadata(
moduleName: String, moduleIdentifier: ModuleIdentifier,
moduleOrigin: ModuleOrigin,
metadata: KotlinProjectStructureMetadata metadata: KotlinProjectStructureMetadata
): KotlinModule = ): KotlinModule =
BasicKotlinModule(moduleName, moduleOrigin).apply { BasicKotlinModule(moduleIdentifier).apply {
metadata.sourceSetNamesByVariantName.keys.forEach { variantName -> metadata.sourceSetNamesByVariantName.keys.forEach { variantName ->
fragments.add(BasicKotlinModuleVariant(this@apply, variantName)) fragments.add(BasicKotlinModuleVariant(this@apply, variantName))
} }
@@ -53,11 +53,10 @@ class ProjectStructureMetadataModuleBuilder {
} }
} }
fun getModule(name: String, moduleOrigin: ModuleOrigin, projectStructureMetadata: KotlinProjectStructureMetadata): KotlinModule { fun getModule(moduleIdentifier: ModuleIdentifier, projectStructureMetadata: KotlinProjectStructureMetadata): KotlinModule {
return modulesCache.getOrPut(name) { return modulesCache.getOrPut(moduleIdentifier) {
buildModuleFromProjectStructureMetadata( buildModuleFromProjectStructureMetadata(
name, moduleIdentifier,
moduleOrigin,
projectStructureMetadata projectStructureMetadata
) )
} }
@@ -77,7 +76,7 @@ class GradleProjectModuleBuilder(private val addInferredSourceSetVisibilityAsExp
else -> return null 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<*>>() val variantToCompilation = mutableMapOf<KotlinModuleFragment, KotlinCompilation<*>>()
targets.forEach { target -> targets.forEach { target ->
@@ -109,7 +108,7 @@ class GradleProjectModuleBuilder(private val addInferredSourceSetVisibilityAsExp
extension.sourceSets.forEach { sourceSet -> extension.sourceSets.forEach { sourceSet ->
val existingVariant = fragments.filterIsInstance<BasicKotlinModuleVariant>().find { it.fragmentName == sourceSet.name } val existingVariant = fragments.filterIsInstance<BasicKotlinModuleVariant>().find { it.fragmentName == sourceSet.name }
val fragment = existingVariant ?: BasicKotlinModuleFragment(this@apply, sourceSet.name).also { fragments.add(it) } 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 // FIXME: Kotlin/Native implementation-effective-api dependencies are missing here. Introduce dependency scopes
project.configurations.getByName(sourceSet.apiConfigurationName).allDependencies.forEach { project.configurations.getByName(sourceSet.apiConfigurationName).allDependencies.forEach {
@@ -163,9 +162,9 @@ internal fun Dependency.toModuleDependency(
project: Project project: Project
) = when (val dependency = this) { ) = when (val dependency = this) {
is ProjectDependency -> is ProjectDependency ->
LocalModuleDependency(LocalBuild(project.currentBuildId().name), dependency.dependencyProject.path) ModuleDependency(LocalModuleIdentifier(project.currentBuildId().name, dependency.dependencyProject.path))
else -> else ->
ExternalModuleDependency(ExternalOrigin(listOfNotNull(dependency.group, dependency.name))) ModuleDependency(MavenModuleIdentifier(dependency.group.orEmpty(), dependency.name))
} }
private fun BasicKotlinModule.fragmentByName(name: String) = private fun BasicKotlinModule.fragmentByName(name: String) =
@@ -184,8 +183,7 @@ class GradleModuleVariantResolver(val project: Project) : ModuleVariantResolver
val module = requestingVariant.containingModule val module = requestingVariant.containingModule
// This implementation can only resolve variants for the current project's KotlinModule // This implementation can only resolve variants for the current project's KotlinModule
require(module.moduleName == project.path) require(module.representsProject(project))
require(module.moduleOrigin.let { it is LocalBuild && it.buildId == project.currentBuildId().name })
val targets = val targets =
project.multiplatformExtensionOrNull?.targets ?: listOf((project.kotlinExtension as KotlinSingleTargetExtension).target) 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.ResolvedComponentResult
import org.gradle.api.artifacts.result.ResolvedDependencyResult import org.gradle.api.artifacts.result.ResolvedDependencyResult
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension 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.ProjectStructureMetadataModuleBuilder
import org.jetbrains.kotlin.gradle.plugin.mpp.GradleProjectModuleBuilder import org.jetbrains.kotlin.gradle.plugin.mpp.GradleProjectModuleBuilder
import org.jetbrains.kotlin.gradle.plugin.mpp.getProjectStructureMetadata import org.jetbrains.kotlin.gradle.plugin.mpp.getProjectStructureMetadata
@@ -41,7 +40,7 @@ class GradleModuleDependencyResolver(
projectModuleBuilder.buildModuleFromProject(project.project(id.projectPath)) projectModuleBuilder.buildModuleFromProject(project.project(id.projectPath))
id is ModuleComponentIdentifier -> { id is ModuleComponentIdentifier -> {
val metadata = getProjectStructureMetadata(project, component, configurationToResolve) ?: return null val metadata = getProjectStructureMetadata(project, component, configurationToResolve) ?: return null
projectStructureMetadataModuleBuilder.getModule(id.displayName, id.toModuleOrigin(), metadata) projectStructureMetadataModuleBuilder.getModule(id.toModuleIdentifier(), metadata)
} }
else -> null 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 // 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 { private fun buildStubModule(resolvedComponentResult: ResolvedComponentResult, singleVariantName: String): KotlinModule {
val moduleDependency = resolvedComponentResult.toModuleDependency() val moduleDependency = resolvedComponentResult.toModuleDependency()
val moduleName = when (val id = resolvedComponentResult.id) { return BasicKotlinModule(moduleDependency.moduleIdentifier).apply {
is ProjectComponentIdentifier -> id.projectPath
else -> id.displayName
}
return BasicKotlinModule(moduleName, moduleDependency.moduleOrigin).apply {
BasicKotlinModuleVariant(this@apply, singleVariantName).apply { BasicKotlinModuleVariant(this@apply, singleVariantName).apply {
fragments.add(this) fragments.add(this)
this.declaredModuleDependencies.addAll( this.declaredModuleDependencies.addAll(
@@ -140,6 +135,7 @@ class VariantDependencyDiscovery(
.find { it.defaultSourceSetName == fragment.fragmentName } .find { it.defaultSourceSetName == fragment.fragmentName }
requireNotNull(compilation) requireNotNull(compilation)
// TODO distinguish between compile and runtime dependencies?
val configuration = project.configurations.getByName(compilation.compileDependencyConfigurationName) val configuration = project.configurations.getByName(compilation.compileDependencyConfigurationName)
return configuration.incoming.resolutionResult.allComponents return configuration.incoming.resolutionResult.allComponents
} }
@@ -150,45 +146,50 @@ class VariantDependencyDiscovery(
val components = resolvedComponentResults(fragment) val components = resolvedComponentResults(fragment)
return components.mapNotNull { component -> return components.mapNotNull { component ->
when (val id = component.id) { val moduleIdentifier = when (val id = component.id) {
is ProjectComponentIdentifier -> LocalModuleDependency(LocalBuild(id.build.name), id.projectPath) is ProjectComponentIdentifier -> LocalModuleIdentifier(id.build.name, id.projectPath)
is ModuleComponentIdentifier -> ExternalModuleDependency(id.toModuleOrigin()) is ModuleComponentIdentifier -> id.toModuleIdentifier()
else -> null // TODO check that no other options are possible, throw errors else -> return@mapNotNull null // TODO check that no other options are possible, throw errors?
} }
ModuleDependency(moduleIdentifier)
} }
} }
} }
private fun ModuleComponentIdentifier.toModuleOrigin(): ExternalOrigin = private fun ModuleComponentIdentifier.toModuleIdentifier(): MavenModuleIdentifier =
ExternalOrigin(listOf(moduleIdentifier.group, moduleIdentifier.name)) MavenModuleIdentifier(moduleIdentifier.group, moduleIdentifier.name)
internal fun ComponentIdentifier.matchesModule(module: KotlinModule): Boolean { internal fun ComponentIdentifier.matchesModule(module: KotlinModule): Boolean {
return when (val moduleSource = module.moduleOrigin) { return when (val moduleId = module.moduleIdentifier) {
is LocalBuild -> { is LocalModuleIdentifier -> {
val projectId = this as? ProjectComponentIdentifier 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 -> { is MavenModuleIdentifier -> {
val moduleId = this as? ModuleComponentIdentifier val componentId = this as? ModuleComponentIdentifier
moduleId?.toModuleOrigin() == moduleSource componentId?.toModuleIdentifier() == moduleId
} }
else -> false
} }
} }
internal fun ResolvedComponentResult.toModuleDependency(): ModuleDependency = when (val id = id) { internal fun ResolvedComponentResult.toModuleDependency(): ModuleDependency = ModuleDependency(
is ProjectComponentIdentifier -> LocalModuleDependency(LocalBuild(id.build.name), id.projectPath) when (val id = id) {
is ModuleComponentIdentifier -> ExternalModuleDependency(id.toModuleOrigin()) is ProjectComponentIdentifier -> LocalModuleIdentifier(id.build.name, id.projectPath)
else -> ExternalModuleDependency(ExternalOrigin(listOf(moduleVersion?.group.orEmpty(), moduleVersion?.name.orEmpty()))) is ModuleComponentIdentifier -> id.toModuleIdentifier()
} else -> MavenModuleIdentifier(moduleVersion?.group.orEmpty(), moduleVersion?.name.orEmpty())
}
)
internal fun ComponentIdentifier.matchesModuleDependency(moduleDependency: ModuleDependency) = internal fun ComponentIdentifier.matchesModuleDependency(moduleDependency: ModuleDependency) =
when (moduleDependency) { when (val id = moduleDependency.moduleIdentifier) {
is LocalModuleDependency -> { is LocalModuleIdentifier -> {
val projectId = this as? ProjectComponentIdentifier 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 -> { is MavenModuleIdentifier -> {
val moduleId = this as? ModuleComponentIdentifier val componentId = this as? ModuleComponentIdentifier
moduleId?.toModuleOrigin() == moduleDependency.moduleOrigin 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.dsl.KotlinSingleTargetExtension
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
import org.jetbrains.kotlin.project.model.KotlinModule 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> val KotlinProjectExtension.targets: Iterable<KotlinTarget>
get() = when (this) { get() = when (this) {
@@ -24,7 +24,7 @@ val KotlinProjectExtension.targets: Iterable<KotlinTarget>
} }
fun KotlinModule.representsProject(project: Project): Boolean = 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? // FIXME internal API?
fun Project.currentBuildId(): BuildIdentifier = fun Project.currentBuildId(): BuildIdentifier =
@@ -5,10 +5,15 @@
package org.jetbrains.kotlin.project.model 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 { interface ModuleDependencyResolver {
fun resolveDependency(moduleDependency: ModuleDependency): KotlinModule? 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 { interface DependencyDiscovery {
// TODO return dependency graph rather than just iterable? // TODO return dependency graph rather than just iterable?
// TODO make this a partial function, too // TODO make this a partial function, too
@@ -5,13 +5,26 @@
package org.jetbrains.kotlin.project.model package org.jetbrains.kotlin.project.model
sealed class ModuleOrigin // TODO sealed with an abstract subclass? this will make exhaustive checks work
data class LocalBuild(val buildId: String) : ModuleOrigin() // TODO add project ID? open class ModuleIdentifier
data class ExternalOrigin(val dependencyIdParts: List<String>) : ModuleOrigin()
// 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 { interface KotlinModule {
val moduleName: String val moduleIdentifier: ModuleIdentifier
val moduleOrigin: ModuleOrigin
val fragments: Iterable<KotlinModuleFragment> val fragments: Iterable<KotlinModuleFragment>
@@ -20,10 +33,9 @@ interface KotlinModule {
} }
class BasicKotlinModule( class BasicKotlinModule(
override val moduleName: String, override val moduleIdentifier: ModuleIdentifier
override val moduleOrigin: ModuleOrigin
) : KotlinModule { ) : KotlinModule {
override val fragments = mutableListOf<BasicKotlinModuleFragment>() 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 package org.jetbrains.kotlin.project.model
sealed class ModuleDependency(open val moduleOrigin: ModuleOrigin) data class ModuleDependency(val moduleIdentifier: ModuleIdentifier) {
override fun toString(): String = "dependency $moduleIdentifier"
data class ExternalModuleDependency(override val moduleOrigin: ExternalOrigin) : ModuleDependency(moduleOrigin) {
override fun toString() = "external dependency ${moduleOrigin.dependencyIdParts.joinToString(":")}"
} }
data class LocalModuleDependency(override val moduleOrigin: LocalBuild, val moduleName: String) : /**
ModuleDependency(moduleOrigin) { * TODO other kinds of dependencies: non-Kotlin: cinterop, CocoaPods, NPM dependencies?
override fun toString() = "local module $moduleName (build ${moduleOrigin.buildId})" * support with different moduleIdentifiers? Introduce other kinds of dependencies than ModuleDependency?
} */
@@ -5,7 +5,7 @@
package org.jetbrains.kotlin.project.model 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 = fun BasicKotlinModule.fragment(vararg nameParts: String): BasicKotlinModuleFragment =
fragment(nameParts.drop(1).joinToString("", nameParts.first()) { it.capitalize() }) fragment(nameParts.drop(1).joinToString("", nameParts.first()) { it.capitalize() })