Fallback for publishing with no Gradle metadata

The following parts of the publications are affected:

* root publication
  * with Gradle metadata, it is published and references the
    other publications using the `available-at` mechanism
  * without metadata opt-in, it is not published

* Kotlin metadata variant
  * with Gradle metadata, it is added as an additional variant to the
    platform-specific publications to allow the IDE to discover it
  * without metadata opt-in, it is added as a dependency to the platform-
    specific modules (like in old MPP)
This commit is contained in:
Sergey Igushkin
2018-09-18 13:16:53 +03:00
parent b388f7fde0
commit 05301e37d8
9 changed files with 191 additions and 23 deletions
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.gradle.dsl
import org.gradle.api.InvalidUserCodeException
import org.gradle.api.NamedDomainObjectCollection
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
import org.jetbrains.kotlin.gradle.plugin.KotlinTargetPreset
@@ -15,4 +16,6 @@ open class KotlinMultiplatformExtension : KotlinProjectExtension() {
lateinit var targets: NamedDomainObjectCollection<KotlinTarget>
internal set
internal var isGradleMetadataAvailable: Boolean = false
}
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.gradle.plugin
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.internal.FeaturePreviews
import org.gradle.api.internal.file.FileResolver
import org.gradle.api.logging.Logger
import org.gradle.api.logging.Logging
@@ -132,12 +133,13 @@ open class Kotlin2JsPluginWrapper @Inject constructor(
open class KotlinMultiplatformPluginWrapper @Inject constructor(
fileResolver: FileResolver,
private val instantiator: Instantiator,
private val buildOutputCleanupRegistry: BuildOutputCleanupRegistry
private val buildOutputCleanupRegistry: BuildOutputCleanupRegistry,
private val featurePreviews: FeaturePreviews
): KotlinBasePluginWrapper(fileResolver) {
override fun getPlugin(project: Project, kotlinGradleBuildServices: KotlinGradleBuildServices): Plugin<Project> =
KotlinMultiplatformPlugin(
buildOutputCleanupRegistry, fileResolver,
instantiator, kotlinPluginVersion
instantiator, kotlinPluginVersion, featurePreviews
)
override val projectExtensionClass: KClass<out KotlinMultiplatformExtension>
@@ -11,12 +11,14 @@ import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.attributes.Attribute
import org.gradle.api.attributes.AttributeContainer
import org.gradle.api.internal.FeaturePreviews
import org.gradle.api.internal.file.FileResolver
import org.gradle.api.internal.plugins.DslObject
import org.gradle.api.plugins.JavaBasePlugin
import org.gradle.api.publish.PublishingExtension
import org.gradle.api.publish.maven.MavenPublication
import org.gradle.api.publish.maven.internal.publication.MavenPublicationInternal
import org.gradle.api.publish.maven.tasks.AbstractPublishToMaven
import org.gradle.internal.cleanup.BuildOutputCleanupRegistry
import org.gradle.internal.reflect.Instantiator
import org.gradle.jvm.tasks.Jar
@@ -34,7 +36,8 @@ class KotlinMultiplatformPlugin(
private val buildOutputCleanupRegistry: BuildOutputCleanupRegistry,
private val fileResolver: FileResolver,
private val instantiator: Instantiator,
private val kotlinPluginVersion: String
private val kotlinPluginVersion: String,
private val featurePreviews: FeaturePreviews // TODO get rid of this internal API usage once we don't need it
) : Plugin<Project> {
private class TargetFromPresetExtension(val targetsContainer: NamedDomainObjectCollection<KotlinTarget>) {
@@ -64,6 +67,11 @@ class KotlinMultiplatformPlugin(
presets = project.container(KotlinTargetPreset::class.java)
addExtension("presets", presets)
isGradleMetadataAvailable =
featurePreviews.activeFeatures.find { it.name == "GRADLE_METADATA" }?.let { metadataFeature ->
featurePreviews.isFeatureEnabled(metadataFeature)
} ?: true // the feature entry will be gone once the feature is stable
}
setupDefaultPresets(project)
@@ -98,8 +106,9 @@ class KotlinMultiplatformPlugin(
val kotlinSoftwareComponent = KotlinSoftwareComponent(project, "kotlin", targets)
project.extensions.configure(PublishingExtension::class.java) { publishing ->
// The root publication.
publishing.publications.create("kotlinMultiplatform", MavenPublication::class.java).apply {
// The root publication that references the platform specific publications as its variants:
val rootPublication = publishing.publications.create("kotlinMultiplatform", MavenPublication::class.java).apply {
from(kotlinSoftwareComponent)
(this as MavenPublicationInternal).publishWithOriginalFileName()
artifactId = project.name
@@ -107,6 +116,11 @@ class KotlinMultiplatformPlugin(
version = project.version.toString()
}
// Publish the root publication only if Gradle metadata publishing is enabled:
project.tasks.withType(AbstractPublishToMaven::class.java).all { publishTask ->
publishTask.onlyIf { publishTask.publication != rootPublication || project.multiplatformExtension!!.isGradleMetadataAvailable }
}
// Create separate publications for all publishable targets
targets.matching { it.publishable }.all { target ->
val variant = target.component as KotlinVariant
@@ -6,15 +6,16 @@
package org.jetbrains.kotlin.gradle.plugin.mpp
import org.gradle.api.Project
import org.gradle.api.artifacts.*
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.DependencyConstraint
import org.gradle.api.artifacts.ModuleDependency
import org.gradle.api.artifacts.PublishArtifact
import org.gradle.api.attributes.AttributeContainer
import org.gradle.api.attributes.Usage
import org.gradle.api.capabilities.Capability
import org.gradle.api.component.ComponentWithCoordinates
import org.gradle.api.component.ComponentWithVariants
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.KotlinTarget
import org.jetbrains.kotlin.gradle.plugin.KotlinTargetComponent
import org.jetbrains.kotlin.gradle.plugin.usageByName
@@ -40,11 +41,13 @@ object NativeUsage {
}
internal class KotlinPlatformUsageContext(
val project: Project,
val kotlinTarget: KotlinTarget,
private val usage: Usage,
val dependencyConfigurationName: String
val dependencyConfigurationName: String,
private val publishWithGradleMetadata: Boolean
) : UsageContext {
private val project: Project get() = kotlinTarget.project
override fun getUsage(): Usage = usage
override fun getName(): String = kotlinTarget.targetName + when (dependencyConfigurationName) {
@@ -19,6 +19,8 @@ import org.gradle.api.plugins.JavaPlugin
import org.gradle.api.publish.maven.MavenPublication
import org.gradle.util.ConfigureUtil
import org.gradle.util.WrapUtil
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.utils.isGradleVersionAtLeast
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
@@ -49,28 +51,50 @@ abstract class AbstractKotlinTarget (
get() = true
override val component: KotlinTargetComponent by lazy {
if (isGradleVersionAtLeast(4, 7))
KotlinVariantWithCoordinates(this)
else
if (isGradleVersionAtLeast(4, 7)) {
createKotlinTargetComponent()
} else {
KotlinVariant(this)
}
}
private fun createKotlinTargetComponent(): KotlinTargetComponent {
val kotlinExtension = project.kotlinExtension as? KotlinMultiplatformExtension
?: return KotlinVariantWithCoordinates(this)
if (targetName == KotlinMultiplatformPlugin.METADATA_TARGET_NAME)
return KotlinVariantWithCoordinates(this)
val separateMetadataTarget = kotlinExtension.targets.getByName(KotlinMultiplatformPlugin.METADATA_TARGET_NAME)
return if (kotlinExtension.isGradleMetadataAvailable) {
KotlinVariantWithMetadataVariant(this, separateMetadataTarget)
} else {
// we should only add the Kotlin metadata dependency if we publish no Gradle metadata related to Kotlin MPP;
// with metadata, such a dependency would get invalid, since a platform module should only depend on modules for that
// same platform, not Kotlin metadata modules
KotlinVariantWithMetadataDependency(this, separateMetadataTarget)
}
}
override fun createUsageContexts(): Set<UsageContext> {
val publishWithKotlinMetadata = (project.kotlinExtension as? KotlinMultiplatformExtension)?.isGradleMetadataAvailable
?: true // In non-MPP project, these usage contexts do not get published anyway
// Here, `JAVA_API` and `JAVA_RUNTIME_JARS` are used intentionally as Gradle needs this for
// ordering of the usage contexts (prioritizing the dependencies);
// These Java usages should not be replaced with the custom Kotlin usages.
return listOfNotNull(
KotlinPlatformUsageContext(
project, this, project.usageByName(JAVA_API),
apiElementsConfigurationName
this, project.usageByName(JAVA_API),
apiElementsConfigurationName, publishWithKotlinMetadata
),
if (compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME) is KotlinCompilationToRunnableFiles)
if (compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME) is KotlinCompilationToRunnableFiles) {
KotlinPlatformUsageContext(
project, this, project.usageByName(JAVA_RUNTIME_JARS),
runtimeElementsConfigurationName
this, project.usageByName(JAVA_RUNTIME_JARS),
runtimeElementsConfigurationName, publishWithKotlinMetadata
)
else null
} else null
).toSet()
}