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