[Gradle] Move creation of GranularMetadataTransformation to source set

Now it is encapsulated to DefaultKotlinSourceSet and is lazily created
upon demand during project import

But metadata configurations still should be created during metadata
target configuration.

^KT-56431 Verification Pending
This commit is contained in:
Anton Lakotka
2023-02-06 10:22:36 +01:00
committed by Space Team
parent db6c6c736a
commit 737afcb343
4 changed files with 119 additions and 86 deletions
@@ -7,14 +7,20 @@ package org.jetbrains.kotlin.gradle.plugin.mpp
import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration
import org.gradle.api.attributes.Category
import org.gradle.api.attributes.Usage
import org.gradle.api.provider.Provider
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension
import org.jetbrains.kotlin.gradle.plugin.categoryByName
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.util.copyAttributes
import org.jetbrains.kotlin.gradle.utils.markResolvable
import org.jetbrains.kotlin.gradle.plugin.sources.InternalKotlinSourceSet
import org.jetbrains.kotlin.gradle.plugin.sources.METADATA_CONFIGURATION_NAME_SUFFIX
import org.jetbrains.kotlin.gradle.plugin.sources.disambiguateName
import org.jetbrains.kotlin.gradle.plugin.sources.getVisibleSourceSetsFromAssociateCompilations
import org.jetbrains.kotlin.gradle.targets.metadata.ALL_COMPILE_METADATA_CONFIGURATION_NAME
import org.jetbrains.kotlin.gradle.plugin.usageByName
import org.jetbrains.kotlin.gradle.plugin.usesPlatformOf
import org.jetbrains.kotlin.gradle.utils.getOrCreate
import org.jetbrains.kotlin.gradle.utils.listProperty
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
import org.jetbrains.kotlin.tooling.core.extrasLazyProperty
@@ -56,7 +62,7 @@ internal val InternalKotlinSourceSet.resolvableMetadataConfiguration: Configurat
}
})
val allCompileMetadataConfiguration = project.configurations.getByName(ALL_COMPILE_METADATA_CONFIGURATION_NAME)
val allCompileMetadataConfiguration = project.allCompileMetadataConfiguration
/* Ensure consistent dependency resolution result within the whole module */
configuration.shouldResolveConsistentlyWith(allCompileMetadataConfiguration)
@@ -65,6 +71,16 @@ internal val InternalKotlinSourceSet.resolvableMetadataConfiguration: Configurat
configuration
}
internal val Project.allCompileMetadataConfiguration get(): Configuration =
configurations.getOrCreate("allSourceSetsCompileDependenciesMetadata", invokeWhenCreated = {
it.markResolvable()
val metadataTarget = multiplatformExtension.metadata()
it.usesPlatformOf(metadataTarget)
it.attributes.attribute(Usage.USAGE_ATTRIBUTE, project.usageByName(KotlinUsages.KOTLIN_METADATA))
it.attributes.attribute(Category.CATEGORY_ATTRIBUTE, project.categoryByName(Category.LIBRARY))
})
private inline fun <reified T> Project.listProvider(noinline provider: () -> List<T>): Provider<List<T>> {
return project.objects.listProperty<T>().apply {
set(project.provider(provider))
@@ -9,13 +9,16 @@ package org.jetbrains.kotlin.gradle.plugin.sources
import org.gradle.api.Action
import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration
import org.gradle.api.file.SourceDirectorySet
import org.jetbrains.kotlin.build.DEFAULT_KOTLIN_SOURCE_FILES_EXTENSIONS
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtensionOrNull
import org.jetbrains.kotlin.gradle.plugin.KotlinDependencyHandler
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
import org.jetbrains.kotlin.gradle.plugin.LanguageSettingsBuilder
import org.jetbrains.kotlin.gradle.plugin.mpp.*
import org.jetbrains.kotlin.gradle.targets.metadata.dependsOnClosureWithInterCompilationDependencies
import org.jetbrains.kotlin.gradle.utils.*
import org.jetbrains.kotlin.tooling.core.MutableExtras
import org.jetbrains.kotlin.tooling.core.closure
@@ -125,7 +128,9 @@ abstract class DefaultKotlinSourceSet @Inject constructor(
* Returns [GranularMetadataTransformation] for all requested compile dependencies
* scopes: API, IMPLEMENTATION, COMPILE_ONLY; See [KotlinDependencyScope.compileScopes]
*/
internal var compileDependenciesTransformation: GranularMetadataTransformation? = null
internal val compileDependenciesTransformation: GranularMetadataTransformation? by lazy {
createAndConfigureDependencyTransformationForSourceSet()
}
private val _requiresVisibilityOf = mutableSetOf<KotlinSourceSet>()
@@ -226,7 +231,75 @@ fun KotlinMultiplatformExtension.findSourceSetsDependingOn(sourceSet: KotlinSour
return sourceSet.closure { seedSourceSet -> sourceSets.filter { otherSourceSet -> seedSourceSet in otherSourceSet.dependsOn } }
}
internal val DefaultKotlinSourceSet.compileDependenciesTransformationOrFail: GranularMetadataTransformation
get() = compileDependenciesTransformation
?: error("Accessing Compile Dependencies Transformations that is not yet initialised; " +
"Check when compileDependenciesTransformation is set")
private fun DefaultKotlinSourceSet.createAndConfigureDependencyTransformationForSourceSet(): GranularMetadataTransformation? {
// Create only for source sets in multiplatform plugin
project.multiplatformExtensionOrNull ?: return null
val parentSourceSetVisibilityProvider = ParentSourceSetVisibilityProvider { componentIdentifier ->
dependsOnClosureWithInterCompilationDependencies(this).filterIsInstance<DefaultKotlinSourceSet>()
.mapNotNull { it.compileDependenciesTransformation }
.flatMap { it.visibleSourceSetsByComponentId[componentIdentifier].orEmpty() }
.toSet()
}
val granularMetadataTransformation = GranularMetadataTransformation(
params = GranularMetadataTransformation.Params(project, this),
parentSourceSetVisibilityProvider = parentSourceSetVisibilityProvider
)
/**
*
* This method is only intended to be called on deprecated DependenciesMetadata configurations to ensure
* correct behaviour in import.
*
* KGP based dependency resolution is therefore unaffected.
*
* Ensure that the [configuration] excludes the dependencies that are classified by this [GranularMetadataTransformation] as
* [MetadataDependencyResolution.Exclude], and uses exactly the same versions as were resolved for the requested
* dependencies during the transformation.
*/
fun applyTransformationToLegacyDependenciesMetadataConfiguration(
configuration: Configuration,
transformation: GranularMetadataTransformation
) {
// Run this action immediately before the configuration first takes part in dependency resolution:
configuration.withDependencies {
val (unrequested, requested) = transformation.metadataDependencyResolutions
.partition { it is MetadataDependencyResolution.Exclude }
unrequested.forEach {
val (group, name) = it.projectDependency(project)?.run {
/** Note: the project dependency notation here should be exactly this, group:name,
* not from [ModuleIds.fromProjectPathDependency], as `exclude` checks it against the project's group:name */
ModuleDependencyIdentifier(group.toString(), name)
} ?: ModuleIds.fromComponent(project, it.dependency)
configuration.exclude(mapOf("group" to group, "module" to name))
}
requested.filter { it.dependency.currentBuildProjectIdOrNull == null }.forEach {
val (group, name) = ModuleIds.fromComponent(project, it.dependency)
val notation = listOfNotNull(group.orEmpty(), name, it.dependency.moduleVersion?.version).joinToString(":")
configuration.resolutionStrategy.force(notation)
}
}
}
@Suppress("DEPRECATION")
/*
Older IDEs still rely on resolving the metadata configurations explicitly.
Dependencies will be coming from extending the newer 'resolvableMetadataConfiguration'.
the intransitiveMetadataConfigurationName will not extend this mechanism, since it only
relies on dependencies being added explicitly by the Kotlin Gradle Plugin
*/
listOf(
apiMetadataConfigurationName,
implementationMetadataConfigurationName,
compileOnlyMetadataConfigurationName
).forEach { configurationName ->
val configuration = project.configurations.getByName(configurationName)
applyTransformationToLegacyDependenciesMetadataConfiguration(configuration, granularMetadataTransformation)
}
return granularMetadataTransformation
}
@@ -6,8 +6,6 @@
package org.jetbrains.kotlin.gradle.targets.metadata
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.result.ResolvedArtifactResult
import org.gradle.api.attributes.Category
import org.gradle.api.attributes.Category.CATEGORY_ATTRIBUTE
@@ -21,8 +19,6 @@ import org.gradle.api.tasks.bundling.Zip
import org.jetbrains.kotlin.commonizer.SharedCommonizerTarget
import org.jetbrains.kotlin.gradle.dsl.*
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.ide.Idea222Api
import org.jetbrains.kotlin.gradle.plugin.ide.ideaImportDependsOn
import org.jetbrains.kotlin.gradle.plugin.mpp.*
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.*
import org.jetbrains.kotlin.gradle.plugin.sources.*
@@ -92,7 +88,7 @@ class KotlinMetadataTargetConfigurator :
configureProjectStructureMetadataGeneration(target.project, allMetadataJar)
setupDependencyTransformationForCommonSourceSets(target)
configureMetadataDependenciesConfigurationsForCommonSourceSets(target)
target.project.configurations.getByName(target.apiElementsConfigurationName).run {
attributes.attribute(USAGE_ATTRIBUTE, target.project.usageByName(KotlinUsages.KOTLIN_METADATA))
@@ -166,10 +162,12 @@ class KotlinMetadataTargetConfigurator :
return result
}
private fun setupDependencyTransformationForCommonSourceSets(target: KotlinMetadataTarget) {
private fun configureMetadataDependenciesConfigurationsForCommonSourceSets(target: KotlinMetadataTarget) {
target.project.whenEvaluated {
kotlinExtension.sourceSets.all {
setupDependencyTransformationForSourceSet(target.project, it)
if (it is DefaultKotlinSourceSet) {
configureMetadataDependenciesConfigurations(target.project, it)
}
}
}
}
@@ -368,79 +366,26 @@ class KotlinMetadataTargetConfigurator :
}
}
private fun setupDependencyTransformationForSourceSet(
private fun configureMetadataDependenciesConfigurations(
project: Project,
sourceSet: KotlinSourceSet
sourceSet: DefaultKotlinSourceSet
) {
val parentSourceSetVisibilityProvider = ParentSourceSetVisibilityProvider { componentIdentifier ->
dependsOnClosureWithInterCompilationDependencies(sourceSet).filterIsInstance<DefaultKotlinSourceSet>()
.flatMap { it.compileDependenciesTransformationOrFail.visibleSourceSetsByComponentId[componentIdentifier].orEmpty() }
.toSet()
}
val granularMetadataTransformation = GranularMetadataTransformation(
params = GranularMetadataTransformation.Params(project, sourceSet),
parentSourceSetVisibilityProvider = parentSourceSetVisibilityProvider
)
/*
Older IDEs still rely on resolving the metadata configurations explicitly.
Dependencies will be coming from extending the newer 'resolvableMetadataConfiguration'.
the intransitiveMetadataConfigurationName will not extend this mechanism, since it only
relies on dependencies being added explicitly by the Kotlin Gradle Plugin
*/
@Suppress("DEPRECATION")
if (sourceSet is DefaultKotlinSourceSet) {
sourceSet.compileDependenciesTransformation = granularMetadataTransformation
/*
Older IDEs still rely on resolving the metadata configurations explicitly.
Dependencies will be coming from extending the newer 'resolvableMetadataConfiguration'.
the intransitiveMetadataConfigurationName will not extend this mechanism, since it only
relies on dependencies being added explicitly by the Kotlin Gradle Plugin
*/
listOf(
sourceSet.apiMetadataConfigurationName,
sourceSet.implementationMetadataConfigurationName,
sourceSet.compileOnlyMetadataConfigurationName
).forEach { configurationName ->
val configuration = project.configurations.getByName(configurationName)
configuration.extendsFrom(sourceSet.resolvableMetadataConfiguration)
configuration.shouldResolveConsistentlyWith(sourceSet.resolvableMetadataConfiguration)
project.applyTransformationToLegacyDependenciesMetadataConfiguration(configuration, granularMetadataTransformation)
}
}
}
/**
*
* This method is only intended to be called on deprecated DependenciesMetadata configurations to ensure
* correct behaviour in import.
*
* KGP based dependency resolution is therefore unaffected.
*
* Ensure that the [configuration] excludes the dependencies that are classified by this [GranularMetadataTransformation] as
* [MetadataDependencyResolution.Exclude], and uses exactly the same versions as were resolved for the requested
* dependencies during the transformation.
*/
private fun Project.applyTransformationToLegacyDependenciesMetadataConfiguration(
configuration: Configuration,
transformation: GranularMetadataTransformation
) {
// Run this action immediately before the configuration first takes part in dependency resolution:
configuration.withDependencies {
val (unrequested, requested) = transformation.metadataDependencyResolutions
.partition { it is MetadataDependencyResolution.Exclude }
unrequested.forEach {
val (group, name) = it.projectDependency(this)?.run {
/** Note: the project dependency notation here should be exactly this, group:name,
* not from [ModuleIds.fromProjectPathDependency], as `exclude` checks it against the project's group:name */
ModuleDependencyIdentifier(group.toString(), name)
} ?: ModuleIds.fromComponent(project, it.dependency)
configuration.exclude(mapOf("group" to group, "module" to name))
}
requested.filter { it.dependency.currentBuildProjectIdOrNull == null }.forEach {
val (group, name) = ModuleIds.fromComponent(project, it.dependency)
val notation = listOfNotNull(group.orEmpty(), name, it.dependency.moduleVersion?.version).joinToString(":")
configuration.resolutionStrategy.force(notation)
}
listOf(
sourceSet.apiMetadataConfigurationName,
sourceSet.implementationMetadataConfigurationName,
sourceSet.compileOnlyMetadataConfigurationName
).forEach { configurationName ->
val configuration = project.configurations.getByName(configurationName)
configuration.extendsFrom(sourceSet.resolvableMetadataConfiguration)
configuration.shouldResolveConsistentlyWith(sourceSet.resolvableMetadataConfiguration)
}
}
@@ -24,7 +24,6 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.MetadataDependencyResolution.Choos
import org.jetbrains.kotlin.gradle.plugin.mpp.MetadataDependencyResolution.ChooseVisibleSourceSets.MetadataProvider.ProjectMetadataProvider
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.toKpmModuleIdentifiers
import org.jetbrains.kotlin.gradle.plugin.sources.DefaultKotlinSourceSet
import org.jetbrains.kotlin.gradle.plugin.sources.compileDependenciesTransformationOrFail
import org.jetbrains.kotlin.gradle.plugin.sources.internal
import org.jetbrains.kotlin.gradle.tasks.dependsOn
import org.jetbrains.kotlin.gradle.tasks.locateOrRegisterTask
@@ -156,8 +155,8 @@ internal open class CInteropMetadataDependencyTransformationTask @Inject constru
@get:Internal
protected val chooseVisibleSourceSets
get() = sourceSet
.compileDependenciesTransformationOrFail
.metadataDependencyResolutions
.compileDependenciesTransformation
.metadataDependencyResolutionsOrEmpty
.resolutionsToTransform()
@Suppress("unused")