[Gradle] Decouple SourceSetVisibilityProvider from Gradle Project

And related ResolvedMppVariantsProvider.

* Introduce PlatformCompilationData that is Configuration Cache friendly
  And contain all needed data to infer visible source sets
* Add factory-methods that can collect PlatformCompilationData from
  project that has Multiplatform Extension.
* hostSpecificMetadataConfiguration collection has parity with related
  ResolvedMppVariantsProvider.resolveArtifacts() part where attributes
  are changed to KOTLIN_METADATA.

ResolvedMppVariantsProvider will be deprecated and removed shortly

^KT-49933
This commit is contained in:
Anton Lakotka
2023-01-12 09:52:33 +01:00
committed by Space Team
parent 504ee1fbb1
commit 6b2111c2a5
2 changed files with 83 additions and 42 deletions
@@ -131,7 +131,7 @@ internal class GranularMetadataTransformation(
val allModuleDependencies = val allModuleDependencies =
configurationToResolve.incoming.resolutionResult.allDependencies.filterIsInstance<ResolvedDependencyResult>() configurationToResolve.incoming.resolutionResult.allDependencies.filterIsInstance<ResolvedDependencyResult>()
val resolvedDependencyQueue: Queue<ResolvedComponentResult> = ArrayDeque<ResolvedComponentResult>().apply { val resolvedDependencyQueue: Queue<ResolvedDependencyResult> = ArrayDeque<ResolvedDependencyResult>().apply {
val requestedModules: Set<ModuleDependencyIdentifier> = allRequestedDependencies.mapTo(mutableSetOf()) { val requestedModules: Set<ModuleDependencyIdentifier> = allRequestedDependencies.mapTo(mutableSetOf()) {
ModuleIds.fromDependency(it) ModuleIds.fromDependency(it)
} }
@@ -140,14 +140,14 @@ internal class GranularMetadataTransformation(
resolutionResult.root.dependencies resolutionResult.root.dependencies
.filter { ModuleIds.fromComponentSelector(project, it.requested) in requestedModules } .filter { ModuleIds.fromComponentSelector(project, it.requested) in requestedModules }
.filterIsInstance<ResolvedDependencyResult>() .filterIsInstance<ResolvedDependencyResult>()
.map { it.selected }
) )
} }
val visitedDependencies = mutableSetOf<ResolvedComponentResult>() val visitedDependencies = mutableSetOf<ResolvedDependencyResult>()
while (resolvedDependencyQueue.isNotEmpty()) { while (resolvedDependencyQueue.isNotEmpty()) {
val resolvedDependency: ResolvedComponentResult = resolvedDependencyQueue.poll() val resolvedDependency: ResolvedDependencyResult = resolvedDependencyQueue.poll()
val selectedComponent = resolvedDependency.selected
if (!visitedDependencies.add(resolvedDependency)) { if (!visitedDependencies.add(resolvedDependency)) {
/* Already processed this dependency */ /* Already processed this dependency */
@@ -156,14 +156,14 @@ internal class GranularMetadataTransformation(
val dependencyResult = processDependency( val dependencyResult = processDependency(
resolvedDependency, resolvedDependency,
parentVisibleSourceSets[resolvedDependency.id.uniqueKey].orEmpty() parentVisibleSourceSets[selectedComponent.id.uniqueKey].orEmpty()
) )
result.add(dependencyResult) result.add(dependencyResult)
val transitiveDependenciesToVisit = when (dependencyResult) { val transitiveDependenciesToVisit = when (dependencyResult) {
is MetadataDependencyResolution.KeepOriginalDependency -> is MetadataDependencyResolution.KeepOriginalDependency ->
resolvedDependency.dependencies.filterIsInstance<ResolvedDependencyResult>() selectedComponent.dependencies.filterIsInstance<ResolvedDependencyResult>()
is MetadataDependencyResolution.ChooseVisibleSourceSets -> dependencyResult.visibleTransitiveDependencies is MetadataDependencyResolution.ChooseVisibleSourceSets -> dependencyResult.visibleTransitiveDependencies
is MetadataDependencyResolution.Exclude.PublishedPlatformSourceSetDependency -> dependencyResult.visibleTransitiveDependencies is MetadataDependencyResolution.Exclude.PublishedPlatformSourceSetDependency -> dependencyResult.visibleTransitiveDependencies
@@ -171,13 +171,12 @@ internal class GranularMetadataTransformation(
} }
resolvedDependencyQueue.addAll( resolvedDependencyQueue.addAll(
transitiveDependenciesToVisit.filter { it.selected !in visitedDependencies } transitiveDependenciesToVisit.filter { it !in visitedDependencies }
.map { it.selected }
) )
} }
allModuleDependencies.forEach { resolvedDependency -> allModuleDependencies.forEach { resolvedDependency ->
if (resolvedDependency.selected !in visitedDependencies) { if (resolvedDependency !in visitedDependencies) {
// val files = resolvedDependency.moduleArtifacts.map { it.file } // val files = resolvedDependency.moduleArtifacts.map { it.file }
result.add( result.add(
MetadataDependencyResolution.Exclude.Unrequested( MetadataDependencyResolution.Exclude.Unrequested(
@@ -205,9 +204,10 @@ internal class GranularMetadataTransformation(
* source sets in *S*, then consider only these transitive dependencies, ignore the others; * source sets in *S*, then consider only these transitive dependencies, ignore the others;
*/ */
private fun processDependency( private fun processDependency(
module: ResolvedComponentResult, dependency: ResolvedDependencyResult,
sourceSetsVisibleInParents: Set<String>, sourceSetsVisibleInParents: Set<String>,
): MetadataDependencyResolution { ): MetadataDependencyResolution {
val module = dependency.selected
val mppDependencyMetadataExtractor = MppDependencyProjectStructureMetadataExtractor.create( val mppDependencyMetadataExtractor = MppDependencyProjectStructureMetadataExtractor.create(
project, module, configurationToResolve, project, module, configurationToResolve,
resolveViaAvailableAt = false // we will process the available-at module as a dependency later in the queue resolveViaAvailableAt = false // we will process the available-at module as a dependency later in the queue
@@ -224,8 +224,8 @@ internal class GranularMetadataTransformation(
val sourceSetVisibility = val sourceSetVisibility =
SourceSetVisibilityProvider(project).getVisibleSourceSets( SourceSetVisibilityProvider(project).getVisibleSourceSets(
kotlinSourceSet, kotlinSourceSet.name,
module, dependency,
projectStructureMetadata, projectStructureMetadata,
resolvedToProject != null resolvedToProject != null
) )
@@ -6,17 +6,17 @@
package org.jetbrains.kotlin.gradle.plugin.mpp package org.jetbrains.kotlin.gradle.plugin.mpp
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration import org.gradle.api.artifacts.result.ResolvedDependencyResult
import org.gradle.api.artifacts.result.ResolvedComponentResult import org.gradle.api.attributes.Usage
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation import org.jetbrains.kotlin.gradle.dsl.multiplatformExtensionOrNull
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilationToRunnableFiles import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType import org.jetbrains.kotlin.gradle.utils.LazyResolvedConfiguration
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet import org.jetbrains.kotlin.gradle.utils.allResolvedDependencies
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.toSingleKpmModuleIdentifier import org.jetbrains.kotlin.gradle.utils.dependencyArtifactsOrNull
import org.jetbrains.kotlin.gradle.plugin.sources.KotlinDependencyScope
import org.jetbrains.kotlin.gradle.plugin.sources.internal
import java.io.File import java.io.File
private typealias KotlinSourceSetName = String
internal data class SourceSetVisibilityResult( internal data class SourceSetVisibilityResult(
/** /**
* Names of source sets that the consumer sees from the requested dependency. * Names of source sets that the consumer sees from the requested dependency.
@@ -30,15 +30,41 @@ internal data class SourceSetVisibilityResult(
val hostSpecificMetadataArtifactBySourceSet: Map<String, File> val hostSpecificMetadataArtifactBySourceSet: Map<String, File>
) )
private fun Project.collectAllPlatformCompilationData(): List<SourceSetVisibilityProvider.PlatformCompilationData> {
val multiplatformExtension = multiplatformExtensionOrNull ?: return emptyList()
return multiplatformExtension
.targets
.filter { it.platformType != KotlinPlatformType.common }
.flatMap { target -> target.compilations.map { it.toPlatformCompilationData() } }
}
private fun KotlinCompilation<*>.toPlatformCompilationData() = SourceSetVisibilityProvider.PlatformCompilationData(
sourceSets = allKotlinSourceSets.map { it.name }.toSet(),
resolvedDependenciesConfiguration = LazyResolvedConfiguration(project.configurations.getByName(compileDependencyConfigurationName)),
hostSpecificMetadataConfiguration = project
.configurations
.getByName(compileDependencyConfigurationName)
.copyRecursive().apply { attributes.attribute(Usage.USAGE_ATTRIBUTE, project.usageByName(KotlinUsages.KOTLIN_METADATA)) }
.let(::LazyResolvedConfiguration)
)
internal class SourceSetVisibilityProvider( internal class SourceSetVisibilityProvider(
private val project: Project private val platformCompilations: List<PlatformCompilationData>,
) { ) {
private val resolvedVariantsProvider = ResolvedMppVariantsProvider.get(project) constructor(project: Project) : this(
platformCompilations = project.collectAllPlatformCompilationData()
)
class PlatformCompilationData(
val sourceSets: Set<KotlinSourceSetName>,
val resolvedDependenciesConfiguration: LazyResolvedConfiguration,
val hostSpecificMetadataConfiguration: LazyResolvedConfiguration?
)
/** /**
* Determine which source sets of the [resolvedRootMppDependency] are visible in the [visibleFrom] source set. * Determine which source sets of the [resolvedRootMppDependency] are visible in the [visibleFromSourceSet] source set.
* *
* This requires resolving dependencies of the compilations which [visibleFrom] takes part in, in order to find which variants the * This requires resolving dependencies of the compilations which [visibleFromSourceSet] takes part in, in order to find which variants the
* [resolvedRootMppDependency] got resolved to for those compilations. * [resolvedRootMppDependency] got resolved to for those compilations.
* *
* Once the variants are known, they are checked against the [dependencyProjectStructureMetadata], and the * Once the variants are known, they are checked against the [dependencyProjectStructureMetadata], and the
@@ -48,28 +74,28 @@ internal class SourceSetVisibilityProvider(
* the Gradle API for dependency variants behaves differently for project dependencies and published ones. * the Gradle API for dependency variants behaves differently for project dependencies and published ones.
*/ */
fun getVisibleSourceSets( fun getVisibleSourceSets(
visibleFrom: KotlinSourceSet, visibleFromSourceSet: KotlinSourceSetName,
resolvedRootMppDependency: ResolvedComponentResult, resolvedRootMppDependency: ResolvedDependencyResult,
dependencyProjectStructureMetadata: KotlinProjectStructureMetadata, dependencyProjectStructureMetadata: KotlinProjectStructureMetadata,
resolvedToOtherProject: Boolean resolvedToOtherProject: Boolean
): SourceSetVisibilityResult { ): SourceSetVisibilityResult {
val compilations = visibleFrom.internal.compilations val component = resolvedRootMppDependency.selected
val componentId = component.id
val component = resolvedRootMppDependency val firstConfigurationByVariant = mutableMapOf<String, PlatformCompilationData>()
val mppModuleIdentifier = component.toSingleKpmModuleIdentifier()
val firstConfigurationByVariant = mutableMapOf<String, Configuration>()
val visiblePlatformVariantNames: Set<String?> = val visiblePlatformVariantNames: Set<String?> =
compilations platformCompilations
.filter { it.target.platformType != KotlinPlatformType.common } .filter { visibleFromSourceSet in it.sourceSets }
.map { compilation -> project.configurations.getByName(compilation.compileDependencyConfigurationName) } .mapTo(mutableSetOf()) { resolvedConfiguration ->
.mapTo(mutableSetOf()) { configuration -> val resolvedVariant = resolvedConfiguration
val resolvedVariant = resolvedVariantsProvider.getResolvedVariantName(mppModuleIdentifier, configuration) .resolvedDependenciesConfiguration
?.let { kotlinVariantNameFromPublishedVariantName(it) } .allResolvedDependencies
.find { it.selected.id == componentId }
?.let { kotlinVariantNameFromPublishedVariantName(it.resolvedVariant.displayName) }
?: return@mapTo null ?: return@mapTo null
firstConfigurationByVariant.putIfAbsent(resolvedVariant, configuration) firstConfigurationByVariant.putIfAbsent(resolvedVariant, resolvedConfiguration)
resolvedVariant resolvedVariant
} }
@@ -107,9 +133,24 @@ internal class SourceSetVisibilityProvider(
} }
someVariantByHostSpecificSourceSet.entries.mapNotNull { (sourceSetName, variantName) -> someVariantByHostSpecificSourceSet.entries.mapNotNull { (sourceSetName, variantName) ->
val configuration = firstConfigurationByVariant.getValue(variantName) val resolvedHostSpecificMetadataConfiguration = firstConfigurationByVariant
resolvedVariantsProvider.getHostSpecificMetadataArtifactByRootModule(mppModuleIdentifier, configuration) .getValue(variantName)
?.let { sourceSetName to it } .hostSpecificMetadataConfiguration
?: return@mapNotNull null
val dependency = resolvedHostSpecificMetadataConfiguration
.allResolvedDependencies
.find { it.selected.id == componentId }
?: return@mapNotNull null
val metadataArtifact = resolvedHostSpecificMetadataConfiguration
// it can happen that related host-specific metadata artifact doesn't exist
// for example on linux machines, then just gracefully return null
.dependencyArtifactsOrNull(dependency)
?.singleOrNull()
?: return@mapNotNull null
sourceSetName to metadataArtifact.file
}.toMap() }.toMap()
} }