[Gradle][MPP] Implement cinterop metadata dependency transformation
^KT-46198 Verification Pending
This commit is contained in:
committed by
Space
parent
9e1fd37685
commit
86296a8ced
+98
@@ -0,0 +1,98 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2021 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.jetbrains.kotlin.gradle.utils.copyZipFilePartially
|
||||||
|
import org.jetbrains.kotlin.gradle.utils.listChildren
|
||||||
|
import java.io.File
|
||||||
|
import java.util.zip.ZipFile
|
||||||
|
|
||||||
|
internal fun CompositeMetadataJar(
|
||||||
|
moduleIdentifier: String,
|
||||||
|
projectStructureMetadata: KotlinProjectStructureMetadata,
|
||||||
|
primaryArtifactFile: File,
|
||||||
|
hostSpecificArtifactsBySourceSet: Map<String, File>,
|
||||||
|
): CompositeMetadataJar = CompositeMetadataJarImpl(
|
||||||
|
moduleIdentifier, projectStructureMetadata, primaryArtifactFile, hostSpecificArtifactsBySourceSet
|
||||||
|
)
|
||||||
|
|
||||||
|
internal interface CompositeMetadataJar {
|
||||||
|
fun getSourceSetCompiledMetadata(
|
||||||
|
sourceSetName: String, outputDirectory: File, materializeFile: Boolean
|
||||||
|
): File
|
||||||
|
|
||||||
|
fun getSourceSetCInteropMetadata(
|
||||||
|
sourceSetName: String, outputDirectory: File, materializeFiles: Boolean
|
||||||
|
): Set<File>
|
||||||
|
}
|
||||||
|
|
||||||
|
private class CompositeMetadataJarImpl(
|
||||||
|
private val moduleIdentifier: String,
|
||||||
|
private val projectStructureMetadata: KotlinProjectStructureMetadata,
|
||||||
|
private val primaryArtifactFile: File,
|
||||||
|
private val hostSpecificArtifactsBySourceSet: Map<String, File>,
|
||||||
|
) : CompositeMetadataJar {
|
||||||
|
|
||||||
|
override fun getSourceSetCompiledMetadata(
|
||||||
|
sourceSetName: String, outputDirectory: File, materializeFile: Boolean
|
||||||
|
): File {
|
||||||
|
val artifactFile = getArtifactFile(sourceSetName)
|
||||||
|
val moduleOutputDirectory = outputDirectory.resolve(moduleIdentifier).also { it.mkdirs() }
|
||||||
|
|
||||||
|
val extension = projectStructureMetadata.sourceSetBinaryLayout[sourceSetName]?.archiveExtension
|
||||||
|
?: SourceSetMetadataLayout.METADATA.archiveExtension
|
||||||
|
|
||||||
|
val metadataOutputFile = moduleOutputDirectory.resolve("$moduleIdentifier-$sourceSetName.$extension")
|
||||||
|
|
||||||
|
/** In composite builds, we don't really need tro process the file in IDE import, so ignore it if it's missing */
|
||||||
|
// refactor: allow only included builds to provide no artifacts, and allow this only in IDE import
|
||||||
|
if (!artifactFile.isFile) {
|
||||||
|
return metadataOutputFile
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!materializeFile) {
|
||||||
|
return metadataOutputFile
|
||||||
|
}
|
||||||
|
|
||||||
|
if (metadataOutputFile.exists()) {
|
||||||
|
metadataOutputFile.delete()
|
||||||
|
}
|
||||||
|
|
||||||
|
copyZipFilePartially(artifactFile, metadataOutputFile, "$sourceSetName/")
|
||||||
|
return metadataOutputFile
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getSourceSetCInteropMetadata(
|
||||||
|
sourceSetName: String, outputDirectory: File, materializeFiles: Boolean
|
||||||
|
): Set<File> {
|
||||||
|
val artifactFile = getArtifactFile(sourceSetName)
|
||||||
|
val moduleOutputDirectory = outputDirectory.resolve(moduleIdentifier).also(File::mkdirs)
|
||||||
|
|
||||||
|
ZipFile(getArtifactFile(sourceSetName)).use { compoundMetadataArtifactZipFile ->
|
||||||
|
val cinteropRootDirectory = compoundMetadataArtifactZipFile.entries().asSequence()
|
||||||
|
.firstOrNull { zipEntry -> zipEntry.name == "$sourceSetName-cinterop/" && zipEntry.isDirectory }
|
||||||
|
?: return emptySet()
|
||||||
|
|
||||||
|
val cinterops = compoundMetadataArtifactZipFile.listChildren(cinteropRootDirectory)
|
||||||
|
|
||||||
|
val cinteropsByOutputFile = cinterops.associateBy { cinteropZipEntry ->
|
||||||
|
moduleOutputDirectory.resolve("${cinteropZipEntry.name.removePrefix(cinteropRootDirectory.name).removeSuffix("/")}.klib")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (materializeFiles) {
|
||||||
|
cinteropsByOutputFile.forEach { (cinteropOutputFile, cinteropZipEntry) ->
|
||||||
|
copyZipFilePartially(artifactFile, cinteropOutputFile, cinteropZipEntry.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cinteropsByOutputFile.keys
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getArtifactFile(sourceSetName: String): File =
|
||||||
|
hostSpecificArtifactsBySourceSet[sourceSetName] ?: primaryArtifactFile
|
||||||
|
}
|
||||||
|
|
||||||
+47
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2021 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.file.ConfigurableFileCollection
|
||||||
|
import org.gradle.api.file.FileCollection
|
||||||
|
import org.jetbrains.kotlin.gradle.plugin.mpp.MetadataDependencyResolution.ChooseVisibleSourceSets
|
||||||
|
import org.jetbrains.kotlin.gradle.plugin.mpp.MetadataDependencyResolution.ChooseVisibleSourceSets.MetadataProvider.JarMetadataProvider
|
||||||
|
import org.jetbrains.kotlin.gradle.plugin.mpp.MetadataDependencyResolution.ChooseVisibleSourceSets.MetadataProvider.ProjectMetadataProvider
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param outputDirectoryWhenMaterialised: Root output directory to put files if [materializeFilesIfNecessary] is set
|
||||||
|
* and the artifact is indeed a [CompositeMetadataJar]
|
||||||
|
*
|
||||||
|
* @param materializeFilesIfNecessary: If the artifact is a [CompositeMetadataJar] then this flag will tell if
|
||||||
|
* compiled source set metadata should be extracted from this jar file into the given [outputDirectoryWhenMaterialised]
|
||||||
|
*/
|
||||||
|
internal fun ChooseVisibleSourceSets.getAllCompiledSourceSetMetadata(
|
||||||
|
project: Project,
|
||||||
|
outputDirectoryWhenMaterialised: File,
|
||||||
|
materializeFilesIfNecessary: Boolean
|
||||||
|
): ConfigurableFileCollection = project.files(
|
||||||
|
visibleSourceSetNamesExcludingDependsOn.map { visibleSourceSetName ->
|
||||||
|
metadataProvider.getSourceSetCompiledMetadata(
|
||||||
|
project, visibleSourceSetName, outputDirectoryWhenMaterialised, materializeFilesIfNecessary
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
internal fun ChooseVisibleSourceSets.MetadataProvider.getSourceSetCompiledMetadata(
|
||||||
|
project: Project,
|
||||||
|
sourceSetName: String,
|
||||||
|
outputDirectoryWhenMaterialised: File,
|
||||||
|
materializeFilesIfNecessary: Boolean
|
||||||
|
): FileCollection = when (this) {
|
||||||
|
is ProjectMetadataProvider -> getSourceSetCompiledMetadata(sourceSetName)
|
||||||
|
is JarMetadataProvider -> project.files(
|
||||||
|
getSourceSetCompiledMetadata(
|
||||||
|
sourceSetName, outputDirectoryWhenMaterialised, materializeFilesIfNecessary
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
+44
-333
@@ -13,24 +13,14 @@ import org.gradle.api.artifacts.result.ResolvedComponentResult
|
|||||||
import org.gradle.api.artifacts.result.ResolvedDependencyResult
|
import org.gradle.api.artifacts.result.ResolvedDependencyResult
|
||||||
import org.gradle.api.attributes.Attribute
|
import org.gradle.api.attributes.Attribute
|
||||||
import org.gradle.api.file.FileCollection
|
import org.gradle.api.file.FileCollection
|
||||||
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
|
|
||||||
import org.jetbrains.kotlin.gradle.dsl.topLevelExtension
|
|
||||||
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
|
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
|
||||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinPm20ProjectExtension
|
import org.jetbrains.kotlin.gradle.plugin.mpp.MetadataDependencyResolution.ChooseVisibleSourceSets.MetadataProvider.Companion.asMetadataProvider
|
||||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.toSingleModuleIdentifier
|
|
||||||
import org.jetbrains.kotlin.gradle.plugin.sources.KotlinDependencyScope
|
import org.jetbrains.kotlin.gradle.plugin.sources.KotlinDependencyScope
|
||||||
import org.jetbrains.kotlin.gradle.plugin.sources.sourceSetDependencyConfigurationByScope
|
import org.jetbrains.kotlin.gradle.plugin.sources.sourceSetDependencyConfigurationByScope
|
||||||
import org.jetbrains.kotlin.gradle.targets.metadata.ALL_COMPILE_METADATA_CONFIGURATION_NAME
|
import org.jetbrains.kotlin.gradle.targets.metadata.ALL_COMPILE_METADATA_CONFIGURATION_NAME
|
||||||
import org.jetbrains.kotlin.gradle.targets.metadata.ALL_RUNTIME_METADATA_CONFIGURATION_NAME
|
import org.jetbrains.kotlin.gradle.targets.metadata.ALL_RUNTIME_METADATA_CONFIGURATION_NAME
|
||||||
import org.jetbrains.kotlin.gradle.targets.metadata.dependsOnClosureWithInterCompilationDependencies
|
import org.jetbrains.kotlin.gradle.targets.metadata.dependsOnClosureWithInterCompilationDependencies
|
||||||
import org.jetbrains.kotlin.project.model.KotlinModuleIdentifier
|
|
||||||
import java.io.File
|
|
||||||
import java.io.InputStream
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.zip.ZipEntry
|
|
||||||
import java.util.zip.ZipFile
|
|
||||||
import java.util.zip.ZipOutputStream
|
|
||||||
import javax.xml.parsers.DocumentBuilderFactory
|
|
||||||
|
|
||||||
internal sealed class MetadataDependencyResolution(
|
internal sealed class MetadataDependencyResolution(
|
||||||
@field:Transient // can't be used with Gradle Instant Execution, but fortunately not needed when deserialized
|
@field:Transient // can't be used with Gradle Instant Execution, but fortunately not needed when deserialized
|
||||||
@@ -60,23 +50,31 @@ internal sealed class MetadataDependencyResolution(
|
|||||||
projectDependency: Project?
|
projectDependency: Project?
|
||||||
) : MetadataDependencyResolution(dependency, projectDependency)
|
) : MetadataDependencyResolution(dependency, projectDependency)
|
||||||
|
|
||||||
abstract class ChooseVisibleSourceSets(
|
class ChooseVisibleSourceSets internal constructor(
|
||||||
dependency: ResolvedComponentResult,
|
dependency: ResolvedComponentResult,
|
||||||
projectDependency: Project?,
|
projectDependency: Project?,
|
||||||
val projectStructureMetadata: KotlinProjectStructureMetadata,
|
val projectStructureMetadata: KotlinProjectStructureMetadata,
|
||||||
val allVisibleSourceSetNames: Set<String>,
|
val allVisibleSourceSetNames: Set<String>,
|
||||||
val visibleSourceSetNamesExcludingDependsOn: Set<String>,
|
val visibleSourceSetNamesExcludingDependsOn: Set<String>,
|
||||||
val visibleTransitiveDependencies: Set<ResolvedDependencyResult>
|
val visibleTransitiveDependencies: Set<ResolvedDependencyResult>,
|
||||||
|
internal val metadataProvider: MetadataProvider
|
||||||
) : MetadataDependencyResolution(dependency, projectDependency) {
|
) : 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].
|
|
||||||
* If [createFiles] is true, these temporary files are actually re-created during the call,
|
|
||||||
* otherwise only their paths are returned, while the files might be missing.
|
|
||||||
*/
|
|
||||||
fun getMetadataFilesBySourceSet(baseDir: File, createFiles: Boolean): Map<String, FileCollection> =
|
|
||||||
getExtractableMetadataFiles(baseDir).getMetadataFilesPerSourceSet(createFiles)
|
|
||||||
|
|
||||||
abstract fun getExtractableMetadataFiles(baseDir: File): ExtractableMetadataFiles
|
internal sealed class MetadataProvider {
|
||||||
|
class JarMetadataProvider(private val compositeMetadataArtifact: CompositeMetadataJar) :
|
||||||
|
MetadataProvider(), CompositeMetadataJar by compositeMetadataArtifact
|
||||||
|
|
||||||
|
abstract class ProjectMetadataProvider : MetadataProvider() {
|
||||||
|
enum class MetadataConsumer { Ide, Cli }
|
||||||
|
|
||||||
|
abstract fun getSourceSetCompiledMetadata(sourceSetName: String): FileCollection
|
||||||
|
abstract fun getSourceSetCInteropMetadata(sourceSetName: String, consumer: MetadataConsumer): FileCollection
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun CompositeMetadataJar.asMetadataProvider() = JarMetadataProvider(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun toString(): String =
|
override fun toString(): String =
|
||||||
super.toString() + ", sourceSets = " + allVisibleSourceSetNames.joinToString(", ", "[", "]") {
|
super.toString() + ", sourceSets = " + allVisibleSourceSetNames.joinToString(", ", "[", "]") {
|
||||||
@@ -109,7 +107,7 @@ internal class GranularMetadataTransformation(
|
|||||||
private val allSourceSetsConfiguration: Configuration =
|
private val allSourceSetsConfiguration: Configuration =
|
||||||
commonMetadataDependenciesConfigurationForScopes(project, sourceSetRequestedScopes)
|
commonMetadataDependenciesConfigurationForScopes(project, sourceSetRequestedScopes)
|
||||||
|
|
||||||
private val configurationToResolve: Configuration by lazy {
|
internal val configurationToResolve: Configuration by lazy {
|
||||||
resolvableMetadataConfiguration(project, allSourceSetsConfiguration, requestedDependencies)
|
resolvableMetadataConfiguration(project, allSourceSetsConfiguration, requestedDependencies)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,10 +202,8 @@ internal class GranularMetadataTransformation(
|
|||||||
parentResolutionsForModule: Iterable<MetadataDependencyResolution>,
|
parentResolutionsForModule: Iterable<MetadataDependencyResolution>,
|
||||||
parent: ResolvedComponentResult?
|
parent: ResolvedComponentResult?
|
||||||
): MetadataDependencyResolution {
|
): MetadataDependencyResolution {
|
||||||
val mppDependencyMetadataExtractor = getMetadataExtractor(
|
val mppDependencyMetadataExtractor = MppDependencyProjectStructureMetadataExtractor.create(
|
||||||
project,
|
project, module, configurationToResolve,
|
||||||
module,
|
|
||||||
configurationToResolve,
|
|
||||||
resolveViaAvailableAt = false // we will process the available-at module as a dependency later in the queue
|
resolveViaAvailableAt = false // we will process the available-at module as a dependency later in the queue
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -225,10 +221,6 @@ internal class GranularMetadataTransformation(
|
|||||||
resolvedToProject
|
resolvedToProject
|
||||||
)
|
)
|
||||||
|
|
||||||
if (mppDependencyMetadataExtractor is JarArtifactMppDependencyMetadataExtractor) {
|
|
||||||
mppDependencyMetadataExtractor.metadataArtifactBySourceSet.putAll(sourceSetVisibility.hostSpecificMetadataArtifactBySourceSet)
|
|
||||||
}
|
|
||||||
|
|
||||||
val allVisibleSourceSets = sourceSetVisibility.visibleSourceSetNames
|
val allVisibleSourceSets = sourceSetVisibility.visibleSourceSetNames
|
||||||
|
|
||||||
val sourceSetsVisibleInParents = parentResolutionsForModule
|
val sourceSetsVisibleInParents = parentResolutionsForModule
|
||||||
@@ -253,33 +245,32 @@ internal class GranularMetadataTransformation(
|
|||||||
|
|
||||||
val visibleSourceSetsExcludingDependsOn = allVisibleSourceSets.filterTo(mutableSetOf()) { it !in sourceSetsVisibleInParents }
|
val visibleSourceSetsExcludingDependsOn = allVisibleSourceSets.filterTo(mutableSetOf()) { it !in sourceSetsVisibleInParents }
|
||||||
|
|
||||||
return ChooseVisibleSourceSetsImpl(
|
val metadataProvider = when (mppDependencyMetadataExtractor) {
|
||||||
module, resolvedToProject, projectStructureMetadata, allVisibleSourceSets, visibleSourceSetsExcludingDependsOn,
|
is ProjectMppDependencyProjectStructureMetadataExtractor -> ProjectMetadataProvider(
|
||||||
transitiveDependenciesToVisit, mppDependencyMetadataExtractor
|
dependencyProject = mppDependencyMetadataExtractor.dependencyProject,
|
||||||
|
moduleIdentifier = mppDependencyMetadataExtractor.moduleIdentifier
|
||||||
|
)
|
||||||
|
|
||||||
|
is JarMppDependencyProjectStructureMetadataExtractor -> CompositeMetadataJar(
|
||||||
|
moduleIdentifier = ModuleIds.fromComponent(project, module).toString(),
|
||||||
|
projectStructureMetadata = projectStructureMetadata,
|
||||||
|
primaryArtifactFile = mppDependencyMetadataExtractor.primaryArtifactFile,
|
||||||
|
hostSpecificArtifactsBySourceSet = sourceSetVisibility.hostSpecificMetadataArtifactBySourceSet,
|
||||||
|
).asMetadataProvider()
|
||||||
|
}
|
||||||
|
|
||||||
|
return MetadataDependencyResolution.ChooseVisibleSourceSets(
|
||||||
|
dependency = module,
|
||||||
|
projectDependency = resolvedToProject,
|
||||||
|
projectStructureMetadata = projectStructureMetadata,
|
||||||
|
allVisibleSourceSetNames = allVisibleSourceSets,
|
||||||
|
visibleSourceSetNamesExcludingDependsOn = visibleSourceSetsExcludingDependsOn,
|
||||||
|
visibleTransitiveDependencies = transitiveDependenciesToVisit,
|
||||||
|
metadataProvider = metadataProvider
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal 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 getExtractableMetadataFiles(baseDir: File): ExtractableMetadataFiles =
|
|
||||||
metadataExtractor.getExtractableMetadataFiles(visibleSourceSetNamesExcludingDependsOn, baseDir)
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun ResolvedComponentResult.toProjectOrNull(currentProject: Project): Project? {
|
internal fun ResolvedComponentResult.toProjectOrNull(currentProject: Project): Project? {
|
||||||
val identifier = id
|
val identifier = id
|
||||||
return when {
|
return when {
|
||||||
@@ -354,283 +345,3 @@ internal fun requestedDependencies(
|
|||||||
val otherContributingSourceSets = dependsOnClosureWithInterCompilationDependencies(project, sourceSet)
|
val otherContributingSourceSets = dependsOnClosureWithInterCompilationDependencies(project, sourceSet)
|
||||||
return listOf(sourceSet, *otherContributingSourceSets.toTypedArray()).flatMap(::collectScopedDependenciesFromSourceSet)
|
return listOf(sourceSet, *otherContributingSourceSets.toTypedArray()).flatMap(::collectScopedDependenciesFromSourceSet)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
internal abstract class MppDependencyMetadataExtractor(val project: Project, val component: ResolvedComponentResult) {
|
|
||||||
abstract fun getProjectStructureMetadata(): KotlinProjectStructureMetadata?
|
|
||||||
|
|
||||||
abstract fun getExtractableMetadataFiles(
|
|
||||||
visibleSourceSetNames: Set<String>,
|
|
||||||
baseDir: File
|
|
||||||
): ExtractableMetadataFiles
|
|
||||||
}
|
|
||||||
|
|
||||||
private class ProjectMppDependencyMetadataExtractor(
|
|
||||||
project: Project,
|
|
||||||
dependency: ResolvedComponentResult,
|
|
||||||
val moduleIdentifier: KotlinModuleIdentifier,
|
|
||||||
val dependencyProject: Project
|
|
||||||
) : MppDependencyMetadataExtractor(project, dependency) {
|
|
||||||
override fun getProjectStructureMetadata(): KotlinProjectStructureMetadata? {
|
|
||||||
return when (val topLevelExtension = dependencyProject.topLevelExtension) {
|
|
||||||
is KotlinPm20ProjectExtension -> buildProjectStructureMetadata(
|
|
||||||
topLevelExtension.modules.single { it.moduleIdentifier == moduleIdentifier }
|
|
||||||
)
|
|
||||||
else -> buildKotlinProjectStructureMetadata(dependencyProject)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getExtractableMetadataFiles(
|
|
||||||
visibleSourceSetNames: Set<String>,
|
|
||||||
baseDir: File
|
|
||||||
): ExtractableMetadataFiles {
|
|
||||||
val result = when (val projectExtension = dependencyProject.topLevelExtension) {
|
|
||||||
is KotlinMultiplatformExtension -> projectExtension.targets.getByName(KotlinMultiplatformPlugin.METADATA_TARGET_NAME).compilations
|
|
||||||
.filter { it.name in visibleSourceSetNames }.associate { it.defaultSourceSet.name to it.output.classesDirs }
|
|
||||||
is KotlinPm20ProjectExtension -> {
|
|
||||||
val moduleId = moduleIdentifier
|
|
||||||
val module = projectExtension.modules.single { it.moduleIdentifier == moduleId }
|
|
||||||
val metadataCompilationRegistry = projectExtension.metadataCompilationRegistryByModuleId.getValue(moduleId)
|
|
||||||
visibleSourceSetNames.associateWith {
|
|
||||||
metadataCompilationRegistry.byFragment(module.fragments.getByName(it)).output.classesDirs
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> error("unexpected top-level Kotlin extension $projectExtension")
|
|
||||||
}
|
|
||||||
|
|
||||||
return object : ExtractableMetadataFiles() {
|
|
||||||
override fun getMetadataFilesPerSourceSet(createFiles: Boolean): Map<String, FileCollection> = result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class IncludedBuildMetadataExtractor(
|
|
||||||
project: Project,
|
|
||||||
dependency: ResolvedComponentResult,
|
|
||||||
primaryArtifact: File
|
|
||||||
) : JarArtifactMppDependencyMetadataExtractor(project, dependency, primaryArtifact) {
|
|
||||||
|
|
||||||
private val id: ProjectComponentIdentifier
|
|
||||||
|
|
||||||
init {
|
|
||||||
val id = dependency.id
|
|
||||||
require(id is ProjectComponentIdentifier) { "dependency should resolve to a project" }
|
|
||||||
require(!id.build.isCurrentBuild) { "should be a project from an included build" }
|
|
||||||
this.id = id
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getProjectStructureMetadata(): KotlinProjectStructureMetadata? =
|
|
||||||
GlobalProjectStructureMetadataStorage.getProjectStructureMetadata(project, id.build.name, id.projectPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
internal open class JarArtifactMppDependencyMetadataExtractor(
|
|
||||||
project: Project,
|
|
||||||
dependency: ResolvedComponentResult,
|
|
||||||
val primaryArtifact: File
|
|
||||||
) : MppDependencyMetadataExtractor(project, dependency) {
|
|
||||||
|
|
||||||
// TODO: add proper API to make an artifact extractor with just the composite artifact "evolve" into one with host-specific artifacts
|
|
||||||
val metadataArtifactBySourceSet: MutableMap<String, File> = mutableMapOf()
|
|
||||||
|
|
||||||
private fun parseJsonProjectStructureMetadata(input: InputStream) =
|
|
||||||
parseKotlinSourceSetMetadataFromJson(input.reader().readText())
|
|
||||||
|
|
||||||
private fun parseXmlProjectStructureMetadata(input: InputStream) =
|
|
||||||
parseKotlinSourceSetMetadataFromXml(DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(input))
|
|
||||||
|
|
||||||
override fun getProjectStructureMetadata(): KotlinProjectStructureMetadata? {
|
|
||||||
return ZipFile(primaryArtifact).use { zip ->
|
|
||||||
val (metadata, parseFunction) =
|
|
||||||
zip.getEntry("META-INF/$MULTIPLATFORM_PROJECT_METADATA_JSON_FILE_NAME")?.to(::parseJsonProjectStructureMetadata)
|
|
||||||
?: zip.getEntry("META-INF/$MULTIPLATFORM_PROJECT_METADATA_FILE_NAME")?.to(::parseXmlProjectStructureMetadata)
|
|
||||||
?: return null
|
|
||||||
|
|
||||||
zip.getInputStream(metadata).use(parseFunction)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getExtractableMetadataFiles(
|
|
||||||
visibleSourceSetNames: Set<String>,
|
|
||||||
baseDir: File
|
|
||||||
): ExtractableMetadataFiles {
|
|
||||||
val primaryArtifact = primaryArtifact
|
|
||||||
val moduleId = ModuleIds.fromComponent(project, component)
|
|
||||||
|
|
||||||
return JarExtractableMetadataFiles(
|
|
||||||
module = moduleId,
|
|
||||||
project = project,
|
|
||||||
baseDir = baseDir,
|
|
||||||
artifactBySourceSet = visibleSourceSetNames.associateWith { (metadataArtifactBySourceSet[it] ?: primaryArtifact) },
|
|
||||||
projectStructureMetadata = checkNotNull(getProjectStructureMetadata()) {
|
|
||||||
"project structure metadata is needed to extract files"
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private class JarExtractableMetadataFiles(
|
|
||||||
private val module: ModuleDependencyIdentifier,
|
|
||||||
private val project: Project,
|
|
||||||
private val baseDir: File,
|
|
||||||
private val artifactBySourceSet: Map<String, File>,
|
|
||||||
private val projectStructureMetadata: KotlinProjectStructureMetadata
|
|
||||||
) : ExtractableMetadataFiles() {
|
|
||||||
|
|
||||||
override fun getMetadataFilesPerSourceSet(createFiles: Boolean): Map<String, FileCollection> {
|
|
||||||
return artifactBySourceSet.mapValues { (sourceSetName, artifact) ->
|
|
||||||
project.files(
|
|
||||||
getCompiledSourceSetMetadata(sourceSetName, artifact, createFiles)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getCompiledSourceSetMetadata(sourceSetName: String, artifact: File, createFiles: Boolean): File {
|
|
||||||
val moduleString = "${module.groupId}-${module.moduleId}"
|
|
||||||
val transformedModuleRoot = run { baseDir.resolve(moduleString).also { it.mkdirs() } }
|
|
||||||
|
|
||||||
val extension = projectStructureMetadata.sourceSetBinaryLayout[sourceSetName]?.archiveExtension
|
|
||||||
?: SourceSetMetadataLayout.METADATA.archiveExtension
|
|
||||||
|
|
||||||
val metadataOutputFile = transformedModuleRoot.resolve("$moduleString-$sourceSetName.$extension")
|
|
||||||
|
|
||||||
/** In composite builds, we don't really need tro process the file in IDE import, so ignore it if it's missing */
|
|
||||||
// refactor: allow only included builds to provide no artifacts, and allow this only in IDE import
|
|
||||||
if (!artifact.isFile) {
|
|
||||||
return metadataOutputFile
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!createFiles) {
|
|
||||||
return metadataOutputFile
|
|
||||||
}
|
|
||||||
|
|
||||||
if (metadataOutputFile.exists()) {
|
|
||||||
metadataOutputFile.delete()
|
|
||||||
}
|
|
||||||
|
|
||||||
ZipFile(artifact).use { zip ->
|
|
||||||
val entries = zip.entries().asSequence().filter { it.name.startsWith("$sourceSetName/") }.toList()
|
|
||||||
|
|
||||||
// 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
|
|
||||||
|
|
||||||
if (entries.any()) {
|
|
||||||
ZipOutputStream(metadataOutputFile.outputStream()).use { resultZipOutput ->
|
|
||||||
for (entry in entries) {
|
|
||||||
if (entry.isDirectory)
|
|
||||||
continue
|
|
||||||
|
|
||||||
// Drop the source set name from the entry path
|
|
||||||
val resultEntry = ZipEntry(entry.name.substringAfter("/"))
|
|
||||||
|
|
||||||
zip.getInputStream(entry).use { inputStream ->
|
|
||||||
resultZipOutput.putNextEntry(resultEntry)
|
|
||||||
inputStream.copyTo(resultZipOutput)
|
|
||||||
resultZipOutput.closeEntry()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return metadataOutputFile
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun getMetadataExtractor(
|
|
||||||
project: Project,
|
|
||||||
resolvedComponentResult: ResolvedComponentResult,
|
|
||||||
configuration: Configuration,
|
|
||||||
resolveViaAvailableAt: Boolean
|
|
||||||
): MppDependencyMetadataExtractor? {
|
|
||||||
val resolvedMppVariantsProvider = ResolvedMppVariantsProvider.get(project)
|
|
||||||
val moduleIdentifier =
|
|
||||||
resolvedComponentResult.toSingleModuleIdentifier() // FIXME this loses information about auxiliary module deps
|
|
||||||
// TODO check how this code works with multi-capability resolutions
|
|
||||||
|
|
||||||
return mppDependencyMetadataExtractor(
|
|
||||||
resolvedMppVariantsProvider,
|
|
||||||
moduleIdentifier,
|
|
||||||
configuration,
|
|
||||||
resolveViaAvailableAt,
|
|
||||||
resolvedComponentResult,
|
|
||||||
project
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun getMetadataExtractor(
|
|
||||||
project: Project,
|
|
||||||
resolvedComponentResult: ResolvedComponentResult,
|
|
||||||
moduleIdentifier: KotlinModuleIdentifier,
|
|
||||||
configuration: Configuration
|
|
||||||
): MppDependencyMetadataExtractor? {
|
|
||||||
val resolvedMppVariantsProvider = ResolvedMppVariantsProvider.get(project)
|
|
||||||
return mppDependencyMetadataExtractor(
|
|
||||||
resolvedMppVariantsProvider,
|
|
||||||
moduleIdentifier,
|
|
||||||
configuration,
|
|
||||||
true,
|
|
||||||
resolvedComponentResult,
|
|
||||||
project
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun mppDependencyMetadataExtractor(
|
|
||||||
resolvedMppVariantsProvider: ResolvedMppVariantsProvider,
|
|
||||||
moduleIdentifier: KotlinModuleIdentifier,
|
|
||||||
configuration: Configuration,
|
|
||||||
resolveViaAvailableAt: Boolean,
|
|
||||||
resolvedComponentResult: ResolvedComponentResult,
|
|
||||||
project: Project
|
|
||||||
): MppDependencyMetadataExtractor? {
|
|
||||||
var resolvedViaAvailableAt = false
|
|
||||||
|
|
||||||
val metadataArtifact = resolvedMppVariantsProvider.getResolvedArtifactByPlatformModule(
|
|
||||||
moduleIdentifier,
|
|
||||||
configuration
|
|
||||||
) ?: if (resolveViaAvailableAt) {
|
|
||||||
resolvedMppVariantsProvider.getHostSpecificMetadataArtifactByRootModule(
|
|
||||||
moduleIdentifier,
|
|
||||||
configuration
|
|
||||||
)?.also {
|
|
||||||
resolvedViaAvailableAt = true
|
|
||||||
}
|
|
||||||
} else null
|
|
||||||
|
|
||||||
val actualComponent = if (resolvedViaAvailableAt) {
|
|
||||||
resolvedComponentResult.dependencies.filterIsInstance<ResolvedDependencyResult>().singleOrNull()?.selected
|
|
||||||
?: resolvedComponentResult
|
|
||||||
} else resolvedComponentResult
|
|
||||||
|
|
||||||
val moduleId = actualComponent.id
|
|
||||||
return when {
|
|
||||||
moduleId is ProjectComponentIdentifier -> when {
|
|
||||||
moduleId.build.isCurrentBuild ->
|
|
||||||
ProjectMppDependencyMetadataExtractor(project, actualComponent, moduleIdentifier, project.project(moduleId.projectPath))
|
|
||||||
metadataArtifact != null ->
|
|
||||||
IncludedBuildMetadataExtractor(project, actualComponent, metadataArtifact)
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
metadataArtifact != null -> JarArtifactMppDependencyMetadataExtractor(project, actualComponent, metadataArtifact)
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun getProjectStructureMetadata(
|
|
||||||
project: Project,
|
|
||||||
module: ResolvedComponentResult,
|
|
||||||
configuration: Configuration,
|
|
||||||
moduleIdentifier: KotlinModuleIdentifier? = null
|
|
||||||
): KotlinProjectStructureMetadata? {
|
|
||||||
val extractor = if (moduleIdentifier != null)
|
|
||||||
getMetadataExtractor(project, module, moduleIdentifier, configuration)
|
|
||||||
else
|
|
||||||
getMetadataExtractor(project, module, configuration, resolveViaAvailableAt = true)
|
|
||||||
|
|
||||||
return extractor?.getProjectStructureMetadata()
|
|
||||||
}
|
|
||||||
|
|
||||||
// This class is needed to encapsulate how we extract the files and point to them in a way that doesn't capture the Gradle project state
|
|
||||||
internal abstract class ExtractableMetadataFiles {
|
|
||||||
abstract fun getMetadataFilesPerSourceSet(createFiles: Boolean): Map<String, FileCollection>
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
+9
-5
@@ -13,7 +13,6 @@ import org.gradle.api.Project
|
|||||||
import org.gradle.api.tasks.Input
|
import org.gradle.api.tasks.Input
|
||||||
import org.gradle.api.tasks.Internal
|
import org.gradle.api.tasks.Internal
|
||||||
import org.gradle.api.tasks.Nested
|
import org.gradle.api.tasks.Nested
|
||||||
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
|
|
||||||
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtensionOrNull
|
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtensionOrNull
|
||||||
import org.jetbrains.kotlin.gradle.dsl.topLevelExtensionOrNull
|
import org.jetbrains.kotlin.gradle.dsl.topLevelExtensionOrNull
|
||||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinGradleModule
|
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinGradleModule
|
||||||
@@ -28,6 +27,7 @@ import org.w3c.dom.Document
|
|||||||
import org.w3c.dom.Element
|
import org.w3c.dom.Element
|
||||||
import org.w3c.dom.Node
|
import org.w3c.dom.Node
|
||||||
import org.w3c.dom.NodeList
|
import org.w3c.dom.NodeList
|
||||||
|
import java.io.Serializable
|
||||||
import java.io.StringWriter
|
import java.io.StringWriter
|
||||||
import javax.xml.parsers.DocumentBuilderFactory
|
import javax.xml.parsers.DocumentBuilderFactory
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@ import javax.xml.parsers.DocumentBuilderFactory
|
|||||||
open class ModuleDependencyIdentifier(
|
open class ModuleDependencyIdentifier(
|
||||||
open val groupId: String?,
|
open val groupId: String?,
|
||||||
open val moduleId: String
|
open val moduleId: String
|
||||||
) {
|
) : Serializable {
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
if (this === other) return true
|
if (this === other) return true
|
||||||
if (other !is ModuleDependencyIdentifier) return false
|
if (other !is ModuleDependencyIdentifier) return false
|
||||||
@@ -54,6 +54,10 @@ open class ModuleDependencyIdentifier(
|
|||||||
|
|
||||||
operator fun component1(): String? = groupId
|
operator fun component1(): String? = groupId
|
||||||
operator fun component2(): String = moduleId
|
operator fun component2(): String = moduleId
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return "${groupId}-${moduleId}"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ChangingModuleDependencyIdentifier(
|
class ChangingModuleDependencyIdentifier(
|
||||||
@@ -71,7 +75,7 @@ sealed class SourceSetMetadataLayout(
|
|||||||
val name: String,
|
val name: String,
|
||||||
@get:Internal
|
@get:Internal
|
||||||
val archiveExtension: String
|
val archiveExtension: String
|
||||||
) {
|
) : Serializable {
|
||||||
object METADATA : SourceSetMetadataLayout("metadata", "jar")
|
object METADATA : SourceSetMetadataLayout("metadata", "jar")
|
||||||
object KLIB : SourceSetMetadataLayout("klib", "klib")
|
object KLIB : SourceSetMetadataLayout("klib", "klib")
|
||||||
|
|
||||||
@@ -109,7 +113,7 @@ data class KotlinProjectStructureMetadata(
|
|||||||
|
|
||||||
@Input
|
@Input
|
||||||
val formatVersion: String = FORMAT_VERSION_0_3_1
|
val formatVersion: String = FORMAT_VERSION_0_3_1
|
||||||
) {
|
) : Serializable {
|
||||||
@Suppress("UNUSED") // Gradle input
|
@Suppress("UNUSED") // Gradle input
|
||||||
@get:Input
|
@get:Input
|
||||||
internal val sourceSetModuleDependenciesInput: Map<String, Set<Pair<String, String>>>
|
internal val sourceSetModuleDependenciesInput: Map<String, Set<Pair<String, String>>>
|
||||||
@@ -296,7 +300,7 @@ internal fun parseKotlinSourceSetMetadataFromXml(document: Document): KotlinProj
|
|||||||
val nodeNamed: Element.(String) -> Element? = { name -> getElementsByTagName(name).elements.singleOrNull() }
|
val nodeNamed: Element.(String) -> Element? = { name -> getElementsByTagName(name).elements.singleOrNull() }
|
||||||
val valueNamed: Element.(String) -> String? =
|
val valueNamed: Element.(String) -> String? =
|
||||||
{ name -> getElementsByTagName(name).run { if (length > 0) item(0).textContent else null } }
|
{ name -> getElementsByTagName(name).run { if (length > 0) item(0).textContent else null } }
|
||||||
val multiObjects: Element.(String) -> Iterable<Element> = { name -> nodeNamed(name)?.childNodes?.elements ?: emptyList()}
|
val multiObjects: Element.(String) -> Iterable<Element> = { name -> nodeNamed(name)?.childNodes?.elements ?: emptyList() }
|
||||||
val multiValues: Element.(String) -> Iterable<String> = { name -> getElementsByTagName(name).elements.map { it.textContent } }
|
val multiValues: Element.(String) -> Iterable<String> = { name -> getElementsByTagName(name).elements.map { it.textContent } }
|
||||||
|
|
||||||
return parseKotlinSourceSetMetadata(
|
return parseKotlinSourceSetMetadata(
|
||||||
|
|||||||
+79
@@ -0,0 +1,79 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2021 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.component.ProjectComponentIdentifier
|
||||||
|
import org.gradle.api.artifacts.result.ResolvedComponentResult
|
||||||
|
import org.jetbrains.kotlin.gradle.dsl.topLevelExtension
|
||||||
|
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinPm20ProjectExtension
|
||||||
|
import org.jetbrains.kotlin.project.model.KotlinModuleIdentifier
|
||||||
|
import java.io.File
|
||||||
|
import java.io.InputStream
|
||||||
|
import java.util.zip.ZipFile
|
||||||
|
import javax.xml.parsers.DocumentBuilderFactory
|
||||||
|
|
||||||
|
sealed class MppDependencyProjectStructureMetadataExtractor {
|
||||||
|
abstract fun getProjectStructureMetadata(): KotlinProjectStructureMetadata?
|
||||||
|
|
||||||
|
companion object Factory
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class ProjectMppDependencyProjectStructureMetadataExtractor(
|
||||||
|
val moduleIdentifier: KotlinModuleIdentifier,
|
||||||
|
val dependencyProject: Project
|
||||||
|
) : MppDependencyProjectStructureMetadataExtractor() {
|
||||||
|
|
||||||
|
override fun getProjectStructureMetadata(): KotlinProjectStructureMetadata? {
|
||||||
|
return when (val topLevelExtension = dependencyProject.topLevelExtension) {
|
||||||
|
is KotlinPm20ProjectExtension -> buildProjectStructureMetadata(
|
||||||
|
topLevelExtension.modules.single { it.moduleIdentifier == moduleIdentifier }
|
||||||
|
)
|
||||||
|
else -> buildKotlinProjectStructureMetadata(dependencyProject)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal open class JarMppDependencyProjectStructureMetadataExtractor(
|
||||||
|
val primaryArtifactFile: File
|
||||||
|
) : MppDependencyProjectStructureMetadataExtractor() {
|
||||||
|
|
||||||
|
private fun parseJsonProjectStructureMetadata(input: InputStream) =
|
||||||
|
parseKotlinSourceSetMetadataFromJson(input.reader().readText())
|
||||||
|
|
||||||
|
private fun parseXmlProjectStructureMetadata(input: InputStream) =
|
||||||
|
parseKotlinSourceSetMetadataFromXml(DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(input))
|
||||||
|
|
||||||
|
override fun getProjectStructureMetadata(): KotlinProjectStructureMetadata? {
|
||||||
|
return ZipFile(primaryArtifactFile).use { zip ->
|
||||||
|
val (metadata, parseFunction) =
|
||||||
|
zip.getEntry("META-INF/$MULTIPLATFORM_PROJECT_METADATA_JSON_FILE_NAME")?.to(::parseJsonProjectStructureMetadata)
|
||||||
|
?: zip.getEntry("META-INF/$MULTIPLATFORM_PROJECT_METADATA_FILE_NAME")?.to(::parseXmlProjectStructureMetadata)
|
||||||
|
?: return null
|
||||||
|
|
||||||
|
zip.getInputStream(metadata).use(parseFunction)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class IncludedBuildMppDependencyProjectStructureMetadataExtractor(
|
||||||
|
private val project: Project,
|
||||||
|
dependency: ResolvedComponentResult,
|
||||||
|
primaryArtifact: File
|
||||||
|
) : JarMppDependencyProjectStructureMetadataExtractor(primaryArtifact) {
|
||||||
|
|
||||||
|
private val id: ProjectComponentIdentifier
|
||||||
|
|
||||||
|
init {
|
||||||
|
val id = dependency.id
|
||||||
|
require(id is ProjectComponentIdentifier) { "dependency should resolve to a project" }
|
||||||
|
require(!id.build.isCurrentBuild) { "should be a project from an included build" }
|
||||||
|
this.id = id
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getProjectStructureMetadata(): KotlinProjectStructureMetadata? =
|
||||||
|
GlobalProjectStructureMetadataStorage.getProjectStructureMetadata(project, id.build.name, id.projectPath)
|
||||||
|
}
|
||||||
+90
@@ -0,0 +1,90 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2021 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.ProjectComponentIdentifier
|
||||||
|
import org.gradle.api.artifacts.result.ResolvedComponentResult
|
||||||
|
import org.gradle.api.artifacts.result.ResolvedDependencyResult
|
||||||
|
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.toSingleModuleIdentifier
|
||||||
|
import org.jetbrains.kotlin.project.model.KotlinModuleIdentifier
|
||||||
|
|
||||||
|
internal fun MppDependencyProjectStructureMetadataExtractor.Factory.create(
|
||||||
|
project: Project,
|
||||||
|
resolvedComponentResult: ResolvedComponentResult,
|
||||||
|
configuration: Configuration,
|
||||||
|
resolveViaAvailableAt: Boolean
|
||||||
|
): MppDependencyProjectStructureMetadataExtractor? {
|
||||||
|
return create(
|
||||||
|
resolvedMppVariantsProvider = ResolvedMppVariantsProvider.get(project),
|
||||||
|
/*
|
||||||
|
FIXME this loses information about auxiliary module deps
|
||||||
|
TODO check how this code works with multi-capability resolutions,
|
||||||
|
*/
|
||||||
|
moduleIdentifier = resolvedComponentResult.toSingleModuleIdentifier(),
|
||||||
|
configuration = configuration,
|
||||||
|
resolveViaAvailableAt = resolveViaAvailableAt,
|
||||||
|
resolvedComponentResult = resolvedComponentResult,
|
||||||
|
project = project
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun MppDependencyProjectStructureMetadataExtractor.Factory.create(
|
||||||
|
project: Project,
|
||||||
|
resolvedComponentResult: ResolvedComponentResult,
|
||||||
|
moduleIdentifier: KotlinModuleIdentifier,
|
||||||
|
configuration: Configuration
|
||||||
|
): MppDependencyProjectStructureMetadataExtractor? {
|
||||||
|
return create(
|
||||||
|
resolvedMppVariantsProvider = ResolvedMppVariantsProvider.get(project),
|
||||||
|
moduleIdentifier = moduleIdentifier,
|
||||||
|
configuration = configuration,
|
||||||
|
resolveViaAvailableAt = true,
|
||||||
|
resolvedComponentResult = resolvedComponentResult,
|
||||||
|
project = project
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun MppDependencyProjectStructureMetadataExtractor.Factory.create(
|
||||||
|
resolvedMppVariantsProvider: ResolvedMppVariantsProvider,
|
||||||
|
moduleIdentifier: KotlinModuleIdentifier,
|
||||||
|
configuration: Configuration,
|
||||||
|
resolveViaAvailableAt: Boolean,
|
||||||
|
resolvedComponentResult: ResolvedComponentResult,
|
||||||
|
project: Project
|
||||||
|
): MppDependencyProjectStructureMetadataExtractor? {
|
||||||
|
var resolvedViaAvailableAt = false
|
||||||
|
|
||||||
|
val metadataArtifact = resolvedMppVariantsProvider.getResolvedArtifactByPlatformModule(
|
||||||
|
moduleIdentifier,
|
||||||
|
configuration
|
||||||
|
) ?: if (resolveViaAvailableAt) {
|
||||||
|
resolvedMppVariantsProvider.getHostSpecificMetadataArtifactByRootModule(
|
||||||
|
moduleIdentifier, configuration
|
||||||
|
)?.also {
|
||||||
|
resolvedViaAvailableAt = true
|
||||||
|
}
|
||||||
|
} else null
|
||||||
|
|
||||||
|
val actualComponent = if (resolvedViaAvailableAt) {
|
||||||
|
resolvedComponentResult.dependencies.filterIsInstance<ResolvedDependencyResult>().singleOrNull()?.selected
|
||||||
|
?: resolvedComponentResult
|
||||||
|
} else resolvedComponentResult
|
||||||
|
|
||||||
|
val moduleId = actualComponent.id
|
||||||
|
return when {
|
||||||
|
moduleId is ProjectComponentIdentifier -> when {
|
||||||
|
moduleId.build.isCurrentBuild ->
|
||||||
|
ProjectMppDependencyProjectStructureMetadataExtractor(moduleIdentifier, project.project(moduleId.projectPath))
|
||||||
|
metadataArtifact != null ->
|
||||||
|
IncludedBuildMppDependencyProjectStructureMetadataExtractor(project, actualComponent, metadataArtifact)
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
metadataArtifact != null -> JarMppDependencyProjectStructureMetadataExtractor(metadataArtifact)
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
+57
@@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2021 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.file.FileCollection
|
||||||
|
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
|
||||||
|
import org.jetbrains.kotlin.gradle.dsl.topLevelExtension
|
||||||
|
import org.jetbrains.kotlin.gradle.plugin.mpp.MetadataDependencyResolution.ChooseVisibleSourceSets.MetadataProvider.ProjectMetadataProvider
|
||||||
|
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinPm20ProjectExtension
|
||||||
|
import org.jetbrains.kotlin.gradle.targets.native.internal.*
|
||||||
|
import org.jetbrains.kotlin.project.model.KotlinModuleIdentifier
|
||||||
|
|
||||||
|
internal fun ProjectMetadataProvider(
|
||||||
|
dependencyProject: Project,
|
||||||
|
moduleIdentifier: KotlinModuleIdentifier
|
||||||
|
): ProjectMetadataProvider {
|
||||||
|
return ProjectMetadataProviderImpl(dependencyProject, moduleIdentifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ProjectMetadataProviderImpl(
|
||||||
|
private val dependencyProject: Project,
|
||||||
|
private val moduleIdentifier: KotlinModuleIdentifier
|
||||||
|
) : ProjectMetadataProvider() {
|
||||||
|
override fun getSourceSetCompiledMetadata(sourceSetName: String): FileCollection {
|
||||||
|
return when (val projectExtension = dependencyProject.topLevelExtension) {
|
||||||
|
is KotlinMultiplatformExtension -> projectExtension.targets.getByName(KotlinMultiplatformPlugin.METADATA_TARGET_NAME).compilations
|
||||||
|
.firstOrNull { it.name == sourceSetName }
|
||||||
|
?.output?.classesDirs ?: dependencyProject.files()
|
||||||
|
|
||||||
|
is KotlinPm20ProjectExtension -> {
|
||||||
|
val moduleId = moduleIdentifier
|
||||||
|
val module = projectExtension.modules.single { it.moduleIdentifier == moduleId }
|
||||||
|
val metadataCompilationRegistry = projectExtension.metadataCompilationRegistryByModuleId.getValue(moduleId)
|
||||||
|
metadataCompilationRegistry.byFragment(module.fragments.getByName(sourceSetName)).output.classesDirs
|
||||||
|
}
|
||||||
|
else -> error("unexpected top-level Kotlin extension $projectExtension")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getSourceSetCInteropMetadata(sourceSetName: String, consumer: MetadataConsumer): FileCollection {
|
||||||
|
val multiplatformExtension = dependencyProject.topLevelExtension as? KotlinMultiplatformExtension
|
||||||
|
?: return dependencyProject.files()
|
||||||
|
|
||||||
|
val commonizeCInteropTask = when (consumer) {
|
||||||
|
MetadataConsumer.Ide -> dependencyProject.copyCommonizeCInteropForIdeTask ?: return dependencyProject.files()
|
||||||
|
MetadataConsumer.Cli -> dependencyProject.commonizeCInteropTask ?: return dependencyProject.files()
|
||||||
|
}
|
||||||
|
|
||||||
|
val sourceSet = multiplatformExtension.sourceSets.findByName(sourceSetName) ?: return dependencyProject.files()
|
||||||
|
val dependent = CInteropCommonizerDependent.from(dependencyProject, sourceSet) ?: return dependencyProject.files()
|
||||||
|
return commonizeCInteropTask.get().commonizedOutputLibraries(dependent)
|
||||||
|
}
|
||||||
|
}
|
||||||
+18
-14
@@ -54,8 +54,8 @@ open class TransformKotlinGranularMetadata
|
|||||||
CompilationSourceSetUtil.compilationsBySourceSets(project)
|
CompilationSourceSetUtil.compilationsBySourceSets(project)
|
||||||
.filterKeys { it in sourceSets }
|
.filterKeys { it in sourceSets }
|
||||||
.entries.associate { (sourceSet, compilations) ->
|
.entries.associate { (sourceSet, compilations) ->
|
||||||
sourceSet.name to compilations.map { it.name }.sorted()
|
sourceSet.name to compilations.map { it.name }.sorted()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val participatingCompilations: Iterable<KotlinCompilation<*>>
|
private val participatingCompilations: Iterable<KotlinCompilation<*>>
|
||||||
@@ -96,18 +96,15 @@ open class TransformKotlinGranularMetadata
|
|||||||
transformation.metadataDependencyResolutions
|
transformation.metadataDependencyResolutions
|
||||||
}
|
}
|
||||||
|
|
||||||
private val extractableFilesByResolution: Map<out MetadataDependencyResolution, ExtractableMetadataFiles>
|
@get:Internal
|
||||||
|
internal val filesByResolution: Map<out MetadataDependencyResolution, FileCollection>
|
||||||
get() = metadataDependencyResolutions
|
get() = metadataDependencyResolutions
|
||||||
.filterIsInstance<MetadataDependencyResolution.ChooseVisibleSourceSets>()
|
.filterIsInstance<MetadataDependencyResolution.ChooseVisibleSourceSets>()
|
||||||
.associateWith { it.getExtractableMetadataFiles(outputsDir) }
|
.associateWith { chooseVisibleSourceSets ->
|
||||||
|
chooseVisibleSourceSets.getAllCompiledSourceSetMetadata(
|
||||||
@get:Internal
|
project, outputDirectoryWhenMaterialised = outputsDir, materializeFilesIfNecessary = false
|
||||||
internal val filesByResolution: Map<MetadataDependencyResolution, FileCollection>
|
).builtBy(this)
|
||||||
get() = extractableFilesByResolution.mapValues { (_, value) ->
|
}
|
||||||
project.files(value.getMetadataFilesPerSourceSet(false).values).builtBy(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
private val extractableFiles by project.provider { extractableFilesByResolution.values }
|
|
||||||
|
|
||||||
@TaskAction
|
@TaskAction
|
||||||
fun transformMetadata() {
|
fun transformMetadata() {
|
||||||
@@ -116,7 +113,13 @@ open class TransformKotlinGranularMetadata
|
|||||||
}
|
}
|
||||||
outputsDir.mkdirs()
|
outputsDir.mkdirs()
|
||||||
|
|
||||||
extractableFiles.forEach { it.getMetadataFilesPerSourceSet(createFiles = true) }
|
metadataDependencyResolutions
|
||||||
|
.filterIsInstance<MetadataDependencyResolution.ChooseVisibleSourceSets>()
|
||||||
|
.forEach { chooseVisibleSourceSets ->
|
||||||
|
chooseVisibleSourceSets.getAllCompiledSourceSetMetadata(
|
||||||
|
project, outputDirectoryWhenMaterialised = outputsDir, materializeFilesIfNecessary = true
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,5 +128,6 @@ internal class SourceSetResolvedMetadataProvider(
|
|||||||
) : ResolvedMetadataFilesProvider {
|
) : ResolvedMetadataFilesProvider {
|
||||||
override val buildDependencies: Iterable<TaskProvider<*>> = listOf(taskProvider)
|
override val buildDependencies: Iterable<TaskProvider<*>> = listOf(taskProvider)
|
||||||
override val metadataResolutions: Iterable<MetadataDependencyResolution> by taskProvider.map { it.metadataDependencyResolutions }
|
override val metadataResolutions: Iterable<MetadataDependencyResolution> by taskProvider.map { it.metadataDependencyResolutions }
|
||||||
override val metadataFilesByResolution: Map<MetadataDependencyResolution, FileCollection> by taskProvider.map { it.filesByResolution }
|
override val metadataFilesByResolution: Map<out MetadataDependencyResolution, FileCollection>
|
||||||
|
by taskProvider.map { it.filesByResolution }
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-2
@@ -13,9 +13,7 @@ import org.gradle.api.artifacts.result.ResolvedDependencyResult
|
|||||||
import org.gradle.api.capabilities.Capability
|
import org.gradle.api.capabilities.Capability
|
||||||
import org.jetbrains.kotlin.gradle.plugin.getKotlinPluginVersion
|
import org.jetbrains.kotlin.gradle.plugin.getKotlinPluginVersion
|
||||||
import org.jetbrains.kotlin.gradle.plugin.mpp.*
|
import org.jetbrains.kotlin.gradle.plugin.mpp.*
|
||||||
import org.jetbrains.kotlin.gradle.plugin.mpp.getProjectStructureMetadata
|
|
||||||
import org.jetbrains.kotlin.gradle.plugin.sources.DefaultLanguageSettingsBuilder
|
import org.jetbrains.kotlin.gradle.plugin.sources.DefaultLanguageSettingsBuilder
|
||||||
import org.jetbrains.kotlin.gradle.utils.getOrPut
|
|
||||||
import org.jetbrains.kotlin.gradle.utils.getOrPutRootProjectProperty
|
import org.jetbrains.kotlin.gradle.utils.getOrPutRootProjectProperty
|
||||||
import org.jetbrains.kotlin.project.model.*
|
import org.jetbrains.kotlin.project.model.*
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@@ -214,3 +212,17 @@ internal fun ComponentIdentifier.matchesModuleIdentifier(id: KotlinModuleIdentif
|
|||||||
}
|
}
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getProjectStructureMetadata(
|
||||||
|
project: Project,
|
||||||
|
module: ResolvedComponentResult,
|
||||||
|
configuration: Configuration,
|
||||||
|
moduleIdentifier: KotlinModuleIdentifier? = null
|
||||||
|
): KotlinProjectStructureMetadata? {
|
||||||
|
val extractor = if (moduleIdentifier != null)
|
||||||
|
MppDependencyProjectStructureMetadataExtractor.create(project, module, moduleIdentifier, configuration)
|
||||||
|
else
|
||||||
|
MppDependencyProjectStructureMetadataExtractor.create(project, module, configuration, resolveViaAvailableAt = true)
|
||||||
|
|
||||||
|
return extractor?.getProjectStructureMetadata()
|
||||||
|
}
|
||||||
|
|||||||
+42
-33
@@ -8,13 +8,11 @@ package org.jetbrains.kotlin.gradle.plugin.mpp.pm20
|
|||||||
import org.gradle.api.Project
|
import org.gradle.api.Project
|
||||||
import org.gradle.api.artifacts.result.ResolvedDependencyResult
|
import org.gradle.api.artifacts.result.ResolvedDependencyResult
|
||||||
import org.jetbrains.kotlin.gradle.plugin.mpp.*
|
import org.jetbrains.kotlin.gradle.plugin.mpp.*
|
||||||
import org.jetbrains.kotlin.gradle.plugin.mpp.ChooseVisibleSourceSetsImpl
|
import org.jetbrains.kotlin.gradle.plugin.mpp.MetadataDependencyResolution.ChooseVisibleSourceSets.MetadataProvider.Companion.asMetadataProvider
|
||||||
import org.jetbrains.kotlin.gradle.plugin.mpp.MetadataDependencyResolution
|
|
||||||
import org.jetbrains.kotlin.gradle.plugin.mpp.getMetadataExtractor
|
|
||||||
import org.jetbrains.kotlin.gradle.plugin.mpp.getProjectStructureMetadata
|
|
||||||
import org.jetbrains.kotlin.project.model.*
|
import org.jetbrains.kotlin.project.model.*
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.flattenTo
|
import org.jetbrains.kotlin.utils.addToStdlib.flattenTo
|
||||||
import java.util.ArrayDeque
|
import java.io.File
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
internal class FragmentGranularMetadataResolver(
|
internal class FragmentGranularMetadataResolver(
|
||||||
private val requestingFragment: KotlinGradleFragment,
|
private val requestingFragment: KotlinGradleFragment,
|
||||||
@@ -80,33 +78,46 @@ internal class FragmentGranularMetadataResolver(
|
|||||||
MetadataDependencyResolution.KeepOriginalDependency(resolvedComponentResult, isResolvedAsProject)
|
MetadataDependencyResolution.KeepOriginalDependency(resolvedComponentResult, isResolvedAsProject)
|
||||||
}
|
}
|
||||||
else -> run {
|
else -> run {
|
||||||
|
|
||||||
val metadataSourceComponent = dependencyNode.run { metadataSourceComponent ?: selectedComponent }
|
val metadataSourceComponent = dependencyNode.run { metadataSourceComponent ?: selectedComponent }
|
||||||
|
|
||||||
val metadataExtractor = getMetadataExtractor(project, resolvedComponentResult, configurationToResolve, true)
|
|
||||||
|
|
||||||
if (dependencyModule is ExternalImportedKotlinModule &&
|
|
||||||
metadataExtractor is JarArtifactMppDependencyMetadataExtractor &&
|
|
||||||
chosenFragments != null
|
|
||||||
) {
|
|
||||||
resolveHostSpecificMetadataArtifacts(dependencyModule, chosenFragments, metadataExtractor)
|
|
||||||
}
|
|
||||||
|
|
||||||
val projectStructureMetadata = (dependencyModule as? ExternalImportedKotlinModule)?.projectStructureMetadata
|
|
||||||
?: checkNotNull(metadataExtractor?.getProjectStructureMetadata())
|
|
||||||
|
|
||||||
val visibleFragmentNames = visibleFragments.map { it.fragmentName }.toSet()
|
val visibleFragmentNames = visibleFragments.map { it.fragmentName }.toSet()
|
||||||
val visibleFragmentNamesExcludingVisibleByParents =
|
val visibleFragmentNamesExcludingVisibleByParents =
|
||||||
visibleFragmentNames
|
visibleFragmentNames.minus(fragmentsNamesVisibleByParents(metadataSourceComponent.toSingleModuleIdentifier()))
|
||||||
.minus(fragmentsNamesVisibleByParents(metadataSourceComponent.toSingleModuleIdentifier()))
|
|
||||||
|
|
||||||
ChooseVisibleSourceSetsImpl(
|
val metadataExtractor = MppDependencyProjectStructureMetadataExtractor.create(
|
||||||
metadataSourceComponent,
|
project, resolvedComponentResult, configurationToResolve, true
|
||||||
isResolvedAsProject,
|
) ?: error("Failed to create 'MppDependencyProjectStructureMetadataExtractor' for ${resolvedComponentResult.id}")
|
||||||
projectStructureMetadata,
|
|
||||||
visibleFragmentNames,
|
val projectStructureMetadata = (dependencyModule as? ExternalImportedKotlinModule)?.projectStructureMetadata
|
||||||
visibleFragmentNamesExcludingVisibleByParents,
|
?: checkNotNull(metadataExtractor.getProjectStructureMetadata())
|
||||||
|
|
||||||
|
|
||||||
|
val metadataProvider = when (metadataExtractor) {
|
||||||
|
is ProjectMppDependencyProjectStructureMetadataExtractor -> ProjectMetadataProvider(
|
||||||
|
dependencyProject = metadataExtractor.dependencyProject,
|
||||||
|
moduleIdentifier = metadataExtractor.moduleIdentifier
|
||||||
|
)
|
||||||
|
|
||||||
|
is JarMppDependencyProjectStructureMetadataExtractor -> CompositeMetadataJar(
|
||||||
|
moduleIdentifier = ModuleIds.fromComponent(project, metadataSourceComponent).toString(),
|
||||||
|
projectStructureMetadata = projectStructureMetadata,
|
||||||
|
primaryArtifactFile = metadataExtractor.primaryArtifactFile,
|
||||||
|
hostSpecificArtifactsBySourceSet = if (
|
||||||
|
dependencyModule is ExternalImportedKotlinModule && chosenFragments != null
|
||||||
|
) resolveHostSpecificMetadataArtifacts(dependencyModule, chosenFragments) else emptyMap(),
|
||||||
|
).asMetadataProvider()
|
||||||
|
}
|
||||||
|
|
||||||
|
MetadataDependencyResolution.ChooseVisibleSourceSets(
|
||||||
|
dependency = metadataSourceComponent,
|
||||||
|
projectDependency = isResolvedAsProject,
|
||||||
|
projectStructureMetadata = projectStructureMetadata,
|
||||||
|
allVisibleSourceSetNames = visibleFragmentNames,
|
||||||
|
visibleSourceSetNamesExcludingDependsOn = visibleFragmentNamesExcludingVisibleByParents,
|
||||||
|
visibleTransitiveDependencies =
|
||||||
visibleTransitiveDependencies.map { resolvedDependenciesByModuleId.getValue(it.module.moduleIdentifier) }.toSet(),
|
visibleTransitiveDependencies.map { resolvedDependenciesByModuleId.getValue(it.module.moduleIdentifier) }.toSet(),
|
||||||
checkNotNull(metadataExtractor)
|
metadataProvider = metadataProvider
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -124,19 +135,18 @@ internal class FragmentGranularMetadataResolver(
|
|||||||
|
|
||||||
private fun fragmentsNamesVisibleByParents(kotlinModuleIdentifier: KotlinModuleIdentifier): MutableSet<String> {
|
private fun fragmentsNamesVisibleByParents(kotlinModuleIdentifier: KotlinModuleIdentifier): MutableSet<String> {
|
||||||
val parentResolutionsForDependency = parentResultsByModuleIdentifier[kotlinModuleIdentifier].orEmpty()
|
val parentResolutionsForDependency = parentResultsByModuleIdentifier[kotlinModuleIdentifier].orEmpty()
|
||||||
return parentResolutionsForDependency.filterIsInstance<ChooseVisibleSourceSetsImpl>()
|
return parentResolutionsForDependency.filterIsInstance<MetadataDependencyResolution.ChooseVisibleSourceSets>()
|
||||||
.flatMapTo(mutableSetOf()) { it.allVisibleSourceSetNames }
|
.flatMapTo(mutableSetOf()) { it.allVisibleSourceSetNames }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun resolveHostSpecificMetadataArtifacts(
|
private fun resolveHostSpecificMetadataArtifacts(
|
||||||
dependencyModule: ExternalImportedKotlinModule,
|
dependencyModule: ExternalImportedKotlinModule,
|
||||||
chosenFragments: FragmentResolution.ChosenFragments,
|
chosenFragments: FragmentResolution.ChosenFragments,
|
||||||
metadataExtractor: JarArtifactMppDependencyMetadataExtractor
|
): Map<String, File> {
|
||||||
) {
|
|
||||||
val visibleFragments = chosenFragments.visibleFragments
|
val visibleFragments = chosenFragments.visibleFragments
|
||||||
val variantResolutions = chosenFragments.variantResolutions
|
val variantResolutions = chosenFragments.variantResolutions
|
||||||
val hostSpecificFragments = dependencyModule.hostSpecificFragments
|
val hostSpecificFragments = dependencyModule.hostSpecificFragments
|
||||||
val hostSpecificFragmentToArtifact = visibleFragments.intersect(hostSpecificFragments).mapNotNull { hostSpecificFragment ->
|
return visibleFragments.intersect(hostSpecificFragments).mapNotNull { hostSpecificFragment ->
|
||||||
val relevantVariantResolution = variantResolutions
|
val relevantVariantResolution = variantResolutions
|
||||||
.filterIsInstance<VariantResolution.VariantMatch>()
|
.filterIsInstance<VariantResolution.VariantMatch>()
|
||||||
// find some of our variants that resolved a dependency's variant containing the fragment
|
// find some of our variants that resolved a dependency's variant containing the fragment
|
||||||
@@ -152,7 +162,6 @@ internal class FragmentGranularMetadataResolver(
|
|||||||
)
|
)
|
||||||
hostSpecificArtifact?.let { hostSpecificFragment.fragmentName to it }
|
hostSpecificArtifact?.let { hostSpecificFragment.fragmentName to it }
|
||||||
}
|
}
|
||||||
}
|
}.toMap()
|
||||||
metadataExtractor.metadataArtifactBySourceSet.putAll(hostSpecificFragmentToArtifact)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-3
@@ -17,9 +17,8 @@ import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformCommonOptionsImpl
|
|||||||
import org.jetbrains.kotlin.gradle.dsl.pm20Extension
|
import org.jetbrains.kotlin.gradle.dsl.pm20Extension
|
||||||
import org.jetbrains.kotlin.gradle.plugin.*
|
import org.jetbrains.kotlin.gradle.plugin.*
|
||||||
import org.jetbrains.kotlin.gradle.plugin.mpp.*
|
import org.jetbrains.kotlin.gradle.plugin.mpp.*
|
||||||
import org.jetbrains.kotlin.gradle.plugin.sources.DefaultLanguageSettingsBuilder
|
|
||||||
import org.jetbrains.kotlin.gradle.targets.metadata.ResolvedMetadataFilesProvider
|
import org.jetbrains.kotlin.gradle.targets.metadata.ResolvedMetadataFilesProvider
|
||||||
import org.jetbrains.kotlin.gradle.targets.metadata.createTransformedMetadataClasspath
|
import org.jetbrains.kotlin.gradle.targets.metadata.createMetadataDependencyTransformationClasspath
|
||||||
import org.jetbrains.kotlin.gradle.utils.getValue
|
import org.jetbrains.kotlin.gradle.utils.getValue
|
||||||
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
|
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
|
||||||
import org.jetbrains.kotlin.konan.target.HostManager
|
import org.jetbrains.kotlin.konan.target.HostManager
|
||||||
@@ -59,7 +58,7 @@ internal abstract class AbstractKotlinFragmentMetadataCompilationData<T : Kotlin
|
|||||||
get() = compileAllTask.name
|
get() = compileAllTask.name
|
||||||
|
|
||||||
override val compileDependencyFiles: FileCollection by project.provider {
|
override val compileDependencyFiles: FileCollection by project.provider {
|
||||||
createTransformedMetadataClasspath(
|
createMetadataDependencyTransformationClasspath(
|
||||||
project,
|
project,
|
||||||
resolvableMetadataConfiguration(fragment.containingModule),
|
resolvableMetadataConfiguration(fragment.containingModule),
|
||||||
lazy { fragment.refinesClosure.minus(fragment).map { metadataCompilationRegistry.byFragment(it).output.classesDirs } },
|
lazy { fragment.refinesClosure.minus(fragment).map { metadataCompilationRegistry.byFragment(it).output.classesDirs } },
|
||||||
|
|||||||
+14
-13
@@ -8,8 +8,8 @@ package org.jetbrains.kotlin.gradle.plugin.mpp.pm20
|
|||||||
import org.gradle.api.DefaultTask
|
import org.gradle.api.DefaultTask
|
||||||
import org.gradle.api.file.FileCollection
|
import org.gradle.api.file.FileCollection
|
||||||
import org.gradle.api.tasks.*
|
import org.gradle.api.tasks.*
|
||||||
import org.jetbrains.kotlin.gradle.plugin.mpp.ExtractableMetadataFiles
|
|
||||||
import org.jetbrains.kotlin.gradle.plugin.mpp.MetadataDependencyResolution
|
import org.jetbrains.kotlin.gradle.plugin.mpp.MetadataDependencyResolution
|
||||||
|
import org.jetbrains.kotlin.gradle.plugin.mpp.getAllCompiledSourceSetMetadata
|
||||||
import org.jetbrains.kotlin.gradle.targets.metadata.ResolvedMetadataFilesProvider
|
import org.jetbrains.kotlin.gradle.targets.metadata.ResolvedMetadataFilesProvider
|
||||||
import org.jetbrains.kotlin.gradle.utils.getValue
|
import org.jetbrains.kotlin.gradle.utils.getValue
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@@ -64,18 +64,15 @@ internal open class TransformKotlinGranularMetadataForFragment
|
|||||||
transformation.resolutions
|
transformation.resolutions
|
||||||
}
|
}
|
||||||
|
|
||||||
private val extractableFilesByResolution: Map<out MetadataDependencyResolution, ExtractableMetadataFiles>
|
@get:Internal
|
||||||
|
internal val filesByResolution: Map<out MetadataDependencyResolution, FileCollection>
|
||||||
get() = metadataDependencyResolutions
|
get() = metadataDependencyResolutions
|
||||||
.filterIsInstance<MetadataDependencyResolution.ChooseVisibleSourceSets>()
|
.filterIsInstance<MetadataDependencyResolution.ChooseVisibleSourceSets>()
|
||||||
.associateWith { it.getExtractableMetadataFiles(outputsDir) }
|
.associateWith { chooseVisibleSourceSets ->
|
||||||
|
chooseVisibleSourceSets.getAllCompiledSourceSetMetadata(
|
||||||
@get:Internal
|
project, outputsDir, materializeFilesIfNecessary = false
|
||||||
internal val filesByResolution: Map<MetadataDependencyResolution, FileCollection>
|
).builtBy(this)
|
||||||
get() = extractableFilesByResolution.mapValues { (_, value) ->
|
}
|
||||||
project.files(value.getMetadataFilesPerSourceSet(false).values).builtBy(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
private val extractableFiles by project.provider { extractableFilesByResolution.values }
|
|
||||||
|
|
||||||
@TaskAction
|
@TaskAction
|
||||||
fun transformMetadata() {
|
fun transformMetadata() {
|
||||||
@@ -84,7 +81,11 @@ internal open class TransformKotlinGranularMetadataForFragment
|
|||||||
}
|
}
|
||||||
outputsDir.mkdirs()
|
outputsDir.mkdirs()
|
||||||
|
|
||||||
extractableFiles.forEach { it.getMetadataFilesPerSourceSet(createFiles = true) }
|
metadataDependencyResolutions
|
||||||
|
.filterIsInstance<MetadataDependencyResolution.ChooseVisibleSourceSets>()
|
||||||
|
.forEach { chooseVisibleSourceSets ->
|
||||||
|
chooseVisibleSourceSets.getAllCompiledSourceSetMetadata(project, outputsDir, materializeFilesIfNecessary = true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,5 +94,5 @@ internal class FragmentResolvedMetadataProvider(
|
|||||||
) : ResolvedMetadataFilesProvider {
|
) : ResolvedMetadataFilesProvider {
|
||||||
override val buildDependencies: Iterable<TaskProvider<*>> = listOf(taskProvider)
|
override val buildDependencies: Iterable<TaskProvider<*>> = listOf(taskProvider)
|
||||||
override val metadataResolutions: Iterable<MetadataDependencyResolution> by taskProvider.map { it.metadataDependencyResolutions }
|
override val metadataResolutions: Iterable<MetadataDependencyResolution> by taskProvider.map { it.metadataDependencyResolutions }
|
||||||
override val metadataFilesByResolution: Map<MetadataDependencyResolution, FileCollection> by taskProvider.map { it.filesByResolution }
|
override val metadataFilesByResolution: Map<out MetadataDependencyResolution, FileCollection> by taskProvider.map { it.filesByResolution }
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-5
@@ -155,7 +155,6 @@ class DefaultKotlinSourceSet(
|
|||||||
return getDependenciesTransformation(scope)
|
return getDependenciesTransformation(scope)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("unused") // Used in IDE import
|
|
||||||
fun getAdditionalVisibleSourceSets(): List<KotlinSourceSet> =
|
fun getAdditionalVisibleSourceSets(): List<KotlinSourceSet> =
|
||||||
getVisibleSourceSetsFromAssociateCompilations(project, this)
|
getVisibleSourceSetsFromAssociateCompilations(project, this)
|
||||||
|
|
||||||
@@ -184,10 +183,14 @@ class DefaultKotlinSourceSet(
|
|||||||
MetadataDependencyTransformation(group, name, projectPath, null, emptySet(), emptyMap())
|
MetadataDependencyTransformation(group, name, projectPath, null, emptySet(), emptyMap())
|
||||||
|
|
||||||
is MetadataDependencyResolution.ChooseVisibleSourceSets -> {
|
is MetadataDependencyResolution.ChooseVisibleSourceSets -> {
|
||||||
val filesBySourceSet = resolution.getMetadataFilesBySourceSet(
|
val filesBySourceSet = resolution.visibleSourceSetNamesExcludingDependsOn.associateWith { visibleSourceSetName ->
|
||||||
baseDir,
|
resolution.metadataProvider.getSourceSetCompiledMetadata(
|
||||||
createFiles = true
|
project,
|
||||||
).filter { it.value.any { it.exists() } }
|
sourceSetName = visibleSourceSetName,
|
||||||
|
outputDirectoryWhenMaterialised = baseDir,
|
||||||
|
materializeFilesIfNecessary = true
|
||||||
|
)
|
||||||
|
}.filter { (_, files) -> files.any(File::exists) }
|
||||||
|
|
||||||
MetadataDependencyTransformation(
|
MetadataDependencyTransformation(
|
||||||
group, name, projectPath,
|
group, name, projectPath,
|
||||||
|
|||||||
+12
-8
@@ -113,7 +113,7 @@ class KotlinMetadataTargetConfigurator :
|
|||||||
override fun setupCompilationDependencyFiles(compilation: KotlinCompilation<KotlinCommonOptions>) {
|
override fun setupCompilationDependencyFiles(compilation: KotlinCompilation<KotlinCommonOptions>) {
|
||||||
val project = compilation.target.project
|
val project = compilation.target.project
|
||||||
|
|
||||||
/** See [createTransformedMetadataClasspath] and its usage. */
|
/** See [createMetadataDependencyTransformationClasspath] and its usage. */
|
||||||
if (project.isKotlinGranularMetadataEnabled && compilation.name != KotlinCompilation.MAIN_COMPILATION_NAME)
|
if (project.isKotlinGranularMetadataEnabled && compilation.name != KotlinCompilation.MAIN_COMPILATION_NAME)
|
||||||
compilation.compileDependencyFiles = project.files()
|
compilation.compileDependencyFiles = project.files()
|
||||||
else
|
else
|
||||||
@@ -332,10 +332,14 @@ class KotlinMetadataTargetConfigurator :
|
|||||||
listOf(sourceSet)
|
listOf(sourceSet)
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
compilation.compileDependencyFiles += createTransformedMetadataClasspath(
|
compilation.compileDependencyFiles += createMetadataDependencyTransformationClasspath(
|
||||||
project.configurations.getByName(ALL_COMPILE_METADATA_CONFIGURATION_NAME),
|
project.configurations.getByName(ALL_COMPILE_METADATA_CONFIGURATION_NAME),
|
||||||
compilation
|
compilation
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (compilation is KotlinSharedNativeCompilation && sourceSet is DefaultKotlinSourceSet) {
|
||||||
|
compilation.compileDependencyFiles += project.createCInteropMetadataDependencyClasspath(sourceSet)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupDependencyTransformationForSourceSet(
|
private fun setupDependencyTransformationForSourceSet(
|
||||||
@@ -418,7 +422,7 @@ class KotlinMetadataTargetConfigurator :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createTransformedMetadataClasspath(
|
private fun createMetadataDependencyTransformationClasspath(
|
||||||
fromFiles: Configuration,
|
fromFiles: Configuration,
|
||||||
compilation: AbstractKotlinCompilation<*>
|
compilation: AbstractKotlinCompilation<*>
|
||||||
): FileCollection {
|
): FileCollection {
|
||||||
@@ -440,7 +444,7 @@ class KotlinMetadataTargetConfigurator :
|
|||||||
transformationTaskHolders.map { SourceSetResolvedMetadataProvider(it) }
|
transformationTaskHolders.map { SourceSetResolvedMetadataProvider(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
return createTransformedMetadataClasspath(
|
return createMetadataDependencyTransformationClasspath(
|
||||||
project,
|
project,
|
||||||
fromFiles,
|
fromFiles,
|
||||||
dependsOnCompilationOutputs,
|
dependsOnCompilationOutputs,
|
||||||
@@ -492,14 +496,14 @@ internal fun Project.createGenerateProjectStructureMetadataTask(): TaskProvider<
|
|||||||
internal interface ResolvedMetadataFilesProvider {
|
internal interface ResolvedMetadataFilesProvider {
|
||||||
val buildDependencies: Iterable<TaskProvider<*>>
|
val buildDependencies: Iterable<TaskProvider<*>>
|
||||||
val metadataResolutions: Iterable<MetadataDependencyResolution>
|
val metadataResolutions: Iterable<MetadataDependencyResolution>
|
||||||
val metadataFilesByResolution: Map<MetadataDependencyResolution, FileCollection>
|
val metadataFilesByResolution: Map<out MetadataDependencyResolution, FileCollection>
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun createTransformedMetadataClasspath(
|
internal fun createMetadataDependencyTransformationClasspath(
|
||||||
project: Project,
|
project: Project,
|
||||||
fromFiles: Configuration,
|
fromFiles: Configuration,
|
||||||
parentCompiledMetadataFiles: Lazy<Iterable<FileCollection>>,
|
parentCompiledMetadataFiles: Lazy<Iterable<FileCollection>>,
|
||||||
metadataResolutionProviders: Lazy<Iterable<ResolvedMetadataFilesProvider>>
|
metadataResolutionProviders: Lazy<Iterable<ResolvedMetadataFilesProvider>>,
|
||||||
): FileCollection {
|
): FileCollection {
|
||||||
return project.files(
|
return project.files(
|
||||||
Callable {
|
Callable {
|
||||||
@@ -547,7 +551,7 @@ internal fun createTransformedMetadataClasspath(
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal fun isSharedNativeSourceSet(project: Project, sourceSet: KotlinSourceSet): Boolean {
|
internal fun isSharedNativeSourceSet(project: Project, sourceSet: KotlinSourceSet): Boolean {
|
||||||
val compilations = CompilationSourceSetUtil.compilationsBySourceSets(project)[sourceSet].orEmpty()
|
val compilations = compilationsBySourceSets(project)[sourceSet].orEmpty()
|
||||||
return compilations.isNotEmpty() && compilations.all {
|
return compilations.isNotEmpty() && compilations.all {
|
||||||
it.platformType == KotlinPlatformType.common || it.platformType == KotlinPlatformType.native
|
it.platformType == KotlinPlatformType.common || it.platformType == KotlinPlatformType.native
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-4
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.commonizer.CommonizerOutputFileLayout.base64Hash
|
|||||||
import org.jetbrains.kotlin.commonizer.CommonizerOutputFileLayout.ensureMaxFileNameLength
|
import org.jetbrains.kotlin.commonizer.CommonizerOutputFileLayout.ensureMaxFileNameLength
|
||||||
import org.jetbrains.kotlin.commonizer.identityString
|
import org.jetbrains.kotlin.commonizer.identityString
|
||||||
import org.jetbrains.kotlin.gradle.utils.filesProvider
|
import org.jetbrains.kotlin.gradle.utils.filesProvider
|
||||||
|
import org.jetbrains.kotlin.gradle.utils.outputFilesProvider
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
internal abstract class AbstractCInteropCommonizerTask : DefaultTask() {
|
internal abstract class AbstractCInteropCommonizerTask : DefaultTask() {
|
||||||
@@ -33,12 +34,10 @@ internal fun AbstractCInteropCommonizerTask.outputDirectory(group: CInteropCommo
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal fun AbstractCInteropCommonizerTask.commonizedOutputLibraries(dependent: CInteropCommonizerDependent): FileCollection {
|
internal fun AbstractCInteropCommonizerTask.commonizedOutputLibraries(dependent: CInteropCommonizerDependent): FileCollection {
|
||||||
val fileProvider = project.filesProvider {
|
return outputFilesProvider {
|
||||||
(commonizedOutputDirectory(dependent) ?: return@filesProvider emptySet<File>())
|
(commonizedOutputDirectory(dependent) ?: return@outputFilesProvider emptySet<File>())
|
||||||
.listFiles().orEmpty().toSet()
|
.listFiles().orEmpty().toSet()
|
||||||
}
|
}
|
||||||
|
|
||||||
return fileProvider.builtBy(this)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun AbstractCInteropCommonizerTask.commonizedOutputDirectory(dependent: CInteropCommonizerDependent): File? {
|
internal fun AbstractCInteropCommonizerTask.commonizedOutputDirectory(dependent: CInteropCommonizerDependent): File? {
|
||||||
|
|||||||
-25
@@ -1,25 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010-2021 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.targets.native.internal
|
|
||||||
|
|
||||||
import org.gradle.api.Project
|
|
||||||
import org.gradle.api.tasks.TaskProvider
|
|
||||||
import org.gradle.api.tasks.bundling.Zip
|
|
||||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinSharedNativeCompilation
|
|
||||||
|
|
||||||
internal fun Project.includeCommonizedCInteropMetadata(metadataKlib: TaskProvider<out Zip>, compilation: KotlinSharedNativeCompilation) {
|
|
||||||
metadataKlib.configure { jar -> includeCommonizedCInteropMetadata(jar, compilation) }
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun Project.includeCommonizedCInteropMetadata(metadataKlib: Zip, compilation: KotlinSharedNativeCompilation) {
|
|
||||||
val commonizerTask = commonizeCInteropTask?.get() ?: return
|
|
||||||
val commonizerDependencyToken = CInteropCommonizerDependent.from(compilation) ?: return
|
|
||||||
val outputDirectory = commonizerTask.commonizedOutputDirectory(commonizerDependencyToken) ?: return
|
|
||||||
|
|
||||||
metadataKlib.from(outputDirectory) { spec ->
|
|
||||||
spec.into(compilation.defaultSourceSet.name + "-cinterop")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+12
@@ -6,9 +6,11 @@
|
|||||||
package org.jetbrains.kotlin.gradle.targets.native.internal
|
package org.jetbrains.kotlin.gradle.targets.native.internal
|
||||||
|
|
||||||
import org.gradle.api.Project
|
import org.gradle.api.Project
|
||||||
|
import org.jetbrains.kotlin.commonizer.SharedCommonizerTarget
|
||||||
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtensionOrNull
|
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtensionOrNull
|
||||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinSharedNativeCompilation
|
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinSharedNativeCompilation
|
||||||
import org.jetbrains.kotlin.gradle.plugin.sources.DefaultKotlinSourceSet
|
import org.jetbrains.kotlin.gradle.plugin.sources.DefaultKotlinSourceSet
|
||||||
|
import org.jetbrains.kotlin.gradle.plugin.whenEvaluated
|
||||||
import org.jetbrains.kotlin.gradle.utils.filesProvider
|
import org.jetbrains.kotlin.gradle.utils.filesProvider
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
@@ -21,6 +23,7 @@ internal fun Project.setupCInteropCommonizerDependencies() {
|
|||||||
|
|
||||||
kotlin.forAllDefaultKotlinSourceSets { sourceSet ->
|
kotlin.forAllDefaultKotlinSourceSets { sourceSet ->
|
||||||
setupCInteropCommonizerDependenciesForIde(sourceSet)
|
setupCInteropCommonizerDependenciesForIde(sourceSet)
|
||||||
|
setupCInteropTransformCompositeMetadataDependenciesForIde(sourceSet)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,3 +53,12 @@ private fun Project.setupCInteropCommonizerDependenciesForIde(sourceSet: Default
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun Project.setupCInteropTransformCompositeMetadataDependenciesForIde(sourceSet: DefaultKotlinSourceSet) {
|
||||||
|
whenEvaluated {
|
||||||
|
if (getCommonizerTarget(sourceSet) !is SharedCommonizerTarget) return@whenEvaluated
|
||||||
|
addIntransitiveMetadataDependencyIfPossible(
|
||||||
|
sourceSet, createCInteropMetadataDependencyClasspathForIde(sourceSet)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+101
@@ -0,0 +1,101 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2021 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.targets.native.internal
|
||||||
|
|
||||||
|
import org.gradle.api.Project
|
||||||
|
import org.gradle.api.file.FileCollection
|
||||||
|
import org.jetbrains.kotlin.gradle.plugin.mpp.MetadataDependencyResolution.ChooseVisibleSourceSets
|
||||||
|
import org.jetbrains.kotlin.gradle.plugin.mpp.MetadataDependencyResolution.ChooseVisibleSourceSets.MetadataProvider.JarMetadataProvider
|
||||||
|
import org.jetbrains.kotlin.gradle.plugin.mpp.MetadataDependencyResolution.ChooseVisibleSourceSets.MetadataProvider.ProjectMetadataProvider
|
||||||
|
import org.jetbrains.kotlin.gradle.plugin.mpp.MetadataDependencyResolution.ChooseVisibleSourceSets.MetadataProvider.ProjectMetadataProvider.MetadataConsumer.Cli
|
||||||
|
import org.jetbrains.kotlin.gradle.plugin.mpp.MetadataDependencyResolution.ChooseVisibleSourceSets.MetadataProvider.ProjectMetadataProvider.MetadataConsumer.Ide
|
||||||
|
import org.jetbrains.kotlin.gradle.plugin.sources.DefaultKotlinSourceSet
|
||||||
|
import org.jetbrains.kotlin.gradle.utils.filesProvider
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
internal fun Project.createCInteropMetadataDependencyClasspath(sourceSet: DefaultKotlinSourceSet): FileCollection {
|
||||||
|
return createCInteropMetadataDependencyClasspath(sourceSet, forIde = false)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun Project.createCInteropMetadataDependencyClasspathForIde(sourceSet: DefaultKotlinSourceSet): FileCollection {
|
||||||
|
return createCInteropMetadataDependencyClasspath(sourceSet, forIde = true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param forIde: A different task for dependency transformation will be used. This task will not use the regular 'build' directory
|
||||||
|
* as transformation output to ensure IDE still being able to resolve the dependencies even when the project is cleaned.
|
||||||
|
*/
|
||||||
|
internal fun Project.createCInteropMetadataDependencyClasspath(sourceSet: DefaultKotlinSourceSet, forIde: Boolean): FileCollection {
|
||||||
|
val dependencyTransformationTask = if (forIde) locateOrRegisterCInteropMetadataDependencyTransformationTaskForIde(sourceSet)
|
||||||
|
else locateOrRegisterCInteropMetadataDependencyTransformationTask(sourceSet)
|
||||||
|
|
||||||
|
/*
|
||||||
|
The classpath will be assembled by three independent parts
|
||||||
|
1) C-Interop Metadata which will be downloaded Jar files that get transformed by the transformation task
|
||||||
|
2) C-Interop Metadata directly provided by dependency projects (in the same build)
|
||||||
|
3) C-Interop Metadata from 'associated compilations' / additionalVisible source sets
|
||||||
|
(e.g. 'nativeTest' will be able to access the classpath from 'nativeMain')
|
||||||
|
*/
|
||||||
|
return project.files(dependencyTransformationTask.map { it.outputLibraryFiles }) +
|
||||||
|
createCInteropMetadataDependencyClasspathFromProjectDependencies(sourceSet, forIde) +
|
||||||
|
createCInteropMetadataDependencyClasspathFromAssociatedCompilations(sourceSet, forIde)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Project.createCInteropMetadataDependencyClasspathFromProjectDependencies(
|
||||||
|
sourceSet: DefaultKotlinSourceSet,
|
||||||
|
forIde: Boolean
|
||||||
|
): FileCollection {
|
||||||
|
return filesProvider {
|
||||||
|
sourceSet.dependencyTransformations.values.flatMap { it.metadataDependencyResolutions }
|
||||||
|
.filterIsInstance<ChooseVisibleSourceSets>()
|
||||||
|
.flatMap { chooseVisibleSourceSets ->
|
||||||
|
/* We only want to access resolutions that provide metadata from dependency projects */
|
||||||
|
val projectMetadataProvider = when (chooseVisibleSourceSets.metadataProvider) {
|
||||||
|
is ProjectMetadataProvider -> chooseVisibleSourceSets.metadataProvider
|
||||||
|
is JarMetadataProvider -> return@flatMap emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
chooseVisibleSourceSets.visibleSourceSetsProvidingCInterops.map { visibleSourceSetName ->
|
||||||
|
projectMetadataProvider.getSourceSetCInteropMetadata(visibleSourceSetName, if (forIde) Ide else Cli)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Project.createCInteropMetadataDependencyClasspathFromAssociatedCompilations(
|
||||||
|
sourceSet: DefaultKotlinSourceSet,
|
||||||
|
forIde: Boolean
|
||||||
|
): FileCollection {
|
||||||
|
return filesProvider files@{
|
||||||
|
val commonizerTarget = getSharedCommonizerTarget(sourceSet) ?: return@files emptySet<File>()
|
||||||
|
|
||||||
|
/*
|
||||||
|
We will find the 'most suitable' / 'closest matching' source set
|
||||||
|
(like 'nativeTest' -> 'nativeMain', 'appleTest' -> 'appleMain', ...).
|
||||||
|
If no source set is found that matches the commonizer target explicitly, the next "bigger" source set shall be chosen
|
||||||
|
*/
|
||||||
|
val (associatedSourceSet, _) = sourceSet.getAdditionalVisibleSourceSets()
|
||||||
|
.filterIsInstance<DefaultKotlinSourceSet>()
|
||||||
|
.mapNotNull { otherSourceSet -> otherSourceSet to (getSharedCommonizerTarget(otherSourceSet) ?: return@mapNotNull null) }
|
||||||
|
.filter { (_, otherCommonizerTarget) -> otherCommonizerTarget.targets.containsAll(commonizerTarget.targets) }
|
||||||
|
.minByOrNull { (_, otherCommonizerTarget) -> otherCommonizerTarget.targets.size } ?: return@files emptySet<File>()
|
||||||
|
|
||||||
|
createCInteropMetadataDependencyClasspath(associatedSourceSet, forIde)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Names of all source sets that may potentially provide necessary cinterops for this resolution.
|
||||||
|
* This will select 'the most bottom' source sets in [ChooseVisibleSourceSets.allVisibleSourceSetNames]
|
||||||
|
*/
|
||||||
|
internal val ChooseVisibleSourceSets.visibleSourceSetsProvidingCInterops: Set<String>
|
||||||
|
get() {
|
||||||
|
val dependsOnSourceSets = allVisibleSourceSetNames
|
||||||
|
.flatMap { projectStructureMetadata.sourceSetsDependsOnRelation[it].orEmpty() }
|
||||||
|
.toSet()
|
||||||
|
|
||||||
|
return allVisibleSourceSetNames.filter { it !in dependsOnSourceSets }.toSet()
|
||||||
|
}
|
||||||
+130
@@ -0,0 +1,130 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2021 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.targets.native.internal
|
||||||
|
|
||||||
|
import org.gradle.api.DefaultTask
|
||||||
|
import org.gradle.api.Project
|
||||||
|
import org.gradle.api.artifacts.Configuration
|
||||||
|
import org.gradle.api.artifacts.component.ProjectComponentIdentifier
|
||||||
|
import org.gradle.api.file.FileCollection
|
||||||
|
import org.gradle.api.tasks.*
|
||||||
|
import org.jetbrains.kotlin.commonizer.SharedCommonizerTarget
|
||||||
|
import org.jetbrains.kotlin.gradle.plugin.mpp.GranularMetadataTransformation
|
||||||
|
import org.jetbrains.kotlin.gradle.plugin.mpp.MetadataDependencyResolution.ChooseVisibleSourceSets
|
||||||
|
import org.jetbrains.kotlin.gradle.plugin.mpp.MetadataDependencyResolution.ChooseVisibleSourceSets.MetadataProvider.JarMetadataProvider
|
||||||
|
import org.jetbrains.kotlin.gradle.plugin.mpp.MetadataDependencyResolution.ChooseVisibleSourceSets.MetadataProvider.ProjectMetadataProvider
|
||||||
|
import org.jetbrains.kotlin.gradle.plugin.sources.DefaultKotlinSourceSet
|
||||||
|
import org.jetbrains.kotlin.gradle.plugin.sources.SourceSetMetadataStorageForIde
|
||||||
|
import org.jetbrains.kotlin.gradle.tasks.dependsOn
|
||||||
|
import org.jetbrains.kotlin.gradle.tasks.locateOrRegisterTask
|
||||||
|
import org.jetbrains.kotlin.gradle.utils.filesProvider
|
||||||
|
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
|
||||||
|
import org.jetbrains.kotlin.gradle.utils.outputFilesProvider
|
||||||
|
import org.jetbrains.kotlin.library.KLIB_FILE_EXTENSION
|
||||||
|
import java.io.File
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
internal fun Project.locateOrRegisterCInteropMetadataDependencyTransformationTask(
|
||||||
|
sourceSet: DefaultKotlinSourceSet,
|
||||||
|
): TaskProvider<CInteropMetadataDependencyTransformationTask> {
|
||||||
|
return locateOrRegisterTask(
|
||||||
|
lowerCamelCaseName("transform", sourceSet.name, "CInteropDependenciesMetadata"),
|
||||||
|
args = listOf(
|
||||||
|
sourceSet,
|
||||||
|
/* outputDirectory = */
|
||||||
|
project.buildDir.resolve("kotlinSourceSetMetadata").resolve(sourceSet.name + "-cinterop")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun Project.locateOrRegisterCInteropMetadataDependencyTransformationTaskForIde(
|
||||||
|
sourceSet: DefaultKotlinSourceSet,
|
||||||
|
): TaskProvider<CInteropMetadataDependencyTransformationTask> {
|
||||||
|
return locateOrRegisterTask(
|
||||||
|
lowerCamelCaseName("transform", sourceSet.name, "CInteropDependenciesMetadataForIde"),
|
||||||
|
invokeWhenRegistered = { commonizeTask.dependsOn(this) },
|
||||||
|
args = listOf(
|
||||||
|
sourceSet,
|
||||||
|
/* outputDirectory = */
|
||||||
|
SourceSetMetadataStorageForIde.sourceSetStorage(project, sourceSet.name).resolve("cinterop")
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal open class CInteropMetadataDependencyTransformationTask @Inject constructor(
|
||||||
|
@get:Internal val sourceSet: DefaultKotlinSourceSet,
|
||||||
|
@get:OutputDirectory val outputDirectory: File
|
||||||
|
) : DefaultTask() {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Esoteric output file that every [CInteropMetadataDependencyTransformationTask] will point to.
|
||||||
|
* Sharing this output file will tell Gradle that those tasks cannot run in parallel.
|
||||||
|
* Parallelling this task is not supported, because parallel access to [GranularMetadataTransformation.metadataDependencyResolutions]
|
||||||
|
* is not supported during Gradle's execution phase. This is due to this property's Lazy's lock and
|
||||||
|
* another Lock used by Gradle which would lead to deadlocking.
|
||||||
|
*
|
||||||
|
* This task could potentially be ordered topologically based on dependsOn edges or parent transformations, however
|
||||||
|
* we actually do not care about the actual order chosen by Gradle.
|
||||||
|
*/
|
||||||
|
@get:OutputFile
|
||||||
|
protected val mutex: File = project.file(".cinteropMetadataDependencyTransformationMutex")
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
@get:InputFiles
|
||||||
|
@get:Classpath
|
||||||
|
protected val inputArtifactFiles: FileCollection = project.filesProvider {
|
||||||
|
sourceSet.dependencyTransformations.values.map { it.configurationToResolve.withoutProjectDependencies() }
|
||||||
|
}
|
||||||
|
|
||||||
|
@get:Internal
|
||||||
|
protected val chooseVisibleSourceSets
|
||||||
|
get() = sourceSet.dependencyTransformations.values
|
||||||
|
.flatMap { it.metadataDependencyResolutions }
|
||||||
|
.filterIsInstance<ChooseVisibleSourceSets>()
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
@get:Input
|
||||||
|
protected val dependencyProjectStructureMetadata
|
||||||
|
get() = chooseVisibleSourceSets.map { it.projectStructureMetadata }
|
||||||
|
|
||||||
|
@get:Internal
|
||||||
|
val outputLibraryFiles = outputFilesProvider {
|
||||||
|
outputDirectory.walkTopDown().maxDepth(2).filter { it.isFile && it.extension == KLIB_FILE_EXTENSION }.toList()
|
||||||
|
}
|
||||||
|
|
||||||
|
@TaskAction
|
||||||
|
protected fun transformDependencies() {
|
||||||
|
if (outputDirectory.isDirectory) {
|
||||||
|
outputDirectory.deleteRecursively()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (project.getCommonizerTarget(sourceSet) !is SharedCommonizerTarget) return
|
||||||
|
chooseVisibleSourceSets.flatMap(::materializeMetadata)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun materializeMetadata(
|
||||||
|
chooseVisibleSourceSets: ChooseVisibleSourceSets
|
||||||
|
): Set<File> = when (chooseVisibleSourceSets.metadataProvider) {
|
||||||
|
/* Nothing to transform: We will use original commonizer output in such cases */
|
||||||
|
is ProjectMetadataProvider -> emptySet()
|
||||||
|
|
||||||
|
/* Extract/Materialize all cinterop files from composite jar file */
|
||||||
|
is JarMetadataProvider ->
|
||||||
|
chooseVisibleSourceSets.visibleSourceSetsProvidingCInterops.flatMap { visibleSourceSetName ->
|
||||||
|
chooseVisibleSourceSets.metadataProvider.getSourceSetCInteropMetadata(
|
||||||
|
visibleSourceSetName, outputDirectory, materializeFiles = true
|
||||||
|
)
|
||||||
|
}.toSet()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Configuration.withoutProjectDependencies(): FileCollection {
|
||||||
|
return incoming.artifactView { view ->
|
||||||
|
view.componentFilter { componentIdentifier ->
|
||||||
|
componentIdentifier !is ProjectComponentIdentifier
|
||||||
|
}
|
||||||
|
}.files
|
||||||
|
}
|
||||||
|
}
|
||||||
+4
@@ -24,3 +24,7 @@ internal fun Project.getCommonizerTarget(sourceSet: KotlinSourceSet): Commonizer
|
|||||||
else -> SharedCommonizerTarget(compilationTargets.allLeaves())
|
else -> SharedCommonizerTarget(compilationTargets.allLeaves())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun Project.getSharedCommonizerTarget(sourceSet: KotlinSourceSet): SharedCommonizerTarget? {
|
||||||
|
return getCommonizerTarget(sourceSet) as? SharedCommonizerTarget
|
||||||
|
}
|
||||||
+6
@@ -6,11 +6,13 @@
|
|||||||
package org.jetbrains.kotlin.gradle.utils
|
package org.jetbrains.kotlin.gradle.utils
|
||||||
|
|
||||||
import org.gradle.api.Project
|
import org.gradle.api.Project
|
||||||
|
import org.gradle.api.Task
|
||||||
import org.gradle.api.file.ConfigurableFileCollection
|
import org.gradle.api.file.ConfigurableFileCollection
|
||||||
import org.gradle.api.file.RegularFileProperty
|
import org.gradle.api.file.RegularFileProperty
|
||||||
import org.gradle.api.model.ObjectFactory
|
import org.gradle.api.model.ObjectFactory
|
||||||
import org.gradle.api.provider.Property
|
import org.gradle.api.provider.Property
|
||||||
import org.gradle.api.provider.Provider
|
import org.gradle.api.provider.Provider
|
||||||
|
import org.gradle.api.tasks.TaskProvider
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import kotlin.properties.ReadOnlyProperty
|
import kotlin.properties.ReadOnlyProperty
|
||||||
import kotlin.reflect.KProperty
|
import kotlin.reflect.KProperty
|
||||||
@@ -91,3 +93,7 @@ internal fun Project.filesProvider(
|
|||||||
): ConfigurableFileCollection {
|
): ConfigurableFileCollection {
|
||||||
return project.files(project.provider(provider)).builtBy(*buildDependencies)
|
return project.files(project.provider(provider)).builtBy(*buildDependencies)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun <T : Task> T.outputFilesProvider(provider: T.() -> Any): ConfigurableFileCollection {
|
||||||
|
return project.filesProvider(this) { provider() }
|
||||||
|
}
|
||||||
|
|||||||
+49
@@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2021 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.utils
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
import java.util.zip.ZipEntry
|
||||||
|
import java.util.zip.ZipFile
|
||||||
|
import java.util.zip.ZipOutputStream
|
||||||
|
|
||||||
|
internal fun copyZipFilePartially(sourceZipFile: File, destinationZipFile: File, path: String) {
|
||||||
|
require(path.endsWith("/")) { "Expected path to end with '/', found '$path'" }
|
||||||
|
|
||||||
|
ZipFile(sourceZipFile).use { zip ->
|
||||||
|
val entries = zip.entries().asSequence()
|
||||||
|
.filter { it.name.startsWith(path) }
|
||||||
|
.filter { !it.isDirectory }.toList()
|
||||||
|
|
||||||
|
if (entries.isEmpty()) return
|
||||||
|
|
||||||
|
ZipOutputStream(destinationZipFile.outputStream()).use { destinationZipOutputStream ->
|
||||||
|
entries.forEach { entry ->
|
||||||
|
// Drop the source set name from the entry path
|
||||||
|
val destinationEntry = ZipEntry(entry.name.substringAfter(path))
|
||||||
|
|
||||||
|
zip.getInputStream(entry).use { inputStream ->
|
||||||
|
destinationZipOutputStream.putNextEntry(destinationEntry)
|
||||||
|
inputStream.copyTo(destinationZipOutputStream)
|
||||||
|
destinationZipOutputStream.closeEntry()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun ZipFile.listDescendants(zipEntry: ZipEntry): Sequence<ZipEntry> {
|
||||||
|
require(zipEntry.isDirectory)
|
||||||
|
return entries().asSequence().filter { entry ->
|
||||||
|
entry.name != zipEntry.name && entry.name.startsWith(zipEntry.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun ZipFile.listChildren(zipEntry: ZipEntry): Sequence<ZipEntry> {
|
||||||
|
return listDescendants(zipEntry).filter { entry ->
|
||||||
|
entry.name.removePrefix(zipEntry.name).count { it == '/' } == 1
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user