Rework source sets metadata consumption

* Add `ResolvedMppVariantsProvider` that provides variant names,
  platform artifacts, and metadata artifacts for MPP dependencies

* Rework SourceSetVisibilityProvider.kt so that it uses the
  `ResolvedMppVariantsProvider` to determine which source sets should be
  read from which metadata artifacts

* Rework `GranularMetadataTransformation` and its consumers so that:

  * it uses the
    visibility result and extracts the source set metadata from the
    host-specific artifacts whenever `SourceSetsVisibilityProvider`
    yields host-specific artifacts in the visibility results

  * it uses the new Gradle API for resolutionResults / artifactView

* Rework module IDs in the code base, unify the logic of their
  construction, also fix possible false-positive visibility in
  `applyToConfiguration` that used a module ID that mismatched the
  published module ID.
This commit is contained in:
Sergey Igushkin
2020-03-02 19:35:09 +03:00
parent 33ef4452b7
commit 2478a57646
7 changed files with 525 additions and 189 deletions
@@ -7,6 +7,9 @@ package org.jetbrains.kotlin.gradle.plugin.mpp
import org.gradle.api.Project
import org.gradle.api.artifacts.*
import org.gradle.api.artifacts.component.*
import org.gradle.api.artifacts.result.ResolvedComponentResult
import org.gradle.api.artifacts.result.ResolvedDependencyResult
import org.gradle.api.file.FileCollection
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
@@ -20,8 +23,8 @@ import java.util.zip.ZipOutputStream
import javax.xml.parsers.DocumentBuilderFactory
internal sealed class MetadataDependencyResolution(
val dependency: ResolvedDependency,
val projectDependency: ProjectDependency?
val dependency: ResolvedComponentResult,
val projectDependency: Project?
) {
override fun toString(): String {
val verb = when (this) {
@@ -33,22 +36,22 @@ internal sealed class MetadataDependencyResolution(
}
class KeepOriginalDependency(
dependency: ResolvedDependency,
projectDependency: ProjectDependency?
dependency: ResolvedComponentResult,
projectDependency: Project?
) : MetadataDependencyResolution(dependency, projectDependency)
class ExcludeAsUnrequested(
dependency: ResolvedDependency,
projectDependency: ProjectDependency?
dependency: ResolvedComponentResult,
projectDependency: Project?
) : MetadataDependencyResolution(dependency, projectDependency)
abstract class ChooseVisibleSourceSets(
dependency: ResolvedDependency,
projectDependency: ProjectDependency?,
dependency: ResolvedComponentResult,
projectDependency: Project?,
val projectStructureMetadata: KotlinProjectStructureMetadata,
val allVisibleSourceSetNames: Set<String>,
val visibleSourceSetNamesExcludingDependsOn: Set<String>,
val visibleTransitiveDependencies: Set<ResolvedDependency>
val visibleTransitiveDependencies: Set<ResolvedDependencyResult>
) : MetadataDependencyResolution(dependency, projectDependency) {
/** Returns the mapping of source set names to files which should be used as the [dependency] parts representing the source sets.
* If any temporary files need to be created, their paths are built from the [baseDir].
@@ -64,64 +67,28 @@ internal sealed class MetadataDependencyResolution(
}
}
private typealias ModuleId = Pair<String?, String> // group ID, artifact ID
private val ResolvedDependency.moduleId: ModuleId
get() = moduleGroup to moduleName
private val Dependency.moduleId: ModuleId
get() = group to name
internal class GranularMetadataTransformation(
val project: Project,
val kotlinSourceSet: KotlinSourceSet,
/** A list of scopes that the dependencies from [kotlinSourceSet] are treated as requested dependencies. */
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 */
val allSourceSetsConfiguration: Configuration,
val parentTransformations: Lazy<Iterable<GranularMetadataTransformation>>
private val allSourceSetsConfiguration: Configuration,
private val parentTransformations: Lazy<Iterable<GranularMetadataTransformation>>
) {
val metadataDependencyResolutions: Iterable<MetadataDependencyResolution> by lazy { doTransform() }
// Keep parents of each dependency, too. We need a dependency's parent when it's an MPP's metadata module dependency:
// in this case, the parent is the MPP's root module.
private data class ResolvedDependencyWithParent(
val dependency: ResolvedDependency,
val parent: ResolvedDependency?
val dependency: ResolvedComponentResult,
val parent: ResolvedComponentResult?
)
private fun collectProjectDependencies(
requestedDependencies: Iterable<ProjectDependency>,
resolvedDependencies: Iterable<ResolvedDependency>
): Map<ModuleId, ProjectDependency> {
val result = mutableMapOf<ModuleId, ProjectDependency>()
val resolvedDependenciesMap: Map<ModuleId, ResolvedDependency> = resolvedDependencies.associateBy { it.moduleId }
fun visitProjectDependency(projectDependency: ProjectDependency) {
val moduleId = projectDependency.group to projectDependency.name
if (moduleId in result) return
result[moduleId] = projectDependency
val resolvedDependency = resolvedDependenciesMap[moduleId] ?: return
projectDependency.dependencyProject.configurations.getByName(resolvedDependency.configuration)
.allDependencies
.withType(ProjectDependency::class.java)
.forEach(::visitProjectDependency)
}
requestedDependencies.forEach(::visitProjectDependency)
return result
}
private val requestedDependencies: Iterable<Dependency> by lazy {
fun collectScopedDependenciesFromSourceSet(sourceSet: KotlinSourceSet): Set<Dependency> =
sourceSetRequestedScopes.flatMapTo(mutableSetOf()) { scope ->
project.sourceSetDependencyConfigurationByScope(sourceSet, scope).allDependencies
project.sourceSetDependencyConfigurationByScope(sourceSet, scope).incoming.dependencies
}
val ownDependencies = collectScopedDependenciesFromSourceSet(kotlinSourceSet)
@@ -130,7 +97,7 @@ internal class GranularMetadataTransformation(
ownDependencies + parentDependencies
}
private val resolvedConfiguration: LenientConfiguration by lazy {
private val configurationToResolve: Configuration by lazy {
/** If [kotlinSourceSet] 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
@@ -147,70 +114,73 @@ internal class GranularMetadataTransformation(
}
}
(modifiedConfiguration ?: allSourceSetsConfiguration).resolvedConfiguration.lenientConfiguration
modifiedConfiguration ?: allSourceSetsConfiguration
}
private fun doTransform(): Iterable<MetadataDependencyResolution> {
val result = mutableListOf<MetadataDependencyResolution>()
val parentResolutions =
parentTransformations.value.flatMap { it.metadataDependencyResolutions }.groupBy { it.dependency.moduleId }
parentTransformations.value.flatMap { it.metadataDependencyResolutions }.groupBy {
ModuleIds.fromComponent(project, it.dependency)
}
val allRequestedDependencies = requestedDependencies
val allModuleDependencies = resolvedConfiguration.allModuleDependencies
val knownProjectDependencies = collectProjectDependencies(
allRequestedDependencies.filterIsInstance<ProjectDependency>(),
allModuleDependencies
)
val resolutionResult = configurationToResolve.incoming.resolutionResult
val allModuleDependencies =
configurationToResolve.incoming.resolutionResult.allDependencies.filterIsInstance<ResolvedDependencyResult>()
val resolvedDependencyQueue: Queue<ResolvedDependencyWithParent> = ArrayDeque<ResolvedDependencyWithParent>().apply {
val requestedModules: Set<ModuleId> = allRequestedDependencies.mapTo(mutableSetOf()) { it.moduleId }
val requestedModules: Set<ModuleDependencyIdentifier> = allRequestedDependencies.mapTo(mutableSetOf()) {
ModuleIds.fromDependency(it)
}
addAll(
resolvedConfiguration.firstLevelModuleDependencies
.filter { it.moduleId in requestedModules }
.map { ResolvedDependencyWithParent(it, null) }
resolutionResult.root.dependencies
.filter { ModuleIds.fromComponentSelector(project, it.requested) in requestedModules }
.filterIsInstance<ResolvedDependencyResult>()
.map { ResolvedDependencyWithParent(it.selected, null) }
)
}
val visitedDependencies = mutableSetOf<ResolvedDependency>()
val visitedDependencies = mutableSetOf<ResolvedComponentResult>()
while (resolvedDependencyQueue.isNotEmpty()) {
val (resolvedDependency, parent: ResolvedDependency?) = resolvedDependencyQueue.poll()
val (resolvedDependency: ResolvedComponentResult, parent: ResolvedComponentResult?) = resolvedDependencyQueue.poll()
val projectDependency: ProjectDependency? = knownProjectDependencies[resolvedDependency.moduleId]
val resolvedToProject: Project? = (resolvedDependency.id as? ProjectComponentIdentifier)?.projectPath?.let(project::project)
visitedDependencies.add(resolvedDependency)
val dependencyResult = processDependency(
resolvedDependency,
parentResolutions[resolvedDependency.moduleId].orEmpty(),
parentResolutions[ModuleIds.fromComponent(project, resolvedDependency)].orEmpty(),
parent,
projectDependency
resolvedToProject
)
result.add(dependencyResult)
val transitiveDependenciesToVisit = when (dependencyResult) {
is MetadataDependencyResolution.KeepOriginalDependency -> resolvedDependency.children
is MetadataDependencyResolution.KeepOriginalDependency ->
resolvedDependency.dependencies.filterIsInstance<ResolvedDependencyResult>()
is MetadataDependencyResolution.ChooseVisibleSourceSets -> dependencyResult.visibleTransitiveDependencies
is MetadataDependencyResolution.ExcludeAsUnrequested -> error("a visited dependency is erroneously considered unrequested")
}
resolvedDependencyQueue.addAll(
transitiveDependenciesToVisit.filter { it !in visitedDependencies }
.map { ResolvedDependencyWithParent(it, resolvedDependency) }
transitiveDependenciesToVisit.filter { it.selected !in visitedDependencies }
.map { ResolvedDependencyWithParent(it.selected, resolvedDependency) }
)
}
allModuleDependencies.forEach { resolvedDependency ->
if (resolvedDependency !in visitedDependencies) {
if (resolvedDependency.selected !in visitedDependencies) {
result.add(
MetadataDependencyResolution.ExcludeAsUnrequested(
resolvedDependency,
knownProjectDependencies[resolvedDependency.moduleGroup to resolvedDependency.moduleName]
resolvedDependency.selected,
(resolvedDependency.selected.id as? ProjectComponentIdentifier)?.let { project.project(it.projectPath) }
)
)
}
@@ -234,30 +204,43 @@ internal class GranularMetadataTransformation(
* source sets in *S*, then consider only these transitive dependencies, ignore the others;
*/
private fun processDependency(
module: ResolvedDependency,
module: ResolvedComponentResult,
parentResolutionsForModule: Iterable<MetadataDependencyResolution>,
parent: ResolvedDependency?,
projectDependency: ProjectDependency?
parent: ResolvedComponentResult?,
resolvedToProject: Project?
): MetadataDependencyResolution {
val resolvedMppVariantsProvider = ResolvedMppVariantsProvider.get(project)
val mppDependencyMetadataExtractor = when {
projectDependency != null -> ProjectMppDependencyMetadataExtractor(project, module, projectDependency.dependencyProject)
parent != null -> JarArtifactMppDependencyMetadataExtractor(project, module)
else -> null
val mppDependencyMetadataExtractor = if (resolvedToProject != null) {
ProjectMppDependencyMetadataExtractor(project, module, resolvedToProject)
} else {
val metadataArtifact = resolvedMppVariantsProvider.getPlatformArtifactByPlatformModule(
ModuleIds.fromComponent(project, module),
configurationToResolve
)
if (metadataArtifact != null) {
JarArtifactMppDependencyMetadataExtractor(project, module, metadataArtifact)
} else null
}
val projectStructureMetadata = mppDependencyMetadataExtractor?.getProjectStructureMetadata()
?: return MetadataDependencyResolution.KeepOriginalDependency(module, projectDependency)
?: return MetadataDependencyResolution.KeepOriginalDependency(module, resolvedToProject)
val allVisibleSourceSets =
SourceSetVisibilityProvider(project).getVisibleSourceSetNames(
val sourceSetVisibility =
SourceSetVisibilityProvider(project).getVisibleSourceSets(
kotlinSourceSet,
sourceSetRequestedScopes,
parent ?: module,
parent, module,
projectStructureMetadata,
projectDependency?.dependencyProject
resolvedToProject
)
if (mppDependencyMetadataExtractor is JarArtifactMppDependencyMetadataExtractor) {
mppDependencyMetadataExtractor.metadataArtifactBySourceSet.putAll(sourceSetVisibility.hostSpecificMetadataArtifactBySourceSet)
}
val allVisibleSourceSets = sourceSetVisibility.visibleSourceSetNames
val sourceSetsVisibleInParents = parentResolutionsForModule
.filterIsInstance<MetadataDependencyResolution.ChooseVisibleSourceSets>()
.flatMapTo(mutableSetOf()) { it.allVisibleSourceSetNames }
@@ -265,36 +248,49 @@ internal class GranularMetadataTransformation(
// Keep only the transitive dependencies requested by the visible source sets:
// Visit the transitive dependencies visible by parents, too (i.e. allVisibleSourceSets), as this source set might get a more
// concrete view on them:
val requestedTransitiveDependencies: Set<ModuleId> =
mutableSetOf<ModuleId>().apply {
val requestedTransitiveDependencies: Set<ModuleDependencyIdentifier> =
mutableSetOf<ModuleDependencyIdentifier>().apply {
projectStructureMetadata.sourceSetModuleDependencies.forEach { (sourceSetName, moduleDependencies) ->
if (sourceSetName in allVisibleSourceSets) {
addAll(moduleDependencies.map { ModuleId(it.groupId, it.moduleId) })
addAll(moduleDependencies.map { ModuleDependencyIdentifier(it.groupId, it.moduleId) })
}
}
}
val transitiveDependenciesToVisit = module.children.filterTo(mutableSetOf()) {
(it.moduleId) in requestedTransitiveDependencies
}
val transitiveDependenciesToVisit = module.dependencies
.filterIsInstance<ResolvedDependencyResult>()
.filterTo(mutableSetOf()) { ModuleIds.fromComponent(project, it.selected) in requestedTransitiveDependencies }
val visibleSourceSetsExcludingDependsOn = allVisibleSourceSets.filterTo(mutableSetOf()) { it !in sourceSetsVisibleInParents }
return object : MetadataDependencyResolution.ChooseVisibleSourceSets(
module,
projectDependency,
projectStructureMetadata,
allVisibleSourceSets,
visibleSourceSetsExcludingDependsOn,
transitiveDependenciesToVisit
) {
override fun getMetadataFilesBySourceSet(baseDir: File, doProcessFiles: Boolean): Map<String, FileCollection> =
mppDependencyMetadataExtractor.getVisibleSourceSetsMetadata(visibleSourceSetsExcludingDependsOn, baseDir, doProcessFiles)
}
return ChooseVisibleSourceSetsImpl(
module, resolvedToProject, projectStructureMetadata, allVisibleSourceSets, visibleSourceSetsExcludingDependsOn,
transitiveDependenciesToVisit, mppDependencyMetadataExtractor
)
}
private class ChooseVisibleSourceSetsImpl(
dependency: ResolvedComponentResult,
projectDependency: Project?,
projectStructureMetadata: KotlinProjectStructureMetadata,
allVisibleSourceSetNames: Set<String>,
visibleSourceSetNamesExcludingDependsOn: Set<String>,
visibleTransitiveDependencies: Set<ResolvedDependencyResult>,
private val metadataExtractor: MppDependencyMetadataExtractor
) : MetadataDependencyResolution.ChooseVisibleSourceSets(
dependency,
projectDependency,
projectStructureMetadata,
allVisibleSourceSetNames,
visibleSourceSetNamesExcludingDependsOn,
visibleTransitiveDependencies
) {
override fun getMetadataFilesBySourceSet(baseDir: File, doProcessFiles: Boolean): Map<String, FileCollection> =
metadataExtractor.getVisibleSourceSetsMetadata(visibleSourceSetNamesExcludingDependsOn, baseDir, doProcessFiles)
}
}
private abstract class MppDependencyMetadataExtractor(val project: Project, val dependency: ResolvedDependency) {
private abstract class MppDependencyMetadataExtractor(val project: Project, val dependency: ResolvedComponentResult) {
abstract fun getProjectStructureMetadata(): KotlinProjectStructureMetadata?
abstract fun getVisibleSourceSetsMetadata(
visibleSourceSetNames: Set<String>,
@@ -305,7 +301,7 @@ private abstract class MppDependencyMetadataExtractor(val project: Project, val
private class ProjectMppDependencyMetadataExtractor(
project: Project,
dependency: ResolvedDependency,
dependency: ResolvedComponentResult,
private val dependencyProject: Project
) : MppDependencyMetadataExtractor(project, dependency) {
override fun getProjectStructureMetadata(): KotlinProjectStructureMetadata? =
@@ -323,16 +319,14 @@ private class ProjectMppDependencyMetadataExtractor(
private class JarArtifactMppDependencyMetadataExtractor(
project: Project,
dependency: ResolvedDependency
dependency: ResolvedComponentResult,
val primaryArtifact: File
) : MppDependencyMetadataExtractor(project, dependency) {
private val artifact: ResolvedArtifact?
get() = dependency.moduleArtifacts.singleOrNull { it.extension == "jar" }
val metadataArtifactBySourceSet: MutableMap<String, File> = mutableMapOf()
override fun getProjectStructureMetadata(): KotlinProjectStructureMetadata? {
val artifactFile = artifact?.file ?: return null
return ZipFile(artifactFile).use { zip ->
return ZipFile(primaryArtifact).use { zip ->
val metadata = zip.getEntry("META-INF/$MULTIPLATFORM_PROJECT_METADATA_FILE_NAME")
?: return null
@@ -349,21 +343,24 @@ private class JarArtifactMppDependencyMetadataExtractor(
baseDir: File,
doProcessFiles: Boolean
): Map<String, FileCollection> {
val jarArtifact = artifact ?: return emptyMap()
val artifactFile = jarArtifact.file
val moduleId = jarArtifact.moduleVersion.id
val primaryArtifact = primaryArtifact
val moduleId = ModuleIds.fromComponent(project, dependency)
return extractSourceSetMetadataFromJar(moduleId, visibleSourceSetNames, artifactFile, baseDir, doProcessFiles)
return extractSourceSetMetadataFromArtifacts(
moduleId,
baseDir,
doProcessFiles,
visibleSourceSetNames.associate { it to (metadataArtifactBySourceSet[it] ?: primaryArtifact) }
)
}
private fun extractSourceSetMetadataFromJar(
module: ModuleVersionIdentifier,
chooseSourceSetsByNames: Set<String>,
artifactJar: File,
private fun extractSourceSetMetadataFromArtifacts(
module: ModuleDependencyIdentifier,
baseDir: File,
doProcessFiles: Boolean
doProcessFiles: Boolean,
artifactBySourceSet: Map<String, File>
): Map<String, FileCollection> {
val moduleString = "${module.group}-${module.name}-${module.version}"
val moduleString = "${module.groupId}-${module.moduleId}"
val transformedModuleRoot = run { baseDir.resolve(moduleString).also { it.mkdirs() } }
val resultFiles = mutableMapOf<String, FileCollection>()
@@ -372,12 +369,10 @@ private class JarArtifactMppDependencyMetadataExtractor(
"can't extract metadata from a module without project structure metadata"
}
ZipFile(artifactJar).use { zip ->
val entriesBySourceSet = zip.entries().asSequence()
.groupBy { it.name.substringBefore("/") }
.filterKeys { it in chooseSourceSetsByNames }
artifactBySourceSet.forEach { (sourceSetName, artifact) ->
ZipFile(artifact).use { zip ->
val entries = zip.entries().asSequence().filter { it.name.startsWith("$sourceSetName/") }
entriesBySourceSet.forEach { (sourceSetName, entries) ->
// TODO: once IJ supports non-JAR metadata dependencies, extract to a directory, not a JAR
// Also, if both IJ and the CLI compiler can read metadata from a path inside a JAR, then no operations will be needed
val extension =
@@ -10,7 +10,6 @@ import org.gradle.api.tasks.Input
import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.Nested
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtensionOrNull
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider
import org.jetbrains.kotlin.gradle.plugin.sources.KotlinDependencyScope
import org.jetbrains.kotlin.gradle.plugin.sources.getSourceSetHierarchy
import org.jetbrains.kotlin.gradle.plugin.sources.sourceSetDependencyConfigurationByScope
@@ -22,7 +21,7 @@ import org.w3c.dom.NodeList
import javax.xml.parsers.DocumentBuilderFactory
data class ModuleDependencyIdentifier(
val groupId: String,
val groupId: String?,
val moduleId: String
)
@@ -68,7 +67,7 @@ data class KotlinProjectStructureMetadata(
@Suppress("UNUSED") // Gradle input
@get:Input
internal val sourceSetModuleDependenciesInput: Map<String, Set<Pair<String, String>>>
get() = sourceSetModuleDependencies.mapValues { (_, ids) -> ids.map { (group, module) -> group to module }.toSet() }
get() = sourceSetModuleDependencies.mapValues { (_, ids) -> ids.map { (group, module) -> group.orEmpty() to module }.toSet() }
companion object {
internal const val FORMAT_VERSION_0_1 = "0.1"
@@ -111,9 +110,7 @@ internal fun buildKotlinProjectStructureMetadata(project: Project): KotlinProjec
}.distinct()
else -> project.configurations.getByName(sourceSet.apiConfigurationName).allDependencies
}
sourceSet.name to sourceSetExportedDependencies.map {
ModuleDependencyIdentifier(it.group.orEmpty(), it.name)
}.toSet()
sourceSet.name to sourceSetExportedDependencies.map { ModuleIds.fromDependency(it) }.toSet()
},
hostSpecificSourceSets = getHostSpecificSourceSets(project).map { it.name }.toSet(),
sourceSetBinaryLayout = sourceSetsWithMetadataCompilations.keys.associate { sourceSet ->
@@ -0,0 +1,59 @@
/*
* 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.gradle.plugin.mpp
import org.gradle.api.Project
import org.gradle.api.artifacts.Dependency
import org.gradle.api.artifacts.ProjectDependency
import org.gradle.api.artifacts.component.*
import org.gradle.api.artifacts.result.ResolvedComponentResult
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtensionOrNull
internal object ModuleIds {
fun fromDependency(dependency: Dependency) = when (dependency) {
is ProjectDependency -> idOfRootModule(dependency.dependencyProject)
else -> ModuleDependencyIdentifier(dependency.group, dependency.name)
}
fun fromComponentSelector(
thisProject: Project,
componentSelector: ComponentSelector
): ModuleDependencyIdentifier = when (componentSelector) {
is ProjectComponentSelector -> idOfRootModuleByProjectPath(thisProject, componentSelector.projectPath)
is ModuleComponentSelector -> ModuleDependencyIdentifier(componentSelector.group, componentSelector.module)
else -> idFromName(componentSelector.displayName)
}
fun fromComponentId(
thisProject: Project,
componentIdentifier: ComponentIdentifier
): ModuleDependencyIdentifier =
when (componentIdentifier) {
is ProjectComponentIdentifier -> idOfRootModuleByProjectPath(thisProject, componentIdentifier.projectPath)
is ModuleComponentIdentifier -> ModuleDependencyIdentifier(componentIdentifier.group, componentIdentifier.module)
else -> idFromName(componentIdentifier.displayName)
}
fun fromComponent(thisProject: Project, component: ResolvedComponentResult) =
fromComponentId(thisProject, component.id)
private fun idOfRootModule(project: Project): ModuleDependencyIdentifier =
if (project.multiplatformExtensionOrNull != null) {
val rootPublication = project.multiplatformExtension.rootSoftwareComponent.publicationDelegate
val group = rootPublication?.groupId ?: project.group.toString()
val name = rootPublication?.artifactId ?: project.name
ModuleDependencyIdentifier(group, name)
} else {
ModuleDependencyIdentifier(project.group.toString(), project.name)
}
private fun idFromName(name: String) =
ModuleDependencyIdentifier(null, name)
private fun idOfRootModuleByProjectPath(thisProject: Project, projectPath: String): ModuleDependencyIdentifier =
idOfRootModule(thisProject.project(projectPath))
}
@@ -0,0 +1,218 @@
/*
* 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.gradle.plugin.mpp
import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.component.ComponentIdentifier
import org.gradle.api.artifacts.component.ProjectComponentIdentifier
import org.gradle.api.artifacts.result.ResolvedArtifactResult
import org.gradle.api.artifacts.result.ResolvedComponentResult
import org.gradle.api.artifacts.result.ResolvedDependencyResult
import org.gradle.api.artifacts.result.ResolvedVariantResult
import org.gradle.api.attributes.Usage
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
import org.jetbrains.kotlin.gradle.plugin.usageByName
import org.jetbrains.kotlin.gradle.utils.isGradleVersionAtLeast
import java.io.File
internal class ResolvedMppVariantsProvider private constructor(private val project: Project) {
companion object {
fun get(project: Project): ResolvedMppVariantsProvider = with(project.extensions.extraProperties) {
val propertyName = "kotlin.mpp.internal.resolvedModuleVariantsProvider"
if (!has(propertyName)) {
set(propertyName, ResolvedMppVariantsProvider(project))
}
@Suppress("UNCHECKED_CAST")
get(propertyName) as ResolvedMppVariantsProvider
}
}
/** Gets the name of the variant that the module specified by the [moduleIdentifier] resolved to in the given [configuration].
* The [moduleIdentifier] may be either the root module or a platform-specific module, the result is the same for the two cases. */
fun getResolvedVariantName(moduleIdentifier: ModuleDependencyIdentifier, configuration: Configuration): String? =
getEntryForModule(moduleIdentifier).run {
if (configuration !in resolvedVariantsByConfiguration) {
resolveConfigurationAndSaveVariants(configuration, artifactResolutionMode = ArtifactResolutionMode.NONE)
}
val variants = resolvedVariantsByConfiguration.getOrPut(configuration) { null }
variants?.singleOrNull()?.displayName
}
/** Gets the artifact that contains the common code metadata for the given [rootModuleIdentifier], which can only denote the root
* module of a multiplatform project, not one of its platform-specific modules, as seen in the given [configuration].
* If the [configuration] requests platform artifacts and not the common code metadata, then this function will resolve its
* dependencies to metadata separately. */
fun getMetadataArtifactByRootModule(rootModuleIdentifier: ModuleDependencyIdentifier, configuration: Configuration): File? {
val rootModuleEntry = getEntryForModule(rootModuleIdentifier)
val platformModuleEntry = rootModuleEntry.run {
if (configuration !in chosenPlatformModuleByConfiguration) {
resolveConfigurationAndSaveVariants(configuration, artifactResolutionMode = ArtifactResolutionMode.METADATA)
}
chosenPlatformModuleByConfiguration.getOrPut(configuration) { null }
}
return platformModuleEntry?.run {
// The condition might be true if the configuration has only been resolved with resolution mode NORMAL
if (configuration !in resolvedMetadataArtifactByConfiguration) {
resolveConfigurationAndSaveVariants(configuration, artifactResolutionMode = ArtifactResolutionMode.METADATA)
}
resolvedMetadataArtifactByConfiguration.getOrPut(configuration) { null }
}
}
/** Gets the artifact of a particular MPP platform-specific [moduleIdentifier] as resolved in the [configuration]. */
fun getPlatformArtifactByPlatformModule(moduleIdentifier: ModuleDependencyIdentifier, configuration: Configuration): File? =
getEntryForModule(moduleIdentifier).run {
if (configuration !in resolvedArtifactByConfiguration) {
resolveConfigurationAndSaveVariants(configuration, artifactResolutionMode = ArtifactResolutionMode.NORMAL)
}
resolvedArtifactByConfiguration.getOrPut(configuration) { null }
}
private fun getEntryForModule(moduleIdentifier: ModuleDependencyIdentifier) =
entriesCache.getOrPut(moduleIdentifier, { ModuleEntry(moduleIdentifier) })
private val entriesCache: MutableMap<ModuleDependencyIdentifier, ModuleEntry> = mutableMapOf()
private val mppComponentIdsByConfiguration: MutableMap<Configuration, Set<ComponentIdentifier>> = mutableMapOf()
private enum class ArtifactResolutionMode {
NONE, NORMAL, METADATA
}
private fun resolveConfigurationAndSaveVariants(
configuration: Configuration,
artifactResolutionMode: ArtifactResolutionMode
) {
val mppComponentIds: Set<ComponentIdentifier> = mppComponentIdsByConfiguration.getOrPut(configuration) {
resolveMppComponents(configuration)
}
if (artifactResolutionMode != ArtifactResolutionMode.NONE) {
val artifacts = resolveArtifacts(artifactResolutionMode, configuration, mppComponentIds)
matchMppComponentsWithResolvedArtifacts(mppComponentIds, artifacts, configuration, artifactResolutionMode)
}
}
private fun resolveMppComponents(configuration: Configuration): MutableSet<ComponentIdentifier> {
val result = mutableListOf<ResolvedComponentResult>()
configuration.incoming.resolutionResult.allComponents { component ->
val moduleId = ModuleIds.fromComponent(project, component)
val variants = component.variantsCompatible
val isMpp = variants.any { variant -> variant.attributes.keySet().any { it.name == KotlinPlatformType.attribute.name } }
if (isMpp) {
result.add(component)
val moduleEntry = getEntryForModule(moduleId)
moduleEntry.resolvedVariantsByConfiguration[configuration] = variants
moduleEntry.dependenciesByConfiguration[configuration] = component.dependencies
.filterIsInstance<ResolvedDependencyResult>()
.map { dependency -> ModuleIds.fromComponent(project, dependency.selected) }
if (component.id is ProjectComponentIdentifier) {
// Then the platform variant chosen for this module is definitely inside the module itself:
moduleEntry.chosenPlatformModuleByConfiguration[configuration] = moduleEntry
}
}
}
return result.mapTo(mutableSetOf()) { it.id }
}
private fun resolveArtifacts(
artifactResolutionMode: ArtifactResolutionMode,
configuration: Configuration,
mppComponentIds: Set<ComponentIdentifier>
): Map<ComponentIdentifier, ResolvedArtifactResult> {
val artifactsConfiguration =
if (
artifactResolutionMode == ArtifactResolutionMode.NORMAL ||
configuration.attributes.getAttribute(Usage.USAGE_ATTRIBUTE)?.name == KotlinUsages.KOTLIN_METADATA
) {
configuration
} else {
configuration.copyRecursive().apply {
attributes.attribute(Usage.USAGE_ATTRIBUTE, project.usageByName(KotlinUsages.KOTLIN_METADATA))
}
}
return artifactsConfiguration.incoming.artifactView { view ->
view.componentFilter { it in mppComponentIds }
view.attributes { attrs -> attrs.attribute(Usage.USAGE_ATTRIBUTE, project.usageByName(KotlinUsages.KOTLIN_METADATA)) }
}.artifacts.associateBy { it.id.componentIdentifier }
}
private fun matchMppComponentsWithResolvedArtifacts(
mppComponentIds: Set<ComponentIdentifier>,
artifacts: Map<ComponentIdentifier, ResolvedArtifactResult>,
configuration: Configuration,
artifactResolutionMode: ArtifactResolutionMode
) {
val mppModuleIds = mppComponentIds.mapTo(mutableSetOf()) { ModuleIds.fromComponentId(project, it) }
mppComponentIds.forEach { componentId ->
val moduleEntry = getEntryForModule(ModuleIds.fromComponentId(project, componentId))
val artifact = artifacts[componentId]
when {
// With project dependencies, we don't need the host-specific metadata artifacts, as we have the compilation outputs:
componentId is ProjectComponentIdentifier -> {
moduleEntry.resolvedMetadataArtifactByConfiguration[configuration] = null
}
// We found the host-specific artifact for a platform variant of some MPP:
artifact != null -> {
val resolvedArtifactMap = when (artifactResolutionMode) {
ArtifactResolutionMode.NORMAL -> moduleEntry.resolvedArtifactByConfiguration
ArtifactResolutionMode.METADATA -> moduleEntry.resolvedMetadataArtifactByConfiguration
else -> error("unexpected $artifactResolutionMode")
}
resolvedArtifactMap[configuration] = artifact.file
}
// Otherwise, this may be a root module of some MPP that resolved to a variant in another module. Take a note of that.
else -> {
// TODO: there's an assumption that resolving a root MPP module to a host-specific metadata artifact and to a platform
// artifact will choose variants that are published within the same Maven module; change this code if that's not
// true anymore.
val singleDependencyId = moduleEntry.dependenciesByConfiguration.getValue(configuration).singleOrNull()
if (singleDependencyId != null && singleDependencyId in mppModuleIds) {
moduleEntry.chosenPlatformModuleByConfiguration[configuration] =
getEntryForModule(singleDependencyId)
}
}
}
}
}
/**
* Stores resolution results of the module denoted by [moduleIdentifier] in different configurations of the project.
* The [moduleIdentifier] may point to a root module of a multiplatform project (then it has meaningful
* [chosenPlatformModuleByConfiguration]) or to a platform module.
*/
private class ModuleEntry(
@Suppress("unused") // simplify debugging
val moduleIdentifier: ModuleDependencyIdentifier
) {
val dependenciesByConfiguration: MutableMap<Configuration, List<ModuleDependencyIdentifier>> = HashMap()
val resolvedVariantsByConfiguration: MutableMap<Configuration, List<ResolvedVariantResult?>?> = HashMap()
val resolvedArtifactByConfiguration: MutableMap<Configuration, File?> = HashMap()
val resolvedMetadataArtifactByConfiguration: MutableMap<Configuration, File?> = HashMap()
val chosenPlatformModuleByConfiguration: MutableMap<Configuration, ModuleEntry?> = HashMap()
}
}
private val ResolvedComponentResult.variantsCompatible: List<ResolvedVariantResult>
get() = if (isGradleVersionAtLeast(5, 2)) {
variants
} else {
@Suppress("DEPRECATION")
listOf(variant)
}
@@ -7,76 +7,135 @@ package org.jetbrains.kotlin.gradle.plugin.mpp
import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.ResolvedDependency
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.sources.KotlinDependencyScope
import org.jetbrains.kotlin.gradle.targets.metadata.getPublishedPlatformCompilations
import java.io.File
internal data class SourceSetVisibilityResult(
/**
* Names of source sets that the consumer sees from the requested dependency.
*/
val visibleSourceSetNames: Set<String>,
/**
* For some of the [visibleSourceSetNames], additional artifacts may be present that
* the consumer should read the compiled source set metadata from.
*/
val hostSpecificMetadataArtifactBySourceSet: Map<String, File>
)
internal class SourceSetVisibilityProvider(
private val project: Project
) {
private val resolvedVariantsProvider = ResolvedMppVariantsProvider.get(project)
/**
* Determine which source sets of the [resolvedMppDependency] are visible in the [visibleFrom] source set.
* Determine which source sets of the [resolvedRootMppDependency] are visible in the [visibleFrom] source set.
*
* This requires resolving dependencies of the compilations which [visibleFrom] takes part in, in order to find which variants the
* [resolvedMppDependency] got resolved to for those compilations. The [resolvedMppDependency] should therefore be the dependency
* [resolvedRootMppDependency] got resolved to for those compilations. The [resolvedRootMppDependency] should therefore be the dependency
* on the 'root' module of the MPP (such as 'com.example:lib-foo', not 'com.example:lib-foo-metadata').
*
* Once the variants are known, they are checked against the [dependencyProjectStructureMetadata], and the
* source sets of the dependency are determined that are compiled for all those variants and thus should be visible here.
*
* If the [resolvedMppDependency] is a project dependency, its project should be passed as [resolvedToOtherProject], as
* If the [resolvedRootMppDependency] is a project dependency, its project should be passed as [resolvedToOtherProject], as
* the Gradle API for dependency variants behaves differently for project dependencies and published ones.
*/
fun getVisibleSourceSetNames(
fun getVisibleSourceSets(
visibleFrom: KotlinSourceSet,
dependencyScopes: Iterable<KotlinDependencyScope>,
resolvedMppDependency: ResolvedDependency,
resolvedRootMppDependency: ResolvedComponentResult?,
resolvedMetadataDependency: ResolvedComponentResult,
dependencyProjectStructureMetadata: KotlinProjectStructureMetadata,
resolvedToOtherProject: Project?
): Set<String> {
): SourceSetVisibilityResult {
val compilations = CompilationSourceSetUtil.compilationsBySourceSets(project).getValue(visibleFrom)
var visiblePlatformVariantNames: Set<String> =
val mppModuleIdentifier = ModuleIds.fromComponent(project, resolvedRootMppDependency ?: resolvedMetadataDependency)
/**
* When Gradle resolves a project dependency, as opposed to an external dependency, the variant name it returns in the resolution
* results is the name of the configuration that is chosen during variant-aware resolution, not the actual name of the variant that
* is written to the Gradle module metadata and the Kotlin project structure metadata. To fix this, get the published Kotlin
* variants of the dependency project and find among them the one that owns the configuration by the given name.
*/
val projectPublishedCompilations = resolvedToOtherProject?.let(::getPublishedPlatformCompilations)?.keys
fun platformVariantName(resolvedToVariantName: String): String =
projectPublishedCompilations?.first { it.dependencyConfigurationName == resolvedToVariantName }?.name ?: resolvedToVariantName
val firstConfigurationByVariant = mutableMapOf<String, Configuration>()
val visiblePlatformVariantNames: Set<String> =
compilations
.filter { it.target.platformType != KotlinPlatformType.common }
.flatMapTo(mutableSetOf()) { compilation ->
val configurations = dependencyScopes.mapNotNullTo(mutableSetOf()) { scope ->
project.resolvableConfigurationFromCompilationByScope(compilation, scope)
}
configurations.mapNotNull { configuration ->
// Resolve the configuration but don't trigger artifacts download, only download component metadata:
configuration.incoming.resolutionResult.allComponents
.find {
it.moduleVersion?.group == resolvedMppDependency.moduleGroup &&
it.moduleVersion?.name == resolvedMppDependency.moduleName
}
?.variant?.displayName
}
// To find out which variant the MPP dependency got resolved for each compilation, take the resolvable configurations
// that we have in the compilations:
dependencyScopes.mapNotNull { scope -> project.resolvableConfigurationFromCompilationByScope(compilation, scope) }
}
.mapNotNullTo(mutableSetOf()) { configuration ->
val resolvedVariant = resolvedVariantsProvider.getResolvedVariantName(mppModuleIdentifier, configuration)
?.let { platformVariantName(it) }
?: return@mapNotNullTo null
firstConfigurationByVariant.putIfAbsent(resolvedVariant, configuration)
resolvedVariant
}
if (visiblePlatformVariantNames.isEmpty()) {
return emptySet()
return SourceSetVisibilityResult(emptySet(), emptyMap())
}
if (resolvedToOtherProject != null) {
val publishedVariants = getPublishedPlatformCompilations(resolvedToOtherProject).keys
visiblePlatformVariantNames = visiblePlatformVariantNames.mapTo(mutableSetOf()) { configurationName ->
publishedVariants.first { it.dependencyConfigurationName == configurationName }.name
}
}
return dependencyProjectStructureMetadata.sourceSetNamesByVariantName
val visibleSourceSetNames = dependencyProjectStructureMetadata.sourceSetNamesByVariantName
.filterKeys { it in visiblePlatformVariantNames }
.values.let { if (it.isEmpty()) emptySet() else it.reduce { acc, item -> acc intersect item } }
val hostSpecificArtifactBySourceSet: Map<String, File> =
if (resolvedToOtherProject != null) {
/**
* When a dependency resolves to a project, we don't need any artifacts from it, we can
* instead use the compilation outputs directly:
*/
emptyMap()
} else {
val hostSpecificSourceSets = visibleSourceSetNames.intersect(dependencyProjectStructureMetadata.hostSpecificSourceSets)
/**
* As all of the variants normally contain the same metadata for each of the relevant host-specific source sets,
* any of the variants that we resolved can be used, so choose the first one that satisfies both:
*
* - it contains the host-specific source set, and
* - we have resolved it for some compilation
*/
val someVariantByHostSpecificSourceSet =
hostSpecificSourceSets.associate { sourceSetName ->
sourceSetName to dependencyProjectStructureMetadata.sourceSetNamesByVariantName
.filterKeys { it in firstConfigurationByVariant }
.filterValues { sourceSetName in it }
.keys.first()
}
someVariantByHostSpecificSourceSet.mapValues { (_, variantName) ->
val configuration = firstConfigurationByVariant.getValue(variantName)
resolvedVariantsProvider.getMetadataArtifactByRootModule(mppModuleIdentifier, configuration)
?: error("Couldn't resolve metadata artifact for $mppModuleIdentifier in $configuration")
}
}
return SourceSetVisibilityResult(
visibleSourceSetNames,
hostSpecificArtifactBySourceSet
)
}
}
internal fun Project.resolvableConfigurationFromCompilationByScope(
private fun Project.resolvableConfigurationFromCompilationByScope(
compilation: KotlinCompilation<*>,
scope: KotlinDependencyScope
): Configuration? {
@@ -89,5 +148,4 @@ internal fun Project.resolvableConfigurationFromCompilationByScope(
}
return project.configurations.getByName(configurationName)
}
}
@@ -16,9 +16,8 @@ import org.jetbrains.kotlin.build.DEFAULT_KOTLIN_SOURCE_FILES_EXTENSIONS
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.DefaultKotlinDependencyHandler
import org.jetbrains.kotlin.gradle.plugin.mpp.*
import org.jetbrains.kotlin.gradle.plugin.mpp.GranularMetadataTransformation
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinProjectStructureMetadata
import org.jetbrains.kotlin.gradle.plugin.mpp.MetadataDependencyResolution
import org.jetbrains.kotlin.gradle.utils.*
import java.io.File
@@ -156,7 +155,7 @@ class DefaultKotlinSourceSet(
internal fun getDependenciesTransformation(scope: KotlinDependencyScope): Iterable<MetadataDependencyTransformation> {
val metadataDependencyResolutionByModule =
dependencyTransformations[scope]?.metadataDependencyResolutions
?.associateBy { it.dependency.moduleGroup to it.dependency.moduleName }
?.associateBy { ModuleIds.fromComponent(project, it.dependency) }
?: emptyMap()
val baseDir = project.buildDir.resolve("tmp/kotlinMetadata/$name/${scope.scopeName}")
@@ -169,7 +168,7 @@ class DefaultKotlinSourceSet(
return metadataDependencyResolutionByModule.mapNotNull { (groupAndName, resolution) ->
val (group, name) = groupAndName
val projectPath = resolution.projectDependency?.dependencyProject?.path
val projectPath = resolution.projectDependency?.path
when (resolution) {
is MetadataDependencyResolution.KeepOriginalDependency -> null
@@ -31,7 +31,6 @@ import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
import org.jetbrains.kotlin.gradle.utils.setArchiveAppendixCompatible
import org.jetbrains.kotlin.gradle.utils.setArchiveClassifierCompatible
import org.jetbrains.kotlin.statistics.metrics.BooleanMetrics
import java.io.File
import java.util.concurrent.Callable
internal const val ALL_COMPILE_METADATA_CONFIGURATION_NAME = "allSourceSetsCompileDependenciesMetadata"
@@ -374,17 +373,25 @@ class KotlinMetadataTargetConfigurator(kotlinPluginVersion: String) :
val (unrequested, requested) = metadataDependencyResolutions
.partition { it is MetadataDependencyResolution.ExcludeAsUnrequested }
unrequested.forEach { configuration.exclude(mapOf("group" to it.dependency.moduleGroup, "module" to it.dependency.moduleName)) }
unrequested.forEach {
val (group, name) = it.projectDependency?.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.forEach {
val notation = listOf(it.dependency.moduleGroup, it.dependency.moduleName, it.dependency.moduleVersion).joinToString(":")
requested.filter { it.projectDependency == 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)
}
}
}
private fun createTransformedMetadataClasspath(
fromFiles: Iterable<File>,
fromFiles: Configuration,
compilation: AbstractKotlinCompilation<*>
): FileCollection {
val project = compilation.target.project
@@ -400,17 +407,12 @@ class KotlinMetadataTargetConfigurator(kotlinPluginVersion: String) :
project.locateTask<TransformKotlinGranularMetadata>(transformGranularMetadataTaskName(hierarchySourceSet.name))
}
val allResolutionsByArtifactFile: Map<File, Iterable<MetadataDependencyResolution>> =
mutableMapOf<File, MutableList<MetadataDependencyResolution>>().apply {
val allResolutionsByModule: Map<ModuleDependencyIdentifier, List<MetadataDependencyResolution>> =
mutableMapOf<ModuleDependencyIdentifier, MutableList<MetadataDependencyResolution>>().apply {
transformationTaskHolders.forEach {
val resolutions = it.get().metadataDependencyResolutions
resolutions.forEach { resolution ->
val artifacts = resolution.dependency.moduleArtifacts.map { it.file }
artifacts.forEach { artifactFile ->
getOrPut(artifactFile) { mutableListOf() }.add(resolution)
}
getOrPut(ModuleIds.fromComponent(project, resolution.dependency)) { mutableListOf() }.add(resolution)
}
}
}
@@ -423,14 +425,22 @@ class KotlinMetadataTargetConfigurator(kotlinPluginVersion: String) :
dependencyCompilation.output.classesDirs.takeIf { hierarchySourceSet != sourceSet }
}
val artifactView = fromFiles.incoming.artifactView { view ->
view.componentFilter { id ->
val moduleId = ModuleIds.fromComponentId(project, id)
allResolutionsByModule[moduleId].let { resolutions ->
resolutions == null || resolutions.any { it !is MetadataDependencyResolution.ExcludeAsUnrequested }
}
}
}
mutableSetOf<Any /* File | FileCollection */>().apply {
addAll(dependsOnCompilationOutputs)
fromFiles.forEach { file ->
val resolutions = allResolutionsByArtifactFile[file]
artifactView.artifacts.forEach { artifact ->
val resolutions =
allResolutionsByModule[ModuleIds.fromComponentId(project, artifact.id.componentIdentifier)]
if (resolutions == null) {
add(file)
add(artifact.file)
} else {
val chooseVisibleSourceSets =
resolutions.filterIsInstance<MetadataDependencyResolution.ChooseVisibleSourceSets>()
@@ -439,7 +449,7 @@ class KotlinMetadataTargetConfigurator(kotlinPluginVersion: String) :
// Wrap the list into a FileCollection, as some older Gradle version failed to resolve the classpath
add(project.files(chooseVisibleSourceSets.map { transformedFilesByResolution.getValue(it) }))
} else if (resolutions.any { it is MetadataDependencyResolution.KeepOriginalDependency }) {
add(file)
add(artifact.file)
} // else: all dependency transformations exclude this dependency as unrequested; don't add any files
}
}