[Gradle, MPP] Publish sources as gradle variants

Now when MPP library is published it will contain "sourcesElements"
variant for each target and one for common code.

Sources variants are published under gradle attributes that were
borrowed from Java Gradle Plugin for consistency reasons.
This makes it compatible with JVM tooling that rely on these attributes.

^KT-36943 Verification Pending
This commit is contained in:
Anton Lakotka
2022-11-29 14:55:14 +01:00
committed by teamcity
parent fd7ceabab6
commit 3fa85bf7fd
11 changed files with 204 additions and 96 deletions
@@ -34,6 +34,7 @@ interface KotlinTarget : Named, HasAttributes {
val defaultConfigurationName: String val defaultConfigurationName: String
val apiElementsConfigurationName: String val apiElementsConfigurationName: String
val runtimeElementsConfigurationName: String val runtimeElementsConfigurationName: String
val sourcesElementsConfigurationName: String
val publishable: Boolean val publishable: Boolean
@@ -13,5 +13,11 @@ interface KotlinTargetComponent : SoftwareComponent {
val publishable: Boolean val publishable: Boolean
val publishableOnCurrentHost: Boolean val publishableOnCurrentHost: Boolean
val defaultArtifactId: String val defaultArtifactId: String
@Deprecated(
message = "Sources artifacts are now published as separate variant " +
"use target.sourcesElementsConfigurationName to obtain necessary information",
replaceWith = ReplaceWith("target.sourcesElementsConfigurationName")
)
val sourcesArtifacts: Set<PublishArtifact> val sourcesArtifacts: Set<PublishArtifact>
} }
@@ -13,9 +13,7 @@ import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.Dependency import org.gradle.api.artifacts.Dependency
import org.gradle.api.artifacts.PublishArtifact import org.gradle.api.artifacts.PublishArtifact
import org.gradle.api.artifacts.type.ArtifactTypeDefinition import org.gradle.api.artifacts.type.ArtifactTypeDefinition
import org.gradle.api.attributes.Attribute import org.gradle.api.attributes.*
import org.gradle.api.attributes.Category
import org.gradle.api.attributes.Usage
import org.gradle.api.attributes.Usage.USAGE_ATTRIBUTE import org.gradle.api.attributes.Usage.USAGE_ATTRIBUTE
import org.gradle.api.file.FileCollection import org.gradle.api.file.FileCollection
import org.gradle.api.internal.artifacts.ArtifactAttributes import org.gradle.api.internal.artifacts.ArtifactAttributes
@@ -200,6 +198,14 @@ abstract class AbstractKotlinTargetConfigurator<KotlinTargetType : KotlinTarget>
} }
} }
configurations.maybeCreate(target.sourcesElementsConfigurationName).apply {
description = "Source files of main compilation of ${target.name}."
isVisible = false
isCanBeResolved = false
isCanBeConsumed = true
configureSourcesPublicationAttributes(target)
}
if (createTestCompilation) { if (createTestCompilation) {
val testCompilation = target.compilations.getByName(KotlinCompilation.TEST_COMPILATION_NAME) val testCompilation = target.compilations.getByName(KotlinCompilation.TEST_COMPILATION_NAME)
val compileTestsConfiguration = testCompilation.internal.configurations.deprecatedCompileConfiguration val compileTestsConfiguration = testCompilation.internal.configurations.deprecatedCompileConfiguration
@@ -390,6 +396,9 @@ internal fun Project.usageByName(usageName: String): Usage =
internal fun Project.categoryByName(categoryName: String): Category = internal fun Project.categoryByName(categoryName: String): Category =
objects.named(Category::class.java, categoryName) objects.named(Category::class.java, categoryName)
internal inline fun <reified T: Named> Project.attributeValueByName(attributeValueName: String): T =
objects.named(T::class.java, attributeValueName)
fun Configuration.usesPlatformOf(target: KotlinTarget): Configuration { fun Configuration.usesPlatformOf(target: KotlinTarget): Configuration {
attributes.attribute(KotlinPlatformType.attribute, target.platformType) attributes.attribute(KotlinPlatformType.attribute, target.platformType)
@@ -50,6 +50,9 @@ abstract class AbstractKotlinTarget(
override val runtimeElementsConfigurationName: String override val runtimeElementsConfigurationName: String
get() = disambiguateName("runtimeElements") get() = disambiguateName("runtimeElements")
override val sourcesElementsConfigurationName: String
get() = disambiguateName("sourcesElements")
override val artifactsTaskName: String override val artifactsTaskName: String
get() = disambiguateName("jar") get() = disambiguateName("jar")
@@ -61,18 +64,24 @@ abstract class AbstractKotlinTarget(
@InternalKotlinGradlePluginApi @InternalKotlinGradlePluginApi
override val kotlinComponents: Set<KotlinTargetComponent> by lazy { 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).toMutableSet()
val componentName = val componentName =
if (project.kotlinExtension is KotlinMultiplatformExtension) if (project.kotlinExtension is KotlinMultiplatformExtension)
targetName targetName
else PRIMARY_SINGLE_COMPONENT_NAME else PRIMARY_SINGLE_COMPONENT_NAME
val result = createKotlinVariant(componentName, mainCompilation, usageContexts) val sourcesArtifact = configureSourcesJarArtifact(mainCompilation, componentName, dashSeparatedName(targetName.toLowerCase()))
if (sourcesArtifact != null) {
usageContexts += DefaultKotlinUsageContext(
compilation = mainCompilation,
dependencyConfigurationName = sourcesElementsConfigurationName,
includeIntoProjectStructureMetadata = false,
includeDependenciesToMavenPublication = false,
)
}
result.sourcesArtifacts = setOf( val result = createKotlinVariant(componentName, mainCompilation, usageContexts)
sourcesJarArtifact(mainCompilation, componentName, dashSeparatedName(targetName.toLowerCase()))
)
setOf(result) setOf(result)
} }
@@ -117,28 +126,21 @@ abstract class AbstractKotlinTarget(
} }
} }
protected fun sourcesJarArtifact( protected fun configureSourcesJarArtifact(
producingCompilation: KotlinCompilation<*>, producingCompilation: KotlinCompilation<*>,
componentName: String, componentName: String,
artifactNameAppendix: String, artifactNameAppendix: String,
classifierPrefix: String? = null classifierPrefix: String? = null,
): PublishArtifact { sourcesElementsConfigurationName: String = this.sourcesElementsConfigurationName,
val sourcesJarTask = sourcesJarTask(producingCompilation, componentName, artifactNameAppendix) ): PublishArtifact? {
val sourceArtifactConfigurationName = producingCompilation.disambiguateName("sourceArtifacts") // If sourcesElements configuration not found, don't create artifact.
// This can happen in pure JVM plugin where source publication is delegated to Java Gradle Plugin
project.configurations.findByName(sourcesElementsConfigurationName) ?: return null
return with(producingCompilation.target.project) { val sourcesJarTask = sourcesJarTask(producingCompilation, componentName, artifactNameAppendix)
(configurations.findByName(sourceArtifactConfigurationName) ?: run { val artifact = project.artifacts.add(sourcesElementsConfigurationName, sourcesJarTask) as ConfigurablePublishArtifact
val configuration = configurations.create(sourceArtifactConfigurationName) { artifact.classifier = dashSeparatedName(classifierPrefix, "sources")
it.isCanBeResolved = false return artifact
it.isCanBeConsumed = false
}
artifacts.add(sourceArtifactConfigurationName, sourcesJarTask)
configuration
}).artifacts.single().apply {
this as ConfigurablePublishArtifact
classifier = dashSeparatedName(classifierPrefix, "sources")
}
}
} }
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
@@ -7,9 +7,7 @@ package org.jetbrains.kotlin.gradle.plugin.mpp
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.artifacts.* import org.gradle.api.artifacts.*
import org.gradle.api.attributes.Attribute import org.gradle.api.attributes.*
import org.gradle.api.attributes.AttributeContainer
import org.gradle.api.attributes.Usage
import org.gradle.api.capabilities.Capability import org.gradle.api.capabilities.Capability
import org.gradle.api.component.ComponentWithCoordinates import org.gradle.api.component.ComponentWithCoordinates
import org.gradle.api.component.ComponentWithVariants import org.gradle.api.component.ComponentWithVariants
@@ -19,12 +17,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.KotlinCompilation import org.jetbrains.kotlin.gradle.plugin.*
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.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.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(
@@ -35,6 +33,8 @@ abstract class KotlinSoftwareComponent(
override fun getName(): String = name override fun getName(): String = name
private val metadataTarget get() = project.multiplatformExtension.metadata()
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 ->
@@ -47,8 +47,6 @@ abstract class KotlinSoftwareComponent(
}.toSet() }.toSet()
private val _usages: Set<UsageContext> by lazy { private val _usages: Set<UsageContext> by lazy {
val metadataTarget = project.multiplatformExtension.metadata()
if (!project.isKotlinGranularMetadataEnabled) { if (!project.isKotlinGranularMetadataEnabled) {
val metadataCompilation = metadataTarget.compilations.getByName(MAIN_COMPILATION_NAME) val metadataCompilation = metadataTarget.compilations.getByName(MAIN_COMPILATION_NAME)
return@lazy metadataTarget.createUsageContexts(metadataCompilation) return@lazy metadataTarget.createUsageContexts(metadataCompilation)
@@ -80,6 +78,14 @@ abstract class KotlinSoftwareComponent(
) )
} }
} }
configureSourcesJarArtifact()
this += DefaultKotlinUsageContext(
compilation = metadataTarget.compilations.getByName(MAIN_COMPILATION_NAME),
dependencyConfigurationName = metadataTarget.sourcesElementsConfigurationName,
includeIntoProjectStructureMetadata = false,
includeDependenciesToMavenPublication = false
)
} }
} }
@@ -87,7 +93,7 @@ abstract class KotlinSoftwareComponent(
return _usages return _usages
} }
val sourcesArtifacts: Set<PublishArtifact> by lazy { private fun configureSourcesJarArtifact(): PublishArtifact {
fun allPublishableCommonSourceSets() = getCommonSourceSetsForMetadataCompilation(project) + fun allPublishableCommonSourceSets() = getCommonSourceSetsForMetadataCompilation(project) +
getHostSpecificMainSharedSourceSets(project) getHostSpecificMainSharedSourceSets(project)
@@ -98,10 +104,10 @@ abstract class KotlinSoftwareComponent(
lazy { allPublishableCommonSourceSets().associate { it.name to it.kotlin } }, lazy { allPublishableCommonSourceSets().associate { it.name to it.kotlin } },
name.toLowerCase() name.toLowerCase()
) )
val sourcesJarArtifact = project.artifacts.add(Dependency.ARCHIVES_CONFIGURATION, sourcesJarTask) { sourcesJarArtifact ->
return project.artifacts.add(metadataTarget.sourcesElementsConfigurationName, sourcesJarTask) { sourcesJarArtifact ->
sourcesJarArtifact.classifier = "sources" sourcesJarArtifact.classifier = "sources"
} }
setOf(sourcesJarArtifact)
} }
// This property is declared in the parent type to allow the usages to reference it without forcing the subtypes to load, // This property is declared in the parent type to allow the usages to reference it without forcing the subtypes to load,
@@ -214,4 +220,4 @@ class DefaultKotlinUsageContext(
*/ */
it.name != "org.gradle.jvm.environment" it.name != "org.gradle.jvm.environment"
} }
} }
@@ -61,6 +61,8 @@ object KotlinUsages {
internal fun producerRuntimeUsage(project: Project, platformType: KotlinPlatformType) = project.usageByName( internal fun producerRuntimeUsage(project: Project, platformType: KotlinPlatformType) = project.usageByName(
when (platformType) { when (platformType) {
// This attribute is deprecated in Gradle and additionally to Usage attribute
// it implicitly adds `org.gradle.libraryelements=jar`
in jvmPlatformTypes -> "java-runtime-jars" in jvmPlatformTypes -> "java-runtime-jars"
else -> KOTLIN_RUNTIME else -> KOTLIN_RUNTIME
} }
@@ -6,6 +6,11 @@
package org.jetbrains.kotlin.gradle.plugin.mpp package org.jetbrains.kotlin.gradle.plugin.mpp
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration
import org.gradle.api.attributes.Bundling
import org.gradle.api.attributes.Category
import org.gradle.api.attributes.DocsType
import org.gradle.api.attributes.Usage
import org.gradle.api.provider.Provider import org.gradle.api.provider.Provider
import org.gradle.api.publish.PublicationContainer import org.gradle.api.publish.PublicationContainer
import org.gradle.api.publish.PublishingExtension import org.gradle.api.publish.PublishingExtension
@@ -14,7 +19,7 @@ import org.gradle.api.publish.maven.MavenPublication
import org.gradle.api.publish.maven.internal.publication.MavenPublicationInternal import org.gradle.api.publish.maven.internal.publication.MavenPublicationInternal
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider
import org.jetbrains.kotlin.gradle.plugin.sources.KotlinDependencyScope import org.jetbrains.kotlin.gradle.plugin.sources.KotlinDependencyScope
import org.jetbrains.kotlin.gradle.plugin.sources.sourceSetDependencyConfigurationByScope import org.jetbrains.kotlin.gradle.plugin.sources.sourceSetDependencyConfigurationByScope
@@ -41,9 +46,6 @@ private fun createRootPublication(project: Project, publishing: PublishingExtens
from(kotlinSoftwareComponent) from(kotlinSoftwareComponent)
(this as MavenPublicationInternal).publishWithOriginalFileName() (this as MavenPublicationInternal).publishWithOriginalFileName()
kotlinSoftwareComponent.publicationDelegate = this@apply kotlinSoftwareComponent.publicationDelegate = this@apply
kotlinSoftwareComponent.sourcesArtifacts.forEach { sourceArtifact ->
artifact(sourceArtifact)
}
addKotlinToolingMetadataArtifactIfNeeded(project) addKotlinToolingMetadataArtifactIfNeeded(project)
} }
@@ -82,9 +84,6 @@ private fun InternalKotlinTarget.createMavenPublications(publications: Publicati
// do this in whenEvaluated since older Gradle versions seem to check the files in the variant eagerly: // do this in whenEvaluated since older Gradle versions seem to check the files in the variant eagerly:
project.whenEvaluated { project.whenEvaluated {
from(gradleComponent) from(gradleComponent)
kotlinComponent.sourcesArtifacts.forEach { sourceArtifact ->
artifact(sourceArtifact)
}
} }
(this as MavenPublicationInternal).publishWithOriginalFileName() (this as MavenPublicationInternal).publishWithOriginalFileName()
artifactId = kotlinComponent.defaultArtifactId artifactId = kotlinComponent.defaultArtifactId
@@ -144,3 +143,20 @@ private fun dependenciesForPomRewriting(target: InternalKotlinTarget): Provider<
commonMainDependencies.map { ModuleCoordinates(it.group, it.name, it.version) }.toSet() commonMainDependencies.map { ModuleCoordinates(it.group, it.name, it.version) }.toSet()
} }
} }
internal fun Configuration.configureSourcesPublicationAttributes(target: KotlinTarget) {
val project = target.project
// In order to be consistent with Java Gradle Plugin, set usage attribute for sources variant
// to be either JAVA_RUNTIME (for jvm) or KOTLIN_RUNTIME (for other targets)
// the latter isn't a strong requirement since there is no tooling that consume kotlin sources through gradle variants at the moment
// so consistency with Java Gradle Plugin seemed most desirable choice.
attributes.attribute(Usage.USAGE_ATTRIBUTE, KotlinUsages.producerRuntimeUsage(target))
attributes.attribute(Category.CATEGORY_ATTRIBUTE, project.attributeValueByName(Category.DOCUMENTATION))
attributes.attribute(DocsType.DOCS_TYPE_ATTRIBUTE, project.attributeValueByName(DocsType.SOURCES))
// Bundling attribute is about component dependencies, external means that they are provided as separate components
// source variants doesn't have any dependencies (at least at the moment) so there is not much sense to use this attribute
// however for Java Gradle Plugin compatibility and in order to prevent weird Variant Resolution errors we include this attribute
attributes.attribute(Bundling.BUNDLING_ATTRIBUTE, project.attributeValueByName(Bundling.EXTERNAL))
usesPlatformOf(target)
}
@@ -74,8 +74,16 @@ open class KotlinVariant(
override val publishableOnCurrentHost: Boolean override val publishableOnCurrentHost: Boolean
get() = publishable && target.publishable get() = publishable && target.publishable
override var sourcesArtifacts: Set<PublishArtifact> = emptySet() @Deprecated(
internal set message = "Sources artifacts are now published as separate variant " +
"use target.sourcesElementsConfigurationName to obtain necessary information",
replaceWith = ReplaceWith("target.sourcesElementsConfigurationName") )
override val sourcesArtifacts: Set<PublishArtifact> get() = target
.project
.configurations
.findByName(target.sourcesElementsConfigurationName)
?.artifacts
?: emptySet()
internal var defaultArtifactIdSuffix: String? = null internal var defaultArtifactIdSuffix: String? = null
@@ -102,8 +110,7 @@ class KotlinVariantWithMetadataVariant(
class JointAndroidKotlinTargetComponent( class JointAndroidKotlinTargetComponent(
override val target: KotlinAndroidTarget, override val target: KotlinAndroidTarget,
private val nestedVariants: Set<KotlinVariant>, private val nestedVariants: Set<KotlinVariant>,
val flavorNames: List<String>, val flavorNames: List<String>
override val sourcesArtifacts: Set<PublishArtifact>
) : KotlinTargetComponentWithCoordinatesAndPublication, SoftwareComponentInternal { ) : KotlinTargetComponentWithCoordinatesAndPublication, SoftwareComponentInternal {
override fun getUsages(): Set<KotlinUsageContext> = nestedVariants.filter { it.publishable }.flatMap { it.usages }.toSet() override fun getUsages(): Set<KotlinUsageContext> = nestedVariants.filter { it.publishable }.flatMap { it.usages }.toSet()
@@ -124,4 +131,11 @@ class JointAndroidKotlinTargetComponent(
) )
override var publicationDelegate: MavenPublication? = null override var publicationDelegate: MavenPublication? = null
@Deprecated(
message = "Sources artifacts are now published as separate variant " +
"use target.sourcesElementsConfigurationName to obtain necessary information",
replaceWith = ReplaceWith("target.sourcesElementsConfigurationName")
)
override val sourcesArtifacts: Set<PublishArtifact> = emptySet()
} }
@@ -11,9 +11,10 @@ import org.gradle.api.InvalidUserDataException
import org.gradle.api.Named import org.gradle.api.Named
import org.gradle.api.NamedDomainObjectContainer import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.attributes.Attribute import org.gradle.api.artifacts.Configuration
import org.gradle.api.attributes.Usage.JAVA_RUNTIME_JARS import org.gradle.api.attributes.*
import org.jetbrains.kotlin.gradle.plugin.* import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.util.copyAttributes
import org.jetbrains.kotlin.gradle.utils.dashSeparatedName import org.jetbrains.kotlin.gradle.utils.dashSeparatedName
import org.jetbrains.kotlin.gradle.utils.forAllAndroidVariants import org.jetbrains.kotlin.gradle.utils.forAllAndroidVariants
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
@@ -120,33 +121,18 @@ abstract class KotlinAndroidTarget @Inject constructor(
val androidVariantName = getVariantName(androidVariant) val androidVariantName = getVariantName(androidVariant)
val compilation = compilations.getByName(androidVariantName) val compilation = compilations.getByName(androidVariantName)
val flavorNames = getFlavorNames(androidVariant) val usageContexts = createAndroidUsageContexts(
val buildTypeName = getBuildTypeName(androidVariant) variant = androidVariant,
compilation = compilation,
val artifactClassifier = buildTypeName.takeIf { it != "release" && publishLibraryVariantsGroupedByFlavor } isSingleBuildType = publishableVariants.filter(::isVariantPublished).map(::getBuildTypeName).distinct().size == 1,
)
createKotlinVariant( createKotlinVariant(
lowerCamelCaseName(compilation.target.name, *flavorGroupNameParts.toTypedArray()), lowerCamelCaseName(compilation.target.name, *flavorGroupNameParts.toTypedArray()),
compilation, compilation,
createAndroidUsageContexts( usageContexts,
androidVariant,
compilation,
artifactClassifier,
publishableVariants.filter(::isVariantPublished).map(::getBuildTypeName).distinct().size == 1
)
).apply { ).apply {
publishable = isVariantPublished(androidVariant) publishable = isVariantPublished(androidVariant)
sourcesArtifacts = setOf(
sourcesJarArtifact(
compilation, compilation.disambiguateName(""),
dashSeparatedName(
compilation.target.name.toLowerCase(),
*flavorNames.map { it.toLowerCase() }.toTypedArray(),
buildTypeName.takeIf { it != "release" }?.toLowerCase()
),
classifierPrefix = artifactClassifier
)
)
if (!publishLibraryVariantsGroupedByFlavor) { if (!publishLibraryVariantsGroupedByFlavor) {
defaultArtifactIdSuffix = defaultArtifactIdSuffix =
@@ -163,7 +149,6 @@ abstract class KotlinAndroidTarget @Inject constructor(
target = this@KotlinAndroidTarget, target = this@KotlinAndroidTarget,
nestedVariants = nestedVariants, nestedVariants = nestedVariants,
flavorNames = flavorGroupNameParts, flavorNames = flavorGroupNameParts,
sourcesArtifacts = nestedVariants.filter { it.publishable }.flatMap { it.sourcesArtifacts }.toSet()
) )
} else { } else {
nestedVariants.single() nestedVariants.single()
@@ -174,9 +159,12 @@ abstract class KotlinAndroidTarget @Inject constructor(
private fun AndroidProjectHandler.createAndroidUsageContexts( private fun AndroidProjectHandler.createAndroidUsageContexts(
variant: BaseVariant, variant: BaseVariant,
compilation: KotlinCompilation<*>, compilation: KotlinCompilation<*>,
artifactClassifier: String?, isSingleBuildType: Boolean,
isSingleBuildType: Boolean
): Set<DefaultKotlinUsageContext> { ): Set<DefaultKotlinUsageContext> {
val flavorNames = getFlavorNames(variant)
val buildTypeName = getBuildTypeName(variant)
val artifactClassifier = buildTypeName.takeIf { it != "release" && publishLibraryVariantsGroupedByFlavor }
val variantName = getVariantName(variant) val variantName = getVariantName(variant)
val outputTaskOrProvider = getLibraryOutputTask(variant) ?: return emptySet() val outputTaskOrProvider = getLibraryOutputTask(variant) ?: return emptySet()
val artifact = run { val artifact = run {
@@ -193,9 +181,46 @@ abstract class KotlinAndroidTarget @Inject constructor(
val apiElementsConfigurationName = lowerCamelCaseName(variantName, "apiElements") val apiElementsConfigurationName = lowerCamelCaseName(variantName, "apiElements")
val runtimeElementsConfigurationName = lowerCamelCaseName(variantName, "runtimeElements") val runtimeElementsConfigurationName = lowerCamelCaseName(variantName, "runtimeElements")
val sourcesElementsConfigurationName = lowerCamelCaseName(variantName, "sourcesElements")
val sourcesElementsConfiguration = createSourcesElementsIfNeeded(
variantName,
apiElementsConfigurationName,
sourcesElementsConfigurationName
)
configureSourcesJarArtifact(
compilation,
compilation.disambiguateName(""),
dashSeparatedName(
compilation.target.name.toLowerCase(),
*flavorNames.map { it.toLowerCase() }.toTypedArray(),
buildTypeName.takeIf { it != "release" }?.toLowerCase()
),
classifierPrefix = artifactClassifier,
sourcesElementsConfigurationName = sourcesElementsConfigurationName
)
fun AttributeContainer.filterOutAndroidVariantAttributes(): AttributeContainer =
HierarchyAttributeContainer(this) {
val valueString = run {
val value = getAttribute(it)
(value as? Named)?.name ?: value.toString()
}
filterOutAndroidVariantAttribute(it) &&
filterOutAndroidBuildTypeAttribute(it, valueString, isSingleBuildType) &&
filterOutAndroidAgpVersionAttribute(it)
}
val sourcesUsageContext = DefaultKotlinUsageContext(
compilation = compilation,
dependencyConfigurationName = sourcesElementsConfigurationName,
overrideConfigurationAttributes = sourcesElementsConfiguration.attributes.filterOutAndroidVariantAttributes(),
includeDependenciesToMavenPublication = false,
includeIntoProjectStructureMetadata = false
)
return listOf( return listOf(
apiElementsConfigurationName to KotlinUsageContext.UsageScope.COMPILE, apiElementsConfigurationName to KotlinUsageContext.UsageScope.COMPILE,
runtimeElementsConfigurationName to KotlinUsageContext.UsageScope.RUNTIME runtimeElementsConfigurationName to KotlinUsageContext.UsageScope.RUNTIME,
).mapTo(mutableSetOf()) { (dependencyConfigurationName, mavenScope) -> ).mapTo(mutableSetOf()) { (dependencyConfigurationName, mavenScope) ->
val configuration = project.configurations.getByName(dependencyConfigurationName) val configuration = project.configurations.getByName(dependencyConfigurationName)
DefaultKotlinUsageContext( DefaultKotlinUsageContext(
@@ -203,16 +228,32 @@ abstract class KotlinAndroidTarget @Inject constructor(
mavenScope, mavenScope,
dependencyConfigurationName, dependencyConfigurationName,
overrideConfigurationArtifacts = project.setProperty { listOf(artifact) }, overrideConfigurationArtifacts = project.setProperty { listOf(artifact) },
overrideConfigurationAttributes = HierarchyAttributeContainer(configuration.attributes) { overrideConfigurationAttributes = configuration.attributes.filterOutAndroidVariantAttributes()
val valueString = run {
val value = configuration.attributes.getAttribute(it)
(value as? Named)?.name ?: value.toString()
}
filterOutAndroidVariantAttribute(it) &&
filterOutAndroidBuildTypeAttribute(it, valueString, isSingleBuildType) &&
filterOutAndroidAgpVersionAttribute(it)
}
) )
} + sourcesUsageContext
}
/**
* TODO: Ask Google about providing such configuration where they could set their attributes and control them.
* Just like as they do with apiElements or runtimeElements
*/
private fun createSourcesElementsIfNeeded(
variantName: String,
apiElementsConfigurationName: String,
sourcesElementsConfigurationName: String
): Configuration {
val existingConfiguration = project.configurations.findByName(sourcesElementsConfigurationName)
if (existingConfiguration != null) return existingConfiguration
val apiElementsConfiguration = project.configurations.getByName(apiElementsConfigurationName)
return project.configurations.create(sourcesElementsConfigurationName).apply {
description = "Source files of Android ${variantName}."
isVisible = false
isCanBeResolved = false
isCanBeConsumed = true
copyAttributes(apiElementsConfiguration.attributes, attributes)
configureSourcesPublicationAttributes(this@KotlinAndroidTarget)
} }
} }
@@ -71,20 +71,26 @@ constructor(
super.kotlinComponents super.kotlinComponents
else { else {
val mainCompilation = compilations.getByName(MAIN_COMPILATION_NAME) val mainCompilation = compilations.getByName(MAIN_COMPILATION_NAME)
val usageContexts = createUsageContexts(mainCompilation) + val usageContexts = createUsageContexts(mainCompilation).toMutableSet()
irTarget!!.createUsageContexts(irTarget!!.compilations.getByName(MAIN_COMPILATION_NAME))
usageContexts += irTarget!!.createUsageContexts(irTarget!!.compilations.getByName(MAIN_COMPILATION_NAME))
val componentName = val componentName =
if (project.kotlinExtension is KotlinMultiplatformExtension) if (project.kotlinExtension is KotlinMultiplatformExtension)
irTarget?.let { targetName.removeJsCompilerSuffix(LEGACY) } ?: targetName irTarget?.let { targetName.removeJsCompilerSuffix(LEGACY) } ?: targetName
else PRIMARY_SINGLE_COMPONENT_NAME else PRIMARY_SINGLE_COMPONENT_NAME
val result = createKotlinVariant(componentName, mainCompilation, usageContexts) configureSourcesJarArtifact(mainCompilation, componentName, dashSeparatedName(targetName.toLowerCase()))
usageContexts += DefaultKotlinUsageContext(
result.sourcesArtifacts = setOf( compilation = mainCompilation,
sourcesJarArtifact(mainCompilation, componentName, dashSeparatedName(targetName.toLowerCase())) usageScope = KotlinUsageContext.UsageScope.RUNTIME,
dependencyConfigurationName = sourcesElementsConfigurationName,
includeIntoProjectStructureMetadata = false,
includeDependenciesToMavenPublication = false,
) )
val result = createKotlinVariant(componentName, mainCompilation, usageContexts)
setOf(result) setOf(result)
} }
} }
@@ -101,11 +101,16 @@ abstract class KotlinNativeTarget @Inject constructor(
} }
} }
val result = createKotlinVariant(targetName, mainCompilation, mutableUsageContexts) configureSourcesJarArtifact(mainCompilation, targetName, dashSeparatedName(targetName.toLowerCase()))
val sourcesUsage = DefaultKotlinUsageContext(
result.sourcesArtifacts = setOf( compilation = mainCompilation,
sourcesJarArtifact(mainCompilation, targetName, dashSeparatedName(targetName.toLowerCase())) dependencyConfigurationName = sourcesElementsConfigurationName,
includeIntoProjectStructureMetadata = false,
includeDependenciesToMavenPublication = false,
) )
mutableUsageContexts += sourcesUsage
val result = createKotlinVariant(targetName, mainCompilation, mutableUsageContexts)
setOf(result) setOf(result)
} }