[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.Project
import org.gradle.api.artifacts.Configuration 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.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.plugin.mpp.pm20.util.copyAttributes
import org.jetbrains.kotlin.gradle.utils.markResolvable import org.jetbrains.kotlin.gradle.utils.markResolvable
import org.jetbrains.kotlin.gradle.plugin.sources.InternalKotlinSourceSet 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.METADATA_CONFIGURATION_NAME_SUFFIX
import org.jetbrains.kotlin.gradle.plugin.sources.disambiguateName import org.jetbrains.kotlin.gradle.plugin.sources.disambiguateName
import org.jetbrains.kotlin.gradle.plugin.sources.getVisibleSourceSetsFromAssociateCompilations 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.listProperty
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
import org.jetbrains.kotlin.tooling.core.extrasLazyProperty 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 */ /* Ensure consistent dependency resolution result within the whole module */
configuration.shouldResolveConsistentlyWith(allCompileMetadataConfiguration) configuration.shouldResolveConsistentlyWith(allCompileMetadataConfiguration)
@@ -65,6 +71,16 @@ internal val InternalKotlinSourceSet.resolvableMetadataConfiguration: Configurat
configuration 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>> { private inline fun <reified T> Project.listProvider(noinline provider: () -> List<T>): Provider<List<T>> {
return project.objects.listProperty<T>().apply { return project.objects.listProperty<T>().apply {
set(project.provider(provider)) set(project.provider(provider))
@@ -9,13 +9,16 @@ package org.jetbrains.kotlin.gradle.plugin.sources
import org.gradle.api.Action import org.gradle.api.Action
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration
import org.gradle.api.file.SourceDirectorySet import org.gradle.api.file.SourceDirectorySet
import org.jetbrains.kotlin.build.DEFAULT_KOTLIN_SOURCE_FILES_EXTENSIONS import org.jetbrains.kotlin.build.DEFAULT_KOTLIN_SOURCE_FILES_EXTENSIONS
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension 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.KotlinDependencyHandler
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
import org.jetbrains.kotlin.gradle.plugin.LanguageSettingsBuilder import org.jetbrains.kotlin.gradle.plugin.LanguageSettingsBuilder
import org.jetbrains.kotlin.gradle.plugin.mpp.* 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.gradle.utils.*
import org.jetbrains.kotlin.tooling.core.MutableExtras import org.jetbrains.kotlin.tooling.core.MutableExtras
import org.jetbrains.kotlin.tooling.core.closure import org.jetbrains.kotlin.tooling.core.closure
@@ -125,7 +128,9 @@ abstract class DefaultKotlinSourceSet @Inject constructor(
* Returns [GranularMetadataTransformation] for all requested compile dependencies * Returns [GranularMetadataTransformation] for all requested compile dependencies
* scopes: API, IMPLEMENTATION, COMPILE_ONLY; See [KotlinDependencyScope.compileScopes] * 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>() 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 } } return sourceSet.closure { seedSourceSet -> sourceSets.filter { otherSourceSet -> seedSourceSet in otherSourceSet.dependsOn } }
} }
internal val DefaultKotlinSourceSet.compileDependenciesTransformationOrFail: GranularMetadataTransformation private fun DefaultKotlinSourceSet.createAndConfigureDependencyTransformationForSourceSet(): GranularMetadataTransformation? {
get() = compileDependenciesTransformation // Create only for source sets in multiplatform plugin
?: error("Accessing Compile Dependencies Transformations that is not yet initialised; " + project.multiplatformExtensionOrNull ?: return null
"Check when compileDependenciesTransformation is set")
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 package org.jetbrains.kotlin.gradle.targets.metadata
import org.gradle.api.Project 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.artifacts.result.ResolvedArtifactResult
import org.gradle.api.attributes.Category import org.gradle.api.attributes.Category
import org.gradle.api.attributes.Category.CATEGORY_ATTRIBUTE 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.commonizer.SharedCommonizerTarget
import org.jetbrains.kotlin.gradle.dsl.* import org.jetbrains.kotlin.gradle.dsl.*
import org.jetbrains.kotlin.gradle.plugin.* 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.*
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.* import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.*
import org.jetbrains.kotlin.gradle.plugin.sources.* import org.jetbrains.kotlin.gradle.plugin.sources.*
@@ -92,7 +88,7 @@ class KotlinMetadataTargetConfigurator :
configureProjectStructureMetadataGeneration(target.project, allMetadataJar) configureProjectStructureMetadataGeneration(target.project, allMetadataJar)
setupDependencyTransformationForCommonSourceSets(target) configureMetadataDependenciesConfigurationsForCommonSourceSets(target)
target.project.configurations.getByName(target.apiElementsConfigurationName).run { target.project.configurations.getByName(target.apiElementsConfigurationName).run {
attributes.attribute(USAGE_ATTRIBUTE, target.project.usageByName(KotlinUsages.KOTLIN_METADATA)) attributes.attribute(USAGE_ATTRIBUTE, target.project.usageByName(KotlinUsages.KOTLIN_METADATA))
@@ -166,10 +162,12 @@ class KotlinMetadataTargetConfigurator :
return result return result
} }
private fun setupDependencyTransformationForCommonSourceSets(target: KotlinMetadataTarget) { private fun configureMetadataDependenciesConfigurationsForCommonSourceSets(target: KotlinMetadataTarget) {
target.project.whenEvaluated { target.project.whenEvaluated {
kotlinExtension.sourceSets.all { 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, project: Project,
sourceSet: KotlinSourceSet sourceSet: DefaultKotlinSourceSet
) { ) {
val parentSourceSetVisibilityProvider = ParentSourceSetVisibilityProvider { componentIdentifier -> /*
dependsOnClosureWithInterCompilationDependencies(sourceSet).filterIsInstance<DefaultKotlinSourceSet>() Older IDEs still rely on resolving the metadata configurations explicitly.
.flatMap { it.compileDependenciesTransformationOrFail.visibleSourceSetsByComponentId[componentIdentifier].orEmpty() } Dependencies will be coming from extending the newer 'resolvableMetadataConfiguration'.
.toSet()
}
val granularMetadataTransformation = GranularMetadataTransformation(
params = GranularMetadataTransformation.Params(project, sourceSet),
parentSourceSetVisibilityProvider = parentSourceSetVisibilityProvider
)
the intransitiveMetadataConfigurationName will not extend this mechanism, since it only
relies on dependencies being added explicitly by the Kotlin Gradle Plugin
*/
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
if (sourceSet is DefaultKotlinSourceSet) { listOf(
sourceSet.compileDependenciesTransformation = granularMetadataTransformation sourceSet.apiMetadataConfigurationName,
sourceSet.implementationMetadataConfigurationName,
/* sourceSet.compileOnlyMetadataConfigurationName
Older IDEs still rely on resolving the metadata configurations explicitly. ).forEach { configurationName ->
Dependencies will be coming from extending the newer 'resolvableMetadataConfiguration'. val configuration = project.configurations.getByName(configurationName)
configuration.extendsFrom(sourceSet.resolvableMetadataConfiguration)
the intransitiveMetadataConfigurationName will not extend this mechanism, since it only configuration.shouldResolveConsistentlyWith(sourceSet.resolvableMetadataConfiguration)
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)
}
} }
} }
@@ -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.MetadataDependencyResolution.ChooseVisibleSourceSets.MetadataProvider.ProjectMetadataProvider
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.toKpmModuleIdentifiers import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.toKpmModuleIdentifiers
import org.jetbrains.kotlin.gradle.plugin.sources.DefaultKotlinSourceSet 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.plugin.sources.internal
import org.jetbrains.kotlin.gradle.tasks.dependsOn import org.jetbrains.kotlin.gradle.tasks.dependsOn
import org.jetbrains.kotlin.gradle.tasks.locateOrRegisterTask import org.jetbrains.kotlin.gradle.tasks.locateOrRegisterTask
@@ -156,8 +155,8 @@ internal open class CInteropMetadataDependencyTransformationTask @Inject constru
@get:Internal @get:Internal
protected val chooseVisibleSourceSets protected val chooseVisibleSourceSets
get() = sourceSet get() = sourceSet
.compileDependenciesTransformationOrFail .compileDependenciesTransformation
.metadataDependencyResolutions .metadataDependencyResolutionsOrEmpty
.resolutionsToTransform() .resolutionsToTransform()
@Suppress("unused") @Suppress("unused")