Resolve dependencies as graph, support multiple modules in a project
This commit is contained in:
committed by
TeamCityServer
parent
f1fb438d00
commit
d66f95b80e
+1
-2
@@ -427,8 +427,7 @@ class HierarchicalMppIT : BaseGradleIT() {
|
|||||||
shouldInclude: Iterable<Pair<String, String>> = emptyList(),
|
shouldInclude: Iterable<Pair<String, String>> = emptyList(),
|
||||||
shouldNotInclude: Iterable<Pair<String, String>> = emptyList()
|
shouldNotInclude: Iterable<Pair<String, String>> = emptyList()
|
||||||
) {
|
) {
|
||||||
val taskOutput = getOutputForTask(taskPath.removePrefix(":"))
|
val compilerArgsLine = output.lines().single { "$taskPath Kotlin compiler args:" in it }
|
||||||
val compilerArgsLine = taskOutput.lines().single { "Kotlin compiler args:" in it }
|
|
||||||
val classpathItems = compilerArgsLine.substringAfter("-classpath").substringBefore(" -").split(File.pathSeparator)
|
val classpathItems = compilerArgsLine.substringAfter("-classpath").substringBefore(" -").split(File.pathSeparator)
|
||||||
|
|
||||||
val actualClasspath = classpathItems.joinToString("\n")
|
val actualClasspath = classpathItems.joinToString("\n")
|
||||||
|
|||||||
+157
-72
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.gradle.plugin.mpp
|
|||||||
import org.gradle.api.Named
|
import org.gradle.api.Named
|
||||||
import org.gradle.api.Project
|
import org.gradle.api.Project
|
||||||
import org.gradle.api.artifacts.Dependency
|
import org.gradle.api.artifacts.Dependency
|
||||||
|
import org.gradle.api.artifacts.ModuleDependency
|
||||||
import org.gradle.api.artifacts.ProjectDependency
|
import org.gradle.api.artifacts.ProjectDependency
|
||||||
import org.gradle.api.attributes.Attribute
|
import org.gradle.api.attributes.Attribute
|
||||||
import org.gradle.api.attributes.AttributeContainer
|
import org.gradle.api.attributes.AttributeContainer
|
||||||
@@ -16,44 +17,53 @@ import org.jetbrains.kotlin.gradle.dsl.KotlinSingleTargetExtension
|
|||||||
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
|
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
|
||||||
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtensionOrNull
|
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.KotlinSourceSet
|
||||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.matchesModule
|
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
|
||||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.representsProject
|
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.*
|
||||||
|
import org.jetbrains.kotlin.gradle.plugin.sources.KotlinDependencyScope
|
||||||
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<ModuleIdentifier, KotlinModule>()
|
private val modulesCache = mutableMapOf<KotlinModuleIdentifier, KotlinModule>()
|
||||||
|
|
||||||
private fun buildModuleFromProjectStructureMetadata(
|
private fun buildModuleFromProjectStructureMetadata(
|
||||||
moduleIdentifier: ModuleIdentifier,
|
moduleIdentifier: KotlinModuleIdentifier,
|
||||||
metadata: KotlinProjectStructureMetadata
|
metadata: KotlinProjectStructureMetadata
|
||||||
): KotlinModule =
|
): KotlinModule =
|
||||||
BasicKotlinModule(moduleIdentifier).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))
|
||||||
}
|
}
|
||||||
|
fun fragment(sourceSetName: String): BasicKotlinModuleFragment {
|
||||||
|
if (fragments.none { it.fragmentName == sourceSetName })
|
||||||
|
fragments.add(BasicKotlinModuleFragment(this@apply, sourceSetName))
|
||||||
|
return fragmentByName(sourceSetName)
|
||||||
|
}
|
||||||
metadata.sourceSetNamesByVariantName.forEach { (variantName, sourceSets) ->
|
metadata.sourceSetNamesByVariantName.forEach { (variantName, sourceSets) ->
|
||||||
val variant = fragmentByName(variantName)
|
val variant = fragmentByName(variantName)
|
||||||
sourceSets.forEach { sourceSetName ->
|
sourceSets.forEach { sourceSetName ->
|
||||||
if (fragments.none { it.fragmentName == sourceSetName })
|
variant.directRefinesDependencies.add(fragment(sourceSetName))
|
||||||
fragments.add(BasicKotlinModuleFragment(this@apply, sourceSetName))
|
|
||||||
val fragment = fragmentByName(sourceSetName)
|
|
||||||
variant.directRefinesDependencies.add(fragment)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
metadata.sourceSetsDependsOnRelation.forEach { (depending, dependencies) ->
|
metadata.sourceSetModuleDependencies.forEach { (sourceSetName, dependencies) ->
|
||||||
val dependingFragment = fragmentByName(depending)
|
val fragment = fragment(sourceSetName)
|
||||||
dependencies.forEach { dependency ->
|
dependencies.forEach { dependency ->
|
||||||
if (fragments.none { it.fragmentName == dependency })
|
fragment.declaredModuleDependencies.add(
|
||||||
fragments.add(BasicKotlinModuleFragment(this@apply, dependency))
|
KotlinModuleDependency(MavenModuleIdentifier(dependency.groupId.orEmpty(), dependency.moduleId, null /* TODO */))
|
||||||
val fragment = fragmentByName(dependency)
|
)
|
||||||
dependingFragment.directRefinesDependencies.add(fragment)
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
metadata.sourceSetsDependsOnRelation.forEach { (depending, dependencies) ->
|
||||||
|
val dependingFragment = fragment(depending)
|
||||||
|
dependencies.forEach { dependency ->
|
||||||
|
dependingFragment.directRefinesDependencies.add(fragment(dependency))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getModule(moduleIdentifier: ModuleIdentifier, projectStructureMetadata: KotlinProjectStructureMetadata): KotlinModule {
|
fun getModule(moduleIdentifier: KotlinModuleIdentifier, projectStructureMetadata: KotlinProjectStructureMetadata): KotlinModule {
|
||||||
return modulesCache.getOrPut(moduleIdentifier) {
|
return modulesCache.getOrPut(moduleIdentifier) {
|
||||||
buildModuleFromProjectStructureMetadata(
|
buildModuleFromProjectStructureMetadata(
|
||||||
moduleIdentifier,
|
moduleIdentifier,
|
||||||
@@ -63,34 +73,81 @@ class ProjectStructureMetadataModuleBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun detectModules(targets: Iterable<KotlinTarget>, sourceSets: Iterable<KotlinSourceSet>): Map<String, List<KotlinCompilation<*>>> {
|
||||||
|
// DSU-like approach: all compilations and source sets that are reachable via dependsOn edges are considered a single module
|
||||||
|
|
||||||
|
val compilations = targets.flatMap { it.compilations }
|
||||||
|
|
||||||
|
val dsu = mutableMapOf<Any, Any>().apply {
|
||||||
|
compilations.forEach { put(it, it) }
|
||||||
|
sourceSets.forEach { put(it, it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun get(item: Any): Any =
|
||||||
|
dsu.getValue(item).let { leader -> if (leader === item) leader else get(leader).also { dsu[item] = it } }
|
||||||
|
|
||||||
|
fun union(item: Any, other: Any) = dsu.put(get(item), get(other))
|
||||||
|
|
||||||
|
sourceSets.forEach { sourceSet ->
|
||||||
|
sourceSet.dependsOn.forEach { other -> union(sourceSet, other) }
|
||||||
|
}
|
||||||
|
compilations.forEach { compilation ->
|
||||||
|
compilation.kotlinSourceSets.forEach { union(compilation, it) }
|
||||||
|
}
|
||||||
|
val uniqueCompilationNamesCounter = mutableMapOf<Set<String>, Int>()
|
||||||
|
|
||||||
|
fun moduleName(compilations: Iterable<KotlinCompilation<*>>): String {
|
||||||
|
val names = compilations.map { it.name }.toSortedSet()
|
||||||
|
val uniqueNumber = uniqueCompilationNamesCounter.put(names, uniqueCompilationNamesCounter[names]?.plus(1) ?: 0)
|
||||||
|
return names.joinToString("-") + (uniqueNumber?.let { "-$it" } ?: "")
|
||||||
|
}
|
||||||
|
|
||||||
|
return compilations.groupBy { get(it) }.values.associateBy { moduleName(it) }
|
||||||
|
}
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
class GradleProjectModuleBuilder(private val addInferredSourceSetVisibilityAsExplicit: Boolean) {
|
class GradleProjectModuleBuilder(private val addInferredSourceSetVisibilityAsExplicit: Boolean) {
|
||||||
fun buildModuleFromProject(project: Project): KotlinModule? {
|
fun buildModulesFromProject(project: Project): List<KotlinModule> {
|
||||||
val extension = project.multiplatformExtensionOrNull
|
val extension = project.multiplatformExtensionOrNull
|
||||||
?: project.kotlinExtension
|
?: project.kotlinExtension
|
||||||
?: return null
|
?: return emptyList()
|
||||||
|
|
||||||
val targets = when (extension) {
|
val targets = when (extension) {
|
||||||
is KotlinMultiplatformExtension -> extension.targets.filter { it.name != "metadata" }
|
is KotlinMultiplatformExtension -> extension.targets.filter { it.name != KotlinMultiplatformPlugin.METADATA_TARGET_NAME }
|
||||||
is KotlinSingleTargetExtension -> listOf(extension.target)
|
is KotlinSingleTargetExtension -> listOf(extension.target)
|
||||||
else -> return null
|
else -> return emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
return BasicKotlinModule(LocalModuleIdentifier(project.currentBuildId().name, project.path)).apply {
|
val moduleCompilationCluster = detectModules(targets, extension.sourceSets)
|
||||||
val variantToCompilation = mutableMapOf<KotlinModuleFragment, KotlinCompilation<*>>()
|
|
||||||
|
|
||||||
targets.forEach { target ->
|
val publishedVariantsByCompilation = targets.flatMap { target ->
|
||||||
val publishedVariantsByCompilation = (target as? AbstractKotlinTarget)?.kotlinComponents.orEmpty()
|
(target as? AbstractKotlinTarget)?.kotlinComponents.orEmpty()
|
||||||
.flatMap { component -> (component as? KotlinVariant)?.usages.orEmpty() }
|
.flatMap { component -> (component as? KotlinVariant)?.usages.orEmpty() }
|
||||||
.groupBy { it.compilation }
|
}.groupBy { it.compilation }
|
||||||
|
|
||||||
target.compilations.forEach { compilation ->
|
val moduleByFragment = mutableMapOf<KotlinModuleFragment, KotlinModule>()
|
||||||
val names =
|
|
||||||
publishedVariantsByCompilation[compilation].orEmpty()
|
|
||||||
.filter { it.includeIntoProjectStructureMetadata }.map { it.name }
|
|
||||||
|
|
||||||
names.forEach { variantName ->
|
val result = moduleCompilationCluster.entries.map { (classifier, compilationsToInclude) ->
|
||||||
|
val sourceSetsToInclude = compilationsToInclude.flatMapTo(mutableSetOf()) { it.allKotlinSourceSets }
|
||||||
|
|
||||||
|
val moduleIdentifier = LocalModuleIdentifier(
|
||||||
|
project.currentBuildId().name,
|
||||||
|
project.path,
|
||||||
|
classifier.takeIf { it != KotlinCompilation.MAIN_COMPILATION_NAME }
|
||||||
|
)
|
||||||
|
|
||||||
|
BasicKotlinModule(moduleIdentifier).apply {
|
||||||
|
val variantToCompilation = mutableMapOf<BasicKotlinModuleFragment, KotlinCompilation<*>>()
|
||||||
|
|
||||||
|
compilationsToInclude.forEach { compilation ->
|
||||||
|
// A compilation may be exposed as more than one variant, so we collect all of its names
|
||||||
|
val variantNames =
|
||||||
|
publishedVariantsByCompilation[compilation]?.filter { it.includeIntoProjectStructureMetadata }?.map { it.name }
|
||||||
|
?: listOf(compilation.defaultSourceSetName)
|
||||||
|
|
||||||
|
variantNames.forEach { variantName ->
|
||||||
val variant = BasicKotlinModuleVariant(this@apply, variantName)
|
val variant = BasicKotlinModuleVariant(this@apply, variantName)
|
||||||
|
moduleByFragment[variant] = this@apply
|
||||||
variantToCompilation[variant] = compilation
|
variantToCompilation[variant] = compilation
|
||||||
fragments.add(variant)
|
fragments.add(variant)
|
||||||
|
|
||||||
@@ -104,50 +161,53 @@ class GradleProjectModuleBuilder(private val addInferredSourceSetVisibilityAsExp
|
|||||||
variant.isExported = compilation.isMain()
|
variant.isExported = compilation.isMain()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
// Once all fragments are created, add dependencies between them
|
||||||
extension.sourceSets.forEach { sourceSet ->
|
sourceSetsToInclude.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()
|
moduleByFragment[fragment] = this@apply
|
||||||
|
fragment.kotlinSourceRoots = 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 {
|
requestedDependencies(project, sourceSet, listOf(KotlinDependencyScope.API_SCOPE)).forEach {
|
||||||
val moduleDependency = it.toModuleDependency(project)
|
val moduleDependency = it.toModuleDependency(project)
|
||||||
fragment.declaredModuleDependencies.add(moduleDependency)
|
fragment.declaredModuleDependencies.add(moduleDependency)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Once all fragments are created, add dependencies between them
|
|
||||||
|
|
||||||
fragments.forEach { fragment ->
|
|
||||||
val sourceSet = extension.sourceSets.findByName(fragment.fragmentName)
|
|
||||||
?: variantToCompilation.getValue(fragment).defaultSourceSet
|
|
||||||
sourceSet.dependsOn.forEach { dependency ->
|
|
||||||
val dependencyFragment = fragmentByName(dependency.name)
|
|
||||||
fragment.directRefinesDependencies.add(dependencyFragment)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
targets.forEach { target ->
|
|
||||||
target.compilations.forEach { compilation ->
|
|
||||||
val fragment = fragmentByName(compilation.defaultSourceSetName)
|
|
||||||
compilation.associateWith.forEach { associate ->
|
|
||||||
val dependencyFragment = fragmentByName(associate.defaultSourceSetName)
|
|
||||||
fragment.declaredContainingModuleFragmentDependencies.add(dependencyFragment)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
fragments.forEach { fragment ->
|
||||||
if (addInferredSourceSetVisibilityAsExplicit) {
|
val sourceSet = extension.sourceSets.findByName(fragment.fragmentName)
|
||||||
extension.sourceSets.forEach { sourceSet ->
|
?: variantToCompilation.getValue(fragment).defaultSourceSet
|
||||||
val fragment = fragmentByName(sourceSet.name)
|
sourceSet.dependsOn.forEach { dependency ->
|
||||||
getVisibleSourceSetsFromAssociateCompilations(project, sourceSet).forEach { dependency ->
|
|
||||||
val dependencyFragment = fragmentByName(dependency.name)
|
val dependencyFragment = fragmentByName(dependency.name)
|
||||||
fragment.declaredContainingModuleFragmentDependencies.add(dependencyFragment)
|
fragment.directRefinesDependencies.add(dependencyFragment)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
fun fragmentByName(name: String) =
|
||||||
|
result.asSequence().flatMap { it.fragments.asSequence() }.first { it.fragmentName == name }
|
||||||
|
|
||||||
|
targets.flatMap { it.compilations }.forEach { compilation ->
|
||||||
|
val variant = fragmentByName(compilation.defaultSourceSetName)
|
||||||
|
compilation.associateWith.forEach { associate ->
|
||||||
|
val associateVariant = fragmentByName(associate.defaultSourceSetName)
|
||||||
|
variant.declaredModuleDependencies.add(KotlinModuleDependency(associateVariant.containingModule.moduleIdentifier))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addInferredSourceSetVisibilityAsExplicit) {
|
||||||
|
project.kotlinExtension.sourceSets.forEach { sourceSet ->
|
||||||
|
val fragment = fragmentByName(sourceSet.name)
|
||||||
|
getVisibleSourceSetsFromAssociateCompilations(project, sourceSet).forEach { dependency ->
|
||||||
|
val dependencyFragment = fragmentByName(dependency.name)
|
||||||
|
fragment.declaredModuleDependencies.add(KotlinModuleDependency(dependencyFragment.containingModule.moduleIdentifier))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
private fun <T : Any> attributeString(container: AttributeContainer, attributeKey: Attribute<T>): String {
|
private fun <T : Any> attributeString(container: AttributeContainer, attributeKey: Attribute<T>): String {
|
||||||
val value = container.getAttribute(attributeKey)
|
val value = container.getAttribute(attributeKey)
|
||||||
@@ -158,13 +218,35 @@ class GradleProjectModuleBuilder(private val addInferredSourceSetVisibilityAsExp
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun <T : Any> attributeString(container: AttributeContainer, attributeKey: Attribute<T>): String {
|
||||||
|
val value = container.getAttribute(attributeKey)
|
||||||
|
return when (value) {
|
||||||
|
is Named -> value.name
|
||||||
|
else -> value.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal fun Dependency.toModuleDependency(
|
internal fun Dependency.toModuleDependency(
|
||||||
project: Project
|
project: Project
|
||||||
) = when (val dependency = this) {
|
): KotlinModuleDependency {
|
||||||
is ProjectDependency ->
|
return KotlinModuleDependency(
|
||||||
ModuleDependency(LocalModuleIdentifier(project.currentBuildId().name, dependency.dependencyProject.path))
|
when (this) {
|
||||||
else ->
|
is ProjectDependency ->
|
||||||
ModuleDependency(MavenModuleIdentifier(dependency.group.orEmpty(), dependency.name))
|
LocalModuleIdentifier(
|
||||||
|
project.currentBuildId().name,
|
||||||
|
dependencyProject.path,
|
||||||
|
moduleClassifiersFromCapabilities(requestedCapabilities).single() // FIXME multiple capabilities
|
||||||
|
)
|
||||||
|
is ModuleDependency ->
|
||||||
|
MavenModuleIdentifier(
|
||||||
|
group.orEmpty(),
|
||||||
|
name,
|
||||||
|
moduleClassifiersFromCapabilities(requestedCapabilities).single() // FIXME multiple capabilities
|
||||||
|
)
|
||||||
|
else -> MavenModuleIdentifier(group.orEmpty(), name, null)
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun BasicKotlinModule.fragmentByName(name: String) =
|
private fun BasicKotlinModule.fragmentByName(name: String) =
|
||||||
@@ -194,7 +276,10 @@ class GradleModuleVariantResolver(val project: Project) : ModuleVariantResolver
|
|||||||
.flatMap { it.usages }
|
.flatMap { it.usages }
|
||||||
.firstOrNull { it.name == requestingVariant.fragmentName }
|
.firstOrNull { it.name == requestingVariant.fragmentName }
|
||||||
?.compilation
|
?.compilation
|
||||||
?: return VariantResolution.NotRequested(requestingVariant, dependencyModule)
|
?: targets.asSequence().flatMap { it.compilations.asSequence() }.single {
|
||||||
|
it.defaultSourceSetName == requestingVariant.fragmentName
|
||||||
|
} // TODO: generalize the mapping PM2.0 <-> MPP
|
||||||
|
?: return VariantResolution.Unknown(requestingVariant, dependencyModule)
|
||||||
|
|
||||||
val compileClasspath = project.configurations.getByName(compilation.compileDependencyConfigurationName)
|
val compileClasspath = project.configurations.getByName(compilation.compileDependencyConfigurationName)
|
||||||
|
|
||||||
|
|||||||
+78
-35
@@ -18,6 +18,8 @@ import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
|
|||||||
import org.jetbrains.kotlin.gradle.plugin.sources.KotlinDependencyScope
|
import org.jetbrains.kotlin.gradle.plugin.sources.KotlinDependencyScope
|
||||||
import org.jetbrains.kotlin.gradle.plugin.sources.sourceSetDependencyConfigurationByScope
|
import org.jetbrains.kotlin.gradle.plugin.sources.sourceSetDependencyConfigurationByScope
|
||||||
import org.jetbrains.kotlin.gradle.targets.metadata.ALL_COMPILE_METADATA_CONFIGURATION_NAME
|
import org.jetbrains.kotlin.gradle.targets.metadata.ALL_COMPILE_METADATA_CONFIGURATION_NAME
|
||||||
|
import org.jetbrains.kotlin.gradle.targets.metadata.ALL_RUNTIME_METADATA_CONFIGURATION_NAME
|
||||||
|
import org.jetbrains.kotlin.gradle.targets.metadata.dependsOnClosureWithInterCompilationDependencies
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@@ -85,7 +87,6 @@ internal class GranularMetadataTransformation(
|
|||||||
/** A list of scopes that the dependencies from [kotlinSourceSet] are treated as requested dependencies. */
|
/** A list of scopes that the dependencies from [kotlinSourceSet] are treated as requested dependencies. */
|
||||||
private val sourceSetRequestedScopes: List<KotlinDependencyScope>,
|
private val sourceSetRequestedScopes: List<KotlinDependencyScope>,
|
||||||
/** A configuration that holds the dependencies of the appropriate scope for all Kotlin source sets in the project */
|
/** A configuration that holds the dependencies of the appropriate scope for all Kotlin source sets in the project */
|
||||||
private val allSourceSetsConfiguration: Configuration,
|
|
||||||
private val parentTransformations: Lazy<Iterable<GranularMetadataTransformation>>
|
private val parentTransformations: Lazy<Iterable<GranularMetadataTransformation>>
|
||||||
) {
|
) {
|
||||||
val metadataDependencyResolutions: Iterable<MetadataDependencyResolution> by lazy { doTransform() }
|
val metadataDependencyResolutions: Iterable<MetadataDependencyResolution> by lazy { doTransform() }
|
||||||
@@ -98,40 +99,14 @@ internal class GranularMetadataTransformation(
|
|||||||
)
|
)
|
||||||
|
|
||||||
private val requestedDependencies: Iterable<Dependency> by lazy {
|
private val requestedDependencies: Iterable<Dependency> by lazy {
|
||||||
fun collectScopedDependenciesFromSourceSet(sourceSet: KotlinSourceSet): Set<Dependency> =
|
requestedDependencies(project, kotlinSourceSet, sourceSetRequestedScopes)
|
||||||
sourceSetRequestedScopes.flatMapTo(mutableSetOf()) { scope ->
|
|
||||||
project.sourceSetDependencyConfigurationByScope(sourceSet, scope).incoming.dependencies
|
|
||||||
}
|
|
||||||
|
|
||||||
val ownDependencies = collectScopedDependenciesFromSourceSet(kotlinSourceSet)
|
|
||||||
val parentDependencies = parentTransformations.value.flatMapTo(mutableSetOf<Dependency>()) { it.requestedDependencies }
|
|
||||||
|
|
||||||
ownDependencies + parentDependencies
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val allSourceSetsConfiguration: Configuration =
|
||||||
|
commonMetadataDependenciesConfigurationForScopes(project, sourceSetRequestedScopes)
|
||||||
|
|
||||||
private val configurationToResolve: Configuration by lazy {
|
private val configurationToResolve: Configuration by lazy {
|
||||||
/** If [kotlinSourceSet] is not a published source set, its dependencies are not included in [allSourceSetsConfiguration].
|
resolvableMetadataConfiguration(project, allSourceSetsConfiguration, requestedDependencies)
|
||||||
* In that case, to resolve the dependencies of the source set in a way that is consistent with the published source sets,
|
|
||||||
* we need to create a new configuration with the dependencies from both [allSourceSetsConfiguration] and the
|
|
||||||
* input configuration(s) of the source set. */
|
|
||||||
var modifiedConfiguration: Configuration? = null
|
|
||||||
|
|
||||||
val originalDependencies = allSourceSetsConfiguration.allDependencies
|
|
||||||
|
|
||||||
requestedDependencies.forEach { dependency ->
|
|
||||||
if (dependency !in originalDependencies) {
|
|
||||||
modifiedConfiguration = modifiedConfiguration ?: project.configurations.detachedConfiguration().apply {
|
|
||||||
fun <T> copyAttribute(key: Attribute<T>) {
|
|
||||||
attributes.attribute(key, allSourceSetsConfiguration.attributes.getAttribute(key)!!)
|
|
||||||
}
|
|
||||||
allSourceSetsConfiguration.attributes.keySet().forEach { copyAttribute(it) }
|
|
||||||
allSourceSetsConfiguration.extendsFrom.forEach { extendsFrom(it) }
|
|
||||||
}
|
|
||||||
modifiedConfiguration!!.dependencies.add(dependency)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
modifiedConfiguration ?: allSourceSetsConfiguration
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun doTransform(): Iterable<MetadataDependencyResolution> {
|
private fun doTransform(): Iterable<MetadataDependencyResolution> {
|
||||||
@@ -229,7 +204,7 @@ internal class GranularMetadataTransformation(
|
|||||||
project,
|
project,
|
||||||
module,
|
module,
|
||||||
configurationToResolve,
|
configurationToResolve,
|
||||||
resolveViaAvailableAt = false // we will process this dependency later in the queue
|
resolveViaAvailableAt = false // we will process the available-at module as a dependency later in the queue
|
||||||
)
|
)
|
||||||
|
|
||||||
val resolvedToProject: Project? = (mppDependencyMetadataExtractor as? ProjectMppDependencyMetadataExtractor)?.dependencyProject
|
val resolvedToProject: Project? = (mppDependencyMetadataExtractor as? ProjectMppDependencyMetadataExtractor)?.dependencyProject
|
||||||
@@ -301,6 +276,75 @@ internal class GranularMetadataTransformation(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun resolvableMetadataConfiguration(
|
||||||
|
project: Project,
|
||||||
|
sourceSets: Iterable<KotlinSourceSet>,
|
||||||
|
scopes: Iterable<KotlinDependencyScope>
|
||||||
|
) = resolvableMetadataConfiguration(
|
||||||
|
project,
|
||||||
|
commonMetadataDependenciesConfigurationForScopes(project, scopes),
|
||||||
|
sourceSets.flatMapTo(mutableListOf()) { requestedDependencies(project, it, scopes) }
|
||||||
|
)
|
||||||
|
|
||||||
|
/** If a source set is not a published source set, its dependencies are not included in [allSourceSetsConfiguration].
|
||||||
|
* In that case, to resolve the dependencies of the source set in a way that is consistent with the published source sets,
|
||||||
|
* we need to create a new configuration with the dependencies from both [allSourceSetsConfiguration] and the
|
||||||
|
* other [requestedDependencies] */
|
||||||
|
// TODO: optimize by caching the resulting configurations?
|
||||||
|
internal fun resolvableMetadataConfiguration(
|
||||||
|
project: Project,
|
||||||
|
allSourceSetsConfiguration: Configuration,
|
||||||
|
requestedDependencies: Iterable<Dependency>
|
||||||
|
): Configuration {
|
||||||
|
var modifiedConfiguration: Configuration? = null
|
||||||
|
|
||||||
|
val originalDependencies = allSourceSetsConfiguration.allDependencies
|
||||||
|
|
||||||
|
requestedDependencies.forEach { dependency ->
|
||||||
|
if (dependency !in originalDependencies) {
|
||||||
|
modifiedConfiguration = modifiedConfiguration ?: project.configurations.detachedConfiguration().apply {
|
||||||
|
fun <T> copyAttribute(key: Attribute<T>) {
|
||||||
|
attributes.attribute(key, allSourceSetsConfiguration.attributes.getAttribute(key)!!)
|
||||||
|
}
|
||||||
|
allSourceSetsConfiguration.attributes.keySet().forEach { copyAttribute(it) }
|
||||||
|
allSourceSetsConfiguration.extendsFrom.forEach { extendsFrom(it) }
|
||||||
|
dependencies.addAll(originalDependencies) // TODO: check if this line is redundant
|
||||||
|
}
|
||||||
|
modifiedConfiguration!!.dependencies.add(dependency)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return modifiedConfiguration ?: allSourceSetsConfiguration
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The configuration that contains the dependencies of the corresponding scopes (and maybe others)
|
||||||
|
* from all published source sets. */
|
||||||
|
internal fun commonMetadataDependenciesConfigurationForScopes(
|
||||||
|
project: Project,
|
||||||
|
scopes: Iterable<KotlinDependencyScope>
|
||||||
|
): Configuration {
|
||||||
|
// TODO: what if 'runtimeOnly' is combined with 'compileOnly'? prohibit this or merge the two? we never do that now, though
|
||||||
|
val configurationName = if (KotlinDependencyScope.RUNTIME_ONLY_SCOPE in scopes)
|
||||||
|
ALL_RUNTIME_METADATA_CONFIGURATION_NAME
|
||||||
|
else
|
||||||
|
ALL_COMPILE_METADATA_CONFIGURATION_NAME
|
||||||
|
return project.configurations.getByName(configurationName)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun requestedDependencies(
|
||||||
|
project: Project,
|
||||||
|
sourceSet: KotlinSourceSet,
|
||||||
|
requestedScopes: Iterable<KotlinDependencyScope>
|
||||||
|
): Iterable<Dependency> {
|
||||||
|
fun collectScopedDependenciesFromSourceSet(sourceSet: KotlinSourceSet): Set<Dependency> =
|
||||||
|
requestedScopes.flatMapTo(mutableSetOf()) { scope ->
|
||||||
|
project.sourceSetDependencyConfigurationByScope(sourceSet, scope).incoming.dependencies
|
||||||
|
}
|
||||||
|
|
||||||
|
val otherContributingSourceSets = dependsOnClosureWithInterCompilationDependencies(project, sourceSet)
|
||||||
|
return listOf(sourceSet, *otherContributingSourceSets.toTypedArray()).flatMap(::collectScopedDependenciesFromSourceSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private abstract class MppDependencyMetadataExtractor(val project: Project, val dependency: ResolvedComponentResult) {
|
private abstract class MppDependencyMetadataExtractor(val project: Project, val dependency: ResolvedComponentResult) {
|
||||||
abstract fun getProjectStructureMetadata(): KotlinProjectStructureMetadata?
|
abstract fun getProjectStructureMetadata(): KotlinProjectStructureMetadata?
|
||||||
|
|
||||||
@@ -343,7 +387,7 @@ private class IncludedBuildMetadataExtractor(
|
|||||||
init {
|
init {
|
||||||
val id = dependency.id
|
val id = dependency.id
|
||||||
require(id is ProjectComponentIdentifier) { "dependency should resolve to a project" }
|
require(id is ProjectComponentIdentifier) { "dependency should resolve to a project" }
|
||||||
require(!(id as ProjectComponentIdentifier).build.isCurrentBuild) { "should be a project from an included build" }
|
require(!id.build.isCurrentBuild) { "should be a project from an included build" }
|
||||||
this.id = id
|
this.id = id
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -493,7 +537,6 @@ internal fun getProjectStructureMetadata(
|
|||||||
module: ResolvedComponentResult,
|
module: ResolvedComponentResult,
|
||||||
configuration: Configuration
|
configuration: Configuration
|
||||||
): KotlinProjectStructureMetadata? {
|
): KotlinProjectStructureMetadata? {
|
||||||
// FIXME test dependencies won't work
|
|
||||||
val extractor = getMetadataExtractor(project, module, configuration, resolveViaAvailableAt = true)
|
val extractor = getMetadataExtractor(project, module, configuration, resolveViaAvailableAt = true)
|
||||||
return extractor?.getProjectStructureMetadata()
|
return extractor?.getProjectStructureMetadata()
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-8
@@ -17,7 +17,9 @@ import org.jetbrains.kotlin.gradle.dsl.multiplatformExtensionOrNull
|
|||||||
import org.jetbrains.kotlin.gradle.plugin.sources.KotlinDependencyScope
|
import org.jetbrains.kotlin.gradle.plugin.sources.KotlinDependencyScope
|
||||||
import org.jetbrains.kotlin.gradle.plugin.sources.withAllDependsOnSourceSets
|
import org.jetbrains.kotlin.gradle.plugin.sources.withAllDependsOnSourceSets
|
||||||
import org.jetbrains.kotlin.gradle.plugin.sources.sourceSetDependencyConfigurationByScope
|
import org.jetbrains.kotlin.gradle.plugin.sources.sourceSetDependencyConfigurationByScope
|
||||||
|
import org.jetbrains.kotlin.gradle.targets.metadata.dependsOnClosureWithInterCompilationDependencies
|
||||||
import org.jetbrains.kotlin.gradle.targets.metadata.getPublishedPlatformCompilations
|
import org.jetbrains.kotlin.gradle.targets.metadata.getPublishedPlatformCompilations
|
||||||
|
import org.jetbrains.kotlin.gradle.targets.metadata.isSharedNativeSourceSet
|
||||||
import org.w3c.dom.Document
|
import org.w3c.dom.Document
|
||||||
import org.w3c.dom.Element
|
import org.w3c.dom.Element
|
||||||
import org.w3c.dom.Node
|
import org.w3c.dom.Node
|
||||||
@@ -114,14 +116,19 @@ internal fun buildKotlinProjectStructureMetadata(project: Project): KotlinProjec
|
|||||||
* published as API dependencies of the metadata module to get into the resolution result, see
|
* published as API dependencies of the metadata module to get into the resolution result, see
|
||||||
* [KotlinMetadataTargetConfigurator.exportDependenciesForPublishing].
|
* [KotlinMetadataTargetConfigurator.exportDependenciesForPublishing].
|
||||||
*/
|
*/
|
||||||
val isNativeSharedSourceSet = sourceSetsWithMetadataCompilations[sourceSet] is KotlinSharedNativeCompilation
|
val isNativeSharedSourceSet = isSharedNativeSourceSet(project, sourceSet)
|
||||||
val sourceSetExportedDependencies = when {
|
val scopes = listOfNotNull(
|
||||||
isNativeSharedSourceSet -> sourceSet.withAllDependsOnSourceSets().flatMap { hierarchySourceSet ->
|
KotlinDependencyScope.API_SCOPE,
|
||||||
listOf(KotlinDependencyScope.API_SCOPE, KotlinDependencyScope.IMPLEMENTATION_SCOPE).flatMap { scope ->
|
KotlinDependencyScope.IMPLEMENTATION_SCOPE.takeIf { isNativeSharedSourceSet }
|
||||||
project.sourceSetDependencyConfigurationByScope(hierarchySourceSet, scope).allDependencies.toList()
|
)
|
||||||
}
|
val sourceSetsToIncludeDependencies =
|
||||||
}.distinct()
|
if (isNativeSharedSourceSet)
|
||||||
else -> project.configurations.getByName(sourceSet.apiConfigurationName).allDependencies
|
dependsOnClosureWithInterCompilationDependencies(project, sourceSet).plus(sourceSet)
|
||||||
|
else listOf(sourceSet)
|
||||||
|
val sourceSetExportedDependencies = scopes.flatMap { scope ->
|
||||||
|
sourceSetsToIncludeDependencies.flatMap { hierarchySourceSet ->
|
||||||
|
project.sourceSetDependencyConfigurationByScope(hierarchySourceSet, scope).allDependencies.toList()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
sourceSet.name to sourceSetExportedDependencies.map { ModuleIds.fromDependency(it) }.toSet()
|
sourceSet.name to sourceSetExportedDependencies.map { ModuleIds.fromDependency(it) }.toSet()
|
||||||
},
|
},
|
||||||
|
|||||||
+3
-3
@@ -15,6 +15,7 @@ import org.jetbrains.kotlin.gradle.plugin.sources.KotlinDependencyScope.*
|
|||||||
import org.jetbrains.kotlin.gradle.plugin.sources.withAllDependsOnSourceSets
|
import org.jetbrains.kotlin.gradle.plugin.sources.withAllDependsOnSourceSets
|
||||||
import org.jetbrains.kotlin.gradle.targets.metadata.ALL_COMPILE_METADATA_CONFIGURATION_NAME
|
import org.jetbrains.kotlin.gradle.targets.metadata.ALL_COMPILE_METADATA_CONFIGURATION_NAME
|
||||||
import org.jetbrains.kotlin.gradle.targets.metadata.KotlinMetadataTargetConfigurator
|
import org.jetbrains.kotlin.gradle.targets.metadata.KotlinMetadataTargetConfigurator
|
||||||
|
import org.jetbrains.kotlin.gradle.targets.metadata.dependsOnClosureWithInterCompilationDependencies
|
||||||
import org.jetbrains.kotlin.gradle.utils.getValue
|
import org.jetbrains.kotlin.gradle.utils.getValue
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@@ -77,9 +78,8 @@ open class TransformKotlinGranularMetadata
|
|||||||
project,
|
project,
|
||||||
kotlinSourceSet,
|
kotlinSourceSet,
|
||||||
listOf(API_SCOPE, IMPLEMENTATION_SCOPE, COMPILE_ONLY_SCOPE),
|
listOf(API_SCOPE, IMPLEMENTATION_SCOPE, COMPILE_ONLY_SCOPE),
|
||||||
project.configurations.getByName(ALL_COMPILE_METADATA_CONFIGURATION_NAME),
|
|
||||||
lazy {
|
lazy {
|
||||||
KotlinMetadataTargetConfigurator.dependsOnWithInterCompilationDependencies(project, kotlinSourceSet).map {
|
dependsOnClosureWithInterCompilationDependencies(project, kotlinSourceSet).map {
|
||||||
project.tasks.withType(TransformKotlinGranularMetadata::class.java)
|
project.tasks.withType(TransformKotlinGranularMetadata::class.java)
|
||||||
.getByName(KotlinMetadataTargetConfigurator.transformGranularMetadataTaskName(it.name))
|
.getByName(KotlinMetadataTargetConfigurator.transformGranularMetadataTaskName(it.name))
|
||||||
.transformation
|
.transformation
|
||||||
@@ -97,7 +97,7 @@ open class TransformKotlinGranularMetadata
|
|||||||
private val extractableFilesByResolution: Map<out MetadataDependencyResolution, ExtractableMetadataFiles>
|
private val extractableFilesByResolution: Map<out MetadataDependencyResolution, ExtractableMetadataFiles>
|
||||||
get() = metadataDependencyResolutions
|
get() = metadataDependencyResolutions
|
||||||
.filterIsInstance<MetadataDependencyResolution.ChooseVisibleSourceSets>()
|
.filterIsInstance<MetadataDependencyResolution.ChooseVisibleSourceSets>()
|
||||||
.associate { it to it.getExtractableMetadataFiles(outputsDir) }
|
.associateWith { it.getExtractableMetadataFiles(outputsDir) }
|
||||||
|
|
||||||
@get:Internal
|
@get:Internal
|
||||||
internal val filesByResolution: Map<out MetadataDependencyResolution, FileCollection>
|
internal val filesByResolution: Map<out MetadataDependencyResolution, FileCollection>
|
||||||
|
|||||||
+4
@@ -338,6 +338,10 @@ internal object CompilationSourceSetUtil {
|
|||||||
return ext.get(EXT_NAME) as Property<CompilationsBySourceSet>
|
return ext.get(EXT_NAME) as Property<CompilationsBySourceSet>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: the results include the compilations of the metadata target; the callers should care about filtering them out
|
||||||
|
// if they need only the platform compilations
|
||||||
|
// TODO: create a separate util function: `platformCompilationsBySourceSets`
|
||||||
|
// TODO: visit all call sites, check if they handle the metadata compilations correctly
|
||||||
fun compilationsBySourceSets(project: Project): CompilationsBySourceSet {
|
fun compilationsBySourceSets(project: Project): CompilationsBySourceSet {
|
||||||
val compilationNamesBySourceSetName = getOrCreateProperty(project) {
|
val compilationNamesBySourceSetName = getOrCreateProperty(project) {
|
||||||
var shouldFinalizeValue = false
|
var shouldFinalizeValue = false
|
||||||
|
|||||||
+67
@@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2021 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.plugin.mpp.pm20
|
||||||
|
|
||||||
|
import org.gradle.api.Project
|
||||||
|
import org.gradle.api.artifacts.Configuration
|
||||||
|
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.resolvableMetadataConfiguration
|
||||||
|
import org.jetbrains.kotlin.gradle.plugin.sources.KotlinDependencyScope
|
||||||
|
import org.jetbrains.kotlin.project.model.*
|
||||||
|
|
||||||
|
class GradleKotlinDependencyGraphResolver(
|
||||||
|
private val project: Project,
|
||||||
|
private val moduleResolver: ModuleDependencyResolver,
|
||||||
|
private val fragmentsResolver: ModuleFragmentsResolver
|
||||||
|
) : KotlinDependencyGraphResolver {
|
||||||
|
|
||||||
|
private val configurationToResolve: Configuration
|
||||||
|
get() = resolvableMetadataConfiguration(
|
||||||
|
project,
|
||||||
|
project.kotlinExtension.sourceSets, // take dependencies from all source sets; TODO introduce consistency scopes?
|
||||||
|
KotlinDependencyScope.compileScopes
|
||||||
|
)
|
||||||
|
|
||||||
|
override fun resolveDependencyGraph(requestingModule: KotlinModule): DependencyGraphResolution {
|
||||||
|
if (!requestingModule.representsProject(project))
|
||||||
|
return DependencyGraphResolution.Unknown(requestingModule)
|
||||||
|
|
||||||
|
val excludeLegacyMetadataModulesFromResult = mutableSetOf<ResolvedComponentResult>()
|
||||||
|
|
||||||
|
val nodeByModuleId = mutableMapOf<KotlinModuleIdentifier, DependencyGraphNode>()
|
||||||
|
|
||||||
|
fun nodeFromComponent(component: ResolvedComponentResult, isRoot: Boolean /*refactor*/): DependencyGraphNode {
|
||||||
|
val id = component.toModuleIdentifier()
|
||||||
|
return nodeByModuleId.getOrPut(id) {
|
||||||
|
val module = if (isRoot)
|
||||||
|
requestingModule
|
||||||
|
else moduleResolver.resolveDependency(component.toModuleDependency())
|
||||||
|
.takeIf { component !in excludeLegacyMetadataModulesFromResult }
|
||||||
|
?: buildStubModule(component, component.variants.singleOrNull()?.displayName ?: "default")
|
||||||
|
|
||||||
|
val resolvedComponentDependencies = component.dependencies
|
||||||
|
.filterIsInstance<ResolvedDependencyResult>()
|
||||||
|
.flatMap { dependency -> dependency.requested.toModuleIdentifiers().map { id -> id to dependency.selected } }
|
||||||
|
.toMap()
|
||||||
|
|
||||||
|
val fragmentDependencies = module.fragments.associateWith { it.declaredModuleDependencies }
|
||||||
|
|
||||||
|
val nodeDependenciesMap = fragmentDependencies.mapValues { (_, deps) ->
|
||||||
|
deps.mapNotNull { resolvedComponentDependencies[it.moduleIdentifier] }.map { nodeFromComponent(it, isRoot = false) }
|
||||||
|
}
|
||||||
|
|
||||||
|
DependencyGraphNode(module, nodeDependenciesMap)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return DependencyGraphResolution.DependencyGraph(
|
||||||
|
requestingModule,
|
||||||
|
nodeFromComponent(configurationToResolve.incoming.resolutionResult.root, isRoot = true)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
+114
-54
@@ -7,16 +7,17 @@ package org.jetbrains.kotlin.gradle.plugin.mpp.pm20
|
|||||||
|
|
||||||
import org.gradle.api.Project
|
import org.gradle.api.Project
|
||||||
import org.gradle.api.artifacts.Configuration
|
import org.gradle.api.artifacts.Configuration
|
||||||
import org.gradle.api.artifacts.component.ComponentIdentifier
|
import org.gradle.api.artifacts.component.*
|
||||||
import org.gradle.api.artifacts.component.ModuleComponentIdentifier
|
|
||||||
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.gradle.api.capabilities.Capability
|
||||||
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
|
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
|
||||||
|
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
|
||||||
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
|
||||||
import org.jetbrains.kotlin.gradle.targets.metadata.ALL_COMPILE_METADATA_CONFIGURATION_NAME
|
import org.jetbrains.kotlin.gradle.plugin.mpp.resolvableMetadataConfiguration
|
||||||
|
import org.jetbrains.kotlin.gradle.plugin.sources.KotlinDependencyScope
|
||||||
import org.jetbrains.kotlin.project.model.*
|
import org.jetbrains.kotlin.project.model.*
|
||||||
import java.util.ArrayDeque
|
import java.util.ArrayDeque
|
||||||
|
|
||||||
@@ -26,21 +27,29 @@ class GradleModuleDependencyResolver(
|
|||||||
private val projectModuleBuilder: GradleProjectModuleBuilder
|
private val projectModuleBuilder: GradleProjectModuleBuilder
|
||||||
) : ModuleDependencyResolver {
|
) : ModuleDependencyResolver {
|
||||||
|
|
||||||
// FIXME this wont work for tests, as their dependencies are currently not in the all compile dependencies configuration
|
|
||||||
private val configurationToResolve: Configuration
|
private val configurationToResolve: Configuration
|
||||||
get() = project.configurations.getByName(ALL_COMPILE_METADATA_CONFIGURATION_NAME)
|
get() = resolvableMetadataConfiguration(
|
||||||
|
project,
|
||||||
|
project.kotlinExtension.sourceSets, // take dependencies from all source sets; TODO introduce consistency scopes?
|
||||||
|
listOf(KotlinDependencyScope.API_SCOPE, KotlinDependencyScope.IMPLEMENTATION_SCOPE, KotlinDependencyScope.COMPILE_ONLY_SCOPE)
|
||||||
|
)
|
||||||
|
|
||||||
override fun resolveDependency(moduleDependency: ModuleDependency): KotlinModule? {
|
override fun resolveDependency(moduleDependency: KotlinModuleDependency): KotlinModule? {
|
||||||
val allComponents = configurationToResolve.incoming.resolutionResult.allComponents
|
val allComponents = configurationToResolve.incoming.resolutionResult.allComponents
|
||||||
// TODO: optimize O(n) search, store the resolved components with dependency keys
|
// TODO: optimize O(n) search, store the resolved components with dependency keys?
|
||||||
val component = allComponents.find { it.id.matchesModuleDependency(moduleDependency) }
|
val component = allComponents.find { it.id.matchesModuleDependency(moduleDependency) }
|
||||||
val id = component?.id
|
val id = component?.id
|
||||||
|
|
||||||
|
//FIXME multiple?
|
||||||
|
val classifier = moduleClassifiersFromCapabilities(component?.variants?.flatMap { it.capabilities }.orEmpty()).single()
|
||||||
|
|
||||||
return when {
|
return when {
|
||||||
id is ProjectComponentIdentifier && id.build.isCurrentBuild ->
|
id is ProjectComponentIdentifier && id.build.isCurrentBuild ->
|
||||||
projectModuleBuilder.buildModuleFromProject(project.project(id.projectPath))
|
projectModuleBuilder.buildModulesFromProject(project.project(id.projectPath))
|
||||||
|
.find { it.moduleIdentifier.moduleClassifier == classifier }
|
||||||
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.toModuleIdentifier(), metadata)
|
projectStructureMetadataModuleBuilder.getModule(id.toModuleIdentifier(classifier), metadata)
|
||||||
}
|
}
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
@@ -52,7 +61,7 @@ class GradleProjectDependencyDiscovery(
|
|||||||
private val variantDependencyDiscovery: VariantDependencyDiscovery,
|
private val variantDependencyDiscovery: VariantDependencyDiscovery,
|
||||||
private val fragmentDependenciesDiscovery: FragmentDependenciesDiscovery
|
private val fragmentDependenciesDiscovery: FragmentDependenciesDiscovery
|
||||||
) : DependencyDiscovery {
|
) : DependencyDiscovery {
|
||||||
override fun discoverDependencies(fragment: KotlinModuleFragment): Iterable<ModuleDependency> {
|
override fun discoverDependencies(fragment: KotlinModuleFragment): Iterable<KotlinModuleDependency> {
|
||||||
require(fragment.containingModule.representsProject(project))
|
require(fragment.containingModule.representsProject(project))
|
||||||
return when (fragment) {
|
return when (fragment) {
|
||||||
is KotlinModuleVariant -> variantDependencyDiscovery.discoverDependencies(fragment)
|
is KotlinModuleVariant -> variantDependencyDiscovery.discoverDependencies(fragment)
|
||||||
@@ -63,63 +72,88 @@ class GradleProjectDependencyDiscovery(
|
|||||||
|
|
||||||
class FragmentDependenciesDiscovery(
|
class FragmentDependenciesDiscovery(
|
||||||
private val project: Project,
|
private val project: Project,
|
||||||
private val expansion: InternalDependencyExpansion,
|
|
||||||
private val moduleResolver: ModuleDependencyResolver,
|
private val moduleResolver: ModuleDependencyResolver,
|
||||||
private val fragmentsResolver: KotlinModuleFragmentsResolver
|
private val fragmentsResolver: ModuleFragmentsResolver
|
||||||
) : DependencyDiscovery {
|
) : DependencyDiscovery {
|
||||||
override fun discoverDependencies(fragment: KotlinModuleFragment): Iterable<ModuleDependency> {
|
private val dependencyScopes = listOf(KotlinDependencyScope.API_SCOPE, KotlinDependencyScope.IMPLEMENTATION_SCOPE)
|
||||||
|
|
||||||
|
private fun KotlinModuleFragment.toSourceSet(): KotlinSourceSet = project.kotlinExtension.sourceSets.getByName(fragmentName)
|
||||||
|
|
||||||
|
// TODO: use the dependency graph resolution implementation?
|
||||||
|
override fun discoverDependencies(fragment: KotlinModuleFragment): Iterable<KotlinModuleDependency> {
|
||||||
require(fragment.containingModule.representsProject(project))
|
require(fragment.containingModule.representsProject(project))
|
||||||
|
|
||||||
val requested = expansion.expandInternalFragmentDependencies(fragment).visibleFragments().plus(fragment.refinesClosure)
|
val fragmentsWhoseDependenciesAreVisible = fragment.refinesClosure
|
||||||
.flatMap { it.declaredModuleDependencies }
|
|
||||||
.toSet()
|
|
||||||
|
|
||||||
// FIXME: test dependencies won't work here
|
// FIXME use dependencyScopes, not just sets of declared module dependencies
|
||||||
val allComponents =
|
val requestedDependencies = fragmentsWhoseDependenciesAreVisible.flatMap { it.declaredModuleDependencies }.toSet()
|
||||||
project.configurations.getByName(ALL_COMPILE_METADATA_CONFIGURATION_NAME).incoming.resolutionResult.allComponents
|
|
||||||
|
|
||||||
val componentByModuleDependency = allComponents.associateBy { it.toModuleDependency() }
|
val configurationToResolve = resolvableMetadataConfiguration(
|
||||||
|
project,
|
||||||
|
fragmentsWhoseDependenciesAreVisible.map { it.toSourceSet() },
|
||||||
|
KotlinDependencyScope.compileScopes
|
||||||
|
)
|
||||||
|
val allComponents = configurationToResolve.incoming.resolutionResult.allComponents
|
||||||
|
|
||||||
|
val componentsByRequestedDependency =
|
||||||
|
allComponents.flatMap { component -> component.dependents.map { it to component } }.toMap()
|
||||||
|
.mapKeys { it.key.requested.toModuleDependency() }
|
||||||
|
|
||||||
val resolvedComponentQueue = ArrayDeque<ResolvedComponentResult>().apply {
|
val resolvedComponentQueue = ArrayDeque<ResolvedComponentResult>().apply {
|
||||||
addAll(componentByModuleDependency.filterKeys { it in requested }.values)
|
addAll(componentsByRequestedDependency.filterKeys { it in requestedDependencies }.values)
|
||||||
}
|
}
|
||||||
|
|
||||||
val visited = mutableSetOf<ResolvedComponentResult>()
|
val visited = mutableSetOf<ResolvedComponentResult>()
|
||||||
|
val excludeLegacyMetadataModulesFromResult = mutableSetOf<ResolvedComponentResult>()
|
||||||
|
|
||||||
while (resolvedComponentQueue.isNotEmpty()) {
|
while (resolvedComponentQueue.isNotEmpty()) {
|
||||||
val component = resolvedComponentQueue.removeFirst()
|
val component = resolvedComponentQueue.removeFirst()
|
||||||
visited.add(component)
|
visited.add(component)
|
||||||
|
|
||||||
val module = moduleResolver.resolveDependency(component.toModuleDependency())
|
val module = moduleResolver.resolveDependency(component.toModuleDependency())
|
||||||
|
.takeIf { component !in excludeLegacyMetadataModulesFromResult }
|
||||||
?: buildStubModule(component, component.variants.singleOrNull()?.displayName ?: "default")
|
?: buildStubModule(component, component.variants.singleOrNull()?.displayName ?: "default")
|
||||||
|
|
||||||
val newTransitiveDeps = when (val fragmentsResolution = fragmentsResolver.getChosenFragments(fragment, module)) {
|
val transitiveDepsToVisit = when (val fragmentsResolution = fragmentsResolver.getChosenFragments(fragment, module)) {
|
||||||
is FragmentResolution.ChosenFragments ->
|
is FragmentResolution.ChosenFragments ->
|
||||||
fragmentsResolution.visibleFragments.flatMap { it.declaredModuleDependencies }.mapNotNull { dependency ->
|
fragmentsResolution.visibleFragments.flatMap { it.declaredModuleDependencies }.mapNotNull { dependency ->
|
||||||
componentByModuleDependency[dependency].takeIf { component -> component !in visited }
|
componentsByRequestedDependency[dependency]
|
||||||
}
|
}
|
||||||
else -> emptyList()
|
else -> emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val newTransitiveDeps = transitiveDepsToVisit.filterTo(mutableListOf()) { it !in visited }
|
||||||
|
|
||||||
|
// With legacy publication scheme, the root MPP module may have a single 'dependency' (available-at) to the metadata module
|
||||||
|
val isMetadataModulePublishedSeparately =
|
||||||
|
getProjectStructureMetadata(project, component, configurationToResolve)
|
||||||
|
?.let { !it.isPublishedAsRoot }
|
||||||
|
?: false
|
||||||
|
val singleDependencyComponentOrNull = (component.dependencies.singleOrNull() as? ResolvedDependencyResult)?.selected
|
||||||
|
if (isMetadataModulePublishedSeparately && singleDependencyComponentOrNull != null) {
|
||||||
|
newTransitiveDeps.add(singleDependencyComponentOrNull)
|
||||||
|
excludeLegacyMetadataModulesFromResult.add(singleDependencyComponentOrNull)
|
||||||
|
}
|
||||||
|
|
||||||
resolvedComponentQueue.addAll(newTransitiveDeps)
|
resolvedComponentQueue.addAll(newTransitiveDeps)
|
||||||
}
|
}
|
||||||
|
|
||||||
return visited.map { it.toModuleDependency() }
|
return (visited - excludeLegacyMetadataModulesFromResult).map { it.toModuleDependency() }
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// refactor extract to a separate class
|
// refactor extract to a separate class/interface
|
||||||
// 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 {
|
internal fun buildStubModule(resolvedComponentResult: ResolvedComponentResult, singleVariantName: String): KotlinModule {
|
||||||
val moduleDependency = resolvedComponentResult.toModuleDependency()
|
val moduleDependency = resolvedComponentResult.toModuleDependency()
|
||||||
return BasicKotlinModule(moduleDependency.moduleIdentifier).apply {
|
return BasicKotlinModule(moduleDependency.moduleIdentifier).apply {
|
||||||
BasicKotlinModuleVariant(this@apply, singleVariantName).apply {
|
BasicKotlinModuleVariant(this@apply, singleVariantName).apply {
|
||||||
fragments.add(this)
|
fragments.add(this)
|
||||||
this.declaredModuleDependencies.addAll(
|
this.declaredModuleDependencies.addAll(
|
||||||
resolvedComponentResult.dependencies
|
resolvedComponentResult.dependencies
|
||||||
.filterIsInstance<ResolvedDependencyResult>()
|
.filterIsInstance<ResolvedDependencyResult>()
|
||||||
.map { it.selected.toModuleDependency() }
|
.map { it.selected.toModuleDependency() }
|
||||||
)
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -142,24 +176,28 @@ class VariantDependencyDiscovery(
|
|||||||
return configuration.incoming.resolutionResult.allComponents
|
return configuration.incoming.resolutionResult.allComponents
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun discoverDependencies(fragment: KotlinModuleFragment): Iterable<ModuleDependency> {
|
override fun discoverDependencies(fragment: KotlinModuleFragment): Iterable<KotlinModuleDependency> {
|
||||||
require(fragment is KotlinModuleVariant)
|
require(fragment is KotlinModuleVariant)
|
||||||
require(fragment.containingModule.representsProject(project))
|
require(fragment.containingModule.representsProject(project))
|
||||||
|
|
||||||
val components = resolvedComponentResults(fragment)
|
val components = resolvedComponentResults(fragment)
|
||||||
return components.mapNotNull { component ->
|
return components.flatMap { component ->
|
||||||
val moduleIdentifier = when (val id = component.id) {
|
val classifiers = moduleClassifiersFromCapabilities(component.variants.flatMap { it.capabilities })
|
||||||
is ProjectComponentIdentifier -> LocalModuleIdentifier(id.build.name, id.projectPath)
|
when (val id = component.id) {
|
||||||
is ModuleComponentIdentifier -> id.toModuleIdentifier()
|
is ProjectComponentIdentifier -> {
|
||||||
else -> return@mapNotNull null // TODO check that no other options are possible, throw errors?
|
classifiers.map { LocalModuleIdentifier(id.build.name, id.projectPath, it) }
|
||||||
|
}
|
||||||
|
is ModuleComponentIdentifier -> {
|
||||||
|
classifiers.map { id.toModuleIdentifier(it) }
|
||||||
|
}
|
||||||
|
else -> return@flatMap emptyList<KotlinModuleIdentifier>() // TODO check that no other options are possible, throw errors?
|
||||||
}
|
}
|
||||||
ModuleDependency(moduleIdentifier)
|
}.map(::KotlinModuleDependency)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun ModuleComponentIdentifier.toModuleIdentifier(): MavenModuleIdentifier =
|
private fun ModuleComponentIdentifier.toModuleIdentifier(classifier: String? = null): MavenModuleIdentifier =
|
||||||
MavenModuleIdentifier(moduleIdentifier.group, moduleIdentifier.name)
|
MavenModuleIdentifier(moduleIdentifier.group, moduleIdentifier.name, classifier)
|
||||||
|
|
||||||
internal fun ComponentIdentifier.matchesModule(module: KotlinModule): Boolean {
|
internal fun ComponentIdentifier.matchesModule(module: KotlinModule): Boolean {
|
||||||
return when (val moduleId = module.moduleIdentifier) {
|
return when (val moduleId = module.moduleIdentifier) {
|
||||||
@@ -175,15 +213,37 @@ internal fun ComponentIdentifier.matchesModule(module: KotlinModule): Boolean {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun ResolvedComponentResult.toModuleDependency(): ModuleDependency = ModuleDependency(
|
internal fun ResolvedComponentResult.toModuleIdentifier(): KotlinModuleIdentifier {
|
||||||
when (val id = id) {
|
val capabilities = variants.flatMap { it.capabilities } // expected to be disjoint
|
||||||
is ProjectComponentIdentifier -> LocalModuleIdentifier(id.build.name, id.projectPath)
|
val moduleClassifier = moduleClassifiersFromCapabilities(capabilities).single() // FIXME handle multiple capabilities
|
||||||
|
return when (val id = id) {
|
||||||
|
is ProjectComponentIdentifier -> LocalModuleIdentifier(id.build.name, id.projectPath, moduleClassifier)
|
||||||
is ModuleComponentIdentifier -> id.toModuleIdentifier()
|
is ModuleComponentIdentifier -> id.toModuleIdentifier()
|
||||||
else -> MavenModuleIdentifier(moduleVersion?.group.orEmpty(), moduleVersion?.name.orEmpty())
|
else -> MavenModuleIdentifier(moduleVersion?.group.orEmpty(), moduleVersion?.name.orEmpty(), moduleClassifier)
|
||||||
}
|
}
|
||||||
)
|
}
|
||||||
|
|
||||||
internal fun ComponentIdentifier.matchesModuleDependency(moduleDependency: ModuleDependency) =
|
internal fun moduleClassifiersFromCapabilities(capabilities: Iterable<Capability>): Iterable<String?> {
|
||||||
|
val classifierCapabilities = capabilities.filter { it.name.contains("..") }
|
||||||
|
return if (classifierCapabilities.none()) listOf(null) else classifierCapabilities.map { it.name.substringAfterLast("..") /*FIXME invent a more stable scheme*/ }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun ComponentSelector.toModuleIdentifiers(): Iterable<KotlinModuleIdentifier> {
|
||||||
|
val moduleClassifiers = moduleClassifiersFromCapabilities(requestedCapabilities)
|
||||||
|
return when (this) {
|
||||||
|
is ProjectComponentSelector -> moduleClassifiers.map { LocalModuleIdentifier(buildName, projectPath, it) }
|
||||||
|
is ModuleComponentSelector -> moduleClassifiers.map { MavenModuleIdentifier(moduleIdentifier.group, moduleIdentifier.name, it) }
|
||||||
|
else -> error("unexpected component selector")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun ResolvedComponentResult.toModuleDependency(): KotlinModuleDependency = KotlinModuleDependency(toModuleIdentifier())
|
||||||
|
internal fun ComponentSelector.toModuleDependency(): KotlinModuleDependency {
|
||||||
|
val moduleId = toModuleIdentifiers().single() // FIXME handle multiple
|
||||||
|
return KotlinModuleDependency(moduleId)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun ComponentIdentifier.matchesModuleDependency(moduleDependency: KotlinModuleDependency) =
|
||||||
when (val id = moduleDependency.moduleIdentifier) {
|
when (val id = moduleDependency.moduleIdentifier) {
|
||||||
is LocalModuleIdentifier -> {
|
is LocalModuleIdentifier -> {
|
||||||
val projectId = this as? ProjectComponentIdentifier
|
val projectId = this as? ProjectComponentIdentifier
|
||||||
|
|||||||
+5
-1
@@ -19,7 +19,11 @@ internal enum class KotlinDependencyScope(val scopeName: String) {
|
|||||||
API_SCOPE(API),
|
API_SCOPE(API),
|
||||||
IMPLEMENTATION_SCOPE(IMPLEMENTATION),
|
IMPLEMENTATION_SCOPE(IMPLEMENTATION),
|
||||||
COMPILE_ONLY_SCOPE(COMPILE_ONLY),
|
COMPILE_ONLY_SCOPE(COMPILE_ONLY),
|
||||||
RUNTIME_ONLY_SCOPE(RUNTIME_ONLY)
|
RUNTIME_ONLY_SCOPE(RUNTIME_ONLY);
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val compileScopes = listOf(KotlinDependencyScope.API_SCOPE, KotlinDependencyScope.IMPLEMENTATION_SCOPE, KotlinDependencyScope.COMPILE_ONLY_SCOPE)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun Project.sourceSetDependencyConfigurationByScope(sourceSet: KotlinSourceSet, scope: KotlinDependencyScope): Configuration =
|
internal fun Project.sourceSetDependencyConfigurationByScope(sourceSet: KotlinSourceSet, scope: KotlinDependencyScope): Configuration =
|
||||||
|
|||||||
+19
-18
@@ -18,6 +18,7 @@ import org.gradle.api.tasks.bundling.Zip
|
|||||||
import org.jetbrains.kotlin.gradle.dsl.KotlinCommonOptions
|
import org.jetbrains.kotlin.gradle.dsl.KotlinCommonOptions
|
||||||
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
|
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
|
||||||
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension
|
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension
|
||||||
|
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtensionOrNull
|
||||||
import org.jetbrains.kotlin.gradle.plugin.*
|
import org.jetbrains.kotlin.gradle.plugin.*
|
||||||
import org.jetbrains.kotlin.gradle.plugin.mpp.*
|
import org.jetbrains.kotlin.gradle.plugin.mpp.*
|
||||||
import org.jetbrains.kotlin.gradle.plugin.mpp.CompilationSourceSetUtil.compilationsBySourceSets
|
import org.jetbrains.kotlin.gradle.plugin.mpp.CompilationSourceSetUtil.compilationsBySourceSets
|
||||||
@@ -53,11 +54,6 @@ class KotlinMetadataTargetConfigurator(kotlinPluginVersion: String) :
|
|||||||
|
|
||||||
internal fun transformGranularMetadataTaskName(sourceSetName: String) =
|
internal fun transformGranularMetadataTaskName(sourceSetName: String) =
|
||||||
lowerCamelCaseName("transform", sourceSetName, "DependenciesMetadata")
|
lowerCamelCaseName("transform", sourceSetName, "DependenciesMetadata")
|
||||||
|
|
||||||
internal fun dependsOnWithInterCompilationDependencies(project: Project, sourceSet: KotlinSourceSet): Set<KotlinSourceSet> =
|
|
||||||
sourceSet.dependsOn.toMutableSet().apply {
|
|
||||||
addAll(getVisibleSourceSetsFromAssociateCompilations(project, sourceSet))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun configureTarget(target: KotlinMetadataTarget) {
|
override fun configureTarget(target: KotlinMetadataTarget) {
|
||||||
@@ -292,11 +288,10 @@ class KotlinMetadataTargetConfigurator(kotlinPluginVersion: String) :
|
|||||||
|
|
||||||
val compilationName = sourceSet.name
|
val compilationName = sourceSet.name
|
||||||
|
|
||||||
val platformCompilations = compilationsBySourceSets(project).getValue(sourceSet)
|
val platformCompilations = compilationsBySourceSets(project)
|
||||||
|
.getValue(sourceSet).filter { it.target.name != KotlinMultiplatformPlugin.METADATA_TARGET_NAME }
|
||||||
|
|
||||||
val isNativeSourceSet = platformCompilations.all { compilation ->
|
val isNativeSourceSet = isSharedNativeSourceSet(project, sourceSet)
|
||||||
compilation.target is KotlinNativeTarget || compilation is KotlinSharedNativeCompilation
|
|
||||||
}
|
|
||||||
|
|
||||||
val compilationFactory: KotlinCompilationFactory<out AbstractKotlinCompilation<*>> = when {
|
val compilationFactory: KotlinCompilationFactory<out AbstractKotlinCompilation<*>> = when {
|
||||||
isNativeSourceSet -> KotlinSharedNativeCompilationFactory(
|
isNativeSourceSet -> KotlinSharedNativeCompilationFactory(
|
||||||
@@ -362,20 +357,12 @@ class KotlinMetadataTargetConfigurator(kotlinPluginVersion: String) :
|
|||||||
isSourceSetPublished: Boolean
|
isSourceSetPublished: Boolean
|
||||||
) {
|
) {
|
||||||
KotlinDependencyScope.values().forEach { scope ->
|
KotlinDependencyScope.values().forEach { scope ->
|
||||||
val allMetadataConfiguration = project.configurations.getByName(
|
|
||||||
when (scope) {
|
|
||||||
KotlinDependencyScope.RUNTIME_ONLY_SCOPE -> ALL_RUNTIME_METADATA_CONFIGURATION_NAME
|
|
||||||
else -> ALL_COMPILE_METADATA_CONFIGURATION_NAME
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
val granularMetadataTransformation = GranularMetadataTransformation(
|
val granularMetadataTransformation = GranularMetadataTransformation(
|
||||||
project,
|
project,
|
||||||
sourceSet,
|
sourceSet,
|
||||||
listOf(scope),
|
listOf(scope),
|
||||||
allMetadataConfiguration,
|
|
||||||
lazy {
|
lazy {
|
||||||
dependsOnWithInterCompilationDependencies(project, sourceSet).filterIsInstance<DefaultKotlinSourceSet>()
|
dependsOnClosureWithInterCompilationDependencies(project, sourceSet).filterIsInstance<DefaultKotlinSourceSet>()
|
||||||
.map { checkNotNull(it.dependencyTransformations[scope]) }
|
.map { checkNotNull(it.dependencyTransformations[scope]) }
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -565,6 +552,20 @@ class KotlinMetadataTargetConfigurator(kotlinPluginVersion: String) :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun isSharedNativeSourceSet(project: Project, sourceSet: KotlinSourceSet): Boolean {
|
||||||
|
val compilations = CompilationSourceSetUtil.compilationsBySourceSets(project)[sourceSet].orEmpty()
|
||||||
|
return compilations.isNotEmpty() && compilations.all {
|
||||||
|
it.platformType == KotlinPlatformType.common || it.platformType == KotlinPlatformType.native
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun dependsOnClosureWithInterCompilationDependencies(project: Project, sourceSet: KotlinSourceSet): Set<KotlinSourceSet> =
|
||||||
|
sourceSet.getSourceSetHierarchy().toMutableSet().apply {
|
||||||
|
/** exclude self from the results of [getSourceSetHierarchy] */
|
||||||
|
remove(sourceSet)
|
||||||
|
addAll(getVisibleSourceSetsFromAssociateCompilations(project, sourceSet))
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return All common source sets that can potentially be published. Right now, not all combinations of platforms actually
|
* @return All common source sets that can potentially be published. Right now, not all combinations of platforms actually
|
||||||
* support metadata compilation (see [KotlinMetadataTargetConfigurator.isMetadataCompilationSupported].
|
* support metadata compilation (see [KotlinMetadataTargetConfigurator.isMetadataCompilationSupported].
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ package org.jetbrains.kotlin.project.model
|
|||||||
// TODO think about state management: unresolved -> (known dependency graph?) ... -> completely resolved
|
// 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
|
// 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: KotlinModuleDependency): KotlinModule?
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO merge with ModuleDependencyResolver?
|
// TODO merge with ModuleDependencyResolver?
|
||||||
@@ -17,5 +17,21 @@ interface ModuleDependencyResolver {
|
|||||||
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
|
||||||
fun discoverDependencies(fragment: KotlinModuleFragment): Iterable<ModuleDependency>
|
fun discoverDependencies(fragment: KotlinModuleFragment): Iterable<KotlinModuleDependency>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface KotlinDependencyGraphResolver {
|
||||||
|
// TODO add explicit dependency consistency scopes if we decide to keep non-production code in the same variant
|
||||||
|
fun resolveDependencyGraph(requestingModule: KotlinModule): DependencyGraphResolution
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class DependencyGraphResolution(val requestingModule: KotlinModule) {
|
||||||
|
class Unknown(requestingModule: KotlinModule) : DependencyGraphResolution(requestingModule)
|
||||||
|
class DependencyGraph(requestingModule: KotlinModule, val root: DependencyGraphNode): DependencyGraphResolution(requestingModule)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: should this be a single graph for all dependency scopes as well, not just for all fragments?
|
||||||
|
class DependencyGraphNode(
|
||||||
|
val module: KotlinModule,
|
||||||
|
val dependenciesByFragment: Map<KotlinModuleFragment, Iterable<DependencyGraphNode>>
|
||||||
|
)
|
||||||
@@ -1,135 +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
|
|
||||||
|
|
||||||
import org.jetbrains.kotlin.project.model.utils.variantsContainingFragment
|
|
||||||
import java.util.ArrayDeque
|
|
||||||
|
|
||||||
interface InternalDependencyExpansion {
|
|
||||||
fun expandInternalFragmentDependencies(consumingFragment: KotlinModuleFragment): InternalDependencyExpansionResult
|
|
||||||
}
|
|
||||||
|
|
||||||
class InternalDependencyExpansionResult(
|
|
||||||
val entries: Iterable<Entry>
|
|
||||||
) {
|
|
||||||
sealed class ExpansionOutcome(val variantMatchingResults: Iterable<VariantResolution>) {
|
|
||||||
class VisibleFragments(
|
|
||||||
val fragments: Iterable<KotlinModuleFragment>,
|
|
||||||
variantMatchingResults: Iterable<VariantResolution>
|
|
||||||
) : ExpansionOutcome(
|
|
||||||
variantMatchingResults
|
|
||||||
)
|
|
||||||
|
|
||||||
class Failure(variantMatchingResults: Iterable<VariantResolution>) : ExpansionOutcome(variantMatchingResults)
|
|
||||||
}
|
|
||||||
|
|
||||||
class Entry(
|
|
||||||
val dependingFragment: KotlinModuleFragment,
|
|
||||||
val dependencyFragment: KotlinModuleFragment,
|
|
||||||
val outcome: ExpansionOutcome
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun InternalDependencyExpansionResult.visibleFragments(): List<KotlinModuleFragment> =
|
|
||||||
entries.flatMap { entry -> entry.outcome.visibleFragments() }
|
|
||||||
|
|
||||||
fun InternalDependencyExpansionResult.ExpansionOutcome.visibleFragments(): Iterable<KotlinModuleFragment> = when (this) {
|
|
||||||
is InternalDependencyExpansionResult.ExpansionOutcome.VisibleFragments -> fragments
|
|
||||||
else -> emptyList()
|
|
||||||
}
|
|
||||||
|
|
||||||
class DefaultInternalDependencyExpansion(
|
|
||||||
private val variantResolver: ContainingModuleVariantResolver
|
|
||||||
) : InternalDependencyExpansion {
|
|
||||||
interface ContainingModuleVariantResolver {
|
|
||||||
fun getChosenVariant(
|
|
||||||
dependingVariant: KotlinModuleVariant,
|
|
||||||
candidateVariants: Iterable<KotlinModuleVariant>
|
|
||||||
): VariantResolution
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun expandInternalFragmentDependencies(consumingFragment: KotlinModuleFragment): InternalDependencyExpansionResult {
|
|
||||||
val visited = mutableSetOf(consumingFragment)
|
|
||||||
val answer = mutableListOf<InternalDependencyExpansionResult.Entry>()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* For each fragment **t** that we consider a source of declared dependencies that we should expand for [consumingFragment]:
|
|
||||||
* - also consider the refines-closure of **t** as sources of declared dependencies
|
|
||||||
* - expand the declared dependencies of **t** to a set of fragments **F**
|
|
||||||
* - add the expansion result to the answer
|
|
||||||
* - also consider all fragments in **F** as sources of declared dependencies
|
|
||||||
*/
|
|
||||||
val declaredDependencySourceQueue = ArrayDeque<KotlinModuleFragment>().apply { add(consumingFragment) }
|
|
||||||
while (declaredDependencySourceQueue.isNotEmpty()) {
|
|
||||||
val declaredDependencySource = declaredDependencySourceQueue.removeFirst()
|
|
||||||
declaredDependencySourceQueue.addAll(declaredDependencySource.refinesClosure.filter(visited::add))
|
|
||||||
val declaredDependenciesToExpand = declaredDependencySource.declaredContainingModuleFragmentDependencies
|
|
||||||
declaredDependenciesToExpand.forEach { declaredDependency ->
|
|
||||||
val answerEntry = expandSingleDeclaredDependency(consumingFragment, declaredDependencySource, declaredDependency)
|
|
||||||
answer += answerEntry
|
|
||||||
declaredDependencySourceQueue.addAll(answerEntry.outcome.visibleFragments())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return InternalDependencyExpansionResult(answer)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun expandSingleDeclaredDependency(
|
|
||||||
consumingFragment: KotlinModuleFragment,
|
|
||||||
declaredDependencySource: KotlinModuleFragment,
|
|
||||||
declaredDependency: KotlinModuleFragment
|
|
||||||
): InternalDependencyExpansionResult.Entry {
|
|
||||||
/**
|
|
||||||
* Go over the variants containing the fragment that we infer the expansion for (note: it's the original consuming fragment, which
|
|
||||||
* may not necessarily be the same as the fragment holding the declared dependency if the latter comes from the closure).
|
|
||||||
*
|
|
||||||
* For each such containing variant v_i, let w_i be the variant containing the declared dependency fragment. Failure to choose
|
|
||||||
* prevents us from inferring the dependency expansion and is reported as a failure to expand the dependency.
|
|
||||||
*
|
|
||||||
* Then intersect the refined fragments of each w_i and use the intersection as the result.
|
|
||||||
*/
|
|
||||||
|
|
||||||
val (consumingVariants, producingVariants) =
|
|
||||||
listOf(consumingFragment, declaredDependency).map { it.containingModule.variantsContainingFragment(it).toSet() }
|
|
||||||
|
|
||||||
val chosenVariants = consumingVariants.map { consumingVariant ->
|
|
||||||
if (consumingVariant in producingVariants)
|
|
||||||
VariantResolution.VariantMatch(consumingVariant, consumingVariant.containingModule, consumingVariant)
|
|
||||||
else
|
|
||||||
variantResolver.getChosenVariant(consumingVariant, producingVariants)
|
|
||||||
}
|
|
||||||
|
|
||||||
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 VariantResolution.VariantMatch).chosenVariant.refinesClosure }
|
|
||||||
.reduce { acc, it -> acc.intersect(it) },
|
|
||||||
chosenVariants
|
|
||||||
)
|
|
||||||
|
|
||||||
return InternalDependencyExpansionResult.Entry(declaredDependencySource, declaredDependency, outcome)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This implementation matches the variants of the module by checking
|
|
||||||
* their [KotlinModuleFragment.declaredContainingModuleFragmentDependencies], similar to how associate compilations work in MPP.
|
|
||||||
* If more than one such dependency is declared, the result is deemed ambiguous.
|
|
||||||
*/
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -6,34 +6,47 @@
|
|||||||
package org.jetbrains.kotlin.project.model
|
package org.jetbrains.kotlin.project.model
|
||||||
|
|
||||||
// TODO sealed with an abstract subclass? this will make exhaustive checks work
|
// TODO sealed with an abstract subclass? this will make exhaustive checks work
|
||||||
open class ModuleIdentifier
|
open class KotlinModuleIdentifier(open val moduleClassifier: String?)
|
||||||
|
|
||||||
// TODO consider id: Any, to allow IDs with custom equality?
|
// TODO consider id: Any, to allow IDs with custom equality?
|
||||||
data class LocalModuleIdentifier(val buildId: String, val projectId: String) : ModuleIdentifier() {
|
data class LocalModuleIdentifier(
|
||||||
|
val buildId: String,
|
||||||
|
val projectId: String,
|
||||||
|
override val moduleClassifier: String?
|
||||||
|
) : KotlinModuleIdentifier(moduleClassifier) {
|
||||||
companion object {
|
companion object {
|
||||||
private const val SINGLE_BUILD_ID = ":"
|
private const val SINGLE_BUILD_ID = ":"
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toString(): String = "project '$projectId'" + buildId.takeIf { it != SINGLE_BUILD_ID }?.let { "(build '$it')" }.orEmpty()
|
override fun toString(): String =
|
||||||
|
"project '$projectId'" +
|
||||||
|
moduleClassifier?.let { " / $it" }.orEmpty() +
|
||||||
|
buildId.takeIf { it != SINGLE_BUILD_ID }?.let { "(build '$it')" }.orEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
data class MavenModuleIdentifier(val group: String, val name: String) : ModuleIdentifier() {
|
data class MavenModuleIdentifier(
|
||||||
override fun toString(): String = "$group:$name"
|
val group: String,
|
||||||
|
val name: String,
|
||||||
|
override val moduleClassifier: String?
|
||||||
|
) : KotlinModuleIdentifier(moduleClassifier) {
|
||||||
|
override fun toString(): String = "$group:$name" + moduleClassifier?.let { " / $it" }.orEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Gradle allows having multiple capabilities in a published module, we need to figure out how we can include them in the module IDs
|
// 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 moduleIdentifier: ModuleIdentifier
|
val moduleIdentifier: KotlinModuleIdentifier
|
||||||
|
|
||||||
val fragments: Iterable<KotlinModuleFragment>
|
val fragments: Iterable<KotlinModuleFragment>
|
||||||
|
|
||||||
val variants: Iterable<KotlinModuleVariant>
|
val variants: Iterable<KotlinModuleVariant>
|
||||||
get() = fragments.filterIsInstance<KotlinModuleVariant>()
|
get() = fragments.filterIsInstance<KotlinModuleVariant>()
|
||||||
|
|
||||||
|
// TODO: isSynthetic?
|
||||||
}
|
}
|
||||||
|
|
||||||
class BasicKotlinModule(
|
class BasicKotlinModule(
|
||||||
override val moduleIdentifier: ModuleIdentifier
|
override val moduleIdentifier: KotlinModuleIdentifier
|
||||||
) : KotlinModule {
|
) : KotlinModule {
|
||||||
override val fragments = mutableListOf<BasicKotlinModuleFragment>()
|
override val fragments = mutableListOf<BasicKotlinModuleFragment>()
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.project.model
|
package org.jetbrains.kotlin.project.model
|
||||||
|
|
||||||
data class ModuleDependency(val moduleIdentifier: ModuleIdentifier) {
|
data class KotlinModuleDependency(val moduleIdentifier: KotlinModuleIdentifier) {
|
||||||
override fun toString(): String = "dependency $moduleIdentifier"
|
override fun toString(): String = "dependency $moduleIdentifier"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -14,10 +14,8 @@ interface KotlinModuleFragment {
|
|||||||
val fragmentName: String
|
val fragmentName: String
|
||||||
val directRefinesDependencies: Iterable<KotlinModuleFragment>
|
val directRefinesDependencies: Iterable<KotlinModuleFragment>
|
||||||
|
|
||||||
val declaredContainingModuleFragmentDependencies: Iterable<KotlinModuleFragment>
|
|
||||||
|
|
||||||
// TODO: scopes
|
// TODO: scopes
|
||||||
val declaredModuleDependencies: Iterable<ModuleDependency>
|
val declaredModuleDependencies: Iterable<KotlinModuleDependency>
|
||||||
|
|
||||||
val kotlinSourceRoots: Iterable<File>
|
val kotlinSourceRoots: Iterable<File>
|
||||||
}
|
}
|
||||||
@@ -54,8 +52,7 @@ open class BasicKotlinModuleFragment(
|
|||||||
|
|
||||||
override val directRefinesDependencies: MutableSet<BasicKotlinModuleFragment> = mutableSetOf()
|
override val directRefinesDependencies: MutableSet<BasicKotlinModuleFragment> = mutableSetOf()
|
||||||
|
|
||||||
override val declaredContainingModuleFragmentDependencies: MutableSet<BasicKotlinModuleFragment> = mutableSetOf()
|
override val declaredModuleDependencies: MutableSet<KotlinModuleDependency> = mutableSetOf()
|
||||||
override val declaredModuleDependencies: MutableSet<ModuleDependency> = mutableSetOf()
|
|
||||||
|
|
||||||
override var kotlinSourceRoots: Iterable<File> = emptyList()
|
override var kotlinSourceRoots: Iterable<File> = emptyList()
|
||||||
override fun toString(): String = "fragment $fragmentName"
|
override fun toString(): String = "fragment $fragmentName"
|
||||||
|
|||||||
+9
-4
@@ -7,7 +7,7 @@ package org.jetbrains.kotlin.project.model
|
|||||||
|
|
||||||
import org.jetbrains.kotlin.project.model.utils.variantsContainingFragment
|
import org.jetbrains.kotlin.project.model.utils.variantsContainingFragment
|
||||||
|
|
||||||
interface KotlinModuleFragmentsResolver {
|
interface ModuleFragmentsResolver {
|
||||||
fun getChosenFragments(
|
fun getChosenFragments(
|
||||||
requestingFragment: KotlinModuleFragment,
|
requestingFragment: KotlinModuleFragment,
|
||||||
dependencyModule: KotlinModule
|
dependencyModule: KotlinModule
|
||||||
@@ -30,18 +30,23 @@ sealed class FragmentResolution(val requestingFragment: KotlinModuleFragment, va
|
|||||||
FragmentResolution(requestingFragment, dependencyModule)
|
FragmentResolution(requestingFragment, dependencyModule)
|
||||||
}
|
}
|
||||||
|
|
||||||
class DefaultKotlinModuleFragmentsResolver(
|
class DefaultModuleFragmentsResolver(
|
||||||
private val variantResolver: ModuleVariantResolver
|
private val variantResolver: ModuleVariantResolver
|
||||||
) : KotlinModuleFragmentsResolver {
|
) : ModuleFragmentsResolver {
|
||||||
override fun getChosenFragments(
|
override fun getChosenFragments(
|
||||||
requestingFragment: KotlinModuleFragment,
|
requestingFragment: KotlinModuleFragment,
|
||||||
dependencyModule: KotlinModule
|
dependencyModule: KotlinModule
|
||||||
): FragmentResolution.ChosenFragments {
|
): FragmentResolution {
|
||||||
val dependingModule = requestingFragment.containingModule
|
val dependingModule = requestingFragment.containingModule
|
||||||
val containingVariants = dependingModule.variantsContainingFragment(requestingFragment)
|
val containingVariants = dependingModule.variantsContainingFragment(requestingFragment)
|
||||||
|
|
||||||
val chosenVariants = containingVariants.map { variantResolver.getChosenVariant(it, dependencyModule) }
|
val chosenVariants = containingVariants.map { variantResolver.getChosenVariant(it, dependencyModule) }
|
||||||
|
|
||||||
|
// TODO: extend this to more cases with non-matching variants, revisit the behavior when no matching variant is found once we fix
|
||||||
|
// local publishing of libraries with missing host-specific parts (it breaks transitive dependencies now)
|
||||||
|
if (chosenVariants.none { it is VariantResolution.VariantMatch })
|
||||||
|
return FragmentResolution.NotRequested(requestingFragment, dependencyModule)
|
||||||
|
|
||||||
val chosenFragments = chosenVariants.map { variantResolution ->
|
val chosenFragments = chosenVariants.map { variantResolution ->
|
||||||
when (variantResolution) {
|
when (variantResolution) {
|
||||||
is VariantResolution.VariantMatch -> variantResolution.chosenVariant.refinesClosure
|
is VariantResolution.VariantMatch -> variantResolution.chosenVariant.refinesClosure
|
||||||
+2
-2
@@ -9,11 +9,11 @@ import org.junit.Assume.assumeTrue
|
|||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
class DefaultKotlinModuleFragmentsResolverTest {
|
class DefaultModuleFragmentsResolverTest {
|
||||||
val moduleFoo = simpleModule("foo")
|
val moduleFoo = simpleModule("foo")
|
||||||
val moduleBar = simpleModule("bar")
|
val moduleBar = simpleModule("bar")
|
||||||
|
|
||||||
val fragmentResolver = DefaultKotlinModuleFragmentsResolver(MatchVariantsByExactAttributes())
|
val fragmentResolver = DefaultModuleFragmentsResolver(MatchVariantsByExactAttributes())
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testFragmentVisibility() {
|
fun testFragmentVisibility() {
|
||||||
Reference in New Issue
Block a user