From bf5feb1b4a716ab96ee828af0206dff9688eaa79 Mon Sep 17 00:00:00 2001 From: Sebastian Sellmair Date: Mon, 18 Jan 2021 08:49:30 +0000 Subject: [PATCH] Remove KotlinMetadataTarget's KotlinTargetComponent in favour of KotlinSoftwareComponent.kt ^KT-44322 fixed --- .../kotlin/gradle/NewMultiplatformIT.kt | 60 ++++---- .../dsl/KotlinMultiplatformExtension.kt | 7 +- .../gradle/dsl/KotlinProjectExtension.kt | 20 +-- .../gradle/plugin/mpp/KotlinMetadataTarget.kt | 62 +-------- .../plugin/mpp/KotlinMultiplatformPlugin.kt | 110 --------------- .../plugin/mpp/KotlinSoftwareComponent.kt | 91 +++++++++--- .../kotlin/gradle/plugin/mpp/Publishing.kt | 131 ++++++++++++++++++ .../KotlinMetadataTargetConfigurator.kt | 1 + 8 files changed, 259 insertions(+), 223 deletions(-) create mode 100644 libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/Publishing.kt diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/NewMultiplatformIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/NewMultiplatformIT.kt index 7f098031483..83dec6bfea6 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/NewMultiplatformIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/NewMultiplatformIT.kt @@ -714,16 +714,18 @@ class NewMultiplatformIT : BaseGradleIT() { } } - gradleBuildScript().appendText("\n" + """ + gradleBuildScript().appendText( + "\n" + """ kotlin.sourceSets.all { it.languageSettings { languageVersion = '1.3' apiVersion = '1.3' } } - """.trimIndent()) + """.trimIndent() + ) - listOf( "compileKotlinMetadata", "compileKotlinJvm6", "compileKotlinNodeJs").forEach { + listOf("compileKotlinMetadata", "compileKotlinJvm6", "compileKotlinNodeJs").forEach { build(it) { assertSuccessful() assertTasksExecuted(":$it") @@ -1015,15 +1017,20 @@ class NewMultiplatformIT : BaseGradleIT() { assertSuccessful() val groupDir = projectDir.resolve("repo/com/example/") - val targetArtifactIdAppendices = listOf("metadata", "jvm6", "nodejs", "linux64") + val targetArtifactIdAppendices = listOf(null, "jvm6", "nodejs", "linux64") - val sourceJarSourceRoots = targetArtifactIdAppendices.associate { artifact -> - val sourcesJar = JarFile(groupDir.resolve("sample-lib-$artifact/1.0/sample-lib-$artifact-1.0-sources.jar")) + val sourceJarSourceRoots = targetArtifactIdAppendices.associateWith { artifact -> + val sourcesJarPath = if (artifact != null) "sample-lib-$artifact/1.0/sample-lib-$artifact-1.0-sources.jar" + else "sample-lib/1.0/sample-lib-1.0-sources.jar" + val sourcesJar = JarFile(groupDir.resolve(sourcesJarPath)) val sourcesDirs = sourcesJar.entries().asSequence().map { it.name.substringBefore("/") }.toSet() - "META-INF" - artifact to sourcesDirs + sourcesDirs } - assertEquals(setOf("commonMain"), sourceJarSourceRoots["metadata"]) + assertEquals( + setOf("commonMain", "jvm6Main", "linux64Main", "macos64Main", "mingw64Main", "mingw86Main", "nodeJsMain"), + sourceJarSourceRoots[null] + ) assertEquals(setOf("commonMain", "jvm6Main"), sourceJarSourceRoots["jvm6"]) assertEquals(setOf("commonMain", "nodeJsMain"), sourceJarSourceRoots["nodejs"]) assertEquals(setOf("commonMain", "linux64Main"), sourceJarSourceRoots["linux64"]) @@ -1361,26 +1368,29 @@ class NewMultiplatformIT : BaseGradleIT() { fun testDependenciesDsl() = with(transformProjectWithPluginsDsl("newMppDependenciesDsl")) { val originalBuildscriptContent = gradleBuildScript("app").readText() - fun testDependencies() = testResolveAllConfigurations("app", options = defaultBuildOptions().copy(warningMode = WarningMode.Summary)) { - assertContains(">> :app:testNonTransitiveStringNotationApiDependenciesMetadata --> junit-4.12.jar") - assertEquals( - 1, - (Regex.escape(">> :app:testNonTransitiveStringNotationApiDependenciesMetadata") + " .*").toRegex().findAll(output).count() - ) + fun testDependencies() = + testResolveAllConfigurations("app", options = defaultBuildOptions().copy(warningMode = WarningMode.Summary)) { + assertContains(">> :app:testNonTransitiveStringNotationApiDependenciesMetadata --> junit-4.12.jar") + assertEquals( + 1, + (Regex.escape(">> :app:testNonTransitiveStringNotationApiDependenciesMetadata") + " .*").toRegex().findAll(output) + .count() + ) - assertContains(">> :app:testNonTransitiveDependencyNotationApiDependenciesMetadata --> kotlin-reflect-${defaultBuildOptions().kotlinVersion}.jar") - assertEquals( - 1, - (Regex.escape(">> :app:testNonTransitiveStringNotationApiDependenciesMetadata") + " .*").toRegex().findAll(output).count() - ) + assertContains(">> :app:testNonTransitiveDependencyNotationApiDependenciesMetadata --> kotlin-reflect-${defaultBuildOptions().kotlinVersion}.jar") + assertEquals( + 1, + (Regex.escape(">> :app:testNonTransitiveStringNotationApiDependenciesMetadata") + " .*").toRegex().findAll(output) + .count() + ) - assertContains(">> :app:testExplicitKotlinVersionApiDependenciesMetadata --> kotlin-reflect-1.3.0.jar") - assertContains(">> :app:testExplicitKotlinVersionImplementationDependenciesMetadata --> kotlin-reflect-1.2.71.jar") - assertContains(">> :app:testExplicitKotlinVersionCompileOnlyDependenciesMetadata --> kotlin-reflect-1.2.70.jar") - assertContains(">> :app:testExplicitKotlinVersionRuntimeOnlyDependenciesMetadata --> kotlin-reflect-1.2.60.jar") + assertContains(">> :app:testExplicitKotlinVersionApiDependenciesMetadata --> kotlin-reflect-1.3.0.jar") + assertContains(">> :app:testExplicitKotlinVersionImplementationDependenciesMetadata --> kotlin-reflect-1.2.71.jar") + assertContains(">> :app:testExplicitKotlinVersionCompileOnlyDependenciesMetadata --> kotlin-reflect-1.2.70.jar") + assertContains(">> :app:testExplicitKotlinVersionRuntimeOnlyDependenciesMetadata --> kotlin-reflect-1.2.60.jar") - assertContains(">> :app:testProjectWithConfigurationApiDependenciesMetadata --> output.txt") - } + assertContains(">> :app:testProjectWithConfigurationApiDependenciesMetadata --> output.txt") + } testDependencies() diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinMultiplatformExtension.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinMultiplatformExtension.kt index a846453e646..c882900a9d3 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinMultiplatformExtension.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinMultiplatformExtension.kt @@ -8,12 +8,13 @@ package org.jetbrains.kotlin.gradle.dsl import groovy.lang.Closure import org.gradle.api.InvalidUserCodeException import org.gradle.api.NamedDomainObjectCollection +import org.gradle.api.Project import org.gradle.util.ConfigureUtil import org.jetbrains.kotlin.gradle.plugin.* import org.jetbrains.kotlin.gradle.plugin.mpp.* -open class KotlinMultiplatformExtension : - KotlinProjectExtension(), +open class KotlinMultiplatformExtension(project: Project) : + KotlinProjectExtension(project), KotlinTargetContainerWithPresetFunctions, KotlinTargetContainerWithJsPresetFunctions, KotlinTargetContainerWithNativeShortcuts { @@ -50,7 +51,7 @@ open class KotlinMultiplatformExtension : fun targetFromPreset(preset: KotlinTargetPreset<*>, configure: Closure<*>) = targetFromPreset(preset, preset.name, configure) internal val rootSoftwareComponent: KotlinSoftwareComponent by lazy { - KotlinSoftwareComponentWithCoordinatesAndPublication("kotlin", targets) + KotlinSoftwareComponentWithCoordinatesAndPublication(project, "kotlin", targets) } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinProjectExtension.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinProjectExtension.kt index f43a5d6a426..d09ee4071e1 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinProjectExtension.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinProjectExtension.kt @@ -26,7 +26,7 @@ import kotlin.reflect.KClass private const val KOTLIN_PROJECT_EXTENSION_NAME = "kotlin" internal fun Project.createKotlinExtension(extensionClass: KClass): KotlinProjectExtension { - val kotlinExt = extensions.create(KOTLIN_PROJECT_EXTENSION_NAME, extensionClass.java) + val kotlinExt = extensions.create(KOTLIN_PROJECT_EXTENSION_NAME, extensionClass.java, this) DslObject(kotlinExt).extensions.create("experimental", ExperimentalExtension::class.java) return kotlinExtension } @@ -43,7 +43,7 @@ internal val Project.multiplatformExtensionOrNull: KotlinMultiplatformExtension? internal val Project.multiplatformExtension: KotlinMultiplatformExtension get() = extensions.getByName(KOTLIN_PROJECT_EXTENSION_NAME) as KotlinMultiplatformExtension -open class KotlinProjectExtension : KotlinSourceSetContainer { +open class KotlinProjectExtension(internal val project: Project) : KotlinSourceSetContainer { val experimental: ExperimentalExtension get() = DslObject(this).extensions.getByType(ExperimentalExtension::class.java) @@ -67,32 +67,32 @@ open class KotlinProjectExtension : KotlinSourceSetContainer { } } -abstract class KotlinSingleTargetExtension : KotlinProjectExtension() { +abstract class KotlinSingleTargetExtension(project: Project) : KotlinProjectExtension(project) { abstract val target: KotlinTarget open fun target(body: Closure) = ConfigureUtil.configure(body, target) } -abstract class KotlinSingleJavaTargetExtension : KotlinSingleTargetExtension() { +abstract class KotlinSingleJavaTargetExtension(project: Project) : KotlinSingleTargetExtension(project) { abstract override val target: KotlinWithJavaTarget<*> } -open class KotlinJvmProjectExtension : KotlinSingleJavaTargetExtension() { +open class KotlinJvmProjectExtension(project: Project) : KotlinSingleJavaTargetExtension(project) { override lateinit var target: KotlinWithJavaTarget internal set open fun target(body: KotlinWithJavaTarget.() -> Unit) = target.run(body) } -open class Kotlin2JsProjectExtension : KotlinSingleJavaTargetExtension() { +open class Kotlin2JsProjectExtension(project: Project) : KotlinSingleJavaTargetExtension(project) { override lateinit var target: KotlinWithJavaTarget internal set open fun target(body: KotlinWithJavaTarget.() -> Unit) = target.run(body) } -open class KotlinJsProjectExtension : - KotlinSingleTargetExtension(), +open class KotlinJsProjectExtension(project: Project) : + KotlinSingleTargetExtension(project), KotlinJsCompilerTypeHolder { lateinit var irPreset: KotlinJsIrSingleTargetPreset @@ -224,14 +224,14 @@ open class KotlinJsProjectExtension : } } -open class KotlinCommonProjectExtension : KotlinSingleJavaTargetExtension() { +open class KotlinCommonProjectExtension(project: Project) : KotlinSingleJavaTargetExtension(project) { override lateinit var target: KotlinWithJavaTarget internal set open fun target(body: KotlinWithJavaTarget.() -> Unit) = target.run(body) } -open class KotlinAndroidProjectExtension : KotlinSingleTargetExtension() { +open class KotlinAndroidProjectExtension(project: Project) : KotlinSingleTargetExtension(project) { override lateinit var target: KotlinAndroidTarget internal set 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 9dad926fe98..e0d7989f6a6 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,8 +15,6 @@ import org.jetbrains.kotlin.gradle.targets.metadata.isCompatibilityMetadataVaria import org.jetbrains.kotlin.gradle.targets.metadata.isKotlinGranularMetadataEnabled import javax.inject.Inject -internal const val COMMON_MAIN_ELEMENTS_CONFIGURATION_NAME = "commonMainMetadataElements" - open class KotlinMetadataTarget @Inject constructor(project: Project) : KotlinOnlyTarget>(project, KotlinPlatformType.common) { @@ -29,57 +27,11 @@ open class KotlinMetadataTarget @Inject constructor(project: Project) : get() = super.artifactsTaskName override val kotlinComponents: Set by lazy { - if (!project.isKotlinGranularMetadataEnabled) - super.kotlinComponents - else { - val usageContexts = mutableSetOf() - - // This usage value is only needed for Maven scope mapping. Don't replace it with a custom Kotlin Usage value - val javaApiUsage = project.usageByName("java-api-jars") - - usageContexts += run { - val allMetadataJar = project.tasks.named(KotlinMetadataTargetConfigurator.ALL_METADATA_JAR_NAME) - val allMetadataArtifact = project.artifacts.add(Dependency.ARCHIVES_CONFIGURATION, allMetadataJar) { - it.classifier = if (project.isCompatibilityMetadataVariantEnabled) "all" else "" - } - - DefaultKotlinUsageContext( - compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME), - javaApiUsage, - apiElementsConfigurationName, - overrideConfigurationArtifacts = setOf(allMetadataArtifact) - ) - } - - 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 - ) - } - } - - val component = - createKotlinVariant(targetName, compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME), usageContexts).apply { - publishable = false // this component is not published on its own, its variants are included in the 'root' module - } - - val sourcesJarTask = - sourcesJarTask(project, lazy { project.kotlinExtension.sourceSets.toSet() }, null, targetName.toLowerCase()) - - component.sourcesArtifacts = setOf( - project.artifacts.add(Dependency.ARCHIVES_CONFIGURATION, sourcesJarTask).apply { - this as ConfigurablePublishArtifact - classifier = "sources" - } - ) - - setOf(component) - } + /* + Metadata Target does not have a KotlinTargetComponent on it's own. + Responsibility is shifted to the root KotlinSoftwareComponent + */ + emptySet() } -} \ No newline at end of file +} + 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 167b83fca05..ebc24f0297d 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 @@ -200,116 +200,6 @@ class KotlinMultiplatformPlugin( } } - private fun configurePublishingWithMavenPublish(project: Project) = project.pluginManager.withPlugin("maven-publish") { _ -> - - val targets = project.multiplatformExtension.targets - val metadataTarget = project.multiplatformExtension.metadata() - val kotlinSoftwareComponent = project.multiplatformExtension.rootSoftwareComponent - - project.extensions.configure(PublishingExtension::class.java) { publishing -> - - // The root publication that references the platform specific publications as its variants: - publishing.publications.create("kotlinMultiplatform", MavenPublication::class.java).apply { - from(kotlinSoftwareComponent) - (this as MavenPublicationInternal).publishWithOriginalFileName() - kotlinSoftwareComponent.publicationDelegate = this@apply - - metadataTarget.kotlinComponents.filterIsInstance() - .single().publicationDelegate = this@apply - - project.whenEvaluated { - if (!metadataTarget.publishable) return@whenEvaluated - metadataTarget.kotlinComponents - .flatMap { component -> component.sourcesArtifacts } - .forEach { sourcesArtifact -> artifact(sourcesArtifact) } - } - } - - // Enforce the order of creating the publications, since the metadata publication is used in the other publications: - metadataTarget.createMavenPublications(publishing.publications) - targets - .withType(AbstractKotlinTarget::class.java).matching { it.publishable && it.name != METADATA_TARGET_NAME } - .all { - if (it is KotlinAndroidTarget || it is KotlinMetadataTarget) - // Android targets have their variants created in afterEvaluate; TODO handle this better? - // Kotlin Metadata targets rely on complete source sets hierearchy and cannot be inspected for publication earlier - project.whenEvaluated { it.createMavenPublications(publishing.publications) } - else - it.createMavenPublications(publishing.publications) - } - } - - project.components.add(kotlinSoftwareComponent) - } - - private fun rewritePom( - pom: MavenPom, - pomRewriter: PomDependenciesRewriter, - shouldRewritePomDependencies: Provider, - includeOnlySpecifiedDependencies: Provider>? - ) { - pom.withXml { xml -> - if (shouldRewritePomDependencies.get()) - pomRewriter.rewritePomMppDependenciesToActualTargetModules(xml, includeOnlySpecifiedDependencies) - } - } - - private fun AbstractKotlinTarget.createMavenPublications(publications: PublicationContainer) { - components - .map { gradleComponent -> gradleComponent to kotlinComponents.single { it.name == gradleComponent.name } } - .filter { (_, kotlinComponent) -> kotlinComponent.publishable } - .forEach { (gradleComponent, kotlinComponent) -> - val componentPublication = publications.create(kotlinComponent.name, MavenPublication::class.java).apply { - // do this in whenEvaluated since older Gradle versions seem to check the files in the variant eagerly: - project.whenEvaluated { - from(gradleComponent) - kotlinComponent.sourcesArtifacts.forEach { sourceArtifact -> - artifact(sourceArtifact) - } - } - (this as MavenPublicationInternal).publishWithOriginalFileName() - artifactId = kotlinComponent.defaultArtifactId - - val pomRewriter = PomDependenciesRewriter(project, kotlinComponent) - val shouldRewritePomDependencies = - project.provider { PropertiesProvider(project).keepMppDependenciesIntactInPoms != true } - - rewritePom( - pom, - pomRewriter, - shouldRewritePomDependencies, - dependenciesForPomRewriting(this@createMavenPublications) - ) - } - - (kotlinComponent as? KotlinTargetComponentWithPublication)?.publicationDelegate = componentPublication - publicationConfigureActions.all { it.execute(componentPublication) } - } - } - - /** - * The metadata targets need their POMs to only include the dependencies from the commonMain API configuration. - * The actual apiElements configurations of metadata targets now contain dependencies from all source sets, but, as the consumers who - * can't read Gradle module metadata won't resolve a dependency on an MPP to the granular metadata variant and won't then choose the - * right dependencies for each source set, we put only the dependencies of the legacy common variant into the POM, i.e. commonMain API. - */ - private fun dependenciesForPomRewriting(target: AbstractKotlinTarget): Provider>? = - if (target !is KotlinMetadataTarget || !target.project.isKotlinGranularMetadataEnabled) - null - else { - val commonMain = target.project.kotlinExtension.sourceSets.findByName(KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME) - if (commonMain == null) - null - else - target.project.provider { - val project = target.project - - // 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) - val commonMainDependencies = commonMainApi.allDependencies - commonMainDependencies.map { ModuleCoordinates(it.group, it.name, it.version) }.toSet() - } - } private fun configureSourceSets(project: Project) = with(project.multiplatformExtension) { val production = sourceSets.create(KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME) diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinSoftwareComponent.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinSoftwareComponent.kt index ccfdaec5c5d..44c9edfea7c 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinSoftwareComponent.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinSoftwareComponent.kt @@ -17,44 +17,95 @@ import org.gradle.api.component.SoftwareComponent import org.gradle.api.internal.component.SoftwareComponentInternal import org.gradle.api.internal.component.UsageContext import org.gradle.api.publish.maven.MavenPublication -import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation -import org.jetbrains.kotlin.gradle.plugin.KotlinTarget -import org.jetbrains.kotlin.gradle.plugin.ProjectLocalConfigurations +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.KotlinCompilation.Companion.MAIN_COMPILATION_NAME +import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider +import org.jetbrains.kotlin.gradle.plugin.usageByName +import org.jetbrains.kotlin.gradle.targets.metadata.COMMON_MAIN_ELEMENTS_CONFIGURATION_NAME +import org.jetbrains.kotlin.gradle.targets.metadata.KotlinMetadataTargetConfigurator +import org.jetbrains.kotlin.gradle.targets.metadata.isCompatibilityMetadataVariantEnabled +import org.jetbrains.kotlin.gradle.targets.metadata.isKotlinGranularMetadataEnabled abstract class KotlinSoftwareComponent( + private val project: Project, private val name: String, protected val kotlinTargets: Iterable ) : SoftwareComponentInternal, ComponentWithVariants { - private val metadataTarget: KotlinMetadataTarget - get() = kotlinTargets.filterIsInstance().single() - - override fun getUsages(): Set = (metadataTarget.components.single() as SoftwareComponentInternal).usages - - override fun getVariants(): Set = - kotlinTargets.minus(metadataTarget).flatMap { it.components }.toSet() - override fun getName(): String = name + override fun getVariants(): Set = kotlinTargets + .filter { target -> target !is KotlinMetadataTarget } + .flatMap { it.components }.toSet() + + private val _usages: Set by lazy { + val metadataTarget = project.multiplatformExtension.metadata() + + if (!project.isKotlinGranularMetadataEnabled) { + val metadataCompilation = metadataTarget.compilations.getByName(MAIN_COMPILATION_NAME) + return@lazy metadataTarget.createUsageContexts(metadataCompilation) + } + + mutableSetOf().apply { + // This usage value is only needed for Maven scope mapping. Don't replace it with a custom Kotlin Usage value + val javaApiUsage = project.usageByName("java-api-jars") + + val allMetadataJar = project.tasks.named(KotlinMetadataTargetConfigurator.ALL_METADATA_JAR_NAME) + val allMetadataArtifact = project.artifacts.add(Dependency.ARCHIVES_CONFIGURATION, allMetadataJar) { allMetadataArtifact -> + allMetadataArtifact.classifier = if (project.isCompatibilityMetadataVariantEnabled) "all" else "" + } + + this += DefaultKotlinUsageContext( + compilation = metadataTarget.compilations.getByName(MAIN_COMPILATION_NAME), + usage = javaApiUsage, + dependencyConfigurationName = metadataTarget.apiElementsConfigurationName, + overrideConfigurationArtifacts = setOf(allMetadataArtifact) + ) + + + if (project.isCompatibilityMetadataVariantEnabled) { + // Ensure that consumers who expect Kotlin 1.2.x metadata package can still get one: + // publish the old metadata artifact: + this += run { + DefaultKotlinUsageContext( + metadataTarget.compilations.getByName(MAIN_COMPILATION_NAME), + javaApiUsage, + /** this configuration is created by [KotlinMetadataTargetConfigurator.createCommonMainElementsConfiguration] */ + COMMON_MAIN_ELEMENTS_CONFIGURATION_NAME + ) + } + } + } + } + + override fun getUsages(): Set { + return _usages + } + + + val sourcesArtifacts: Set by lazy { + val sourcesJarTask = sourcesJarTask(project, lazy { project.kotlinExtension.sourceSets.toSet() }, null, name.toLowerCase()) + val sourcesJarArtifact = project.artifacts.add(Dependency.ARCHIVES_CONFIGURATION, sourcesJarTask) { sourcesJarArtifact -> + sourcesJarArtifact.classifier = "sources" + } + setOf(sourcesJarArtifact) + } + // This property is declared in the parent type to allow the usages to reference it without forcing the subtypes to load, // which is needed for compatibility with older Gradle versions var publicationDelegate: MavenPublication? = null } -class KotlinSoftwareComponentWithCoordinatesAndPublication(name: String, kotlinTargets: Iterable) : - KotlinSoftwareComponent(name, kotlinTargets), ComponentWithCoordinates { +class KotlinSoftwareComponentWithCoordinatesAndPublication(project: Project, name: String, kotlinTargets: Iterable) : + KotlinSoftwareComponent(project, name, kotlinTargets), ComponentWithCoordinates { override fun getCoordinates(): ModuleVersionIdentifier = getCoordinatesFromPublicationDelegateAndProject( publicationDelegate, kotlinTargets.first().project, null ) } -// At the moment all KN artifacts have JAVA_API usage. -// TODO: Replace it with a specific usage -object NativeUsage { - const val KOTLIN_KLIB = "kotlin-klib" -} - interface KotlinUsageContext : UsageContext { val compilation: KotlinCompilation<*> val dependencyConfigurationName: String @@ -117,4 +168,4 @@ class DefaultKotlinUsageContext( override fun getCapabilities(): Set = emptySet() override fun getGlobalExcludes(): Set = emptySet() -} \ No newline at end of file +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/Publishing.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/Publishing.kt new file mode 100644 index 00000000000..4b6556bb3e8 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/Publishing.kt @@ -0,0 +1,131 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.gradle.plugin.mpp + +import org.gradle.api.Project +import org.gradle.api.provider.Provider +import org.gradle.api.publish.PublicationContainer +import org.gradle.api.publish.PublishingExtension +import org.gradle.api.publish.maven.MavenPom +import org.gradle.api.publish.maven.MavenPublication +import org.gradle.api.publish.maven.internal.publication.MavenPublicationInternal +import org.jetbrains.kotlin.gradle.dsl.kotlinExtension +import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension +import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet +import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider +import org.jetbrains.kotlin.gradle.plugin.sources.KotlinDependencyScope +import org.jetbrains.kotlin.gradle.plugin.sources.sourceSetDependencyConfigurationByScope +import org.jetbrains.kotlin.gradle.plugin.whenEvaluated +import org.jetbrains.kotlin.gradle.targets.metadata.isKotlinGranularMetadataEnabled + +internal fun configurePublishingWithMavenPublish(project: Project) = project.pluginManager.withPlugin("maven-publish") { + project.extensions.configure(PublishingExtension::class.java) { publishing -> + createRootPublication(project, publishing) + createTargetPublications(project, publishing) + } + + project.components.add(project.multiplatformExtension.rootSoftwareComponent) +} + +/** + * The root publication that references the platform specific publications as its variants + */ +private fun createRootPublication(project: Project, publishing: PublishingExtension) { + val kotlinSoftwareComponent = project.multiplatformExtension.rootSoftwareComponent + + publishing.publications.create("kotlinMultiplatform", MavenPublication::class.java).apply { + from(kotlinSoftwareComponent) + (this as MavenPublicationInternal).publishWithOriginalFileName() + kotlinSoftwareComponent.publicationDelegate = this@apply + kotlinSoftwareComponent.sourcesArtifacts.forEach { sourceArtifact -> + artifact(sourceArtifact) + } + } +} + +private fun createTargetPublications(project: Project, publishing: PublishingExtension) { + val kotlin = project.multiplatformExtension + // Enforce the order of creating the publications, since the metadata publication is used in the other publications: + kotlin.targets + .withType(AbstractKotlinTarget::class.java) + .matching { it.publishable } + .all { kotlinTarget -> + if (kotlinTarget is KotlinAndroidTarget) + // Android targets have their variants created in afterEvaluate; TODO handle this better? + project.whenEvaluated { kotlinTarget.createMavenPublications(publishing.publications) } + else + kotlinTarget.createMavenPublications(publishing.publications) + } +} + +private fun AbstractKotlinTarget.createMavenPublications(publications: PublicationContainer) { + components + .map { gradleComponent -> gradleComponent to kotlinComponents.single { it.name == gradleComponent.name } } + .filter { (_, kotlinComponent) -> kotlinComponent.publishable } + .forEach { (gradleComponent, kotlinComponent) -> + val componentPublication = publications.create(kotlinComponent.name, MavenPublication::class.java).apply { + // do this in whenEvaluated since older Gradle versions seem to check the files in the variant eagerly: + project.whenEvaluated { + from(gradleComponent) + kotlinComponent.sourcesArtifacts.forEach { sourceArtifact -> + artifact(sourceArtifact) + } + } + (this as MavenPublicationInternal).publishWithOriginalFileName() + artifactId = kotlinComponent.defaultArtifactId + + val pomRewriter = PomDependenciesRewriter(project, kotlinComponent) + val shouldRewritePomDependencies = + project.provider { PropertiesProvider(project).keepMppDependenciesIntactInPoms != true } + + rewritePom( + pom, + pomRewriter, + shouldRewritePomDependencies, + dependenciesForPomRewriting(this@createMavenPublications) + ) + } + + (kotlinComponent as? KotlinTargetComponentWithPublication)?.publicationDelegate = componentPublication + publicationConfigureActions.all { it.execute(componentPublication) } + } +} + +private fun rewritePom( + pom: MavenPom, + pomRewriter: PomDependenciesRewriter, + shouldRewritePomDependencies: Provider, + includeOnlySpecifiedDependencies: Provider>? +) { + pom.withXml { xml -> + if (shouldRewritePomDependencies.get()) + pomRewriter.rewritePomMppDependenciesToActualTargetModules(xml, includeOnlySpecifiedDependencies) + } +} + +/** + * The metadata targets need their POMs to only include the dependencies from the commonMain API configuration. + * The actual apiElements configurations of metadata targets now contain dependencies from all source sets, but, as the consumers who + * can't read Gradle module metadata won't resolve a dependency on an MPP to the granular metadata variant and won't then choose the + * right dependencies for each source set, we put only the dependencies of the legacy common variant into the POM, i.e. commonMain API. + */ +private fun dependenciesForPomRewriting(target: AbstractKotlinTarget): Provider>? = + if (target !is KotlinMetadataTarget || !target.project.isKotlinGranularMetadataEnabled) + null + else { + val commonMain = target.project.kotlinExtension.sourceSets.findByName(KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME) + if (commonMain == null) + null + else + target.project.provider { + val project = target.project + + // 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) + val commonMainDependencies = commonMainApi.allDependencies + commonMainDependencies.map { ModuleCoordinates(it.group, it.name, it.version) }.toSet() + } + } 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 2d69d8d6f75..18edd842078 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 @@ -31,6 +31,7 @@ import org.jetbrains.kotlin.gradle.utils.addExtendsFromRelation import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName import org.jetbrains.kotlin.statistics.metrics.BooleanMetrics +internal const val COMMON_MAIN_ELEMENTS_CONFIGURATION_NAME = "commonMainMetadataElements" internal const val ALL_COMPILE_METADATA_CONFIGURATION_NAME = "allSourceSetsCompileDependenciesMetadata" internal const val ALL_RUNTIME_METADATA_CONFIGURATION_NAME = "allSourceSetsRuntimeDependenciesMetadata"