Rework legacy metadata variant in MPP with source sets metadata

* Make publishing the compatibility variant optional, and don't publish
  the compatibility variant by default -- the library maintainer should
  do this explicitly
* To build the compatibility artifact, compile the commonMain source
  set using a separate compilation (the default `main` compilation,
  while creating a separate `commonMain` compilation; this separation
  is needed to be able to still compile *.kotlin_metadata from
  commonMain even when compiling *.klib from all source sets)
* When the compatibility variant is disabled, clear the dependencies of
  the `main` compilation and disable its Kotlin compilation task
* When the compatibility variant is enabled, exposed it for project
  dependencies resolution as well, so that its artifact can be used by
  the dependent project instead of the granular metadata artifact that
  will contain *.klib (which can't be read by the legacy compiler)
* Refactor the configuration of the metadata compilation dependencies:
  concentrate all the logic in one place and make it process the source
  sets graph lazily.
This commit is contained in:
Sergey Igushkin
2020-01-29 17:57:05 +03:00
parent 8a08fef2b3
commit 0ef8d23f57
14 changed files with 201 additions and 134 deletions
@@ -167,7 +167,7 @@ class HierarchicalMppIT : BaseGradleIT() {
publishedMetadataJar.checkAllEntryNamesArePresent(
"META-INF/$MULTIPLATFORM_PROJECT_METADATA_FILE_NAME",
"commonMain/META-INF/my-lib-foo.kotlin_module",
"commonMain/META-INF/my-lib-foo_commonMain.kotlin_module",
"commonMain/com/example/foo/FooKt.kotlin_metadata",
"jvmAndJsMain/META-INF/my-lib-foo_jvmAndJsMain.kotlin_module",
@@ -205,7 +205,7 @@ class HierarchicalMppIT : BaseGradleIT() {
publishedMetadataJar.checkAllEntryNamesArePresent(
"META-INF/$MULTIPLATFORM_PROJECT_METADATA_FILE_NAME",
"commonMain/META-INF/my-lib-bar.kotlin_module",
"commonMain/META-INF/my-lib-bar_commonMain.kotlin_module",
"commonMain/com/example/bar/BarKt.kotlin_metadata",
"jvmAndJsMain/META-INF/my-lib-bar_jvmAndJsMain.kotlin_module",
@@ -108,7 +108,7 @@ class KlibBasedMppIT : BaseGradleIT() {
fun classpathHasKNStdlib(classpath: Iterable<String>) = classpath.any { "klib/common/stdlib" in it.replace("\\", "/") }
assertFalse(classpathHasKNStdlib(getClasspath(":compileKotlinMetadata")))
assertFalse(classpathHasKNStdlib(getClasspath(":compileCommonMainKotlinMetadata")))
assertFalse(classpathHasKNStdlib(getClasspath(":compileJvmAndJsMainKotlinMetadata")))
}
}
@@ -1 +1,2 @@
kotlin.mpp.enableGranularSourceSetsMetadata=true
kotlin.mpp.enableGranularSourceSetsMetadata=true
kotlin.mpp.enableCompatibilityMetadataVariant=true
@@ -1,2 +1,3 @@
kotlin.mpp.enableGranularSourceSetsMetadata=true
kotlin.native.disableCompilerDaemon=true
kotlin.native.disableCompilerDaemon=true
kotlin.mpp.enableCompatibilityMetadataVariant=true
@@ -1,2 +1,3 @@
kotlin.mpp.enableGranularSourceSetsMetadata=true
kotlin.native.disableCompilerDaemon=true
kotlin.native.disableCompilerDaemon=true
kotlin.mpp.enableCompatibilityMetadataVariant=true
@@ -1,2 +1,3 @@
kotlin.mpp.enableGranularSourceSetsMetadata=true
kotlin.native.disableCompilerDaemon=true
kotlin.native.disableCompilerDaemon=true
kotlin.mpp.enableCompatibilityMetadataVariant=true
@@ -97,6 +97,9 @@ internal class PropertiesProvider private constructor(private val project: Proje
val enableCommonKlibs: Boolean?
get() = booleanProperty("kotlin.mpp.enableCommonKlibs")
val enableCompatibilityMetadataVariant: Boolean?
get() = booleanProperty("kotlin.mpp.enableCompatibilityMetadataVariant")
val ignoreDisabledNativeTargets: Boolean?
get() = booleanProperty(DisabledNativeTargetsReporter.DISABLE_WARNING_PROPERTY_NAME)
@@ -317,7 +317,7 @@ private class ProjectMppDependencyMetadataExtractor(
doProcessFiles: Boolean
): Map<String, FileCollection> =
dependencyProject.multiplatformExtension.targets.getByName(KotlinMultiplatformPlugin.METADATA_TARGET_NAME).compilations
.filter { it.defaultSourceSet.name in visibleSourceSetNames }
.filter { it.name in visibleSourceSetNames }
.associate { it.defaultSourceSet.name to it.output.classesDirs }
}
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider
import org.jetbrains.kotlin.gradle.tasks.KotlinCompileCommon
interface KotlinMetadataCompilation<T : KotlinCommonOptions> : KotlinCompilation<T>
@@ -21,4 +22,9 @@ class KotlinCommonCompilation(
) : AbstractKotlinCompilation<KotlinMultiplatformCommonOptions>(target, name), KotlinMetadataCompilation<KotlinMultiplatformCommonOptions> {
override val compileKotlinTask: KotlinCompileCommon
get() = super.compileKotlinTask as KotlinCompileCommon
internal val isKlibCompilation: Boolean
get() = PropertiesProvider(target.project).enableCommonKlibs == true && !forceCompilationToKotlinMetadata
internal var forceCompilationToKotlinMetadata: Boolean = false
}
@@ -15,6 +15,7 @@ import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.sources.KotlinDependencyScope
import org.jetbrains.kotlin.gradle.plugin.sources.sourceSetDependencyConfigurationByScope
import org.jetbrains.kotlin.gradle.targets.metadata.KotlinMetadataTargetConfigurator
import org.jetbrains.kotlin.gradle.targets.metadata.isCompatibilityMetadataVariantEnabled
import org.jetbrains.kotlin.gradle.targets.metadata.isKotlinGranularMetadataEnabled
import org.jetbrains.kotlin.gradle.utils.isGradleVersionAtLeast
import javax.inject.Inject
@@ -35,7 +36,9 @@ open class KotlinMetadataTarget @Inject constructor(project: Project) :
usageContexts += run {
val allMetadataJar = project.tasks.getByName(KotlinMetadataTargetConfigurator.ALL_METADATA_JAR_NAME)
val allMetadataArtifact = project.artifacts.add(Dependency.ARCHIVES_CONFIGURATION, allMetadataJar) { it.classifier = "all" }
val allMetadataArtifact = project.artifacts.add(Dependency.ARCHIVES_CONFIGURATION, allMetadataJar) {
it.classifier = if (project.isCompatibilityMetadataVariantEnabled) "all" else ""
}
DefaultKotlinUsageContext(
compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME),
@@ -45,28 +48,17 @@ open class KotlinMetadataTarget @Inject constructor(project: Project) :
)
}
// Ensure that consumers who expect Kotlin 1.2.x metadata package can still get one: publish the old metadata artifact as well:
usageContexts += run {
project.configurations.create(COMMON_MAIN_ELEMENTS_CONFIGURATION_NAME).apply {
isCanBeConsumed = false
isCanBeResolved = false
usesPlatformOf(this@KotlinMetadataTarget)
attributes.attribute(USAGE_ATTRIBUTE, KotlinUsages.producerApiUsage(this@KotlinMetadataTarget)) // 'kotlin-api' usage
val commonMainApiConfiguration = project.sourceSetDependencyConfigurationByScope(
project.kotlinExtension.sourceSets.getByName(KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME),
KotlinDependencyScope.API_SCOPE
if (PropertiesProvider(project).enableCompatibilityMetadataVariant == true) {
// Ensure that consumers who expect Kotlin 1.2.x metadata package can still get one:
// publish the old metadata artifact:
usageContexts += run {
DefaultKotlinUsageContext(
compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME),
javaApiUsage,
/** this configuration is created by [KotlinMetadataTargetConfigurator.createCommonMainElementsConfiguration] */
COMMON_MAIN_ELEMENTS_CONFIGURATION_NAME
)
extendsFrom(commonMainApiConfiguration)
project.artifacts.add(name, project.tasks.getByName(artifactsTaskName))
}
DefaultKotlinUsageContext(
compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME),
javaApiUsage,
COMMON_MAIN_ELEMENTS_CONFIGURATION_NAME
)
}
val component =
@@ -27,7 +27,9 @@ import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.sources.DefaultLanguageSettingsBuilder
import org.jetbrains.kotlin.gradle.plugin.sources.KotlinDependencyScope
import org.jetbrains.kotlin.gradle.plugin.sources.checkSourceSetVisibilityRequirements
import org.jetbrains.kotlin.gradle.plugin.sources.sourceSetDependencyConfigurationByScope
import org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatsService
import org.jetbrains.kotlin.gradle.scripting.internal.ScriptingGradleSubplugin
import org.jetbrains.kotlin.gradle.targets.metadata.isKotlinGranularMetadataEnabled
@@ -246,9 +248,13 @@ class KotlinMultiplatformPlugin(
val (group, name, _) = groupNameVersion
val project = target.project
val metadataApiLegacyElements = project.configurations.getByName(COMMON_MAIN_ELEMENTS_CONFIGURATION_NAME)
val commonMain = project.kotlinExtension.sourceSets?.findByName(KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME)
?: return true
return metadataApiLegacyElements.allDependencies.any { it.group == group && it.name == name }
// Only the commonMain API dependencies can be published for consumers who can't read Gradle project metadata
val commonMainApi = project.sourceSetDependencyConfigurationByScope(commonMain, KotlinDependencyScope.API_SCOPE)
return commonMainApi.allDependencies.any { it.group == group && it.name == name }
}
private fun configureSourceSets(project: Project) = with(project.multiplatformExtension) {
@@ -75,7 +75,7 @@ open class TransformKotlinGranularMetadata
lazy {
KotlinMetadataTargetConfigurator.dependsOnWithInterCompilationDependencies(project, kotlinSourceSet).map {
project.tasks.withType(TransformKotlinGranularMetadata::class.java)
.getByName(KotlinMetadataTargetConfigurator.transformGranularMetadataTaskName(it))
.getByName(KotlinMetadataTargetConfigurator.transformGranularMetadataTaskName(it.name))
.transformation
}
}
@@ -7,10 +7,11 @@ package org.jetbrains.kotlin.gradle.targets.metadata
import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration
import org.gradle.api.attributes.Usage
import org.gradle.api.attributes.Usage.USAGE_ATTRIBUTE
import org.gradle.api.file.FileCollection
import org.gradle.api.internal.component.SoftwareComponentInternal
import org.gradle.api.plugins.BasePlugin
import org.gradle.api.tasks.AbstractCopyTask
import org.gradle.api.tasks.TaskProvider
import org.gradle.api.tasks.bundling.Jar
import org.jetbrains.kotlin.gradle.dsl.KotlinCommonOptions
@@ -38,18 +39,20 @@ internal const val ALL_RUNTIME_METADATA_CONFIGURATION_NAME = "allSourceSetsRunti
internal val Project.isKotlinGranularMetadataEnabled: Boolean
get() = PropertiesProvider(rootProject).enableGranularSourceSetsMetadata == true
internal val Project.isCompatibilityMetadataVariantEnabled: Boolean
get() = PropertiesProvider(this).enableCompatibilityMetadataVariant == true
class KotlinMetadataTargetConfigurator(kotlinPluginVersion: String) :
KotlinOnlyTargetConfigurator<AbstractKotlinCompilation<*>, KotlinMetadataTarget>(
createDefaultSourceSets = false,
createTestCompilation = false,
kotlinPluginVersion = kotlinPluginVersion
) {
companion object {
internal const val ALL_METADATA_JAR_NAME = "allMetadataJar"
internal fun transformGranularMetadataTaskName(sourceSet: KotlinSourceSet) =
lowerCamelCaseName("transform", sourceSet.name, "DependenciesMetadata")
internal fun transformGranularMetadataTaskName(sourceSetName: String) =
lowerCamelCaseName("transform", sourceSetName, "DependenciesMetadata")
// TODO generalize once a general production-test and other kinds of inter-compilation visibility are supported
// Currently, this is a temporary ad-hoc mechanism for exposing the commonMain dependencies to the test source sets
@@ -60,24 +63,38 @@ class KotlinMetadataTargetConfigurator(kotlinPluginVersion: String) :
}
}
private val KotlinOnlyTarget<*>.apiElementsConfiguration: Configuration
get() = project.configurations.getByName(apiElementsConfigurationName)
override fun configureTarget(target: KotlinMetadataTarget) {
super.configureTarget(target)
if (target.project.isKotlinGranularMetadataEnabled) {
KotlinBuildStatsService.getInstance()?.report(BooleanMetrics.ENABLED_HMPP, true)
target.compilations.withType(KotlinCommonCompilation::class.java).getByName(KotlinCompilation.MAIN_COMPILATION_NAME).run {
if (target.project.isCompatibilityMetadataVariantEnabled) {
// Force the default 'main' compilation to produce *.kotlin_metadata regardless of the klib feature flag.
forceCompilationToKotlinMetadata = true
} else {
// Clear the dependencies of the compilation so that they don't take time resolving during task graph construction:
compileDependencyFiles = target.project.files()
}
compileKotlinTaskHolder.configure { it.onlyIf { target.project.isCompatibilityMetadataVariantEnabled } }
}
createMergedAllSourceSetsConfigurations(target)
val allMetadataJar = target.project.tasks.getByName(ALL_METADATA_JAR_NAME) as Jar
createMetadataCompilationsForCommonSourceSets(target, allMetadataJar)
configureProjectStructureMetadataGeneration(allMetadataJar)
setupDependencyTransformationForCommonSourceSets(target)
target.project.configurations.getByName(target.apiElementsConfigurationName).attributes
.attribute(Usage.USAGE_ATTRIBUTE, target.project.usageByName(KotlinUsages.KOTLIN_METADATA))
.attribute(USAGE_ATTRIBUTE, target.project.usageByName(KotlinUsages.KOTLIN_METADATA))
if (target.project.isCompatibilityMetadataVariantEnabled) {
createCommonMainElementsConfiguration(target)
}
}
}
@@ -85,7 +102,7 @@ class KotlinMetadataTargetConfigurator(kotlinPluginVersion: String) :
val project = compilation.target.project
/** See [createTransformedMetadataClasspath] and its usage. */
if (project.isKotlinGranularMetadataEnabled)
if (project.isKotlinGranularMetadataEnabled && compilation.name != KotlinCompilation.MAIN_COMPILATION_NAME)
compilation.compileDependencyFiles = project.files()
else
super.setupCompilationDependencyFiles(compilation)
@@ -118,16 +135,26 @@ class KotlinMetadataTargetConfigurator(kotlinPluginVersion: String) :
super.createJarTasks(target)
if (target.project.isKotlinGranularMetadataEnabled) {
target.project.locateTask<Jar>(target.artifactsTaskName)!!.configure {
if (!target.project.isCompatibilityMetadataVariantEnabled) {
it.setArchiveClassifierCompatible { "commonMain" }
}
it.onlyIf { target.project.isCompatibilityMetadataVariantEnabled }
}
/** This JAR is created in addition to the main one, published with a classifier, but is by default used
* for project dependencies (as the Kotlin Granular metadata is enabled across all projects in a build, this is OK).
* See also [KotlinMetadataTarget.kotlinComponents]
*/
target.project.tasks.create(ALL_METADATA_JAR_NAME, Jar::class.java).apply {
description = "Assembles a jar archive containing the metadata for all Kotlin source sets."
group = BasePlugin.BUILD_GROUP
target.project.registerTask<Jar>(ALL_METADATA_JAR_NAME) { allMetadataJar ->
allMetadataJar.description = "Assembles a jar archive containing the metadata for all Kotlin source sets."
allMetadataJar.group = BasePlugin.BUILD_GROUP
setArchiveAppendixCompatible { target.name.toLowerCase() }
setArchiveClassifierCompatible { "all" }
allMetadataJar.setArchiveAppendixCompatible { target.name.toLowerCase() }
if (target.project.isCompatibilityMetadataVariantEnabled) {
allMetadataJar.setArchiveClassifierCompatible { "all" }
}
}
}
}
@@ -152,40 +179,26 @@ class KotlinMetadataTargetConfigurator(kotlinPluginVersion: String) :
val sourceSetsWithMetadataCompilations: Map<KotlinSourceSet, AbstractKotlinCompilation<*>> =
publishedCommonSourceSets.associate { sourceSet ->
sourceSet to configureMetadataCompilation(target, sourceSet, allMetadataJar)
sourceSet to createMetadataCompilation(target, sourceSet, allMetadataJar)
}
sourceSetsWithMetadataCompilations.forEach { (sourceSet, metadataCompilation) ->
val compileMetadataTransformationTasksForHierarchy = mutableSetOf<TaskProvider<TransformKotlinGranularMetadata>>()
// Adjust metadata compilation to support source set hierarchies, i.e. use both the outputs of dependsOn source set compilation
// and their dependencies metadata transformed for compilation:
sourceSet.getSourceSetHierarchy().forEach { hierarchySourceSet ->
if (hierarchySourceSet != sourceSet) {
val dependencyCompilation = sourceSetsWithMetadataCompilations.getValue(hierarchySourceSet as DefaultKotlinSourceSet)
metadataCompilation.compileDependencyFiles += dependencyCompilation.output.classesDirs.filter { it.exists() }
}
project.locateTask<TransformKotlinGranularMetadata>(transformGranularMetadataTaskName(hierarchySourceSet))
?.let(compileMetadataTransformationTasksForHierarchy::add)
}
metadataCompilation.compileDependencyFiles += createTransformedMetadataClasspath(
project.configurations.getByName(ALL_COMPILE_METADATA_CONFIGURATION_NAME),
project,
compileMetadataTransformationTasksForHierarchy
)
if (project.isCompatibilityMetadataVariantEnabled) {
val mainCompilation = target.compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME)
configureMetadataDependenciesForCompilation(mainCompilation)
}
sourceSetsWithMetadataCompilations.values.forEach { compilation ->
exportDependenciesForPublishing(compilation)
}
}
val generateMetadata = createGenerateProjectStructureMetadataTask()
private fun configureProjectStructureMetadataGeneration(allMetadataJar: Jar): AbstractCopyTask {
val project = allMetadataJar.project
val generateMetadata = project.createGenerateProjectStructureMetadataTask()
allMetadataJar.from(project.files(Callable {
generateMetadata.get().resultXmlFile
}).builtBy(generateMetadata)) { spec ->
return allMetadataJar.from(
project.files(Callable { generateMetadata.get().resultXmlFile }).builtBy(generateMetadata)
) { spec ->
spec.into("META-INF").rename { MULTIPLATFORM_PROJECT_METADATA_FILE_NAME }
}
}
@@ -226,54 +239,52 @@ class KotlinMetadataTargetConfigurator(kotlinPluginVersion: String) :
isCanBeResolved = true
usesPlatformOf(target)
attributes.attribute(Usage.USAGE_ATTRIBUTE, project.usageByName(KotlinUsages.KOTLIN_METADATA))
attributes.attribute(USAGE_ATTRIBUTE, project.usageByName(KotlinUsages.KOTLIN_METADATA))
}
}
}
private fun configureMetadataCompilation(
private fun createMetadataCompilation(
target: KotlinMetadataTarget,
sourceSet: KotlinSourceSet,
allMetadataJar: Jar
): AbstractKotlinCompilation<*> {
val project = target.project
// With the metadata target, we publish all API dependencies of all the published source sets together:
target.apiElementsConfiguration.extendsFrom(project.configurations.getByName(sourceSet.apiConfigurationName))
val compilationName = sourceSet.name
val compilationName = when (sourceSet.name) {
KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME -> KotlinCompilation.MAIN_COMPILATION_NAME
else -> sourceSet.name
val isNativeSourceSet = CompilationSourceSetUtil.compilationsBySourceSets(project).getValue(sourceSet)
.all { compilation -> compilation.target is KotlinNativeTarget }
val compilationFactory: KotlinCompilationFactory<out AbstractKotlinCompilation<*>> = when {
isNativeSourceSet -> KotlinSharedNativeCompilationFactory(target)
else -> KotlinCommonCompilationFactory(target)
}
val metadataCompilation = run {
if (compilationName == KotlinCompilation.MAIN_COMPILATION_NAME) {
// It already exists. TODO Create it here?
return@run target.compilations.getByName(compilationName)
}
return compilationFactory.create(compilationName).apply {
target.compilations.add(this@apply)
addExactSourceSetsEagerly(setOf(sourceSet))
val isNativeSourceSet = CompilationSourceSetUtil.compilationsBySourceSets(project).getValue(sourceSet)
.all { compilation -> compilation.target is KotlinNativeTarget }
configureMetadataDependenciesForCompilation(this@apply)
val compilationFactory: KotlinCompilationFactory<out AbstractKotlinCompilation<*>> = when {
isNativeSourceSet -> KotlinSharedNativeCompilationFactory(target)
else -> KotlinCommonCompilationFactory(target)
}
compilationFactory.create(compilationName).apply {
target.compilations.add(this@apply)
addExactSourceSetsEagerly(setOf(sourceSet))
}
val metadataContent = project.filesWithUnpackedArchives(this@apply.output.allOutputs, setOf("klib"))
allMetadataJar.from(metadataContent) { spec -> spec.into(this@apply.defaultSourceSet.name) }
}
}
project.addExtendsFromRelation(metadataCompilation.compileDependencyConfigurationName, ALL_COMPILE_METADATA_CONFIGURATION_NAME)
private fun configureMetadataDependenciesForCompilation(compilation: AbstractKotlinCompilation<*>) {
val project = compilation.target.project
val sourceSet = compilation.defaultSourceSet
val metadataContent = project.filesWithUnpackedArchives(metadataCompilation.output.allOutputs, setOf("klib"))
allMetadataJar.from(metadataContent) { spec -> spec.into(metadataCompilation.defaultSourceSet.name) }
project.registerTask<TransformKotlinGranularMetadata>(
transformGranularMetadataTaskName(compilation.name),
listOf(sourceSet)
) { }
project.registerTask<TransformKotlinGranularMetadata>(transformGranularMetadataTaskName(sourceSet), listOf(sourceSet)) { }
return metadataCompilation
compilation.compileDependencyFiles += createTransformedMetadataClasspath(
project.configurations.getByName(ALL_COMPILE_METADATA_CONFIGURATION_NAME),
compilation
)
}
private fun setupDependencyTransformationForSourceSet(
@@ -358,62 +369,106 @@ class KotlinMetadataTargetConfigurator(kotlinPluginVersion: String) :
private fun createTransformedMetadataClasspath(
fromFiles: Iterable<File>,
project: Project,
transformationTaskHolders: Set<TaskProvider<TransformKotlinGranularMetadata>>
compilation: AbstractKotlinCompilation<*>
): FileCollection {
return project.files(Callable {
val allResolutionsByArtifactFile: Map<File, Iterable<MetadataDependencyResolution>> =
mutableMapOf<File, MutableList<MetadataDependencyResolution>>().apply {
transformationTaskHolders.forEach {
val resolutions = it.get().metadataDependencyResolutions
val project = compilation.target.project
resolutions.forEach { resolution ->
val artifacts = resolution.dependency.moduleArtifacts.map { it.file }
// Adjust metadata compilation to support source set hierarchies, i.e. use both the outputs of dependsOn source set compilation
// and their dependencies metadata transformed for compilation:
return project.files(
project.provider {
val metadataTarget = compilation.target
val sourceSet = compilation.defaultSourceSet
artifacts.forEach { artifactFile ->
getOrPut(artifactFile) { mutableListOf() }.add(resolution)
val transformationTaskHolders = sourceSet.getSourceSetHierarchy().mapNotNull { hierarchySourceSet ->
project.locateTask<TransformKotlinGranularMetadata>(transformGranularMetadataTaskName(hierarchySourceSet.name))
}
val allResolutionsByArtifactFile: Map<File, Iterable<MetadataDependencyResolution>> =
mutableMapOf<File, MutableList<MetadataDependencyResolution>>().apply {
transformationTaskHolders.forEach {
val resolutions = it.get().metadataDependencyResolutions
resolutions.forEach { resolution ->
val artifacts = resolution.dependency.moduleArtifacts.map { it.file }
artifacts.forEach { artifactFile ->
getOrPut(artifactFile) { mutableListOf() }.add(resolution)
}
}
}
}
val transformedFilesByResolution: Map<MetadataDependencyResolution, FileCollection> =
transformationTaskHolders.flatMap { it.get().filesByResolution.toList() }.toMap()
val dependsOnCompilationOutputs = sourceSet.getSourceSetHierarchy().mapNotNull { hierarchySourceSet ->
val dependencyCompilation = metadataTarget.compilations.getByName(hierarchySourceSet.name)
dependencyCompilation.output.classesDirs.takeIf { hierarchySourceSet != sourceSet }
}
val transformedFilesByResolution: Map<MetadataDependencyResolution, FileCollection> =
transformationTaskHolders.flatMap { it.get().filesByResolution.toList() }.toMap()
mutableSetOf<Any /* File | FileCollection */>().apply {
mutableSetOf<Any /* File | FileCollection */>().apply {
fromFiles.forEach { file ->
val resolutions = allResolutionsByArtifactFile[file]
if (resolutions == null) {
add(file)
} else {
val chooseVisibleSourceSets =
resolutions.filterIsInstance<MetadataDependencyResolution.ChooseVisibleSourceSets>()
addAll(dependsOnCompilationOutputs)
if (chooseVisibleSourceSets.isNotEmpty()) {
add(chooseVisibleSourceSets.map { transformedFilesByResolution.getValue(it) })
} else if (resolutions.any { it is MetadataDependencyResolution.KeepOriginalDependency }) {
fromFiles.forEach { file ->
val resolutions = allResolutionsByArtifactFile[file]
if (resolutions == null) {
add(file)
} // else: all dependency transformations exclude this dependency as unrequested; don't add any files
} else {
val chooseVisibleSourceSets =
resolutions.filterIsInstance<MetadataDependencyResolution.ChooseVisibleSourceSets>()
if (chooseVisibleSourceSets.isNotEmpty()) {
// Wrap the list into a FileCollection, as some older Gradle version failed to resolve the classpath
add(project.files(chooseVisibleSourceSets.map { transformedFilesByResolution.getValue(it) }))
} else if (resolutions.any { it is MetadataDependencyResolution.KeepOriginalDependency }) {
add(file)
} // else: all dependency transformations exclude this dependency as unrequested; don't add any files
}
}
// Add a build dependency on the granular metadata transformations of the dependency source sets
add(project.files().builtBy(transformationTaskHolders))
}
}
}).builtBy(transformationTaskHolders)
)
}
private fun createCommonMainElementsConfiguration(target: KotlinMetadataTarget) {
val project = target.project
project.configurations.create(COMMON_MAIN_ELEMENTS_CONFIGURATION_NAME).apply {
isCanBeConsumed = true
isCanBeResolved = false
usesPlatformOf(target)
attributes.attribute(
USAGE_ATTRIBUTE,
KotlinUsages.producerApiUsage(target)
) // 'kotlin-api' usage
val commonMainApiConfiguration = project.sourceSetDependencyConfigurationByScope(
project.kotlinExtension.sourceSets.getByName(KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME),
KotlinDependencyScope.API_SCOPE
)
extendsFrom(commonMainApiConfiguration)
project.artifacts.add(name, project.tasks.getByName(target.artifactsTaskName))
}
}
private fun getPublishedCommonSourceSets(project: Project): Set<KotlinSourceSet> {
val compilationsBySourceSet: Map<KotlinSourceSet, Set<KotlinCompilation<*>>> =
CompilationSourceSetUtil.compilationsBySourceSets(project)
// For now, we will only compile metadata from source sets used by multiple platforms
// TODO once the compiler is able to analyze common code with platform-specific features and dependencies, lift this restriction
val sourceSetsUsedInMultipleTargets = compilationsBySourceSet.filterValues { compilations ->
compilations.map { it.target.platformType }.distinct().run {
size > 1 || (
PropertiesProvider(project.rootProject).enableCommonKlibs == true &&
singleOrNull() == KotlinPlatformType.native &&
size > 1 && toSet() != setOf(KotlinPlatformType.androidJvm, KotlinPlatformType.jvm) || (
singleOrNull() == KotlinPlatformType.native &&
compilations.map { it.target }.distinct().size > 1 &&
compilations.all { (it as AbstractKotlinNativeCompilation).konanTarget.enabledOnCurrentHost }
compilations.any { (it as AbstractKotlinNativeCompilation).konanTarget.enabledOnCurrentHost }
)
// TODO: platform-shared source sets other than Kotlin/Native ones are not yet supported; support will be needed for JVM, JS
}
}
@@ -27,7 +27,8 @@ import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformCommonOptionsImpl
import org.jetbrains.kotlin.gradle.dsl.fillDefaultValues
import org.jetbrains.kotlin.gradle.internal.tasks.allOutputFiles
import org.jetbrains.kotlin.gradle.logging.GradlePrintingMessageCollector
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinCommonCompilation
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinMetadataCompilation
import org.jetbrains.kotlin.incremental.ChangedFiles
import java.io.File
@@ -53,7 +54,7 @@ open class KotlinCompileCommon : AbstractKotlinCompile<K2MetadataCompilerArgumen
args.moduleName = this@KotlinCompileCommon.moduleName
if (PropertiesProvider(project.rootProject).enableCommonKlibs == true) {
if ((taskData.compilation as? KotlinCommonCompilation)?.isKlibCompilation == true) {
args.klibBasedMpp = true
}