From 7103aa310077da7286f021609bbf0e080f44be3a Mon Sep 17 00:00:00 2001 From: Sergey Igushkin Date: Tue, 1 Dec 2020 14:59:40 +0300 Subject: [PATCH] ModuleOrigin -> ModuleIdentifier --- .../plugin/mpp/GradleProjectModuleBuilder.kt | 26 ++++---- .../plugin/mpp/pm20/DependencyResolution.kt | 61 ++++++++++--------- .../kotlin/gradle/plugin/mpp/pm20/Utils.kt | 4 +- .../src/main/kotlin/DependencyResolution.kt | 5 ++ .../src/main/kotlin/KotlinModule.kt | 28 ++++++--- .../src/main/kotlin/ModuleDependency.kt | 14 ++--- .../kotlin/project/model/SimpleModel.kt | 2 +- 7 files changed, 77 insertions(+), 63 deletions(-) diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/GradleProjectModuleBuilder.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/GradleProjectModuleBuilder.kt index ae3b1e50648..494732ecc97 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/GradleProjectModuleBuilder.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/GradleProjectModuleBuilder.kt @@ -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() + private val modulesCache = mutableMapOf() 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>() targets.forEach { target -> @@ -109,7 +108,7 @@ class GradleProjectModuleBuilder(private val addInferredSourceSetVisibilityAsExp extension.sourceSets.forEach { sourceSet -> val existingVariant = fragments.filterIsInstance().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) diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/pm20/DependencyResolution.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/pm20/DependencyResolution.kt index 9031d1607c9..a4a468b874a 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/pm20/DependencyResolution.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/pm20/DependencyResolution.kt @@ -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 } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/pm20/Utils.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/pm20/Utils.kt index 49d58a155a9..cf3b17216ea 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/pm20/Utils.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/pm20/Utils.kt @@ -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 get() = when (this) { @@ -24,7 +24,7 @@ val KotlinProjectExtension.targets: Iterable } 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 = diff --git a/libraries/tools/kotlin-project-model/src/main/kotlin/DependencyResolution.kt b/libraries/tools/kotlin-project-model/src/main/kotlin/DependencyResolution.kt index 3ac66198bd2..3695caa7e7e 100644 --- a/libraries/tools/kotlin-project-model/src/main/kotlin/DependencyResolution.kt +++ b/libraries/tools/kotlin-project-model/src/main/kotlin/DependencyResolution.kt @@ -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 diff --git a/libraries/tools/kotlin-project-model/src/main/kotlin/KotlinModule.kt b/libraries/tools/kotlin-project-model/src/main/kotlin/KotlinModule.kt index f1586a6df7a..aba533eb4a4 100644 --- a/libraries/tools/kotlin-project-model/src/main/kotlin/KotlinModule.kt +++ b/libraries/tools/kotlin-project-model/src/main/kotlin/KotlinModule.kt @@ -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) : 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 @@ -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() - override fun toString(): String = "module '$moduleName'" + override fun toString(): String = "module $moduleIdentifier" } \ No newline at end of file diff --git a/libraries/tools/kotlin-project-model/src/main/kotlin/ModuleDependency.kt b/libraries/tools/kotlin-project-model/src/main/kotlin/ModuleDependency.kt index 25a07ab9571..e96d46089ec 100644 --- a/libraries/tools/kotlin-project-model/src/main/kotlin/ModuleDependency.kt +++ b/libraries/tools/kotlin-project-model/src/main/kotlin/ModuleDependency.kt @@ -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})" -} \ No newline at end of file +/** + * TODO other kinds of dependencies: non-Kotlin: cinterop, CocoaPods, NPM dependencies? + * support with different moduleIdentifiers? Introduce other kinds of dependencies than ModuleDependency? + */ diff --git a/libraries/tools/kotlin-project-model/src/test/kotlin/org/jetbrains/kotlin/project/model/SimpleModel.kt b/libraries/tools/kotlin-project-model/src/test/kotlin/org/jetbrains/kotlin/project/model/SimpleModel.kt index 0fc4cbc602e..fda0d2f9b9b 100644 --- a/libraries/tools/kotlin-project-model/src/test/kotlin/org/jetbrains/kotlin/project/model/SimpleModel.kt +++ b/libraries/tools/kotlin-project-model/src/test/kotlin/org/jetbrains/kotlin/project/model/SimpleModel.kt @@ -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() })