From 6b2111c2a5c2b89ae904dd67d4bf451756b9e065 Mon Sep 17 00:00:00 2001 From: Anton Lakotka Date: Thu, 12 Jan 2023 09:52:33 +0100 Subject: [PATCH] [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 --- .../mpp/GranularMetadataTransformation.kt | 24 ++--- .../plugin/mpp/SourceSetVisibilityProvider.kt | 101 ++++++++++++------ 2 files changed, 83 insertions(+), 42 deletions(-) diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/GranularMetadataTransformation.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/GranularMetadataTransformation.kt index 778b43cfe2d..466d5234cf5 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/GranularMetadataTransformation.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/GranularMetadataTransformation.kt @@ -131,7 +131,7 @@ internal class GranularMetadataTransformation( val allModuleDependencies = configurationToResolve.incoming.resolutionResult.allDependencies.filterIsInstance() - val resolvedDependencyQueue: Queue = ArrayDeque().apply { + val resolvedDependencyQueue: Queue = ArrayDeque().apply { val requestedModules: Set = allRequestedDependencies.mapTo(mutableSetOf()) { ModuleIds.fromDependency(it) } @@ -140,14 +140,14 @@ internal class GranularMetadataTransformation( resolutionResult.root.dependencies .filter { ModuleIds.fromComponentSelector(project, it.requested) in requestedModules } .filterIsInstance() - .map { it.selected } ) } - val visitedDependencies = mutableSetOf() + val visitedDependencies = mutableSetOf() while (resolvedDependencyQueue.isNotEmpty()) { - val resolvedDependency: ResolvedComponentResult = resolvedDependencyQueue.poll() + val resolvedDependency: ResolvedDependencyResult = resolvedDependencyQueue.poll() + val selectedComponent = resolvedDependency.selected if (!visitedDependencies.add(resolvedDependency)) { /* Already processed this dependency */ @@ -156,14 +156,14 @@ internal class GranularMetadataTransformation( val dependencyResult = processDependency( resolvedDependency, - parentVisibleSourceSets[resolvedDependency.id.uniqueKey].orEmpty() + parentVisibleSourceSets[selectedComponent.id.uniqueKey].orEmpty() ) result.add(dependencyResult) val transitiveDependenciesToVisit = when (dependencyResult) { is MetadataDependencyResolution.KeepOriginalDependency -> - resolvedDependency.dependencies.filterIsInstance() + selectedComponent.dependencies.filterIsInstance() is MetadataDependencyResolution.ChooseVisibleSourceSets -> dependencyResult.visibleTransitiveDependencies is MetadataDependencyResolution.Exclude.PublishedPlatformSourceSetDependency -> dependencyResult.visibleTransitiveDependencies @@ -171,13 +171,12 @@ internal class GranularMetadataTransformation( } resolvedDependencyQueue.addAll( - transitiveDependenciesToVisit.filter { it.selected !in visitedDependencies } - .map { it.selected } + transitiveDependenciesToVisit.filter { it !in visitedDependencies } ) } allModuleDependencies.forEach { resolvedDependency -> - if (resolvedDependency.selected !in visitedDependencies) { + if (resolvedDependency !in visitedDependencies) { // val files = resolvedDependency.moduleArtifacts.map { it.file } result.add( MetadataDependencyResolution.Exclude.Unrequested( @@ -205,9 +204,10 @@ internal class GranularMetadataTransformation( * source sets in *S*, then consider only these transitive dependencies, ignore the others; */ private fun processDependency( - module: ResolvedComponentResult, + dependency: ResolvedDependencyResult, sourceSetsVisibleInParents: Set, ): MetadataDependencyResolution { + val module = dependency.selected val mppDependencyMetadataExtractor = MppDependencyProjectStructureMetadataExtractor.create( project, module, configurationToResolve, 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 = SourceSetVisibilityProvider(project).getVisibleSourceSets( - kotlinSourceSet, - module, + kotlinSourceSet.name, + dependency, projectStructureMetadata, resolvedToProject != null ) diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/SourceSetVisibilityProvider.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/SourceSetVisibilityProvider.kt index d27496ff9cd..849d1327ee4 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/SourceSetVisibilityProvider.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/SourceSetVisibilityProvider.kt @@ -6,17 +6,17 @@ package org.jetbrains.kotlin.gradle.plugin.mpp import org.gradle.api.Project -import org.gradle.api.artifacts.Configuration -import org.gradle.api.artifacts.result.ResolvedComponentResult -import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation -import org.jetbrains.kotlin.gradle.plugin.KotlinCompilationToRunnableFiles -import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType -import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet -import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.toSingleKpmModuleIdentifier -import org.jetbrains.kotlin.gradle.plugin.sources.KotlinDependencyScope -import org.jetbrains.kotlin.gradle.plugin.sources.internal +import org.gradle.api.artifacts.result.ResolvedDependencyResult +import org.gradle.api.attributes.Usage +import org.jetbrains.kotlin.gradle.dsl.multiplatformExtensionOrNull +import org.jetbrains.kotlin.gradle.plugin.* +import org.jetbrains.kotlin.gradle.utils.LazyResolvedConfiguration +import org.jetbrains.kotlin.gradle.utils.allResolvedDependencies +import org.jetbrains.kotlin.gradle.utils.dependencyArtifactsOrNull import java.io.File +private typealias KotlinSourceSetName = String + internal data class SourceSetVisibilityResult( /** * Names of source sets that the consumer sees from the requested dependency. @@ -30,15 +30,41 @@ internal data class SourceSetVisibilityResult( val hostSpecificMetadataArtifactBySourceSet: Map ) +private fun Project.collectAllPlatformCompilationData(): List { + 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( - private val project: Project + private val platformCompilations: List, ) { - private val resolvedVariantsProvider = ResolvedMppVariantsProvider.get(project) + constructor(project: Project) : this( + platformCompilations = project.collectAllPlatformCompilationData() + ) + + class PlatformCompilationData( + val sourceSets: Set, + 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. * * 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. */ fun getVisibleSourceSets( - visibleFrom: KotlinSourceSet, - resolvedRootMppDependency: ResolvedComponentResult, + visibleFromSourceSet: KotlinSourceSetName, + resolvedRootMppDependency: ResolvedDependencyResult, dependencyProjectStructureMetadata: KotlinProjectStructureMetadata, resolvedToOtherProject: Boolean ): SourceSetVisibilityResult { - val compilations = visibleFrom.internal.compilations + val component = resolvedRootMppDependency.selected + val componentId = component.id - val component = resolvedRootMppDependency - val mppModuleIdentifier = component.toSingleKpmModuleIdentifier() - - val firstConfigurationByVariant = mutableMapOf() + val firstConfigurationByVariant = mutableMapOf() val visiblePlatformVariantNames: Set = - compilations - .filter { it.target.platformType != KotlinPlatformType.common } - .map { compilation -> project.configurations.getByName(compilation.compileDependencyConfigurationName) } - .mapTo(mutableSetOf()) { configuration -> - val resolvedVariant = resolvedVariantsProvider.getResolvedVariantName(mppModuleIdentifier, configuration) - ?.let { kotlinVariantNameFromPublishedVariantName(it) } + platformCompilations + .filter { visibleFromSourceSet in it.sourceSets } + .mapTo(mutableSetOf()) { resolvedConfiguration -> + val resolvedVariant = resolvedConfiguration + .resolvedDependenciesConfiguration + .allResolvedDependencies + .find { it.selected.id == componentId } + ?.let { kotlinVariantNameFromPublishedVariantName(it.resolvedVariant.displayName) } ?: return@mapTo null - firstConfigurationByVariant.putIfAbsent(resolvedVariant, configuration) + firstConfigurationByVariant.putIfAbsent(resolvedVariant, resolvedConfiguration) resolvedVariant } @@ -107,9 +133,24 @@ internal class SourceSetVisibilityProvider( } someVariantByHostSpecificSourceSet.entries.mapNotNull { (sourceSetName, variantName) -> - val configuration = firstConfigurationByVariant.getValue(variantName) - resolvedVariantsProvider.getHostSpecificMetadataArtifactByRootModule(mppModuleIdentifier, configuration) - ?.let { sourceSetName to it } + val resolvedHostSpecificMetadataConfiguration = firstConfigurationByVariant + .getValue(variantName) + .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() }