Dependency resolution API in PM2.0
This commit is contained in:
committed by
TeamCityServer
parent
f189ebc983
commit
994f940a31
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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.project.model
|
||||
|
||||
interface ModuleDependencyResolver {
|
||||
fun resolveDependency(moduleDependency: ModuleDependency): KotlinModule?
|
||||
}
|
||||
|
||||
interface DependencyDiscovery {
|
||||
// TODO return dependency graph rather than just iterable?
|
||||
// TODO make this a partial function, too
|
||||
fun discoverDependencies(fragment: KotlinModuleFragment): Iterable<ModuleDependency>
|
||||
}
|
||||
+19
-7
@@ -15,15 +15,15 @@ interface InternalDependencyExpansion {
|
||||
class InternalDependencyExpansionResult(
|
||||
val entries: Iterable<Entry>
|
||||
) {
|
||||
sealed class ExpansionOutcome(val variantMatchingResults: Iterable<KotlinVariantMatchingResult>) {
|
||||
sealed class ExpansionOutcome(val variantMatchingResults: Iterable<VariantResolution>) {
|
||||
class VisibleFragments(
|
||||
val fragments: Iterable<KotlinModuleFragment>,
|
||||
variantMatchingResults: Iterable<KotlinVariantMatchingResult>
|
||||
variantMatchingResults: Iterable<VariantResolution>
|
||||
) : ExpansionOutcome(
|
||||
variantMatchingResults
|
||||
)
|
||||
|
||||
class Failure(variantMatchingResults: Iterable<KotlinVariantMatchingResult>) : ExpansionOutcome(variantMatchingResults)
|
||||
class Failure(variantMatchingResults: Iterable<VariantResolution>) : ExpansionOutcome(variantMatchingResults)
|
||||
}
|
||||
|
||||
class Entry(
|
||||
@@ -48,7 +48,7 @@ class DefaultInternalDependencyExpansion(
|
||||
fun getChosenVariant(
|
||||
dependingVariant: KotlinModuleVariant,
|
||||
candidateVariants: Iterable<KotlinModuleVariant>
|
||||
): KotlinVariantMatchingResult
|
||||
): VariantResolution
|
||||
}
|
||||
|
||||
override fun expandInternalFragmentDependencies(consumingFragment: KotlinModuleFragment): InternalDependencyExpansionResult {
|
||||
@@ -97,22 +97,34 @@ class DefaultInternalDependencyExpansion(
|
||||
|
||||
val chosenVariants = consumingVariants.map { consumingVariant ->
|
||||
if (consumingVariant in producingVariants)
|
||||
VariantMatch(consumingVariant, consumingVariant.containingModule, consumingVariant)
|
||||
VariantResolution.VariantMatch(consumingVariant, consumingVariant.containingModule, consumingVariant)
|
||||
else
|
||||
variantResolver.getChosenVariant(consumingVariant, producingVariants)
|
||||
}
|
||||
|
||||
val mismatchedConsumingVariants = chosenVariants.filter { it !is VariantMatch }
|
||||
val mismatchedConsumingVariants = chosenVariants.filter { it !is VariantResolution.VariantMatch }
|
||||
|
||||
val outcome =
|
||||
if (mismatchedConsumingVariants.isNotEmpty())
|
||||
InternalDependencyExpansionResult.ExpansionOutcome.Failure(chosenVariants)
|
||||
else
|
||||
InternalDependencyExpansionResult.ExpansionOutcome.VisibleFragments(
|
||||
chosenVariants.map { (it as VariantMatch).chosenVariant.refinesClosure }.reduce { acc, it -> acc.intersect(it) },
|
||||
chosenVariants
|
||||
.map { (it as VariantResolution.VariantMatch).chosenVariant.refinesClosure }
|
||||
.reduce { acc, it -> acc.intersect(it) },
|
||||
chosenVariants
|
||||
)
|
||||
|
||||
return InternalDependencyExpansionResult.Entry(declaredDependencySource, declaredDependency, outcome)
|
||||
}
|
||||
}
|
||||
|
||||
class AssociateVariants : DefaultInternalDependencyExpansion.ContainingModuleVariantResolver {
|
||||
override fun getChosenVariant(
|
||||
dependingVariant: KotlinModuleVariant,
|
||||
candidateVariants: Iterable<KotlinModuleVariant>
|
||||
): VariantResolution {
|
||||
val result = candidateVariants.filter { it in dependingVariant.declaredContainingModuleFragmentDependencies }
|
||||
return VariantResolution.fromMatchingVariants(dependingVariant, dependingVariant.containingModule, result)
|
||||
}
|
||||
}
|
||||
@@ -5,14 +5,13 @@
|
||||
|
||||
package org.jetbrains.kotlin.project.model
|
||||
|
||||
sealed class ModuleSource {
|
||||
class LocalBuild(val buildId: String) : ModuleSource()
|
||||
class ExternalDependency(val dependencyId: String) : ModuleSource()
|
||||
}
|
||||
sealed class ModuleOrigin
|
||||
data class LocalBuild(val buildId: String) : ModuleOrigin() // TODO add project ID?
|
||||
data class ExternalOrigin(val dependencyIdParts: List<String>) : ModuleOrigin()
|
||||
|
||||
interface KotlinModule {
|
||||
val moduleName: String
|
||||
val moduleSource: ModuleSource
|
||||
val moduleOrigin: ModuleOrigin
|
||||
|
||||
val fragments: Iterable<KotlinModuleFragment>
|
||||
|
||||
@@ -22,7 +21,7 @@ interface KotlinModule {
|
||||
|
||||
class BasicKotlinModule(
|
||||
override val moduleName: String,
|
||||
override val moduleSource: ModuleSource
|
||||
override val moduleOrigin: ModuleOrigin
|
||||
) : KotlinModule {
|
||||
override val fragments = mutableListOf<BasicKotlinModuleFragment>()
|
||||
|
||||
|
||||
@@ -16,6 +16,9 @@ interface KotlinModuleFragment {
|
||||
|
||||
val declaredContainingModuleFragmentDependencies: Iterable<KotlinModuleFragment>
|
||||
|
||||
// TODO: scopes
|
||||
val declaredModuleDependencies: Iterable<ModuleDependency>
|
||||
|
||||
val kotlinSourceRoots: Iterable<File>
|
||||
}
|
||||
|
||||
@@ -28,7 +31,7 @@ interface KotlinModuleVariant : KotlinModuleFragment {
|
||||
val KotlinModuleFragment.fragmentAttributeSets: Map<KotlinAttributeKey, Set<String>>
|
||||
get() = mutableMapOf<KotlinAttributeKey, MutableSet<String>>().apply {
|
||||
containingModule.variantsContainingFragment(this@fragmentAttributeSets).forEach { variant ->
|
||||
variant.variantAttributes.forEach { attribute, value ->
|
||||
variant.variantAttributes.forEach { (attribute, value) ->
|
||||
getOrPut(attribute) { mutableSetOf() }.add(value)
|
||||
}
|
||||
}
|
||||
@@ -49,14 +52,16 @@ open class BasicKotlinModuleFragment(
|
||||
override val fragmentName: String
|
||||
) : KotlinModuleFragment {
|
||||
|
||||
override val directRefinesDependencies: MutableList<BasicKotlinModuleFragment> = mutableListOf()
|
||||
override val directRefinesDependencies: MutableSet<BasicKotlinModuleFragment> = mutableSetOf()
|
||||
|
||||
override val declaredContainingModuleFragmentDependencies: MutableList<BasicKotlinModuleFragment> = mutableListOf()
|
||||
override val kotlinSourceRoots: Iterable<File> = emptyList()
|
||||
override val declaredContainingModuleFragmentDependencies: MutableSet<BasicKotlinModuleFragment> = mutableSetOf()
|
||||
override val declaredModuleDependencies: MutableSet<ModuleDependency> = mutableSetOf()
|
||||
|
||||
override var kotlinSourceRoots: Iterable<File> = emptyList()
|
||||
override fun toString(): String = "fragment $fragmentName"
|
||||
}
|
||||
|
||||
class BasicKotlinVariant(
|
||||
class BasicKotlinModuleVariant(
|
||||
containingModule: KotlinModule,
|
||||
fragmentName: String
|
||||
) : BasicKotlinModuleFragment (
|
||||
|
||||
+3
-3
@@ -14,11 +14,11 @@ interface KotlinModuleFragmentResolver {
|
||||
class KotlinChosenFragments(
|
||||
val module: KotlinModule,
|
||||
val chosenFragments: Iterable<KotlinModuleFragment>,
|
||||
val variantMatchingResults: Iterable<KotlinVariantMatchingResult>
|
||||
val variantMatchingResults: Iterable<VariantResolution>
|
||||
)
|
||||
|
||||
class DefaultKotlinModuleFragmentResolver(
|
||||
private val variantResolver: KotlinModuleVariantResolver
|
||||
private val variantResolver: ModuleVariantResolver
|
||||
) : KotlinModuleFragmentResolver {
|
||||
override fun getChosenFragments(dependingFragment: KotlinModuleFragment, dependencyModule: KotlinModule): KotlinChosenFragments {
|
||||
val dependingModule = dependingFragment.containingModule
|
||||
@@ -28,7 +28,7 @@ class DefaultKotlinModuleFragmentResolver(
|
||||
|
||||
val chosenFragments = chosenVariants.map { variantResolution ->
|
||||
when (variantResolution) {
|
||||
is VariantMatch -> variantResolution.chosenVariant.refinesClosure
|
||||
is VariantResolution.VariantMatch -> variantResolution.chosenVariant.refinesClosure
|
||||
else -> emptySet()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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.project.model
|
||||
|
||||
/*
|
||||
* Copyright 2010-2020 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.
|
||||
*/
|
||||
|
||||
interface KotlinModuleVariantResolver {
|
||||
fun getChosenVariant(dependingVariant: KotlinModuleVariant, dependencyModule: KotlinModule): KotlinVariantMatchingResult
|
||||
}
|
||||
|
||||
sealed class KotlinVariantMatchingResult(
|
||||
val requestingVariant: KotlinModuleVariant,
|
||||
val dependencyModule: KotlinModule
|
||||
) {
|
||||
companion object {
|
||||
fun fromMatchingVariants(
|
||||
requestingVariant: KotlinModuleVariant,
|
||||
dependencyModule: KotlinModule,
|
||||
matchingVariants: Collection<KotlinModuleVariant>
|
||||
) = when (matchingVariants.size) {
|
||||
0 -> NoVariantMatch(requestingVariant, dependencyModule)
|
||||
1 -> VariantMatch(requestingVariant, dependencyModule, matchingVariants.single())
|
||||
else -> AmbiguousVariants(requestingVariant, dependencyModule, matchingVariants)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class VariantMatch(
|
||||
requestingVariant: KotlinModuleVariant,
|
||||
dependencyModule: KotlinModule,
|
||||
val chosenVariant: KotlinModuleVariant
|
||||
) : KotlinVariantMatchingResult(requestingVariant, dependencyModule)
|
||||
|
||||
class NoVariantMatch(
|
||||
requestingVariant: KotlinModuleVariant,
|
||||
dependencyModule: KotlinModule
|
||||
) : KotlinVariantMatchingResult(requestingVariant, dependencyModule)
|
||||
|
||||
class AmbiguousVariants(
|
||||
requestingVariant: KotlinModuleVariant,
|
||||
dependencyModule: KotlinModule,
|
||||
val matchingVariants: Iterable<KotlinModuleVariant>
|
||||
) : KotlinVariantMatchingResult(requestingVariant, dependencyModule)
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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.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 LocalModuleDependency(override val moduleOrigin: LocalBuild, val moduleName: String) :
|
||||
ModuleDependency(moduleOrigin) {
|
||||
override fun toString() = "local module $moduleName (build ${moduleOrigin.buildId})"
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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.project.model
|
||||
|
||||
/*
|
||||
* Copyright 2010-2020 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.
|
||||
*/
|
||||
|
||||
interface ModuleVariantResolver {
|
||||
/**
|
||||
* Find the matching module variant of the [dependencyModule] for the consumer's [requestingVariant].
|
||||
*
|
||||
* This is a partial function. A particular resolver may not be capable to do variant matching for some [dependencyModule] or
|
||||
* some [requestingVariant] (to such an extent that a resolver may only know how to resolve a single [dependencyModule]'s variants, or
|
||||
* how to resolve module variants for a particular [requestingVariant]).
|
||||
* In this case it should return [VariantResolution.Unknown], and the caller (which might be an aggregating [ModuleVariantResolver])
|
||||
* may consult other sources to get the variant match.
|
||||
*/
|
||||
fun getChosenVariant(requestingVariant: KotlinModuleVariant, dependencyModule: KotlinModule): VariantResolution
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the results of [dependencyModule]'s variant resolution for the [requestingVariant],
|
||||
* usually done by some [ModuleVariantResolver].
|
||||
*/
|
||||
sealed class VariantResolution(
|
||||
val requestingVariant: KotlinModuleVariant,
|
||||
val dependencyModule: KotlinModule
|
||||
) {
|
||||
companion object {
|
||||
fun fromMatchingVariants(
|
||||
requestingVariant: KotlinModuleVariant,
|
||||
dependencyModule: KotlinModule,
|
||||
matchingVariants: Collection<KotlinModuleVariant>
|
||||
) = when (matchingVariants.size) {
|
||||
0 -> NoVariantMatch(requestingVariant, dependencyModule)
|
||||
1 -> VariantMatch(requestingVariant, dependencyModule, matchingVariants.single())
|
||||
else -> AmbiguousVariants(requestingVariant, dependencyModule, matchingVariants)
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString(): String = when (this) {
|
||||
is VariantMatch -> "match: ${chosenVariant.fragmentName}"
|
||||
is Unknown -> "unknown"
|
||||
is NotRequested -> "not requested"
|
||||
is NoVariantMatch -> "no match"
|
||||
is AmbiguousVariants -> "ambiguity: ${matchingVariants.joinToString { it.fragmentName }}"
|
||||
}
|
||||
|
||||
/**
|
||||
* The resolver decided that the [chosenVariant] is the best variant match.
|
||||
*/
|
||||
class VariantMatch(
|
||||
requestingVariant: KotlinModuleVariant,
|
||||
dependencyModule: KotlinModule,
|
||||
val chosenVariant: KotlinModuleVariant
|
||||
) : VariantResolution(requestingVariant, dependencyModule)
|
||||
|
||||
class Unknown(requestingVariant: KotlinModuleVariant, dependencyModule: KotlinModule) :
|
||||
VariantResolution(requestingVariant, dependencyModule)
|
||||
|
||||
/**
|
||||
* Returned when the resolver detects that the [requestingVariant] does not depend on [dependencyModule] and therefore should not get
|
||||
* any variant of that module at all.
|
||||
*/
|
||||
class NotRequested(requestingVariant: KotlinModuleVariant, dependencyModule: KotlinModule) :
|
||||
VariantResolution(requestingVariant, dependencyModule)
|
||||
|
||||
/**
|
||||
* Returned when the resolver could not find any matching of the [dependencyModule] for the [requestingVariant], or variant matching was
|
||||
* done externally and the external system did not provide any details of the failure.
|
||||
*/
|
||||
class NoVariantMatch(
|
||||
requestingVariant: KotlinModuleVariant,
|
||||
dependencyModule: KotlinModule
|
||||
) : VariantResolution(requestingVariant, dependencyModule)
|
||||
|
||||
/**
|
||||
* Returned when the resolver found multiple matching variants in the [dependencyModule] and failed to choose one of them as the
|
||||
* best match for the [requestingVariant].
|
||||
*/
|
||||
class AmbiguousVariants(
|
||||
requestingVariant: KotlinModuleVariant,
|
||||
dependencyModule: KotlinModule,
|
||||
val matchingVariants: Iterable<KotlinModuleVariant>
|
||||
) : VariantResolution(requestingVariant, dependencyModule)
|
||||
}
|
||||
+5
-15
@@ -5,24 +5,14 @@
|
||||
|
||||
package org.jetbrains.kotlin.project.model
|
||||
|
||||
class MatchVariantsByExactAttributes : KotlinModuleVariantResolver {
|
||||
override fun getChosenVariant(dependingVariant: KotlinModuleVariant, dependencyModule: KotlinModule): KotlinVariantMatchingResult {
|
||||
class MatchVariantsByExactAttributes : ModuleVariantResolver {
|
||||
override fun getChosenVariant(requestingVariant: KotlinModuleVariant, dependencyModule: KotlinModule): VariantResolution {
|
||||
val candidates = dependencyModule.variants
|
||||
return candidates.filter { candidate ->
|
||||
candidate.isExported && candidate.variantAttributes.all { (attributeKey, candidateValue) ->
|
||||
attributeKey !in dependingVariant.variantAttributes.keys ||
|
||||
candidateValue == dependingVariant.variantAttributes.getValue(attributeKey)
|
||||
attributeKey !in requestingVariant.variantAttributes.keys ||
|
||||
candidateValue == requestingVariant.variantAttributes.getValue(attributeKey)
|
||||
}
|
||||
}.let { KotlinVariantMatchingResult.fromMatchingVariants(dependingVariant, dependencyModule, it) }
|
||||
}
|
||||
}
|
||||
|
||||
class AssociateVariants : DefaultInternalDependencyExpansion.ContainingModuleVariantResolver {
|
||||
override fun getChosenVariant(
|
||||
dependingVariant: KotlinModuleVariant,
|
||||
candidateVariants: Iterable<KotlinModuleVariant>
|
||||
): KotlinVariantMatchingResult {
|
||||
val result = candidateVariants.filter { it in dependingVariant.declaredContainingModuleFragmentDependencies }
|
||||
return KotlinVariantMatchingResult.fromMatchingVariants(dependingVariant, dependingVariant.containingModule, result)
|
||||
}.let { VariantResolution.fromMatchingVariants(requestingVariant, dependencyModule, it) }
|
||||
}
|
||||
}
|
||||
+7
-7
@@ -5,7 +5,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.project.model
|
||||
|
||||
fun module(name: String) = BasicKotlinModule(name, ModuleSource.LocalBuild("current"))
|
||||
fun module(name: String) = BasicKotlinModule(name, LocalBuild("current"))
|
||||
|
||||
fun BasicKotlinModule.fragment(vararg nameParts: String): BasicKotlinModuleFragment =
|
||||
fragment(nameParts.drop(1).joinToString("", nameParts.first()) { it.capitalize() })
|
||||
@@ -13,13 +13,13 @@ fun BasicKotlinModule.fragment(vararg nameParts: String): BasicKotlinModuleFragm
|
||||
fun BasicKotlinModule.fragment(name: String): BasicKotlinModuleFragment =
|
||||
fragments.firstOrNull { it.fragmentName == name } ?: BasicKotlinModuleFragment(this, name).also { fragments.add(it) }
|
||||
|
||||
fun BasicKotlinModule.variant(vararg nameParts: String): BasicKotlinVariant =
|
||||
fun BasicKotlinModule.variant(vararg nameParts: String): BasicKotlinModuleVariant =
|
||||
variant(nameParts.drop(1).joinToString("", nameParts.first()) { it.capitalize() })
|
||||
|
||||
fun BasicKotlinModule.variant(name: String): BasicKotlinVariant =
|
||||
fun BasicKotlinModule.variant(name: String): BasicKotlinModuleVariant =
|
||||
fragments.firstOrNull { it.fragmentName == name }
|
||||
?.let { it as? BasicKotlinVariant ?: error("$name is not a variant") }
|
||||
?: BasicKotlinVariant(this, name).also { fragments.add(it) }
|
||||
?.let { it as? BasicKotlinModuleVariant ?: error("$name is not a variant") }
|
||||
?: BasicKotlinModuleVariant(this, name).also { fragments.add(it) }
|
||||
|
||||
|
||||
fun BasicKotlinModuleFragment.depends(fragment: BasicKotlinModuleFragment) {
|
||||
@@ -61,12 +61,12 @@ fun simpleModule(name: String) = module(name).apply {
|
||||
}
|
||||
}
|
||||
|
||||
val jvmAndJs = fragment("jvmAndJs", purpose).apply {
|
||||
fragment("jvmAndJs", purpose).apply {
|
||||
refines(common)
|
||||
refinedBy(jvm)
|
||||
refinedBy(js)
|
||||
}
|
||||
val jsAndLinux = fragment("jsAndLinux", purpose).apply {
|
||||
fragment("jsAndLinux", purpose).apply {
|
||||
refines(common)
|
||||
refinedBy(js)
|
||||
refinedBy(linux)
|
||||
|
||||
Reference in New Issue
Block a user