From 0ef8d23f57140ea81b5e24867e5042b45ca8a280 Mon Sep 17 00:00:00 2001 From: Sergey Igushkin Date: Wed, 29 Jan 2020 17:57:05 +0300 Subject: [PATCH] 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. --- .../kotlin/gradle/HierarchicalMppIT.kt | 4 +- .../jetbrains/kotlin/gradle/KlibBasedMppIT.kt | 2 +- .../gradle.properties | 3 +- .../my-app/gradle.properties | 3 +- .../my-lib-bar/gradle.properties | 3 +- .../my-lib-foo/gradle.properties | 3 +- .../kotlin/gradle/plugin/KotlinProperties.kt | 3 + .../mpp/GranularMetadataTransformation.kt | 2 +- .../plugin/mpp/KotlinCommonCompilation.kt | 6 + .../gradle/plugin/mpp/KotlinMetadataTarget.kt | 34 +-- .../plugin/mpp/KotlinMultiplatformPlugin.kt | 10 +- .../mpp/TransformKotlinGranularMetadata.kt | 2 +- .../KotlinMetadataTargetConfigurator.kt | 255 +++++++++++------- .../gradle/tasks/KotlinCompileCommon.kt | 5 +- 14 files changed, 201 insertions(+), 134 deletions(-) diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/HierarchicalMppIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/HierarchicalMppIT.kt index bc7b62bb4ac..a1758472d9f 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/HierarchicalMppIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/HierarchicalMppIT.kt @@ -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", diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/KlibBasedMppIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/KlibBasedMppIT.kt index 2f5f2e8c936..507233bf353 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/KlibBasedMppIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/KlibBasedMppIT.kt @@ -108,7 +108,7 @@ class KlibBasedMppIT : BaseGradleIT() { fun classpathHasKNStdlib(classpath: Iterable) = classpath.any { "klib/common/stdlib" in it.replace("\\", "/") } - assertFalse(classpathHasKNStdlib(getClasspath(":compileKotlinMetadata"))) + assertFalse(classpathHasKNStdlib(getClasspath(":compileCommonMainKotlinMetadata"))) assertFalse(classpathHasKNStdlib(getClasspath(":compileJvmAndJsMainKotlinMetadata"))) } } diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/hierarchical-mpp-project-dependency/gradle.properties b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/hierarchical-mpp-project-dependency/gradle.properties index 2a391fe78b0..0ae1e640ff1 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/hierarchical-mpp-project-dependency/gradle.properties +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/hierarchical-mpp-project-dependency/gradle.properties @@ -1 +1,2 @@ -kotlin.mpp.enableGranularSourceSetsMetadata=true \ No newline at end of file +kotlin.mpp.enableGranularSourceSetsMetadata=true +kotlin.mpp.enableCompatibilityMetadataVariant=true \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/hierarchical-mpp-published-modules/my-app/gradle.properties b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/hierarchical-mpp-published-modules/my-app/gradle.properties index 23dd8b88e88..2a559ef78a9 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/hierarchical-mpp-published-modules/my-app/gradle.properties +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/hierarchical-mpp-published-modules/my-app/gradle.properties @@ -1,2 +1,3 @@ kotlin.mpp.enableGranularSourceSetsMetadata=true -kotlin.native.disableCompilerDaemon=true \ No newline at end of file +kotlin.native.disableCompilerDaemon=true +kotlin.mpp.enableCompatibilityMetadataVariant=true \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/hierarchical-mpp-published-modules/my-lib-bar/gradle.properties b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/hierarchical-mpp-published-modules/my-lib-bar/gradle.properties index 23dd8b88e88..2a559ef78a9 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/hierarchical-mpp-published-modules/my-lib-bar/gradle.properties +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/hierarchical-mpp-published-modules/my-lib-bar/gradle.properties @@ -1,2 +1,3 @@ kotlin.mpp.enableGranularSourceSetsMetadata=true -kotlin.native.disableCompilerDaemon=true \ No newline at end of file +kotlin.native.disableCompilerDaemon=true +kotlin.mpp.enableCompatibilityMetadataVariant=true \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/hierarchical-mpp-published-modules/my-lib-foo/gradle.properties b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/hierarchical-mpp-published-modules/my-lib-foo/gradle.properties index 23dd8b88e88..2a559ef78a9 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/hierarchical-mpp-published-modules/my-lib-foo/gradle.properties +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/hierarchical-mpp-published-modules/my-lib-foo/gradle.properties @@ -1,2 +1,3 @@ kotlin.mpp.enableGranularSourceSetsMetadata=true -kotlin.native.disableCompilerDaemon=true \ No newline at end of file +kotlin.native.disableCompilerDaemon=true +kotlin.mpp.enableCompatibilityMetadataVariant=true \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinProperties.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinProperties.kt index a5d1ca2e5da..c337bff5fd8 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinProperties.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinProperties.kt @@ -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) diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/GranularMetadataTransformation.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/GranularMetadataTransformation.kt index 6a67da148ce..66a2a02e36c 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/GranularMetadataTransformation.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/GranularMetadataTransformation.kt @@ -317,7 +317,7 @@ private class ProjectMppDependencyMetadataExtractor( doProcessFiles: Boolean ): Map = 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 } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinCommonCompilation.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinCommonCompilation.kt index 249ad979f8f..704c94e6faf 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinCommonCompilation.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinCommonCompilation.kt @@ -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 : KotlinCompilation @@ -21,4 +22,9 @@ class KotlinCommonCompilation( ) : AbstractKotlinCompilation(target, name), KotlinMetadataCompilation { 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 } \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinMetadataTarget.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinMetadataTarget.kt index 510070b03af..acaacac10a5 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinMetadataTarget.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinMetadataTarget.kt @@ -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 = diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinMultiplatformPlugin.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinMultiplatformPlugin.kt index b1c4bdd308f..4354b26ee51 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinMultiplatformPlugin.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinMultiplatformPlugin.kt @@ -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) { diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/TransformKotlinGranularMetadata.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/TransformKotlinGranularMetadata.kt index 35ebc8a261e..9162359282b 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/TransformKotlinGranularMetadata.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/TransformKotlinGranularMetadata.kt @@ -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 } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/metadata/KotlinMetadataTargetConfigurator.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/metadata/KotlinMetadataTargetConfigurator.kt index f7993aba502..044f902a28d 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/metadata/KotlinMetadataTargetConfigurator.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/metadata/KotlinMetadataTargetConfigurator.kt @@ -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, 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(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(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> = publishedCommonSourceSets.associate { sourceSet -> - sourceSet to configureMetadataCompilation(target, sourceSet, allMetadataJar) + sourceSet to createMetadataCompilation(target, sourceSet, allMetadataJar) } - sourceSetsWithMetadataCompilations.forEach { (sourceSet, metadataCompilation) -> - val compileMetadataTransformationTasksForHierarchy = mutableSetOf>() - - // 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(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> = 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> = 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( + transformGranularMetadataTaskName(compilation.name), + listOf(sourceSet) + ) { } - project.registerTask(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, - project: Project, - transformationTaskHolders: Set> + compilation: AbstractKotlinCompilation<*> ): FileCollection { - return project.files(Callable { - val allResolutionsByArtifactFile: Map> = - mutableMapOf>().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(transformGranularMetadataTaskName(hierarchySourceSet.name)) + } + + val allResolutionsByArtifactFile: Map> = + mutableMapOf>().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 = + 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 = - transformationTaskHolders.flatMap { it.get().filesByResolution.toList() }.toMap() + mutableSetOf().apply { - mutableSetOf().apply { - fromFiles.forEach { file -> - val resolutions = allResolutionsByArtifactFile[file] - if (resolutions == null) { - add(file) - } else { - val chooseVisibleSourceSets = - resolutions.filterIsInstance() + 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() + + 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 { val compilationsBySourceSet: Map>> = 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 } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinCompileCommon.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinCompileCommon.kt index f90339f4ac2..aa1127302b4 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinCompileCommon.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinCompileCommon.kt @@ -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