wip
diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/pm20/variantPublishing.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/pm20/variantPublishing.kt
index 5c2f3b3d..a9a2051 100644
--- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/pm20/variantPublishing.kt
+++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/pm20/variantPublishing.kt
@@ -8,6 +8,7 @@
import com.android.build.gradle.internal.publishing.AndroidArtifacts
import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration
+import org.gradle.api.artifacts.Dependency
import org.gradle.api.artifacts.ModuleDependency
import org.gradle.api.artifacts.PublishArtifact
import org.gradle.api.attributes.*
@@ -19,15 +20,23 @@
import org.gradle.api.publish.maven.MavenPublication
import org.gradle.api.publish.maven.internal.publication.DefaultMavenPublication
import org.gradle.api.tasks.bundling.AbstractArchiveTask
+import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
+import org.jetbrains.kotlin.gradle.dsl.pm20Extension
+import org.jetbrains.kotlin.gradle.dsl.topLevelExtensionOrNull
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinUsages
+import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.util.*
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.util.ComputedCapability
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.util.copyAttributes
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.util.copyConfigurationForPublishing
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.util.disambiguateName
import org.jetbrains.kotlin.gradle.plugin.mpp.publishedConfigurationName
+import org.jetbrains.kotlin.gradle.plugin.mpp.toModuleDependency
import org.jetbrains.kotlin.gradle.plugin.usageByName
import org.jetbrains.kotlin.gradle.tasks.withType
import org.jetbrains.kotlin.gradle.utils.dashSeparatedName
+import org.jetbrains.kotlin.project.model.KotlinModuleDependency
+import org.jetbrains.kotlin.project.model.LocalModuleIdentifier
+import org.jetbrains.kotlin.project.model.MavenModuleIdentifier
import java.util.*
import javax.inject.Inject
@@ -103,7 +112,11 @@
project.objects.newInstance(VariantPublishingConfigurator::class.java, project)
}
- open fun platformComponentName(variant: KotlinGradleVariant) = variant.disambiguateName("")
+ open fun platformComponentName(variant: KotlinGradleVariant) = when (variant.containingModule.publicationMode) {
+ Private -> error("software component is prohibited for non-published $variant")
+ is Standalone -> variant.disambiguateName("")
+ is Embedded -> variant.name // Use the same software component for same-named variants in all modules
+ }
open fun inferMavenScope(variant: KotlinGradleVariant, configurationName: String): String? =
when {
@@ -115,40 +128,57 @@
open fun configurePublishing(
request: PlatformPublicationToMavenRequest
) {
- val componentName = request.componentName
+ request.fromModule.ifMadePublic {
+ val componentName = request.componentName
- registerPlatformModulePublication(
- componentName,
- request.publicationHolder,
- request.variantPublicationRequests,
- request.fromModule::ifMadePublic
- )
+ registerPlatformModulePublication(
+ componentName,
+ request.publicationHolder,
+ request.variantPublicationRequests
+ )
- val publishFromVariants = request.variantPublicationRequests.mapTo(mutableSetOf()) { it.fromVariant }
+ val publishFromVariants = request.variantPublicationRequests.mapTo(mutableSetOf()) { it.fromVariant }
- // Collecting sources for multiple variants is not yet supported;
- // TODO make callers provide the source variants?
- // The MPP plugin doesn't publish the source artifacts as variants; keep that behavior for legacy-mapped variants for now
- if (
- publishFromVariants.size == 1 &&
- publishFromVariants.none { it is LegacyMappedVariant }
- ) {
- val singlePublishedVariant = publishFromVariants.single()
- configureSourceElementsPublishing(componentName, singlePublishedVariant)
+ // Collecting sources for multiple variants is not yet supported;
+ // TODO make callers provide the source variants?
+ // The MPP plugin doesn't publish the source artifacts as variants; keep that behavior for legacy-mapped variants for now
+ if (
+ publishFromVariants.size == 1 &&
+ publishFromVariants.none { it is LegacyMappedVariant }
+ ) {
+ val singlePublishedVariant = publishFromVariants.single()
+ configureSourceElementsPublishing(componentName, singlePublishedVariant, request.publicationHolder)
+ }
+
+ registerPlatformVariantsInRootModule(request)
}
-
- registerPlatformVariantsInRootModule(
- request.publicationHolder,
- request.fromModule,
- request.variantPublicationRequests
- )
}
- protected open fun configureSourceElementsPublishing(componentName: String, variant: KotlinGradleVariant) {
+ protected open fun configureSourceElementsPublishing(
+ componentName: String,
+ variant: KotlinGradleVariant,
+ publishedModuleHolder: SingleMavenPublishedModuleHolder
+ ) {
val configurationName = variant.disambiguateName("sourceElements")
+
+ // FIXME create this one not only in ifMadePublic but before that unconditionally?
val docsVariants = DocumentationVariantConfigurator().createSourcesElementsConfiguration(configurationName, variant)
+
+ val docsVariantForPublishing = copyConfigurationForPublishing(
+ project,
+ docsVariants.name + "-published",
+ docsVariants,
+ overrideCapabilities = {
+ val capability =
+ ComputedCapability.forPublishedPlatformVariant(variant, publishedModuleHolder)
+ if (capability != null) {
+ outgoing.capability(capability)
+ }
+ }
+ )
+
project.components.withType(AdhocComponentWithVariants::class.java).named(componentName).configure { component ->
- component.addVariantsFromConfiguration(docsVariants) { }
+ component.addVariantsFromConfiguration(docsVariantForPublishing) { }
}
}
@@ -158,77 +188,110 @@
* Assigns the created Maven publication to the [publishedModuleHolder].
*/
protected open fun registerPlatformModulePublication(
+ module: KotlinGradleModule,
componentName: String,
publishedModuleHolder: SingleMavenPublishedModuleHolder,
- variantRequests: Iterable<VariantPublicationRequest>,
- whenShouldRegisterPublication: (() -> Unit) -> Unit
+ variantRequests: Iterable<VariantPublicationRequest>
) {
- val platformComponent = softwareComponentFactory.adhoc(componentName)
- project.components.add(platformComponent)
+ module.ifMadePublic {
+ val platformComponent =
+ project.components.withType(AdhocComponentWithVariants::class.java).findByName(componentName)
+ ?: softwareComponentFactory.adhoc(componentName).also { project.components.add(it) }
- variantRequests.forEach { request ->
- val originalConfiguration = request.publishConfiguration
- val mavenScopeOrNull = inferMavenScope(request.fromVariant, originalConfiguration.name)
+ variantRequests.forEach { request ->
+ val originalConfiguration = request.publishConfiguration
+ val mavenScopeOrNull = inferMavenScope(request.fromVariant, originalConfiguration.name)
- val publishedConfiguration = copyConfigurationForPublishing(
- request.fromVariant.project,
- newName = publishedConfigurationName(originalConfiguration.name) + "-platform",
- configuration = originalConfiguration,
- overrideArtifacts = (request as? AdvancedVariantPublicationRequest)
- ?.overrideConfigurationArtifactsForPublication
- ?.let { override -> { artifacts -> artifacts.addAllLater(override) } },
- overrideAttributes = (request as? AdvancedVariantPublicationRequest)
- ?.overrideConfigurationAttributesForPublication
- ?.let { override -> { attributes -> copyAttributes(override, attributes) } }
- )
-
- platformComponent.addVariantsFromConfiguration(publishedConfiguration) details@{ variantDetails ->
- mavenScopeOrNull?.let { variantDetails.mapToMavenScope(it) }
- }
- }
-
- whenShouldRegisterPublication {
- project.pluginManager.withPlugin("maven-publish") {
- project.extensions.getByType(PublishingExtension::class.java).apply {
- publications.create(componentName, MavenPublication::class.java).apply {
- (this as DefaultMavenPublication).isAlias = true
- from(platformComponent)
- publishedModuleHolder.assignMavenPublication(this)
- artifactId = dashSeparatedName(
- project.name, publishedModuleHolder.defaultPublishedModuleSuffix
- ).toLowerCase(Locale.ENGLISH)
+ val publishedConfiguration = copyConfigurationForPublishing(
+ request.fromVariant.project,
+ newName = publishedConfigurationName(originalConfiguration.name) + "-platform",
+ configuration = originalConfiguration,
+ overrideArtifacts = (request as? AdvancedVariantPublicationRequest)
+ ?.overrideConfigurationArtifactsForPublication
+ ?.let { override -> { artifacts -> artifacts.addAllLater(override) } },
+ overrideAttributes = (request as? AdvancedVariantPublicationRequest)
+ ?.overrideConfigurationAttributesForPublication
+ ?.let { override -> { attributes -> copyAttributes(override, attributes) } },
+ overrideDependencies = {
+ addAllLater(project.listProperty {
+ replaceProjectDependenciesWithPublishedMavenDependencies(
+ project,
+ originalConfiguration.allDependencies
+ )
+ })
+ },
+ overrideCapabilities = {
+ ComputedCapability.forPublishedPlatformVariant(variant, publishedModuleHolder)
+ ?.let(outgoing::capability)
}
+ )
+
+ platformComponent.addVariantsFromConfiguration(publishedConfiguration) details@{ variantDetails ->
+ mavenScopeOrNull?.let { variantDetails.mapToMavenScope(it) }
+ }
+ }
+
+ project.pluginManager.withPlugin("maven-publish") {
+ val publication = project.extensions.getByType(PublishingExtension::class.java).run {
+ if (module.publicationMode is Standalone) {
+ publications.create(componentName, MavenPublication::class.java).apply {
+ // TODO: remove internal API usage. This prevents Gradle from reporting errors during publication because of multiple
+ // Maven publications with different coordinates
+ (this as DefaultMavenPublication).isAlias = true
+
+ from(platformComponent)
+ artifactId = dashSeparatedName(
+ project.name, publishedModuleHolder.defaultPublishedModuleSuffix
+ ).toLowerCase(Locale.ENGLISH)
+ }
+ } else {
+ // TODO still create the publication for embedded module's variant if one with this name is absent in the main module?
+ publications.findByName(componentName) as? MavenPublication
+ }
+ }
+
+ if (publication != null) {
+ publishedModuleHolder.assignMavenPublication(publication)
}
}
}
}
protected open fun registerPlatformVariantsInRootModule(
- publishedModuleHolder: SingleMavenPublishedModuleHolder,
- kotlinModule: KotlinGradleModule,
- variantRequests: Iterable<VariantPublicationRequest>
+ request: PlatformPublicationToMavenRequest
) {
val platformModuleDependencyProvider = project.provider {
- val coordinates = publishedModuleHolder.publishedMavenModuleCoordinates
+ val variants = request.variantPublicationRequests.mapTo(mutableSetOf()) { it.fromVariant }
+ val singleVariant = variants.singleOrNull() ?: error("expected single variant: ${variants.joinToString()}") // TODO NOW: test and remove?
+ val coordinates = request.publicationHolder.publishedMavenModuleCoordinates
(project.dependencies.create("${coordinates.group}:${coordinates.name}:${coordinates.version}") as ModuleDependency).apply {
- if (kotlinModule.moduleClassifier != null) {
- capabilities { it.requireCapability(ComputedCapability.fromModule(kotlinModule)) }
+ capabilities {
+ val capability = ComputedCapability.forPublishedPlatformVariant(singleVariant, request.publicationHolder)
+ if (capability != null) {
+ it.requireCapability(capability)
+ }
}
}
}
- val rootSoftwareComponent =
- project.components
+ val rootSoftwareComponent = when (request.fromModule.publicationMode) {
+ Private -> error("expected to be published")
+ Embedded -> project.components
.withType(AdhocComponentWithVariants::class.java)
- .getByName(rootPublicationComponentName(kotlinModule))
+ .getByName(rootPublicationComponentName(project.pm20Extension.main))
+ is Standalone ->
+ project.components
+ .withType(AdhocComponentWithVariants::class.java)
+ .getByName(rootPublicationComponentName(request.fromModule))
+ }
- variantRequests.forEach { variantRequest ->
+ request.variantPublicationRequests.forEach { variantRequest ->
val configuration = variantRequest.publishConfiguration
project.configurations.create(publishedConfigurationName(configuration.name)).apply {
isCanBeConsumed = false
isCanBeResolved = false
- setModuleCapability(this, kotlinModule)
+ setGradlePublishedModuleCapability(this, request.fromModule)
dependencies.addLater(platformModuleDependencyProvider)
copyAttributes(configuration.attributes, this.attributes)
rootSoftwareComponent.addVariantsFromConfiguration(this) { }
@@ -280,7 +343,116 @@
configurationName,
sourcesArtifactTask.get(),
artifactClassifier,
- ComputedCapability.fromModuleOrNull(variant.containingModule)
+ ComputedCapability.forProjectDependenciesOnModule(variant.containingModule)
)
}
-}
\ No newline at end of file
+}
+
+
+internal data class VersionedMavenModuleIdentifier(val moduleId: MavenModuleIdentifier, val version: String)
+
+internal fun localModuleDependenciesToPublishedModuleMapping(
+ project: Project,
+ dependencies: Iterable<KotlinModuleDependency>
+): Map<KotlinModuleDependency, VersionedMavenModuleIdentifier> {
+ return dependencies.mapNotNull mapping@{ dependency ->
+ val moduleIdentifier = dependency.moduleIdentifier
+ val resolvesToProject =
+ if (moduleIdentifier is LocalModuleIdentifier && moduleIdentifier.buildId == project.currentBuildId().name)
+ project.project(moduleIdentifier.projectId)
+ else
+ return@mapping null
+
+ val moduleClassifier = moduleIdentifier.moduleClassifier
+ when (val ext = resolvesToProject.topLevelExtensionOrNull) {
+ is KotlinPm20ProjectExtension -> {
+ val module = ext.modules.find { it.moduleClassifier == moduleClassifier } ?: return@mapping null
+
+ when (module.publicationMode) {
+ Private -> {
+ error("A dependency on $module can't be published because the module is not published.")
+ }
+
+ is Standalone, Embedded -> {
+ val coordinates = module.publicationHolder()?.publishedMavenModuleCoordinates
+ ?: return@mapping null
+
+ dependency to VersionedMavenModuleIdentifier(
+ MavenModuleIdentifier(
+ coordinates.group,
+ coordinates.name,
+ module.moduleClassifier.takeIf { module.publicationMode is Embedded }
+ ),
+ coordinates.version
+ )
+ }
+ }
+ }
+
+ is KotlinMultiplatformExtension -> {
+ val rootPublication = ext.rootSoftwareComponent.publicationDelegate
+ val group = rootPublication?.groupId ?: project.group.toString()
+ val name = rootPublication?.artifactId ?: project.name
+ val version = rootPublication?.version ?: project.version.toString()
+ dependency to VersionedMavenModuleIdentifier(MavenModuleIdentifier(group, name, null), version)
+ }
+
+ else -> null
+ }
+ }.toMap()
+}
+
+internal fun replaceProjectDependenciesWithPublishedMavenIdentifiers(
+ project: Project,
+ dependencies: Iterable<KotlinModuleDependency>
+): Set<MavenModuleIdentifier> {
+ val mapping = localModuleDependenciesToPublishedModuleMapping(project, dependencies)
+ return dependencies.mapNotNull { dependency ->
+ val replacement = mapping[dependency]
+ val id = dependency.moduleIdentifier
+ when {
+ replacement != null -> replacement.moduleId
+
+ id is MavenModuleIdentifier -> id
+
+ id is LocalModuleIdentifier && id.buildId == project.currentBuildId().name -> {
+ val otherProject = project.project(id.projectId)
+ // TODO: find single publication with maven-publish in non-MPP projects?
+ MavenModuleIdentifier(otherProject.group.toString(), otherProject.name, otherProject.version.toString())
+ }
+
+ else -> null
+ }
+ }.toSet()
+}
+
+internal fun replaceProjectDependenciesWithPublishedMavenDependencies(
+ project: Project,
+ dependencies: Iterable<Dependency>
+): List<Dependency> {
+ val dependencyToKotlinModuleDependency = dependencies.associateWith { it.toModuleDependency(project) }
+ val mapping = localModuleDependenciesToPublishedModuleMapping(project, dependencyToKotlinModuleDependency.values)
+ return dependencies.map { dependency ->
+ val replacement = mapping[dependencyToKotlinModuleDependency.getValue(dependency)]
+ if (replacement != null)
+ project.dependencies.create("${replacement.moduleId.group}:${replacement.moduleId.name}:${replacement.version}").apply {
+ if (replacement.moduleId.moduleClassifier != null) {
+ (this as ModuleDependency).capabilities {
+ it.requireCapability(checkNotNull(ComputedCapability.forAuxiliaryModuleByCoordinatesAndName(project, replacement)))
+ }
+ }
+ }
+ else {
+ dependency
+ }
+ }
+
+}
+
+internal fun KotlinGradleModule.publicationHolder(): SingleMavenPublishedModuleHolder? =
+ when (this) {
+ is KotlinGradleModuleInternal -> publicationHolder
+ // TODO NOW: Wtf?
+ is SingleMavenPublishedModuleHolder -> this
+ else -> null
+ }