[Gradle] ExternalKotlinTarget Api: Add initial APIs to configure publications

KT-54981
This commit is contained in:
Sebastian Sellmair
2022-11-16 18:10:51 +01:00
committed by Space Team
parent c33b172988
commit bc531610f7
13 changed files with 304 additions and 69 deletions
@@ -5,7 +5,7 @@ plugins {
dependencies { dependencies {
compileOnly(gradleKotlinDsl()) compileOnly(gradleKotlinDsl())
compileOnly("com.android.tools.build:gradle:7.0.0") compileOnly("com.android.tools.build:gradle:7.2.1")
compileOnly(project(":kotlin-gradle-plugin")) compileOnly(project(":kotlin-gradle-plugin"))
} }
@@ -7,7 +7,8 @@
package org.jetbrains.kotlin.gradle.android package org.jetbrains.kotlin.gradle.android
import com.android.build.gradle.AppExtension import com.android.build.api.attributes.AgpVersionAttr
import com.android.build.gradle.LibraryExtension
import com.android.build.gradle.internal.publishing.AndroidArtifacts import com.android.build.gradle.internal.publishing.AndroidArtifacts
import org.gradle.api.attributes.java.TargetJvmEnvironment import org.gradle.api.attributes.java.TargetJvmEnvironment
import org.gradle.api.attributes.java.TargetJvmEnvironment.TARGET_JVM_ENVIRONMENT_ATTRIBUTE import org.gradle.api.attributes.java.TargetJvmEnvironment.TARGET_JVM_ENVIRONMENT_ATTRIBUTE
@@ -23,7 +24,7 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.external.ExternalKotlinTargetDescr
@OptIn(ExternalVariantApi::class) @OptIn(ExternalVariantApi::class)
fun KotlinMultiplatformExtension.androidTargetPrototype(): PrototypeAndroidTarget { fun KotlinMultiplatformExtension.androidTargetPrototype(): PrototypeAndroidTarget {
val project = this.project val project = this.project
val androidExtension = project.extensions.getByType<AppExtension>() val androidExtension = project.extensions.getByType<LibraryExtension>()
/* /*
Set a variant filter and only allow 'debug'. Set a variant filter and only allow 'debug'.
@@ -47,19 +48,37 @@ fun KotlinMultiplatformExtension.androidTargetPrototype(): PrototypeAndroidTarge
platformType = KotlinPlatformType.jvm platformType = KotlinPlatformType.jvm
targetFactory = TargetFactory { delegate -> PrototypeAndroidTarget(delegate, PrototypeAndroidDsl(31)) } targetFactory = TargetFactory { delegate -> PrototypeAndroidTarget(delegate, PrototypeAndroidDsl(31)) }
/*
Configure apiElements configuration attributes (project to project dependency)
In this example we hardcoded AGP version 7.4.0-beta02 as demo
*/
apiElements.configure { _, configuration -> apiElements.configure { _, configuration ->
configuration.attributes.attribute(TARGET_JVM_ENVIRONMENT_ATTRIBUTE, project.objects.named(TargetJvmEnvironment.ANDROID)) configuration.attributes.attribute(TARGET_JVM_ENVIRONMENT_ATTRIBUTE, project.objects.named(TargetJvmEnvironment.ANDROID))
configuration.attributes.attribute(AgpVersionAttr.ATTRIBUTE, project.objects.named("7.4.0-beta02")) /* For demo */
} }
/*
Configure runtimeElements configuration attributes (project to project dependency)
In this example we hardcoded AGP version 7.4.0-beta02 as demo
*/
runtimeElements.configure { _, configuration -> runtimeElements.configure { _, configuration ->
configuration.attributes.attribute(TARGET_JVM_ENVIRONMENT_ATTRIBUTE, project.objects.named(TargetJvmEnvironment.ANDROID)) configuration.attributes.attribute(TARGET_JVM_ENVIRONMENT_ATTRIBUTE, project.objects.named(TargetJvmEnvironment.ANDROID))
configuration.attributes.attribute(AgpVersionAttr.ATTRIBUTE, project.objects.named("7.4.0-beta02")) /* For demo */
} }
/*
Configure published configurations (maven publication):
We override KotlinPlatformType to be androidJvm for now (to be discussed w/ Google later)
*/
apiElementsPublished.configure { _, configuration -> apiElementsPublished.configure { _, configuration ->
/* TODO w/ Google: Find a way to deprecate this attribute */
configuration.attributes.attribute(KotlinPlatformType.attribute, KotlinPlatformType.androidJvm)
configuration.attributes.attribute(TARGET_JVM_ENVIRONMENT_ATTRIBUTE, project.objects.named(TargetJvmEnvironment.ANDROID)) configuration.attributes.attribute(TARGET_JVM_ENVIRONMENT_ATTRIBUTE, project.objects.named(TargetJvmEnvironment.ANDROID))
} }
runtimeElementsPublished.configure { _, configuration -> runtimeElementsPublished.configure { _, configuration ->
/* TODO w/ Google: Find a way to deprecate this attribute */
configuration.attributes.attribute(KotlinPlatformType.attribute, KotlinPlatformType.androidJvm)
configuration.attributes.attribute(TARGET_JVM_ENVIRONMENT_ATTRIBUTE, project.objects.named(TargetJvmEnvironment.ANDROID)) configuration.attributes.attribute(TARGET_JVM_ENVIRONMENT_ATTRIBUTE, project.objects.named(TargetJvmEnvironment.ANDROID))
} }
} }
@@ -67,8 +86,8 @@ fun KotlinMultiplatformExtension.androidTargetPrototype(): PrototypeAndroidTarge
/* /*
Whilst using the .all hook, we only expect the single 'debug' variant to be available through this API. Whilst using the .all hook, we only expect the single 'debug' variant to be available through this API.
*/ */
androidExtension.applicationVariants.all { applicationVariant -> androidExtension.libraryVariants.all { androidVariant ->
project.logger.quiet("Setting up applicationVariant: ${applicationVariant.name}") project.logger.quiet("Setting up applicationVariant: ${androidVariant.name}")
/* /*
Create Compilations: main, unitTest and instrumentedTest Create Compilations: main, unitTest and instrumentedTest
@@ -97,21 +116,21 @@ fun KotlinMultiplatformExtension.androidTargetPrototype(): PrototypeAndroidTarge
Wire the Kotlin Compilations output (.class files) into the Android artifacts Wire the Kotlin Compilations output (.class files) into the Android artifacts
by using the 'registerPreJavacGeneratedBytecode' function by using the 'registerPreJavacGeneratedBytecode' function
*/ */
applicationVariant.registerPreJavacGeneratedBytecode(mainCompilation.output.classesDirs) androidVariant.registerPreJavacGeneratedBytecode(mainCompilation.output.classesDirs)
applicationVariant.unitTestVariant.registerPreJavacGeneratedBytecode(unitTestCompilation.output.classesDirs) androidVariant.unitTestVariant.registerPreJavacGeneratedBytecode(unitTestCompilation.output.classesDirs)
applicationVariant.testVariant.registerPreJavacGeneratedBytecode(instrumentedTestCompilation.output.classesDirs) androidVariant.testVariant.registerPreJavacGeneratedBytecode(instrumentedTestCompilation.output.classesDirs)
/* /*
Add dependencies coming from Kotlin to Android by adding all dependencies from Kotlin to the variants Add dependencies coming from Kotlin to Android by adding all dependencies from Kotlin to the variants
compileConfiguration or runtimeConfiguration compileConfiguration or runtimeConfiguration
*/ */
applicationVariant.compileConfiguration.extendsFrom(mainCompilation.configurations.compileDependencyConfiguration) androidVariant.compileConfiguration.extendsFrom(mainCompilation.configurations.compileDependencyConfiguration)
applicationVariant.runtimeConfiguration.extendsFrom(mainCompilation.configurations.runtimeDependencyConfiguration) androidVariant.runtimeConfiguration.extendsFrom(mainCompilation.configurations.runtimeDependencyConfiguration)
applicationVariant.unitTestVariant.compileConfiguration.extendsFrom(unitTestCompilation.configurations.compileDependencyConfiguration) androidVariant.unitTestVariant.compileConfiguration.extendsFrom(unitTestCompilation.configurations.compileDependencyConfiguration)
applicationVariant.unitTestVariant.runtimeConfiguration.extendsFrom(unitTestCompilation.configurations.runtimeDependencyConfiguration) androidVariant.unitTestVariant.runtimeConfiguration.extendsFrom(unitTestCompilation.configurations.runtimeDependencyConfiguration)
applicationVariant.testVariant.compileConfiguration.extendsFrom(instrumentedTestCompilation.configurations.compileDependencyConfiguration) androidVariant.testVariant.compileConfiguration.extendsFrom(instrumentedTestCompilation.configurations.compileDependencyConfiguration)
applicationVariant.testVariant.runtimeConfiguration.extendsFrom(instrumentedTestCompilation.configurations.runtimeDependencyConfiguration) androidVariant.testVariant.runtimeConfiguration.extendsFrom(instrumentedTestCompilation.configurations.runtimeDependencyConfiguration)
/* /*
@@ -148,12 +167,18 @@ fun KotlinMultiplatformExtension.androidTargetPrototype(): PrototypeAndroidTarge
} }
} }
/*
Add .aar artifacts to publications!
*/
androidTarget.apiElementsPublishedConfiguration.outgoing.artifact(androidVariant.packageLibraryProvider)
androidTarget.runtimeElementsPublishedConfiguration.outgoing.artifact(androidVariant.packageLibraryProvider)
/* /*
"Disable" configurations from plain Android plugin "Disable" configurations from plain Android plugin
This hack will not be necessary in the final implementation This hack will not be necessary in the final implementation
*/ */
project.configurations.findByName("${applicationVariant.name}ApiElements")?.isCanBeConsumed = false project.configurations.findByName("${androidVariant.name}ApiElements")?.isCanBeConsumed = false
project.configurations.findByName("${applicationVariant.name}RuntimeElements")?.isCanBeConsumed = false project.configurations.findByName("${androidVariant.name}RuntimeElements")?.isCanBeConsumed = false
} }
return androidTarget return androidTarget
@@ -20,6 +20,7 @@ import org.gradle.api.internal.component.SoftwareComponentInternal
import org.gradle.api.internal.component.UsageContext import org.gradle.api.internal.component.UsageContext
import org.gradle.api.internal.project.ProjectInternal import org.gradle.api.internal.project.ProjectInternal
import org.gradle.api.publish.maven.MavenPublication import org.gradle.api.publish.maven.MavenPublication
import org.jetbrains.kotlin.gradle.InternalKotlinGradlePluginApi
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
import org.jetbrains.kotlin.gradle.plugin.* import org.jetbrains.kotlin.gradle.plugin.*
@@ -58,7 +59,8 @@ abstract class AbstractKotlinTarget(
override val publishable: Boolean override val publishable: Boolean
get() = true get() = true
internal open val kotlinComponents: Set<KotlinTargetComponent> by lazy { @InternalKotlinGradlePluginApi
override val kotlinComponents: Set<KotlinTargetComponent> by lazy {
val mainCompilation = compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME) val mainCompilation = compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME)
val usageContexts = createUsageContexts(mainCompilation) val usageContexts = createUsageContexts(mainCompilation)
@@ -144,14 +146,18 @@ abstract class AbstractKotlinTarget(
} }
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
internal val publicationConfigureActions: DomainObjectSet<Action<MavenPublication>> = project private val publicationConfigureActions: DomainObjectSet<Action<MavenPublication>> = project.objects
.objects
.domainObjectSet(Action::class.java) as DomainObjectSet<Action<MavenPublication>> .domainObjectSet(Action::class.java) as DomainObjectSet<Action<MavenPublication>>
override fun mavenPublication(action: Action<MavenPublication>) { override fun mavenPublication(action: Action<MavenPublication>) {
publicationConfigureActions.add(action) publicationConfigureActions.add(action)
} }
@InternalKotlinGradlePluginApi
override fun onPublicationCreated(publication: MavenPublication) {
publicationConfigureActions.all { action -> action.execute(publication) }
}
override var preset: KotlinTargetPreset<out KotlinTarget>? = null override var preset: KotlinTargetPreset<out KotlinTarget>? = null
internal set internal set
} }
@@ -5,9 +5,14 @@
package org.jetbrains.kotlin.gradle.plugin.mpp package org.jetbrains.kotlin.gradle.plugin.mpp
import org.gradle.api.publish.maven.MavenPublication
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
import org.jetbrains.kotlin.gradle.plugin.KotlinTargetComponent
internal interface InternalKotlinTarget : KotlinTarget internal interface InternalKotlinTarget : KotlinTarget {
val kotlinComponents: Set<KotlinTargetComponent>
fun onPublicationCreated(publication: MavenPublication)
}
internal val KotlinTarget.internal: InternalKotlinTarget internal val KotlinTarget.internal: InternalKotlinTarget
get() = (this as? InternalKotlinTarget) ?: throw IllegalArgumentException( get() = (this as? InternalKotlinTarget) ?: throw IllegalArgumentException(
@@ -19,14 +19,12 @@ import org.gradle.api.internal.component.UsageContext
import org.gradle.api.provider.SetProperty import org.gradle.api.provider.SetProperty
import org.gradle.api.publish.maven.MavenPublication import org.gradle.api.publish.maven.MavenPublication
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension
import org.jetbrains.kotlin.gradle.plugin.* import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation.Companion.MAIN_COMPILATION_NAME import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation.Companion.MAIN_COMPILATION_NAME
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
import org.jetbrains.kotlin.gradle.plugin.ProjectLocalConfigurations
import org.jetbrains.kotlin.gradle.plugin.usageByName import org.jetbrains.kotlin.gradle.plugin.usageByName
import org.jetbrains.kotlin.gradle.targets.metadata.* import org.jetbrains.kotlin.gradle.targets.metadata.*
import org.jetbrains.kotlin.gradle.targets.metadata.COMMON_MAIN_ELEMENTS_CONFIGURATION_NAME
import org.jetbrains.kotlin.gradle.targets.metadata.getCommonSourceSetsForMetadataCompilation
import org.jetbrains.kotlin.gradle.targets.metadata.isCompatibilityMetadataVariantEnabled
import org.jetbrains.kotlin.gradle.targets.metadata.isKotlinGranularMetadataEnabled
import org.jetbrains.kotlin.gradle.utils.setProperty import org.jetbrains.kotlin.gradle.utils.setProperty
abstract class KotlinSoftwareComponent( abstract class KotlinSoftwareComponent(
@@ -40,11 +38,12 @@ abstract class KotlinSoftwareComponent(
override fun getVariants(): Set<SoftwareComponent> = kotlinTargets override fun getVariants(): Set<SoftwareComponent> = kotlinTargets
.filter { target -> target !is KotlinMetadataTarget } .filter { target -> target !is KotlinMetadataTarget }
.flatMap { target -> .flatMap { target ->
val targetPublishableComponentNames = val targetPublishableComponentNames = target.internal.kotlinComponents
(target as? AbstractKotlinTarget)?.kotlinComponents?.mapNotNullTo(mutableSetOf()) { component -> .filter { component -> component.publishable }
component.name.takeIf { component.publishable } .map { component -> component.name }
} .toSet()
target.components.filter { targetPublishableComponentNames?.contains(it.name) ?: true }
target.components.filter { it.name in targetPublishableComponentNames }
}.toSet() }.toSet()
private val _usages: Set<UsageContext> by lazy { private val _usages: Set<UsageContext> by lazy {
@@ -62,7 +62,7 @@ private fun createTargetPublications(project: Project, publishing: PublishingExt
val kotlin = project.multiplatformExtension val kotlin = project.multiplatformExtension
// Enforce the order of creating the publications, since the metadata publication is used in the other publications: // Enforce the order of creating the publications, since the metadata publication is used in the other publications:
kotlin.targets kotlin.targets
.withType(AbstractKotlinTarget::class.java) .withType(InternalKotlinTarget::class.java)
.matching { it.publishable } .matching { it.publishable }
.all { kotlinTarget -> .all { kotlinTarget ->
if (kotlinTarget is KotlinAndroidTarget) if (kotlinTarget is KotlinAndroidTarget)
@@ -73,7 +73,7 @@ private fun createTargetPublications(project: Project, publishing: PublishingExt
} }
} }
private fun AbstractKotlinTarget.createMavenPublications(publications: PublicationContainer) { private fun InternalKotlinTarget.createMavenPublications(publications: PublicationContainer) {
components components
.map { gradleComponent -> gradleComponent to kotlinComponents.single { it.name == gradleComponent.name } } .map { gradleComponent -> gradleComponent to kotlinComponents.single { it.name == gradleComponent.name } }
.filter { (_, kotlinComponent) -> kotlinComponent.publishableOnCurrentHost } .filter { (_, kotlinComponent) -> kotlinComponent.publishableOnCurrentHost }
@@ -102,7 +102,7 @@ private fun AbstractKotlinTarget.createMavenPublications(publications: Publicati
} }
(kotlinComponent as? KotlinTargetComponentWithPublication)?.publicationDelegate = componentPublication (kotlinComponent as? KotlinTargetComponentWithPublication)?.publicationDelegate = componentPublication
publicationConfigureActions.all { it.execute(componentPublication) } onPublicationCreated(componentPublication)
} }
} }
@@ -124,7 +124,7 @@ private fun rewritePom(
* 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 * 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. * 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<Set<ModuleCoordinates>>? = private fun dependenciesForPomRewriting(target: InternalKotlinTarget): Provider<Set<ModuleCoordinates>>? =
if (target !is KotlinMetadataTarget || !target.project.isKotlinGranularMetadataEnabled) if (target !is KotlinMetadataTarget || !target.project.isKotlinGranularMetadataEnabled)
null null
else { else {
@@ -7,8 +7,9 @@ package org.jetbrains.kotlin.gradle.plugin.mpp.external
import org.gradle.api.artifacts.Configuration import org.gradle.api.artifacts.Configuration
import org.gradle.api.logging.Logger import org.gradle.api.logging.Logger
import org.gradle.api.logging.Logging
import org.jetbrains.kotlin.gradle.ExternalKotlinTargetApi import org.jetbrains.kotlin.gradle.ExternalKotlinTargetApi
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtensionOrNull
import org.jetbrains.kotlin.gradle.plugin.mpp.InternalKotlinTarget import org.jetbrains.kotlin.gradle.plugin.mpp.InternalKotlinTarget
@ExternalKotlinTargetApi @ExternalKotlinTargetApi
@@ -23,5 +24,15 @@ abstract class DecoratedExternalKotlinTarget internal constructor(
val runtimeElementsConfiguration: Configuration = delegate.runtimeElementsConfiguration val runtimeElementsConfiguration: Configuration = delegate.runtimeElementsConfiguration
internal val logger: Logger = Logging.getLogger("${ExternalKotlinTargetImpl::class.qualifiedName}: $name") val apiElementsPublishedConfiguration: Configuration = delegate.apiElementsPublishedConfiguration
}
val runtimeElementsPublishedConfiguration: Configuration = delegate.runtimeElementsPublishedConfiguration
internal val logger: Logger = delegate.logger
}
internal val ExternalKotlinTargetImpl.decoratedInstance: DecoratedExternalKotlinTarget
get() = project.multiplatformExtension.targets.getByName(name) as DecoratedExternalKotlinTarget
internal val ExternalKotlinTargetImpl.decoratedInstanceOrNull: DecoratedExternalKotlinTarget?
get() = project.multiplatformExtensionOrNull?.targets?.findByName(name) as? DecoratedExternalKotlinTarget
@@ -0,0 +1,59 @@
/*
* Copyright 2010-2022 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.external
import org.gradle.api.artifacts.ModuleVersionIdentifier
import org.gradle.api.artifacts.PublishArtifact
import org.gradle.api.component.ComponentWithCoordinates
import org.gradle.api.publish.maven.MavenPublication
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinTargetComponentWithPublication
import org.jetbrains.kotlin.gradle.plugin.mpp.external.ExternalKotlinTargetComponent.TargetProvider
import org.jetbrains.kotlin.gradle.plugin.mpp.getCoordinatesFromPublicationDelegateAndProject
import org.jetbrains.kotlin.gradle.utils.dashSeparatedName
internal class ExternalKotlinTargetComponent(
val targetProvider: TargetProvider
) : KotlinTargetComponentWithPublication, ComponentWithCoordinates {
/*
Target creation requires this component. We will provide the target once it is required
*/
fun interface TargetProvider {
operator fun invoke(): DecoratedExternalKotlinTarget
companion object {
fun byTargetName(extension: KotlinMultiplatformExtension, targetName: String) = TargetProvider {
extension.targets.getByName(targetName) as DecoratedExternalKotlinTarget
}
}
}
/* Required for getting correct coordinates */
override var publicationDelegate: MavenPublication? = null
override val target: KotlinTarget by lazy { targetProvider() }
override val publishable: Boolean
get() = target.publishable
override val publishableOnCurrentHost: Boolean
get() = true
override val defaultArtifactId: String
get() = dashSeparatedName(target.project.name, target.name.toLowerCase())
override val sourcesArtifacts: Set<PublishArtifact>
get() = emptySet()
override fun getName(): String = target.name
override fun getCoordinates(): ModuleVersionIdentifier =
getCoordinatesFromPublicationDelegateAndProject(publicationDelegate, target.project, null)
}
@@ -5,39 +5,46 @@
package org.jetbrains.kotlin.gradle.plugin.mpp.external package org.jetbrains.kotlin.gradle.plugin.mpp.external
import org.gradle.api.Action import org.gradle.api.*
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.artifacts.Configuration import org.gradle.api.artifacts.Configuration
import org.gradle.api.attributes.AttributeContainer import org.gradle.api.attributes.AttributeContainer
import org.gradle.api.component.SoftwareComponent import org.gradle.api.component.SoftwareComponent
import org.gradle.api.logging.Logger
import org.gradle.api.logging.Logging
import org.gradle.api.publish.maven.MavenPublication import org.gradle.api.publish.maven.MavenPublication
import org.gradle.api.tasks.TaskProvider import org.gradle.api.tasks.TaskProvider
import org.jetbrains.kotlin.gradle.InternalKotlinGradlePluginApi
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
import org.jetbrains.kotlin.gradle.plugin.KotlinTargetComponent import org.jetbrains.kotlin.gradle.plugin.KotlinTargetComponent
import org.jetbrains.kotlin.gradle.plugin.mpp.HierarchyAttributeContainer
import org.jetbrains.kotlin.gradle.plugin.mpp.InternalKotlinTarget import org.jetbrains.kotlin.gradle.plugin.mpp.InternalKotlinTarget
import org.jetbrains.kotlin.gradle.plugin.mpp.buildAdhocComponentsFromKotlinVariants
internal class ExternalKotlinTargetImpl internal constructor( internal class ExternalKotlinTargetImpl internal constructor(
override val project: Project, override val project: Project,
override val targetName: String, override val targetName: String,
override val platformType: KotlinPlatformType, override val platformType: KotlinPlatformType,
override val publishable: Boolean,
val defaultConfiguration: Configuration, val defaultConfiguration: Configuration,
val apiElementsConfiguration: Configuration, val apiElementsConfiguration: Configuration,
val runtimeElementsConfiguration: Configuration, val runtimeElementsConfiguration: Configuration,
override val publishable: Boolean, val apiElementsPublishedConfiguration: Configuration,
internal val kotlinComponents: Set<KotlinTargetComponent>, val runtimeElementsPublishedConfiguration: Configuration,
val kotlinTargetComponent: ExternalKotlinTargetComponent,
private val artifactsTaskLocator: ArtifactsTaskLocator, private val artifactsTaskLocator: ArtifactsTaskLocator,
) : InternalKotlinTarget { ) : InternalKotlinTarget {
fun interface ArtifactsTaskLocator { fun interface ArtifactsTaskLocator {
fun locate(target: ExternalKotlinTargetImpl): TaskProvider<out Task> fun locate(target: ExternalKotlinTargetImpl): TaskProvider<out Task>
} }
val kotlin = project.multiplatformExtension val kotlin = project.multiplatformExtension
override val preset: Nothing? = null
internal val logger: Logger = Logging.getLogger("${ExternalKotlinTargetImpl::class.qualifiedName}: $name")
override val useDisambiguationClassifierAsSourceSetNamePrefix: Boolean = true override val useDisambiguationClassifierAsSourceSetNamePrefix: Boolean = true
override val overrideDisambiguationClassifierOnIdeImport: String? = null override val overrideDisambiguationClassifierOnIdeImport: String? = null
@@ -58,26 +65,36 @@ internal class ExternalKotlinTargetImpl internal constructor(
override val runtimeElementsConfigurationName: String override val runtimeElementsConfigurationName: String
get() = runtimeElementsConfiguration.name get() = runtimeElementsConfiguration.name
@InternalKotlinGradlePluginApi
override val kotlinComponents: Set<KotlinTargetComponent> = setOf(kotlinTargetComponent)
override val components: Set<SoftwareComponent> by lazy { override val components: Set<SoftwareComponent> by lazy {
project.buildAdhocComponentsFromKotlinVariants(kotlinComponents) logger.debug("Creating SoftwareComponent")
setOf(ExternalKotlinTargetSoftwareComponent(this))
} }
override val compilations: NamedDomainObjectContainer<ExternalDecoratedKotlinCompilation> by lazy { override val compilations: NamedDomainObjectContainer<ExternalDecoratedKotlinCompilation> by lazy {
project.container(ExternalDecoratedKotlinCompilation::class.java) project.container(ExternalDecoratedKotlinCompilation::class.java)
} }
@Suppress("unchecked_cast")
private val mavenPublicationActions = project.objects.domainObjectSet(Action::class.java)
as DomainObjectSet<Action<MavenPublication>>
override fun mavenPublication(action: Action<MavenPublication>) { override fun mavenPublication(action: Action<MavenPublication>) {
TODO("Not yet implemented") mavenPublicationActions.add(action)
} }
override val preset: Nothing? = null @InternalKotlinGradlePluginApi
override fun onPublicationCreated(publication: MavenPublication) {
override fun getAttributes(): AttributeContainer { mavenPublicationActions.all { action -> action.execute(publication) }
TODO("Not yet implemented")
} }
private val attributeContainer = HierarchyAttributeContainer(parent = null)
override fun getAttributes(): AttributeContainer = attributeContainer
internal fun onCreated() { internal fun onCreated() {
artifactsTask artifactsTask
components
} }
} }
@@ -0,0 +1,51 @@
/*
* Copyright 2010-2022 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.external
import org.gradle.api.artifacts.ModuleVersionIdentifier
import org.gradle.api.component.ComponentWithCoordinates
import org.gradle.api.component.ComponentWithVariants
import org.gradle.api.component.SoftwareComponent
import org.gradle.api.component.SoftwareComponentFactory
import org.gradle.api.internal.component.SoftwareComponentInternal
import org.gradle.api.internal.component.UsageContext
import org.gradle.api.internal.project.ProjectInternal
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension
import org.jetbrains.kotlin.tooling.core.UnsafeApi
internal fun ExternalKotlinTargetSoftwareComponent(
target: ExternalKotlinTargetImpl,
): ExternalKotlinTargetSoftwareComponent {
val softwareComponentFactory = (target.project as ProjectInternal).services.get(SoftwareComponentFactory::class.java)
val adhocSoftwareComponent = softwareComponentFactory.adhoc(target.targetName)
adhocSoftwareComponent.addVariantsFromConfiguration(target.apiElementsPublishedConfiguration) { details ->
details.mapToMavenScope("compile")
}
adhocSoftwareComponent.addVariantsFromConfiguration(target.runtimeElementsPublishedConfiguration) { details ->
details.mapToMavenScope("runtime")
}
@OptIn(UnsafeApi::class)
return ExternalKotlinTargetSoftwareComponent(
target.project.multiplatformExtension,
adhocSoftwareComponent as SoftwareComponentInternal,
target.kotlinTargetComponent
)
}
internal class ExternalKotlinTargetSoftwareComponent @UnsafeApi constructor(
private val multiplatformExtension: KotlinMultiplatformExtension,
private val adhocSoftwareComponent: SoftwareComponentInternal,
private val kotlinTargetComponent: ExternalKotlinTargetComponent
) : ComponentWithCoordinates, ComponentWithVariants, SoftwareComponentInternal {
override fun getName(): String = adhocSoftwareComponent.name
override fun getUsages(): Set<UsageContext> = adhocSoftwareComponent.usages
override fun getVariants(): Set<SoftwareComponent> = multiplatformExtension.metadata().components
override fun getCoordinates(): ModuleVersionIdentifier = kotlinTargetComponent.coordinates
}
@@ -7,12 +7,14 @@
package org.jetbrains.kotlin.gradle.plugin.mpp.external package org.jetbrains.kotlin.gradle.plugin.mpp.external
import org.gradle.api.artifacts.Configuration
import org.gradle.api.attributes.Category import org.gradle.api.attributes.Category
import org.gradle.api.attributes.Usage import org.gradle.api.attributes.Usage
import org.gradle.jvm.tasks.Jar import org.gradle.jvm.tasks.Jar
import org.jetbrains.kotlin.gradle.ExternalKotlinTargetApi import org.jetbrains.kotlin.gradle.ExternalKotlinTargetApi
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinUsages import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinUsages
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.util.markConsumable
import org.jetbrains.kotlin.gradle.plugin.usesPlatformOf import org.jetbrains.kotlin.gradle.plugin.usesPlatformOf
import org.jetbrains.kotlin.gradle.tasks.locateOrRegisterTask import org.jetbrains.kotlin.gradle.tasks.locateOrRegisterTask
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
@@ -26,6 +28,16 @@ fun <T : DecoratedExternalKotlinTarget> KotlinMultiplatformExtension.createExter
val apiElementsConfiguration = project.configurations.maybeCreate(lowerCamelCaseName(descriptor.targetName, "apiElements")) val apiElementsConfiguration = project.configurations.maybeCreate(lowerCamelCaseName(descriptor.targetName, "apiElements"))
val runtimeElementsConfiguration = project.configurations.maybeCreate(lowerCamelCaseName(descriptor.targetName, "runtimeElements")) val runtimeElementsConfiguration = project.configurations.maybeCreate(lowerCamelCaseName(descriptor.targetName, "runtimeElements"))
val apiElementsPublishedConfiguration =
project.configurations.maybeCreate(lowerCamelCaseName(descriptor.targetName, "apiElements-published"))
val runtimeElementsPublishedConfiguration =
project.configurations.maybeCreate(lowerCamelCaseName(descriptor.targetName, "runtimeElements-published"))
val kotlinTargetComponent = ExternalKotlinTargetComponent(
ExternalKotlinTargetComponent.TargetProvider.byTargetName(this, descriptor.targetName)
)
val artifactsTaskLocator = ExternalKotlinTargetImpl.ArtifactsTaskLocator { target -> val artifactsTaskLocator = ExternalKotlinTargetImpl.ArtifactsTaskLocator { target ->
target.project.locateOrRegisterTask<Jar>(lowerCamelCaseName(descriptor.targetName, "jar")) target.project.locateOrRegisterTask<Jar>(lowerCamelCaseName(descriptor.targetName, "jar"))
} }
@@ -34,25 +46,28 @@ fun <T : DecoratedExternalKotlinTarget> KotlinMultiplatformExtension.createExter
project = project, project = project,
targetName = descriptor.targetName, targetName = descriptor.targetName,
platformType = descriptor.platformType, platformType = descriptor.platformType,
publishable = true,
defaultConfiguration = defaultConfiguration, defaultConfiguration = defaultConfiguration,
apiElementsConfiguration = apiElementsConfiguration, apiElementsConfiguration = apiElementsConfiguration,
runtimeElementsConfiguration = runtimeElementsConfiguration, runtimeElementsConfiguration = runtimeElementsConfiguration,
publishable = true, apiElementsPublishedConfiguration = apiElementsPublishedConfiguration,
kotlinComponents = emptySet(), runtimeElementsPublishedConfiguration = runtimeElementsPublishedConfiguration,
kotlinTargetComponent = kotlinTargetComponent,
artifactsTaskLocator = artifactsTaskLocator artifactsTaskLocator = artifactsTaskLocator
) )
apiElementsConfiguration.isCanBeConsumed = true target.setupApiElements(apiElementsConfiguration)
apiElementsConfiguration.isCanBeResolved = false target.setupApiElements(apiElementsPublishedConfiguration)
apiElementsConfiguration.usesPlatformOf(target) target.setupRuntimeElements(runtimeElementsConfiguration)
apiElementsConfiguration.attributes.attribute(Usage.USAGE_ATTRIBUTE, KotlinUsages.producerApiUsage(target)) target.setupRuntimeElements(runtimeElementsPublishedConfiguration)
apiElementsConfiguration.attributes.attribute(Category.CATEGORY_ATTRIBUTE, project.objects.named(Category.LIBRARY)) apiElementsConfiguration.markConsumable()
runtimeElementsConfiguration.markConsumable()
runtimeElementsConfiguration.isCanBeConsumed = true /* Those configurations can not be resolved but also not consumed (not suitable for project to proejct dependencies */
runtimeElementsConfiguration.isCanBeResolved = false apiElementsPublishedConfiguration.isCanBeConsumed = false
runtimeElementsConfiguration.usesPlatformOf(target) apiElementsPublishedConfiguration.isCanBeResolved = false
runtimeElementsConfiguration.attributes.attribute(Usage.USAGE_ATTRIBUTE, KotlinUsages.producerRuntimeUsage(target)) runtimeElementsPublishedConfiguration.isCanBeResolved = false
runtimeElementsConfiguration.attributes.attribute(Category.CATEGORY_ATTRIBUTE, project.objects.named(Category.LIBRARY)) runtimeElementsPublishedConfiguration.isCanBeConsumed = false
val decorated = descriptor.targetFactory.create(DecoratedExternalKotlinTarget.Delegate(target)) val decorated = descriptor.targetFactory.create(DecoratedExternalKotlinTarget.Delegate(target))
target.onCreated() target.onCreated()
@@ -60,6 +75,8 @@ fun <T : DecoratedExternalKotlinTarget> KotlinMultiplatformExtension.createExter
descriptor.configure?.invoke(decorated) descriptor.configure?.invoke(decorated)
descriptor.apiElements.configure?.invoke(decorated, apiElementsConfiguration) descriptor.apiElements.configure?.invoke(decorated, apiElementsConfiguration)
descriptor.runtimeElements.configure?.invoke(decorated, runtimeElementsConfiguration) descriptor.runtimeElements.configure?.invoke(decorated, runtimeElementsConfiguration)
descriptor.apiElementsPublished.configure?.invoke(decorated, apiElementsPublishedConfiguration)
descriptor.runtimeElementsPublished.configure?.invoke(decorated, runtimeElementsPublishedConfiguration)
targets.add(decorated) targets.add(decorated)
decorated.logger.info("Created ${descriptor.platformType} target") decorated.logger.info("Created ${descriptor.platformType} target")
@@ -72,3 +89,15 @@ fun <T : DecoratedExternalKotlinTarget> KotlinMultiplatformExtension.createExter
): T { ): T {
return createExternalKotlinTarget(ExternalKotlinTargetDescriptor(descriptor)) return createExternalKotlinTarget(ExternalKotlinTargetDescriptor(descriptor))
} }
private fun ExternalKotlinTargetImpl.setupApiElements(configuration: Configuration) {
configuration.usesPlatformOf(this)
configuration.attributes.attribute(Usage.USAGE_ATTRIBUTE, KotlinUsages.producerApiUsage(this))
configuration.attributes.attribute(Category.CATEGORY_ATTRIBUTE, project.objects.named(Category.LIBRARY))
}
private fun ExternalKotlinTargetImpl.setupRuntimeElements(configuration: Configuration) {
configuration.usesPlatformOf(this)
configuration.attributes.attribute(Usage.USAGE_ATTRIBUTE, KotlinUsages.producerRuntimeUsage(this))
configuration.attributes.attribute(Category.CATEGORY_ATTRIBUTE, project.objects.named(Category.LIBRARY))
}
@@ -6,7 +6,9 @@
package org.jetbrains.kotlin.gradle.plugin.mpp.pm20.util package org.jetbrains.kotlin.gradle.plugin.mpp.pm20.util
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.artifacts.* import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.DependencySet
import org.gradle.api.artifacts.PublishArtifactSet
import org.gradle.api.attributes.Attribute import org.gradle.api.attributes.Attribute
import org.gradle.api.attributes.AttributeContainer import org.gradle.api.attributes.AttributeContainer
import org.gradle.api.capabilities.Capability import org.gradle.api.capabilities.Capability
@@ -97,4 +99,15 @@ internal fun copyAttributes(from: AttributeContainer, to: AttributeContainer, ke
} }
internal inline fun <reified T> Project.listProperty(noinline itemsProvider: () -> Iterable<T>) = internal inline fun <reified T> Project.listProperty(noinline itemsProvider: () -> Iterable<T>) =
objects.listProperty(T::class.java).apply { set(provider(itemsProvider)) } objects.listProperty(T::class.java).apply { set(provider(itemsProvider)) }
internal fun Configuration.markConsumable(): Configuration = apply {
this.isCanBeConsumed = true
this.isCanBeResolved = false
}
internal fun Configuration.markResolvable(): Configuration = apply {
this.isCanBeConsumed = false
this.isCanBeResolved = true
}
@@ -7,15 +7,20 @@
package org.jetbrains.kotlin.gradle.externalTargetApi package org.jetbrains.kotlin.gradle.externalTargetApi
import org.gradle.api.publish.PublishingExtension
import org.gradle.api.publish.maven.MavenPublication
import org.gradle.api.publish.maven.plugins.MavenPublishPlugin
import org.jetbrains.kotlin.gradle.android.androidTargetPrototype import org.jetbrains.kotlin.gradle.android.androidTargetPrototype
import org.jetbrains.kotlin.gradle.androidApplication import org.jetbrains.kotlin.gradle.androidLibrary
import org.jetbrains.kotlin.gradle.assumeAndroidSdkAvailable import org.jetbrains.kotlin.gradle.assumeAndroidSdkAvailable
import org.jetbrains.kotlin.gradle.buildProjectWithMPP import org.jetbrains.kotlin.gradle.buildProjectWithMPP
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension
import org.jetbrains.kotlin.gradle.kpm.idea.mavenCentralCacheRedirector import org.jetbrains.kotlin.gradle.kpm.idea.mavenCentralCacheRedirector
import org.jetbrains.kotlin.gradle.utils.getByType
import org.junit.Test import org.junit.Test
import kotlin.test.BeforeTest import kotlin.test.BeforeTest
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.fail
class ExternalAndroidTargetPrototypeSmokeTest { class ExternalAndroidTargetPrototypeSmokeTest {
@@ -27,7 +32,7 @@ class ExternalAndroidTargetPrototypeSmokeTest {
@Test @Test
fun `apply prototype - evaluate - compilations exist`() { fun `apply prototype - evaluate - compilations exist`() {
val project = buildProjectWithMPP() val project = buildProjectWithMPP()
project.androidApplication { compileSdk = 31 } project.androidLibrary { compileSdk = 31 }
val androidTargetPrototype = project.multiplatformExtension.androidTargetPrototype() val androidTargetPrototype = project.multiplatformExtension.androidTargetPrototype()
project.evaluate() project.evaluate()
@@ -40,7 +45,7 @@ class ExternalAndroidTargetPrototypeSmokeTest {
@Test @Test
fun `apply prototype - evaluate - configurations can be resolved`() { fun `apply prototype - evaluate - configurations can be resolved`() {
val project = buildProjectWithMPP() val project = buildProjectWithMPP()
project.androidApplication { compileSdk = 31 } project.androidLibrary { compileSdk = 31 }
val androidTargetPrototype = project.multiplatformExtension.androidTargetPrototype() val androidTargetPrototype = project.multiplatformExtension.androidTargetPrototype()
project.repositories.mavenLocal() project.repositories.mavenLocal()
@@ -52,4 +57,19 @@ class ExternalAndroidTargetPrototypeSmokeTest {
compilation.runtimeDependencyFiles?.files compilation.runtimeDependencyFiles?.files
} }
} }
@Test
fun `apply prototype - with maven publish plugin - publication exists`() {
val project = buildProjectWithMPP()
project.androidLibrary { compileSdk = 31 }
project.plugins.apply(MavenPublishPlugin::class.java)
project.multiplatformExtension.androidTargetPrototype()
project.evaluate()
val publishing = project.extensions.getByType<PublishingExtension>()
val androidPublication = publishing.publications.getByName("android") as MavenPublication
if (androidPublication.artifacts.size != 1) fail("Expected one artifact. Found ${androidPublication.artifacts}")
val aarArtifact = androidPublication.artifacts.first()
assertEquals("aar", aarArtifact.extension)
}
} }