diff --git a/libraries/tools/kotlin-gradle-plugin-api/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinTarget.kt b/libraries/tools/kotlin-gradle-plugin-api/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinTarget.kt index 8c493f52ff5..3e7e20aab3a 100644 --- a/libraries/tools/kotlin-gradle-plugin-api/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinTarget.kt +++ b/libraries/tools/kotlin-gradle-plugin-api/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinTarget.kt @@ -10,6 +10,7 @@ import org.gradle.api.Action import org.gradle.api.Named import org.gradle.api.NamedDomainObjectContainer import org.gradle.api.Project +import org.gradle.api.artifacts.PublishArtifact import org.gradle.api.attributes.AttributeContainer import org.gradle.api.attributes.HasAttributes import org.gradle.api.component.SoftwareComponent @@ -21,6 +22,7 @@ interface KotlinTargetComponent : SoftwareComponent { val target: KotlinTarget val publishable: Boolean val defaultArtifactId: String + val sourcesArtifacts: Set } interface KotlinTarget : Named, HasAttributes { diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/AbstractKotlinAndroidGradleTests.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/AbstractKotlinAndroidGradleTests.kt index 094b6aa7057..945c540e059 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/AbstractKotlinAndroidGradleTests.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/AbstractKotlinAndroidGradleTests.kt @@ -116,11 +116,11 @@ class KotlinAndroid32GradleIT : KotlinAndroid3GradleIT(androidGradlePluginVersio // Add one flavor dimension with two flavors, check that the flavors produce grouped publications: gradleBuildScript("lib").appendText( - "\nandroid { flavorDimensions('foo'); productFlavors { foo1 { dimension 'foo' }; foo2 { dimension 'foo' } } }" + "\nandroid { flavorDimensions('foo'); productFlavors { fooBar { dimension 'foo' }; fooBaz { dimension 'foo' } } }" ) build("publish") { assertSuccessful() - listOf("foo1", "foo2").forEach { flavor -> + listOf("foobar", "foobaz").forEach { flavor -> assertFileExists(groupDir + "lib-androidlib-$flavor/1.0/lib-androidlib-$flavor-1.0.aar") assertFileExists(groupDir + "lib-androidlib-$flavor/1.0/lib-androidlib-$flavor-1.0-sources.jar") assertFileExists(groupDir + "lib-androidlib-$flavor/1.0/lib-androidlib-$flavor-1.0-debug.aar") @@ -135,7 +135,7 @@ class KotlinAndroid32GradleIT : KotlinAndroid3GradleIT(androidGradlePluginVersio ) build("publish") { assertSuccessful() - listOf("foo1", "foo2").forEach { flavor -> + listOf("foobar", "foobaz").forEach { flavor -> listOf("-debug", "").forEach { buildType -> assertFileExists(groupDir + "lib-androidlib-$flavor$buildType/1.0/lib-androidlib-$flavor$buildType-1.0.aar") assertFileExists(groupDir + "lib-androidlib-$flavor$buildType/1.0/lib-androidlib-$flavor$buildType-1.0-sources.jar") @@ -153,13 +153,13 @@ class KotlinAndroid32GradleIT : KotlinAndroid3GradleIT(androidGradlePluginVersio apply plugin: 'maven-publish' publishing { repositories { maven { url = uri("${'$'}buildDir/repo") } } } kotlin.android('androidApp') { publishAllLibraryVariants() } - android { flavorDimensions('foo'); productFlavors { foo1 { dimension 'foo' }; foo2 { dimension 'foo' } } } + android { flavorDimensions('foo'); productFlavors { fooBar { dimension 'foo' }; fooBaz { dimension 'foo' } } } """.trimIndent() } build("publish") { assertSuccessful() val appGroupDir = "app/build/repo/com/example/" - listOf("foo1", "foo2").forEach { flavor -> + listOf("foobar", "foobaz").forEach { flavor -> listOf("-debug", "").forEach { buildType -> assertFileExists(appGroupDir + "app-androidapp-$flavor$buildType/1.0/app-androidapp-$flavor$buildType-1.0.aar") assertFileExists(appGroupDir + "app-androidapp-$flavor$buildType/1.0/app-androidapp-$flavor$buildType-1.0-sources.jar") diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/NewMultiplatformIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/NewMultiplatformIT.kt index f7bb7eef15a..a3743259da5 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/NewMultiplatformIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/NewMultiplatformIT.kt @@ -1117,7 +1117,7 @@ class NewMultiplatformIT : BaseGradleIT() { assertTasksExecuted(":publishedLibrary:cinteropStdio${host.capitalize()}") assertTrue(output.contains("Published test"), "No test output found") assertFileExists("publishedLibrary/build/classes/kotlin/$host/main/publishedLibrary-cinterop-stdio.klib") - assertFileExists("repo/org/example/publishedlibrary-$host/1.0/publishedlibrary-$host-1.0-cinterop-stdio.klib") + assertFileExists("repo/org/example/publishedLibrary-$host/1.0/publishedLibrary-$host-1.0-cinterop-stdio.klib") } build(":build") { diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinMultiplatformPlugin.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinMultiplatformPlugin.kt index 8d39b9606f2..b359e0bc8ce 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinMultiplatformPlugin.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinMultiplatformPlugin.kt @@ -166,7 +166,6 @@ class KotlinMultiplatformPlugin( // The root publication that references the platform specific publications as its variants: val rootPublication = publishing.publications.create("kotlinMultiplatform", MavenPublication::class.java).apply { - artifactId = project.name.toLowerCase() from(kotlinSoftwareComponent) (this as MavenPublicationInternal).publishWithOriginalFileName() } @@ -200,9 +199,8 @@ class KotlinMultiplatformPlugin( // do this in whenEvaluated since older Gradle versions seem to check the files in the variant eagerly: project.whenEvaluated { from(variant) - if (variant is SoftwareComponentInternal) { - variant.usages.filterIsInstance().map { it.sourcesArtifact }.distinct() - .forEach { artifact(it) } + variant.sourcesArtifacts.forEach { sourceArtifact -> + artifact(sourceArtifact) } } (this as MavenPublicationInternal).publishWithOriginalFileName() diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinSoftwareComponent.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinSoftwareComponent.kt index 7fc86818b5b..ceb6a1b0059 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinSoftwareComponent.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinSoftwareComponent.kt @@ -16,7 +16,6 @@ import org.gradle.api.internal.component.SoftwareComponentInternal import org.gradle.api.internal.component.UsageContext import org.gradle.api.publish.maven.MavenPublication import org.jetbrains.kotlin.gradle.plugin.* -import org.jetbrains.kotlin.utils.ifEmpty open class KotlinSoftwareComponent( private val name: String, @@ -52,14 +51,12 @@ object NativeUsage { interface KotlinUsageContext : UsageContext { val compilation: KotlinCompilation<*> val dependencyConfigurationName: String - val sourcesArtifact: PublishArtifact? } class DefaultKotlinUsageContext( override val compilation: KotlinCompilation<*>, private val usage: Usage, override val dependencyConfigurationName: String, - override val sourcesArtifact: PublishArtifact? = null, private val overrideConfigurationArtifacts: Set? = null ) : KotlinUsageContext { diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinTargets.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinTargets.kt index 32d43821f90..f4bbb5d60f9 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinTargets.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinTargets.kt @@ -22,9 +22,9 @@ import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension import org.jetbrains.kotlin.gradle.dsl.KotlinNativeBinaryContainer import org.jetbrains.kotlin.gradle.dsl.kotlinExtension import org.jetbrains.kotlin.gradle.plugin.* +import org.jetbrains.kotlin.gradle.utils.dashSeparatedName import org.jetbrains.kotlin.gradle.utils.isGradleVersionAtLeast import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName -import org.jetbrains.kotlin.gradle.utils.lowerSpinalCaseName import org.jetbrains.kotlin.konan.target.KonanTarget abstract class AbstractKotlinTarget( @@ -53,10 +53,18 @@ abstract class AbstractKotlinTarget( override val components: Set by lazy { val mainCompilation = compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME) - val usageContexts = createUsageContexts(mainCompilation, targetName, targetName.toLowerCase()) + val usageContexts = createUsageContexts(mainCompilation) setOf( if (isGradleVersionAtLeast(4, 7)) { - createKotlinVariant(mainCompilation.target.name, mainCompilation, usageContexts) + val componentName = mainCompilation.target.name + createKotlinVariant(componentName, mainCompilation, usageContexts).apply { + sourcesArtifacts = setOf( + sourcesJarArtifact( + mainCompilation, componentName, + dashSeparatedName(target.name.toLowerCase(), componentName.toLowerCase()) + ) + ) + } } else { KotlinVariant(mainCompilation, usageContexts) } @@ -91,12 +99,8 @@ abstract class AbstractKotlinTarget( } private fun createUsageContexts( - producingCompilation: KotlinCompilation<*>, - componentName: String, - artifactNameAppendix: String + producingCompilation: KotlinCompilation<*> ): Set { - val sourcesJarArtifact = sourcesJarArtifact(producingCompilation, componentName, artifactNameAppendix) - // Here, `JAVA_API` and `JAVA_RUNTIME_JARS` are used intentionally as Gradle needs this for // ordering of the usage contexts (prioritizing the dependencies) when merging them into the POM; // These Java usages should not be replaced with the custom Kotlin usages. @@ -107,8 +111,7 @@ abstract class AbstractKotlinTarget( DefaultKotlinUsageContext( producingCompilation, project.usageByName(usageName), - dependenciesConfigurationName, - sourcesJarArtifact + dependenciesConfigurationName ) } } @@ -121,7 +124,7 @@ abstract class AbstractKotlinTarget( ): PublishArtifact { val sourcesJarTask = sourcesJarTask(producingCompilation, componentName, artifactNameAppendix) val sourceArtifactConfigurationName = producingCompilation.disambiguateName("sourceArtifacts") - val sourcesJarArtifact = producingCompilation.target.project.run { + return producingCompilation.target.project.run { (configurations.findByName(sourceArtifactConfigurationName) ?: run { val configuration = configurations.create(sourceArtifactConfigurationName) { it.isCanBeResolved = false @@ -131,10 +134,9 @@ abstract class AbstractKotlinTarget( configuration }).artifacts.single().apply { this as ConfigurablePublishArtifact - classifier = lowerSpinalCaseName(classifierPrefix, "sources") + classifier = dashSeparatedName(classifierPrefix, "sources") } } - return sourcesJarArtifact } @Suppress("UNCHECKED_CAST") @@ -182,6 +184,8 @@ open class KotlinAndroidTarget( * If set to null, which can also be done with [publishAllLibraryVariants], * all library variants will be published, but not test or application variants. */ var publishLibraryVariants: List? = listOf() + // Workaround for Groovy GString items in a list: + set(value) { field = value?.map(Any::toString) } /** Add Android library variant names to [publishLibraryVariants]. */ fun publishLibraryVariants(vararg names: String) { @@ -200,21 +204,27 @@ open class KotlinAndroidTarget( private fun checkPublishLibraryVariantsExist() { // Capture type parameter T - fun AbstractAndroidProjectHandler.getVariantNames() = - mutableSetOf().apply { forEachVariant(project) { add(getVariantName(it)) } } + fun AbstractAndroidProjectHandler.getLibraryVariantNames() = + mutableSetOf().apply { + forEachVariant(project) { + if (getLibraryOutputTask(it) != null) + add(getVariantName(it)) + } + } val variantNames = KotlinAndroidPlugin.androidTargetHandler(project.getKotlinPluginVersion()!!, this) - .getVariantNames() + .getLibraryVariantNames() val missingVariants = publishLibraryVariants?.minus(variantNames).orEmpty() if (missingVariants.isNotEmpty()) throw InvalidUserDataException( - "Kotlin target '$targetName' tried to set up publishing for Android library variants that are not present " + - "in the project:\n" + missingVariants.joinToString("\n") { "* $it" } + - "\nCheck the 'publishLibraryVariants' property, it should point to existing Android build variants." + "Kotlin target '$targetName' tried to set up publishing for Android build variants that are not library variants " + + "or do not exist:\n" + missingVariants.joinToString("\n") { "* $it" } + + "\nCheck the 'publishLibraryVariants' property, it should point to existing Android library variants. Publishing " + + "of application and test variants is not supported." ) } @@ -250,17 +260,36 @@ open class KotlinAndroidTarget( val nestedVariants = androidVariants.mapTo(mutableSetOf()) { androidVariant -> val androidVariantName = getVariantName(androidVariant) val compilation = compilations.getByName(androidVariantName) + + val flavorNames = getFlavorNames(androidVariant) val buildTypeName = getBuildTypeName(androidVariant) - val usageContexts = createAndroidUsageContexts(androidVariant, compilation, getFlavorNames(androidVariant), buildTypeName) + + val artifactClassifier = buildTypeName.takeIf { it != "release" && publishLibraryVariantsGroupedByFlavor } + + val usageContexts = createAndroidUsageContexts(androidVariant, compilation, artifactClassifier) createKotlinVariant( lowerCamelCaseName(compilation.target.name, *flavorGroupNameParts.toTypedArray()), compilation, usageContexts ).apply { + 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) { defaultArtifactIdSuffix = - lowerSpinalCaseName(getFlavorNames(androidVariant) + getBuildTypeName(androidVariant).takeIf { it != "release" }) - .takeIf { it.isNotEmpty() } + dashSeparatedName( + (getFlavorNames(androidVariant) + getBuildTypeName(androidVariant).takeIf { it != "release" }) + .map { it?.toLowerCase() } + ).takeIf { it.isNotEmpty() } } } } @@ -269,7 +298,8 @@ open class KotlinAndroidTarget( JointAndroidKotlinTargetComponent( this@KotlinAndroidTarget, nestedVariants, - flavorGroupNameParts + flavorGroupNameParts, + nestedVariants.flatMap { it.sourcesArtifacts }.toSet() ) } else { nestedVariants.single() @@ -280,17 +310,8 @@ open class KotlinAndroidTarget( private fun AbstractAndroidProjectHandler.createAndroidUsageContexts( variant: T, compilation: KotlinCompilation<*>, - flavorNames: List, - buildTypeName: String + artifactClassifier: String? ): Set { - val artifactClassifier = buildTypeName.takeIf { it != "release" && publishLibraryVariantsGroupedByFlavor } - - val sourcesJarArtifact = sourcesJarArtifact( - compilation, compilation.disambiguateName(""), - lowerSpinalCaseName(compilation.target.name, *flavorNames.toTypedArray(), buildTypeName.takeIf { it != "release" }), - classifierPrefix = artifactClassifier - ) - val variantName = getVariantName(variant) val outputTask = getLibraryOutputTask(variant) ?: return emptySet() val artifact = run { @@ -318,7 +339,6 @@ open class KotlinAndroidTarget( compilation, project.usageByName(usageName), dependencyConfigurationName, - sourcesJarArtifact, overrideConfigurationArtifacts = setOf(artifact) ) } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinVariants.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinVariants.kt index 8387de3caae..ecdcb166582 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinVariants.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinVariants.kt @@ -6,10 +6,7 @@ package org.jetbrains.kotlin.gradle.plugin.mpp import org.gradle.api.Project -import org.gradle.api.artifacts.ExcludeRule -import org.gradle.api.artifacts.ModuleDependency -import org.gradle.api.artifacts.ModuleIdentifier -import org.gradle.api.artifacts.ModuleVersionIdentifier +import org.gradle.api.artifacts.* import org.gradle.api.component.ComponentWithCoordinates import org.gradle.api.component.ComponentWithVariants import org.gradle.api.internal.component.SoftwareComponentInternal @@ -18,8 +15,8 @@ import org.gradle.api.publish.maven.MavenPublication import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation import org.jetbrains.kotlin.gradle.plugin.KotlinTarget import org.jetbrains.kotlin.gradle.plugin.KotlinTargetComponent +import org.jetbrains.kotlin.gradle.utils.dashSeparatedName import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName -import org.jetbrains.kotlin.gradle.utils.lowerSpinalCaseName internal interface KotlinTargetComponentWithPublication : KotlinTargetComponent { // This property is declared in the separate parent type to allow the usages to reference it without forcing the subtypes to load, @@ -35,7 +32,7 @@ internal fun getCoordinatesFromPublicationDelegateAndProject( object : ModuleVersionIdentifier { private val moduleName: String get() = - publication?.artifactId ?: lowerSpinalCaseName(project.name, target?.name) + publication?.artifactId ?: dashSeparatedName(project.name, target?.name?.toLowerCase()) private val moduleGroup: String get() = @@ -74,10 +71,13 @@ open class KotlinVariant( override val publishable: Boolean get() = target.publishable + override var sourcesArtifacts: Set = emptySet() + internal set + internal var defaultArtifactIdSuffix: String? = null override val defaultArtifactId: String - get() = lowerSpinalCaseName(target.project.name, target.targetName, defaultArtifactIdSuffix) + get() = dashSeparatedName(target.project.name, target.targetName.toLowerCase(), defaultArtifactIdSuffix) override var publicationDelegate: MavenPublication? = null } @@ -126,8 +126,9 @@ class KotlinVariantWithMetadataDependency( class JointAndroidKotlinTargetComponent( override val target: KotlinAndroidTarget, private val nestedVariants: Set, - val flavorNames: List -) : KotlinTargetComponentWithCoordinatesAndPublication, SoftwareComponentInternal { + val flavorNames: List, + override val sourcesArtifacts: Set + ) : KotlinTargetComponentWithCoordinatesAndPublication, SoftwareComponentInternal { override fun getUsages(): Set = nestedVariants.flatMap { it.usages }.toSet() @@ -137,7 +138,11 @@ class JointAndroidKotlinTargetComponent( get() = target.publishable override val defaultArtifactId: String = - lowerSpinalCaseName(target.project.name, target.targetName, *flavorNames.toTypedArray()) + dashSeparatedName( + target.project.name, + target.targetName.toLowerCase(), + *flavorNames.map { it.toLowerCase() }.toTypedArray() + ) override var publicationDelegate: MavenPublication? = null } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/utils/stringUtils.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/utils/stringUtils.kt index b55535d582d..6da109d68af 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/utils/stringUtils.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/utils/stringUtils.kt @@ -14,12 +14,9 @@ internal fun lowerCamelCaseName(vararg nameParts: String?): String { ) } -internal fun lowerSpinalCaseName(nameParts: Iterable) = lowerSpinalCaseName(*nameParts.toList().toTypedArray()) +internal fun dashSeparatedName(nameParts: Iterable) = dashSeparatedName(*nameParts.toList().toTypedArray()) -internal fun lowerSpinalCaseName(vararg nameParts: String?): String { +internal fun dashSeparatedName(vararg nameParts: String?): String { val nonEmptyParts = nameParts.mapNotNull { it?.takeIf(String::isNotEmpty) } - return nonEmptyParts.joinToString( - separator = "-", - transform = String::toLowerCase - ) + return nonEmptyParts.joinToString(separator = "-") } \ No newline at end of file