[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 {
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"))
}
@@ -7,7 +7,8 @@
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 org.gradle.api.attributes.java.TargetJvmEnvironment
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)
fun KotlinMultiplatformExtension.androidTargetPrototype(): PrototypeAndroidTarget {
val project = this.project
val androidExtension = project.extensions.getByType<AppExtension>()
val androidExtension = project.extensions.getByType<LibraryExtension>()
/*
Set a variant filter and only allow 'debug'.
@@ -47,19 +48,37 @@ fun KotlinMultiplatformExtension.androidTargetPrototype(): PrototypeAndroidTarge
platformType = KotlinPlatformType.jvm
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 ->
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 ->
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 ->
/* 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))
}
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))
}
}
@@ -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.
*/
androidExtension.applicationVariants.all { applicationVariant ->
project.logger.quiet("Setting up applicationVariant: ${applicationVariant.name}")
androidExtension.libraryVariants.all { androidVariant ->
project.logger.quiet("Setting up applicationVariant: ${androidVariant.name}")
/*
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
by using the 'registerPreJavacGeneratedBytecode' function
*/
applicationVariant.registerPreJavacGeneratedBytecode(mainCompilation.output.classesDirs)
applicationVariant.unitTestVariant.registerPreJavacGeneratedBytecode(unitTestCompilation.output.classesDirs)
applicationVariant.testVariant.registerPreJavacGeneratedBytecode(instrumentedTestCompilation.output.classesDirs)
androidVariant.registerPreJavacGeneratedBytecode(mainCompilation.output.classesDirs)
androidVariant.unitTestVariant.registerPreJavacGeneratedBytecode(unitTestCompilation.output.classesDirs)
androidVariant.testVariant.registerPreJavacGeneratedBytecode(instrumentedTestCompilation.output.classesDirs)
/*
Add dependencies coming from Kotlin to Android by adding all dependencies from Kotlin to the variants
compileConfiguration or runtimeConfiguration
*/
applicationVariant.compileConfiguration.extendsFrom(mainCompilation.configurations.compileDependencyConfiguration)
applicationVariant.runtimeConfiguration.extendsFrom(mainCompilation.configurations.runtimeDependencyConfiguration)
applicationVariant.unitTestVariant.compileConfiguration.extendsFrom(unitTestCompilation.configurations.compileDependencyConfiguration)
applicationVariant.unitTestVariant.runtimeConfiguration.extendsFrom(unitTestCompilation.configurations.runtimeDependencyConfiguration)
applicationVariant.testVariant.compileConfiguration.extendsFrom(instrumentedTestCompilation.configurations.compileDependencyConfiguration)
applicationVariant.testVariant.runtimeConfiguration.extendsFrom(instrumentedTestCompilation.configurations.runtimeDependencyConfiguration)
androidVariant.compileConfiguration.extendsFrom(mainCompilation.configurations.compileDependencyConfiguration)
androidVariant.runtimeConfiguration.extendsFrom(mainCompilation.configurations.runtimeDependencyConfiguration)
androidVariant.unitTestVariant.compileConfiguration.extendsFrom(unitTestCompilation.configurations.compileDependencyConfiguration)
androidVariant.unitTestVariant.runtimeConfiguration.extendsFrom(unitTestCompilation.configurations.runtimeDependencyConfiguration)
androidVariant.testVariant.compileConfiguration.extendsFrom(instrumentedTestCompilation.configurations.compileDependencyConfiguration)
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
This hack will not be necessary in the final implementation
*/
project.configurations.findByName("${applicationVariant.name}ApiElements")?.isCanBeConsumed = false
project.configurations.findByName("${applicationVariant.name}RuntimeElements")?.isCanBeConsumed = false
project.configurations.findByName("${androidVariant.name}ApiElements")?.isCanBeConsumed = false
project.configurations.findByName("${androidVariant.name}RuntimeElements")?.isCanBeConsumed = false
}
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.project.ProjectInternal
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.kotlinExtension
import org.jetbrains.kotlin.gradle.plugin.*
@@ -58,7 +59,8 @@ abstract class AbstractKotlinTarget(
override val publishable: Boolean
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 usageContexts = createUsageContexts(mainCompilation)
@@ -144,14 +146,18 @@ abstract class AbstractKotlinTarget(
}
@Suppress("UNCHECKED_CAST")
internal val publicationConfigureActions: DomainObjectSet<Action<MavenPublication>> = project
.objects
private val publicationConfigureActions: DomainObjectSet<Action<MavenPublication>> = project.objects
.domainObjectSet(Action::class.java) as DomainObjectSet<Action<MavenPublication>>
override fun mavenPublication(action: Action<MavenPublication>) {
publicationConfigureActions.add(action)
}
@InternalKotlinGradlePluginApi
override fun onPublicationCreated(publication: MavenPublication) {
publicationConfigureActions.all { action -> action.execute(publication) }
}
override var preset: KotlinTargetPreset<out KotlinTarget>? = null
internal set
}
@@ -5,9 +5,14 @@
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.KotlinTargetComponent
internal interface InternalKotlinTarget : KotlinTarget
internal interface InternalKotlinTarget : KotlinTarget {
val kotlinComponents: Set<KotlinTargetComponent>
fun onPublicationCreated(publication: MavenPublication)
}
internal val KotlinTarget.internal: InternalKotlinTarget
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.publish.maven.MavenPublication
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.KotlinTarget
import org.jetbrains.kotlin.gradle.plugin.ProjectLocalConfigurations
import org.jetbrains.kotlin.gradle.plugin.usageByName
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
abstract class KotlinSoftwareComponent(
@@ -40,11 +38,12 @@ abstract class KotlinSoftwareComponent(
override fun getVariants(): Set<SoftwareComponent> = kotlinTargets
.filter { target -> target !is KotlinMetadataTarget }
.flatMap { target ->
val targetPublishableComponentNames =
(target as? AbstractKotlinTarget)?.kotlinComponents?.mapNotNullTo(mutableSetOf()) { component ->
component.name.takeIf { component.publishable }
}
target.components.filter { targetPublishableComponentNames?.contains(it.name) ?: true }
val targetPublishableComponentNames = target.internal.kotlinComponents
.filter { component -> component.publishable }
.map { component -> component.name }
.toSet()
target.components.filter { it.name in targetPublishableComponentNames }
}.toSet()
private val _usages: Set<UsageContext> by lazy {
@@ -62,7 +62,7 @@ private fun createTargetPublications(project: Project, publishing: PublishingExt
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)
.withType(InternalKotlinTarget::class.java)
.matching { it.publishable }
.all { kotlinTarget ->
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
.map { gradleComponent -> gradleComponent to kotlinComponents.single { it.name == gradleComponent.name } }
.filter { (_, kotlinComponent) -> kotlinComponent.publishableOnCurrentHost }
@@ -102,7 +102,7 @@ private fun AbstractKotlinTarget.createMavenPublications(publications: Publicati
}
(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
* 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)
null
else {
@@ -7,8 +7,9 @@ package org.jetbrains.kotlin.gradle.plugin.mpp.external
import org.gradle.api.artifacts.Configuration
import org.gradle.api.logging.Logger
import org.gradle.api.logging.Logging
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
@ExternalKotlinTargetApi
@@ -23,5 +24,15 @@ abstract class DecoratedExternalKotlinTarget internal constructor(
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
import org.gradle.api.Action
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.*
import org.gradle.api.artifacts.Configuration
import org.gradle.api.attributes.AttributeContainer
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.tasks.TaskProvider
import org.jetbrains.kotlin.gradle.InternalKotlinGradlePluginApi
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
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.buildAdhocComponentsFromKotlinVariants
internal class ExternalKotlinTargetImpl internal constructor(
override val project: Project,
override val targetName: String,
override val platformType: KotlinPlatformType,
override val publishable: Boolean,
val defaultConfiguration: Configuration,
val apiElementsConfiguration: Configuration,
val runtimeElementsConfiguration: Configuration,
override val publishable: Boolean,
internal val kotlinComponents: Set<KotlinTargetComponent>,
val apiElementsPublishedConfiguration: Configuration,
val runtimeElementsPublishedConfiguration: Configuration,
val kotlinTargetComponent: ExternalKotlinTargetComponent,
private val artifactsTaskLocator: ArtifactsTaskLocator,
) : InternalKotlinTarget {
fun interface ArtifactsTaskLocator {
fun locate(target: ExternalKotlinTargetImpl): TaskProvider<out Task>
}
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 overrideDisambiguationClassifierOnIdeImport: String? = null
@@ -58,26 +65,36 @@ internal class ExternalKotlinTargetImpl internal constructor(
override val runtimeElementsConfigurationName: String
get() = runtimeElementsConfiguration.name
@InternalKotlinGradlePluginApi
override val kotlinComponents: Set<KotlinTargetComponent> = setOf(kotlinTargetComponent)
override val components: Set<SoftwareComponent> by lazy {
project.buildAdhocComponentsFromKotlinVariants(kotlinComponents)
logger.debug("Creating SoftwareComponent")
setOf(ExternalKotlinTargetSoftwareComponent(this))
}
override val compilations: NamedDomainObjectContainer<ExternalDecoratedKotlinCompilation> by lazy {
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>) {
TODO("Not yet implemented")
mavenPublicationActions.add(action)
}
override val preset: Nothing? = null
override fun getAttributes(): AttributeContainer {
TODO("Not yet implemented")
@InternalKotlinGradlePluginApi
override fun onPublicationCreated(publication: MavenPublication) {
mavenPublicationActions.all { action -> action.execute(publication) }
}
private val attributeContainer = HierarchyAttributeContainer(parent = null)
override fun getAttributes(): AttributeContainer = attributeContainer
internal fun onCreated() {
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
import org.gradle.api.artifacts.Configuration
import org.gradle.api.attributes.Category
import org.gradle.api.attributes.Usage
import org.gradle.jvm.tasks.Jar
import org.jetbrains.kotlin.gradle.ExternalKotlinTargetApi
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
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.tasks.locateOrRegisterTask
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 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 ->
target.project.locateOrRegisterTask<Jar>(lowerCamelCaseName(descriptor.targetName, "jar"))
}
@@ -34,25 +46,28 @@ fun <T : DecoratedExternalKotlinTarget> KotlinMultiplatformExtension.createExter
project = project,
targetName = descriptor.targetName,
platformType = descriptor.platformType,
publishable = true,
defaultConfiguration = defaultConfiguration,
apiElementsConfiguration = apiElementsConfiguration,
runtimeElementsConfiguration = runtimeElementsConfiguration,
publishable = true,
kotlinComponents = emptySet(),
apiElementsPublishedConfiguration = apiElementsPublishedConfiguration,
runtimeElementsPublishedConfiguration = runtimeElementsPublishedConfiguration,
kotlinTargetComponent = kotlinTargetComponent,
artifactsTaskLocator = artifactsTaskLocator
)
apiElementsConfiguration.isCanBeConsumed = true
apiElementsConfiguration.isCanBeResolved = false
apiElementsConfiguration.usesPlatformOf(target)
apiElementsConfiguration.attributes.attribute(Usage.USAGE_ATTRIBUTE, KotlinUsages.producerApiUsage(target))
apiElementsConfiguration.attributes.attribute(Category.CATEGORY_ATTRIBUTE, project.objects.named(Category.LIBRARY))
target.setupApiElements(apiElementsConfiguration)
target.setupApiElements(apiElementsPublishedConfiguration)
target.setupRuntimeElements(runtimeElementsConfiguration)
target.setupRuntimeElements(runtimeElementsPublishedConfiguration)
apiElementsConfiguration.markConsumable()
runtimeElementsConfiguration.markConsumable()
runtimeElementsConfiguration.isCanBeConsumed = true
runtimeElementsConfiguration.isCanBeResolved = false
runtimeElementsConfiguration.usesPlatformOf(target)
runtimeElementsConfiguration.attributes.attribute(Usage.USAGE_ATTRIBUTE, KotlinUsages.producerRuntimeUsage(target))
runtimeElementsConfiguration.attributes.attribute(Category.CATEGORY_ATTRIBUTE, project.objects.named(Category.LIBRARY))
/* Those configurations can not be resolved but also not consumed (not suitable for project to proejct dependencies */
apiElementsPublishedConfiguration.isCanBeConsumed = false
apiElementsPublishedConfiguration.isCanBeResolved = false
runtimeElementsPublishedConfiguration.isCanBeResolved = false
runtimeElementsPublishedConfiguration.isCanBeConsumed = false
val decorated = descriptor.targetFactory.create(DecoratedExternalKotlinTarget.Delegate(target))
target.onCreated()
@@ -60,6 +75,8 @@ fun <T : DecoratedExternalKotlinTarget> KotlinMultiplatformExtension.createExter
descriptor.configure?.invoke(decorated)
descriptor.apiElements.configure?.invoke(decorated, apiElementsConfiguration)
descriptor.runtimeElements.configure?.invoke(decorated, runtimeElementsConfiguration)
descriptor.apiElementsPublished.configure?.invoke(decorated, apiElementsPublishedConfiguration)
descriptor.runtimeElementsPublished.configure?.invoke(decorated, runtimeElementsPublishedConfiguration)
targets.add(decorated)
decorated.logger.info("Created ${descriptor.platformType} target")
@@ -72,3 +89,15 @@ fun <T : DecoratedExternalKotlinTarget> KotlinMultiplatformExtension.createExter
): T {
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
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.AttributeContainer
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>) =
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
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.androidApplication
import org.jetbrains.kotlin.gradle.androidLibrary
import org.jetbrains.kotlin.gradle.assumeAndroidSdkAvailable
import org.jetbrains.kotlin.gradle.buildProjectWithMPP
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension
import org.jetbrains.kotlin.gradle.kpm.idea.mavenCentralCacheRedirector
import org.jetbrains.kotlin.gradle.utils.getByType
import org.junit.Test
import kotlin.test.BeforeTest
import kotlin.test.assertEquals
import kotlin.test.fail
class ExternalAndroidTargetPrototypeSmokeTest {
@@ -27,7 +32,7 @@ class ExternalAndroidTargetPrototypeSmokeTest {
@Test
fun `apply prototype - evaluate - compilations exist`() {
val project = buildProjectWithMPP()
project.androidApplication { compileSdk = 31 }
project.androidLibrary { compileSdk = 31 }
val androidTargetPrototype = project.multiplatformExtension.androidTargetPrototype()
project.evaluate()
@@ -40,7 +45,7 @@ class ExternalAndroidTargetPrototypeSmokeTest {
@Test
fun `apply prototype - evaluate - configurations can be resolved`() {
val project = buildProjectWithMPP()
project.androidApplication { compileSdk = 31 }
project.androidLibrary { compileSdk = 31 }
val androidTargetPrototype = project.multiplatformExtension.androidTargetPrototype()
project.repositories.mavenLocal()
@@ -52,4 +57,19 @@ class ExternalAndroidTargetPrototypeSmokeTest {
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)
}
}