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:
+2
-2
@@ -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",
|
||||
|
||||
+1
-1
@@ -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")))
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -1 +1,2 @@
|
||||
kotlin.mpp.enableGranularSourceSetsMetadata=true
|
||||
kotlin.mpp.enableGranularSourceSetsMetadata=true
|
||||
kotlin.mpp.enableCompatibilityMetadataVariant=true
|
||||
+2
-1
@@ -1,2 +1,3 @@
|
||||
kotlin.mpp.enableGranularSourceSetsMetadata=true
|
||||
kotlin.native.disableCompilerDaemon=true
|
||||
kotlin.native.disableCompilerDaemon=true
|
||||
kotlin.mpp.enableCompatibilityMetadataVariant=true
|
||||
+2
-1
@@ -1,2 +1,3 @@
|
||||
kotlin.mpp.enableGranularSourceSetsMetadata=true
|
||||
kotlin.native.disableCompilerDaemon=true
|
||||
kotlin.native.disableCompilerDaemon=true
|
||||
kotlin.mpp.enableCompatibilityMetadataVariant=true
|
||||
+2
-1
@@ -1,2 +1,3 @@
|
||||
kotlin.mpp.enableGranularSourceSetsMetadata=true
|
||||
kotlin.native.disableCompilerDaemon=true
|
||||
kotlin.native.disableCompilerDaemon=true
|
||||
kotlin.mpp.enableCompatibilityMetadataVariant=true
|
||||
+3
@@ -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)
|
||||
|
||||
|
||||
+1
-1
@@ -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 }
|
||||
}
|
||||
|
||||
|
||||
+6
@@ -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
|
||||
}
|
||||
+13
-21
@@ -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 =
|
||||
|
||||
+8
-2
@@ -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) {
|
||||
|
||||
+1
-1
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
+155
-100
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-2
@@ -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
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user