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 50895027e92..4847a09f501 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 @@ -11,6 +11,7 @@ import org.gradle.api.Project import org.gradle.api.attributes.Attribute import org.gradle.api.attributes.AttributeContainer import org.gradle.api.internal.FeaturePreviews +import org.gradle.api.internal.component.SoftwareComponentInternal import org.gradle.api.internal.file.FileResolver import org.gradle.api.internal.plugins.DslObject import org.gradle.api.plugins.JavaBasePlugin @@ -29,6 +30,7 @@ import org.jetbrains.kotlin.gradle.dsl.kotlinExtension import org.jetbrains.kotlin.gradle.plugin.* import org.jetbrains.kotlin.gradle.plugin.sources.DefaultLanguageSettingsBuilder import org.jetbrains.kotlin.gradle.utils.SingleWarningPerBuild +import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName import org.jetbrains.kotlin.konan.target.HostManager import org.jetbrains.kotlin.konan.target.presetName @@ -87,8 +89,6 @@ class KotlinMultiplatformPlugin( KotlinMetadataTargetPreset(project, instantiator, fileResolver, kotlinPluginVersion), METADATA_TARGET_NAME ) - configureSourceJars(project) - configurePublishingWithMavenPublish(project) // propagate compiler plugin options to the source set language settings @@ -190,8 +190,9 @@ class KotlinMultiplatformPlugin( // do this in whenEvaluated since older Gradle versions seem to check the files in the variant eagerly: project.whenEvaluated { from(variant) - (project.tasks.findByName(sourcesJarTaskName) as Jar?)?.let { sourcesJar -> - artifact(sourcesJar) + if (variant is SoftwareComponentInternal) { + variant.usages.filterIsInstance().map { it.sourcesArtifact }.distinct() + .forEach { artifact(it) } } } (this as MavenPublicationInternal).publishWithOriginalFileName() @@ -200,31 +201,7 @@ class KotlinMultiplatformPlugin( (variant as? KotlinTargetComponentWithPublication)?.publicationDelegate = variantPublication publicationConfigureActions.all { it.execute(variantPublication) } - } - } - - private val KotlinTarget.sourcesJarTaskName get() = disambiguateName("sourcesJar") - - private fun configureSourceJars(project: Project) = with(project.kotlinExtension as KotlinMultiplatformExtension) { - targets.all { target -> - val mainCompilation = target.compilations.findByName(KotlinCompilation.MAIN_COMPILATION_NAME) - // If a target has no `main` compilation (e.g. Android), don't create the source JAR - ?: return@all - - val sourcesJar = project.tasks.create(target.sourcesJarTaskName, Jar::class.java) { sourcesJar -> - sourcesJar.appendix = target.targetName.toLowerCase() - sourcesJar.classifier = "sources" } - - project.afterEvaluate { _ -> - val compiledSourceSets = mainCompilation.allKotlinSourceSets - compiledSourceSets.forEach { sourceSet -> - sourcesJar.from(sourceSet.kotlin) { copySpec -> - copySpec.into(sourceSet.name) - } - } - } - } } private fun configureSourceSets(project: Project) = with(project.kotlinExtension as KotlinMultiplatformExtension) { @@ -293,6 +270,28 @@ class KotlinMultiplatformPlugin( } } +internal fun sourcesJarTask(compilation: KotlinCompilation<*>, componentName: String?, artifactNameAppendix: String): Jar { + val project = compilation.target.project + val taskName = lowerCamelCaseName(componentName, "sourcesJar") + + (project.tasks.findByName(taskName) as? Jar)?.let { return it } + + val result = project.tasks.create(taskName, Jar::class.java) { sourcesJar -> + sourcesJar.appendix = artifactNameAppendix + sourcesJar.classifier = "sources" + } + + project.whenEvaluated { + compilation.allKotlinSourceSets.forEach { sourceSet -> + result.from(sourceSet.kotlin) { copySpec -> + copySpec.into(sourceSet.name) + } + } + } + + return result +} + internal fun compilationsBySourceSet(project: Project): Map>> = HashMap>>().also { result -> for (target in project.multiplatformExtension!!.targets) { 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 c39c9e56fba..3f031ab27b7 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 @@ -38,6 +38,7 @@ object NativeUsage { interface KotlinUsageContext : UsageContext { val compilation: KotlinCompilation<*> val dependencyConfigurationName: String + val sourcesArtifact: PublishArtifact? } class DefaultKotlinUsageContext( @@ -45,6 +46,7 @@ class DefaultKotlinUsageContext( private val usage: Usage, override val dependencyConfigurationName: String, private val publishWithGradleMetadata: Boolean, + override val sourcesArtifact: PublishArtifact? = null, private val overrideConfigurationArtifacts: Set? = null ) : KotlinUsageContext { @@ -73,7 +75,7 @@ class DefaultKotlinUsageContext( override fun getArtifacts(): Set = overrideConfigurationArtifacts ?: - // TODO Gradle Java plugin does that in a different way; check whether we can improve this + // TODO Gradle Java plugin does that in a different way; check whether we can improve this configuration.artifacts override fun getAttributes(): AttributeContainer = diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinTargetPresets.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinTargetPresets.kt index 58157efb262..2f5d2e97158 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinTargetPresets.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinTargetPresets.kt @@ -55,6 +55,9 @@ abstract class KotlinOnlyTargetPreset>( result.compilations.all { compilation -> buildCompilationProcessor(compilation).run() + if (compilation.name == KotlinCompilation.MAIN_COMPILATION_NAME) { + sourcesJarTask(compilation, result.targetName, result.targetName.toLowerCase()) + } } return result 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 21210c355ca..1581b1a0a1a 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 @@ -9,7 +9,9 @@ import org.gradle.api.Action import org.gradle.api.DomainObjectSet import org.gradle.api.NamedDomainObjectContainer import org.gradle.api.Project +import org.gradle.api.artifacts.ConfigurablePublishArtifact import org.gradle.api.artifacts.Dependency +import org.gradle.api.artifacts.PublishArtifact import org.gradle.api.attributes.Attribute import org.gradle.api.attributes.AttributeContainer import org.gradle.api.attributes.Usage.JAVA_API @@ -25,6 +27,7 @@ import org.jetbrains.kotlin.gradle.dsl.kotlinExtension import org.jetbrains.kotlin.gradle.plugin.* import org.jetbrains.kotlin.gradle.utils.isGradleVersionAtLeast import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName +import org.jetbrains.kotlin.gradle.utils.lowerSpinalCaseName import org.jetbrains.kotlin.konan.target.KonanTarget abstract class AbstractKotlinTarget( @@ -98,6 +101,8 @@ abstract class AbstractKotlinTarget( val publishWithKotlinMetadata = (project.kotlinExtension as? KotlinMultiplatformExtension)?.isGradleMetadataAvailable ?: true // In non-MPP project, these usage contexts do not get published anyway + 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. @@ -109,11 +114,36 @@ abstract class AbstractKotlinTarget( producingCompilation, project.usageByName(usageName), dependenciesConfigurationName, - publishWithKotlinMetadata - ) + publishWithKotlinMetadata, + sourcesJarArtifact + ) } } + protected fun sourcesJarArtifact( + producingCompilation: KotlinCompilation<*>, + componentName: String, + artifactNameAppendix: String, + classifierPrefix: String? = null + ): PublishArtifact { + val sourcesJarTask = sourcesJarTask(producingCompilation, componentName, artifactNameAppendix) + val sourceArtifactConfigurationName = producingCompilation.disambiguateName("sourceArtifacts") + val sourcesJarArtifact = producingCompilation.target.project.run { + (configurations.findByName(sourceArtifactConfigurationName) ?: run { + val configuration = configurations.create(sourceArtifactConfigurationName) { + it.isCanBeResolved = false + it.isCanBeConsumed = false + } + artifacts.add(sourceArtifactConfigurationName, sourcesJarTask) + configuration + }).artifacts.single().apply { + this as ConfigurablePublishArtifact + classifier = lowerSpinalCaseName(classifierPrefix, "sources") + } + } + return sourcesJarArtifact + } + @Suppress("UNCHECKED_CAST") internal val publicationConfigureActions = WrapUtil.toDomainObjectSet(Action::class.java) as DomainObjectSet>