Always rewrite dependencies in POMs when publishing an MPP (KT-28482)

As non-Gradle consumers and Gradle consumers with metadata disabled
cannot read Gradle metadata, in POMs (the only source of dependencies
for the consumers mentioned above), we should publish the dependencies
on modules with metadata as the target artifact IDs rather than the root
MPP module ID (e.g. 'foo-jvm' rather than 'foo').

To do that, we rewrite the POMs of the publications even when Gradle
metadata is enabled. Note: in the POMs, a project dependency is already
written in the form of the artifact ID of the root Kotlin software
component, so it complicates the dependencies rewriting a little.

To rewrite third-party dependencies, we detect dependencies that
resolved to no artifact and have a single child in the Gradle
dependencies graph of the resolved configuration – this is what a
dependency looks like which was redirected to another module via
'available-at'.

Issue #KT-28482 Fixed
This commit is contained in:
Sergey Igushkin
2019-01-10 22:17:50 +03:00
parent 2fd524f1c0
commit f9c45c30a2
6 changed files with 250 additions and 77 deletions
@@ -10,7 +10,10 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.NativeOutputKind
import org.jetbrains.kotlin.gradle.plugin.mpp.UnusedSourceSetsChecker import org.jetbrains.kotlin.gradle.plugin.mpp.UnusedSourceSetsChecker
import org.jetbrains.kotlin.gradle.plugin.sources.METADATA_CONFIGURATION_NAME_SUFFIX import org.jetbrains.kotlin.gradle.plugin.sources.METADATA_CONFIGURATION_NAME_SUFFIX
import org.jetbrains.kotlin.gradle.plugin.sources.SourceSetConsistencyChecks import org.jetbrains.kotlin.gradle.plugin.sources.SourceSetConsistencyChecks
import org.jetbrains.kotlin.gradle.util.* import org.jetbrains.kotlin.gradle.util.checkBytecodeContains
import org.jetbrains.kotlin.gradle.util.isWindows
import org.jetbrains.kotlin.gradle.util.modify
import org.jetbrains.kotlin.gradle.util.testResolveAllConfigurations
import org.jetbrains.kotlin.konan.target.CompilerOutputKind import org.jetbrains.kotlin.konan.target.CompilerOutputKind
import org.jetbrains.kotlin.konan.target.HostManager import org.jetbrains.kotlin.konan.target.HostManager
import org.junit.Assert import org.junit.Assert
@@ -603,7 +606,7 @@ class NewMultiplatformIT : BaseGradleIT() {
fun testPublishWithoutGradleMetadata() { fun testPublishWithoutGradleMetadata() {
val libProject = Project("sample-lib", gradleVersion, "new-mpp-lib-and-app") val libProject = Project("sample-lib", gradleVersion, "new-mpp-lib-and-app")
with (libProject) { with(libProject) {
setupWorkingDir() setupWorkingDir()
projectDir.resolve("settings.gradle").modify { it.replace("enableFeaturePreview", "// enableFeaturePreview") } projectDir.resolve("settings.gradle").modify { it.replace("enableFeaturePreview", "// enableFeaturePreview") }
@@ -1160,8 +1163,34 @@ class NewMultiplatformIT : BaseGradleIT() {
} }
@Test @Test
fun testPublishMultimoduleProjectWithNoMetadata() { fun testPublishMultimoduleProjectWithNoMetadata() = doTestPublishMultimoduleProject(withMetadata = false)
val libProject = Project("sample-lib", gradleVersion, "new-mpp-lib-and-app")
@Test
fun testPublishMultimoduleProjectWithMetadata() = doTestPublishMultimoduleProject(withMetadata = true)
private fun doTestPublishMultimoduleProject(withMetadata: Boolean) {
val libProject = Project("sample-lib", gradleVersion, "new-mpp-lib-and-app").apply {
if (withMetadata) {
setupWorkingDir()
// Make another publication that will be consumed as a module with metadata, see a similar conditional block below:
projectDir.resolve("settings.gradle").writeText("rootProject.name = 'external'; enableFeaturePreview 'GRADLE_METADATA'")
gradleBuildScript().appendText(
"\n" + """
group = "com.external.dependency"
version = "1.2.3"
publishing {
repositories {
maven { url "file://${'$'}{rootProject.projectDir.absolutePath.replace('\\', '/')}/repo" }
}
}
""".trimIndent()
)
build("publish") {
assertSuccessful()
gradleBuildScript().appendText("\ngroup = 'com.example'; version = '1.0'")
}
}
}
val appProject = Project("sample-app", gradleVersion, "new-mpp-lib-and-app") val appProject = Project("sample-app", gradleVersion, "new-mpp-lib-and-app")
with(libProject) { with(libProject) {
@@ -1169,7 +1198,7 @@ class NewMultiplatformIT : BaseGradleIT() {
appProject.setupWorkingDir() appProject.setupWorkingDir()
appProject.projectDir.copyRecursively(projectDir.resolve("sample-app")) appProject.projectDir.copyRecursively(projectDir.resolve("sample-app"))
projectDir.resolve("settings.gradle").writeText("include 'sample-app'") // also disables feature preview 'GRADLE_METADATA' gradleSettingsScript().writeText("include 'sample-app'") // disables feature preview 'GRADLE_METADATA', resets rootProject name
gradleBuildScript("sample-app").modify { gradleBuildScript("sample-app").modify {
it.replace("'com.example:sample-lib:1.0'", "project(':')") + "\n" + """ it.replace("'com.example:sample-lib:1.0'", "project(':')") + "\n" + """
apply plugin: 'maven-publish' apply plugin: 'maven-publish'
@@ -1182,6 +1211,20 @@ class NewMultiplatformIT : BaseGradleIT() {
} }
""".trimIndent() """.trimIndent()
} }
if (withMetadata) {
gradleSettingsScript().appendText("\nenableFeaturePreview(\"GRADLE_METADATA\")")
// Add a dependency that is resolved with metadata:
gradleBuildScript("sample-app").appendText(
"\n" + """
repositories {
maven { url "file://${'$'}{rootProject.projectDir.absolutePath.replace('\\', '/')}/repo" }
}
dependencies {
commonMainApi 'com.external.dependency:external:1.2.3'
}
""".trimIndent()
)
}
gradleBuildScript().appendText("\n" + """ gradleBuildScript().appendText("\n" + """
publishing { publishing {
@@ -1195,7 +1238,7 @@ class NewMultiplatformIT : BaseGradleIT() {
} }
""".trimIndent()) """.trimIndent())
build("publish") { build("clean", "publish") {
assertSuccessful() assertSuccessful()
assertFileContains( assertFileContains(
"repo/com/exampleapp/sample-app-nodejs/1.0/sample-app-nodejs-1.0.pom", "repo/com/exampleapp/sample-app-nodejs/1.0/sample-app-nodejs-1.0.pom",
@@ -1209,6 +1252,22 @@ class NewMultiplatformIT : BaseGradleIT() {
"<artifactId>bar</artifactId>", "<artifactId>bar</artifactId>",
"<version>42</version>" "<version>42</version>"
) )
if (withMetadata) {
// Check that the external dependency that was resolved with metadata is written to the POM as the artifactId it
// resolved to:
assertFileContains(
"repo/com/exampleapp/sample-app-jvm8/1.0/sample-app-jvm8-1.0.pom",
"<groupId>com.external.dependency</groupId>",
"<artifactId>external-jvm6</artifactId>",
"<version>1.2.3</version>"
)
// Check that, despite the rewritten POM, the module metadata contains the original dependency:
val moduleMetadata = projectDir.resolve("repo/com/exampleapp/sample-app-jvm8/1.0/sample-app-jvm8-1.0.module").readText()
.replace("\\s+".toRegex(), "").replace("\n", "")
assertTrue { "\"group\":\"com.example\",\"module\":\"sample-lib\"" in moduleMetadata }
assertTrue { "\"group\":\"com.external.dependency\",\"module\":\"external\"" in moduleMetadata }
}
assertFileExists("repo/foo/bar/42/bar-42.jar") assertFileExists("repo/foo/bar/42/bar-42.jar")
} }
} }
@@ -1285,10 +1344,10 @@ class NewMultiplatformIT : BaseGradleIT() {
assertFileContains("build/classes/kotlin/nodeJs/main/sample-lib.js", "Override") assertFileContains("build/classes/kotlin/nodeJs/main/sample-lib.js", "Override")
val (compilerPluginArgsBySourceSet, compilerPluginClasspathBySourceSet) = val (compilerPluginArgsBySourceSet, compilerPluginClasspathBySourceSet) =
listOf(compilerPluginArgsRegex, compilerPluginClasspathRegex) listOf(compilerPluginArgsRegex, compilerPluginClasspathRegex)
.map { marker -> .map { marker ->
marker.findAll(output).associate { it.groupValues[1] to it.groupValues[2] } marker.findAll(output).associate { it.groupValues[1] to it.groupValues[2] }
} }
// TODO once Kotlin/Native properly supports compiler plugins, expand this to all source sets: // TODO once Kotlin/Native properly supports compiler plugins, expand this to all source sets:
listOf("commonMain", "commonTest", "jvm6Main", "jvm6Test", "nodeJsMain", "nodeJsTest").forEach { listOf("commonMain", "commonTest", "jvm6Main", "jvm6Test", "nodeJsMain", "nodeJsTest").forEach {
@@ -12,9 +12,8 @@ import org.gradle.util.ConfigureUtil
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
import org.jetbrains.kotlin.gradle.plugin.KotlinTargetPreset import org.jetbrains.kotlin.gradle.plugin.KotlinTargetPreset
import org.jetbrains.kotlin.gradle.plugin.KotlinTargetsContainerWithPresets import org.jetbrains.kotlin.gradle.plugin.KotlinTargetsContainerWithPresets
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinCommonCompilation import org.jetbrains.kotlin.gradle.plugin.mpp.*
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinMultiplatformPlugin import org.jetbrains.kotlin.gradle.utils.isGradleVersionAtLeast
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinOnlyTarget
open class KotlinMultiplatformExtension : KotlinProjectExtension(), KotlinTargetContainerWithPresetFunctions { open class KotlinMultiplatformExtension : KotlinProjectExtension(), KotlinTargetContainerWithPresetFunctions {
override lateinit var presets: NamedDomainObjectCollection<KotlinTargetPreset<*>> override lateinit var presets: NamedDomainObjectCollection<KotlinTargetPreset<*>>
@@ -44,6 +43,14 @@ open class KotlinMultiplatformExtension : KotlinProjectExtension(), KotlinTarget
fun targetFromPreset(preset: KotlinTargetPreset<*>) = targetFromPreset(preset, preset.name) { } fun targetFromPreset(preset: KotlinTargetPreset<*>) = targetFromPreset(preset, preset.name) { }
fun targetFromPreset(preset: KotlinTargetPreset<*>, name: String) = targetFromPreset(preset, name) { } fun targetFromPreset(preset: KotlinTargetPreset<*>, name: String) = targetFromPreset(preset, name) { }
fun targetFromPreset(preset: KotlinTargetPreset<*>, configure: Closure<*>) = targetFromPreset(preset, preset.name, configure) fun targetFromPreset(preset: KotlinTargetPreset<*>, configure: Closure<*>) = targetFromPreset(preset, preset.name, configure)
internal val rootSoftwareComponent: KotlinSoftwareComponent by lazy {
if (isGradleVersionAtLeast(4, 7)) {
KotlinSoftwareComponentWithCoordinatesAndPublication("kotlin", targets)
} else {
KotlinSoftwareComponent("kotlin", targets)
}
}
} }
internal fun KotlinTarget.isProducedFromPreset(kotlinTargetPreset: KotlinTargetPreset<*>): Boolean = internal fun KotlinTarget.isProducedFromPreset(kotlinTargetPreset: KotlinTargetPreset<*>): Boolean =
@@ -6,8 +6,12 @@
package org.jetbrains.kotlin.gradle.plugin.mpp package org.jetbrains.kotlin.gradle.plugin.mpp
import groovy.lang.Closure import groovy.lang.Closure
import groovy.util.Node
import groovy.util.NodeList
import org.gradle.api.Plugin import org.gradle.api.Plugin
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.XmlProvider
import org.gradle.api.artifacts.ModuleDependency
import org.gradle.api.attributes.Attribute import org.gradle.api.attributes.Attribute
import org.gradle.api.attributes.AttributeContainer import org.gradle.api.attributes.AttributeContainer
import org.gradle.api.internal.FeaturePreviews import org.gradle.api.internal.FeaturePreviews
@@ -156,7 +160,7 @@ class KotlinMultiplatformPlugin(
} }
val targets = project.multiplatformExtension!!.targets val targets = project.multiplatformExtension!!.targets
val kotlinSoftwareComponent = KotlinSoftwareComponent("kotlin", targets) val kotlinSoftwareComponent = project.multiplatformExtension!!.rootSoftwareComponent
project.extensions.configure(PublishingExtension::class.java) { publishing -> project.extensions.configure(PublishingExtension::class.java) { publishing ->
@@ -203,6 +207,8 @@ class KotlinMultiplatformPlugin(
} }
(this as MavenPublicationInternal).publishWithOriginalFileName() (this as MavenPublicationInternal).publishWithOriginalFileName()
artifactId = variant.defaultArtifactId artifactId = variant.defaultArtifactId
pom.withXml { xml -> project.rewritePomMppDependenciesToActualTargetModules(xml, variant) }
} }
(variant as? KotlinTargetComponentWithPublication)?.publicationDelegate = variantPublication (variant as? KotlinTargetComponentWithPublication)?.publicationDelegate = variantPublication
@@ -308,3 +314,62 @@ internal fun compilationsBySourceSet(project: Project): Map<KotlinSourceSet, Set
} }
} }
} }
private fun Project.rewritePomMppDependenciesToActualTargetModules(
pomXml: XmlProvider,
component: KotlinTargetComponent
) {
if (component !is SoftwareComponentInternal)
return
val dependenciesNodeList = pomXml.asNode().get("dependencies") as NodeList
val dependencyNodes = dependenciesNodeList.filterIsInstance<Node>().flatMap {
(it.get("dependency") as? NodeList).orEmpty()
}.filterIsInstance<Node>()
val dependencyByNode = mutableMapOf<Node, ModuleDependency>()
// Collect all the dependencies from the nodes:
val dependencies = dependencyNodes.map { dependencyNode ->
fun Node.getSingleChildValueOrNull(childName: String): String? =
((get(childName) as NodeList?)?.singleOrNull() as Node?)?.text()
val groupId = dependencyNode.getSingleChildValueOrNull("groupId")
val artifactId = dependencyNode.getSingleChildValueOrNull("artifactId")
val version = dependencyNode.getSingleChildValueOrNull("version")
(project.dependencies.module("$groupId:$artifactId:$version") as ModuleDependency)
.also { dependencyByNode[dependencyNode] = it }
}.toSet()
// Get the dependencies mapping according to the component's UsageContexts:
val resultDependenciesForEachUsageContext =
component.usages.mapNotNull { usage ->
if (usage is KotlinUsageContext)
rewriteDependenciesToActualModuleDependencies(usage, dependencies)
// We are only interested in dependencies that are mapped to some other dependencies:
.filter { (from, to) -> Triple(from.group, from.name, from.version) != Triple(to.group, to.name, to.version) }
else null
}
// Rewrite the dependency nodes according to the mapping:
dependencyNodes.forEach { dependencyNode ->
val moduleDependency = dependencyByNode[dependencyNode]
val mapDependencyTo = resultDependenciesForEachUsageContext.find { moduleDependency in it }?.get(moduleDependency)
if (mapDependencyTo != null) {
fun Node.setChildNodeByName(name: String, value: String?) {
val childNode: Node? = (get(name) as NodeList?)?.firstOrNull() as Node?
if (value != null) {
(childNode ?: appendNode(name)).setValue(value)
} else {
childNode?.let { remove(it) }
}
}
dependencyNode.setChildNodeByName("groupId", mapDependencyTo.group)
dependencyNode.setChildNodeByName("artifactId", mapDependencyTo.name)
dependencyNode.setChildNodeByName("version", mapDependencyTo.version)
}
}
}
@@ -10,15 +10,17 @@ import org.gradle.api.artifacts.*
import org.gradle.api.attributes.AttributeContainer import org.gradle.api.attributes.AttributeContainer
import org.gradle.api.attributes.Usage 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.ComponentWithVariants import org.gradle.api.component.ComponentWithVariants
import org.gradle.api.internal.component.SoftwareComponentInternal import org.gradle.api.internal.component.SoftwareComponentInternal
import org.gradle.api.internal.component.UsageContext import org.gradle.api.internal.component.UsageContext
import org.gradle.api.publish.maven.MavenPublication
import org.jetbrains.kotlin.gradle.plugin.* import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.utils.ifEmpty import org.jetbrains.kotlin.utils.ifEmpty
class KotlinSoftwareComponent( open class KotlinSoftwareComponent(
private val name: String, private val name: String,
private val kotlinTargets: Iterable<KotlinTarget> protected val kotlinTargets: Iterable<KotlinTarget>
) : SoftwareComponentInternal, ComponentWithVariants { ) : SoftwareComponentInternal, ComponentWithVariants {
override fun getUsages(): Set<UsageContext> = emptySet() override fun getUsages(): Set<UsageContext> = emptySet()
@@ -27,6 +29,18 @@ class KotlinSoftwareComponent(
kotlinTargets.flatMap { it.components }.toSet() kotlinTargets.flatMap { it.components }.toSet()
override fun getName(): String = name override fun getName(): String = name
// This property is declared in the parent type to allow the usages to reference it without forcing the subtypes to load,
// which is needed for compatibility with older Gradle versions
var publicationDelegate: MavenPublication? = null
}
class KotlinSoftwareComponentWithCoordinatesAndPublication(name: String, kotlinTargets: Iterable<KotlinTarget>) :
KotlinSoftwareComponent(name, kotlinTargets), ComponentWithCoordinates {
override fun getCoordinates(): ModuleVersionIdentifier = getCoordinatesFromPublicationDelegateAndProject(
publicationDelegate, kotlinTargets.first().project, null
)
} }
// At the moment all KN artifacts have JAVA_API usage. // At the moment all KN artifacts have JAVA_API usage.
@@ -45,7 +59,6 @@ class DefaultKotlinUsageContext(
override val compilation: KotlinCompilation<*>, override val compilation: KotlinCompilation<*>,
private val usage: Usage, private val usage: Usage,
override val dependencyConfigurationName: String, override val dependencyConfigurationName: String,
private val publishWithGradleMetadata: Boolean,
override val sourcesArtifact: PublishArtifact? = null, override val sourcesArtifact: PublishArtifact? = null,
private val overrideConfigurationArtifacts: Set<PublishArtifact>? = null private val overrideConfigurationArtifacts: Set<PublishArtifact>? = null
) : KotlinUsageContext { ) : KotlinUsageContext {
@@ -65,10 +78,7 @@ class DefaultKotlinUsageContext(
get() = project.configurations.getByName(dependencyConfigurationName) get() = project.configurations.getByName(dependencyConfigurationName)
override fun getDependencies(): MutableSet<out ModuleDependency> = override fun getDependencies(): MutableSet<out ModuleDependency> =
if (publishWithGradleMetadata) configuration.incoming.dependencies.withType(ModuleDependency::class.java)
configuration.incoming.dependencies.withType(ModuleDependency::class.java)
else
rewriteMppDependenciesToTargetModuleDependencies(this, configuration).toMutableSet()
override fun getDependencyConstraints(): MutableSet<out DependencyConstraint> = override fun getDependencyConstraints(): MutableSet<out DependencyConstraint> =
configuration.incoming.dependencyConstraints configuration.incoming.dependencyConstraints
@@ -86,14 +96,14 @@ class DefaultKotlinUsageContext(
override fun getGlobalExcludes(): Set<ExcludeRule> = emptySet() override fun getGlobalExcludes(): Set<ExcludeRule> = emptySet()
} }
private fun rewriteMppDependenciesToTargetModuleDependencies( internal fun rewriteDependenciesToActualModuleDependencies(
usageContext: KotlinUsageContext, usageContext: KotlinUsageContext,
fromConfiguration: Configuration moduleDependencies: Set<ModuleDependency>
): Set<ModuleDependency> = with(usageContext.compilation.target.project) { ): Map<ModuleDependency, ModuleDependency> {
val compilation = usageContext.compilation val compilation = usageContext.compilation
val moduleDependencies = fromConfiguration.incoming.dependencies.withType(ModuleDependency::class.java).ifEmpty { return emptySet() } val project = compilation.target.project
val targetCompileDependenciesConfiguration = project.configurations.getByName( val targetDependenciesConfiguration = project.configurations.getByName(
when (compilation) { when (compilation) {
is KotlinJvmAndroidCompilation -> { is KotlinJvmAndroidCompilation -> {
// TODO handle Android configuration names in a general way once we drop AGP < 3.0.0 // TODO handle Android configuration names in a general way once we drop AGP < 3.0.0
@@ -112,52 +122,86 @@ private fun rewriteMppDependenciesToTargetModuleDependencies(
} }
) )
val resolvedCompileDependencies by lazy { val resolvedDependencies by lazy {
// don't resolve if no project dependencies on MPP projects are found // don't resolve if no project dependencies on MPP projects are found
targetCompileDependenciesConfiguration.resolvedConfiguration.lenientConfiguration.allModuleDependencies.associateBy { targetDependenciesConfiguration.resolvedConfiguration.lenientConfiguration.allModuleDependencies.associateBy {
Triple(it.moduleGroup, it.moduleName, it.moduleVersion) Triple(it.moduleGroup, it.moduleName, it.moduleVersion)
} }
} }
moduleDependencies.map { dependency -> val resolvedModulesByRootModuleCoordinates = targetDependenciesConfiguration
when (dependency) { .allDependencies.withType(ModuleDependency::class.java)
!is ProjectDependency -> dependency .associate { dependency ->
else -> { when (dependency) {
val dependencyProject = dependency.dependencyProject is ProjectDependency -> {
val dependencyProjectKotlinExtension = dependencyProject.multiplatformExtension val dependencyProject = dependency.dependencyProject
?: return@map dependency val dependencyProjectKotlinExtension = dependencyProject.multiplatformExtension
?: return@associate dependency to dependency
if (dependencyProjectKotlinExtension.isGradleMetadataAvailable) val resolved = resolvedDependencies[Triple(dependency.group, dependency.name, dependency.version)]
return@map dependency ?: return@associate dependency to dependency
val resolved = resolvedCompileDependencies[Triple(dependency.group, dependency.name, dependency.version)] val resolvedToConfiguration = resolved.configuration
?: return@map dependency val dependencyTargetComponent: KotlinTargetComponent = run {
dependencyProjectKotlinExtension.targets.forEach { target ->
val resolvedToConfiguration = resolved.configuration target.components.forEach { component ->
if (component.findUsageContext(resolvedToConfiguration) != null)
val dependencyTargetComponent: KotlinTargetComponent = run { return@run component
dependencyProjectKotlinExtension.targets.forEach { target -> }
target.components.forEach { component ->
if (component.findUsageContext(resolvedToConfiguration) != null)
return@run component
} }
// Failed to find a matching component:
return@associate dependency to dependency
} }
// Failed to find a matching component:
return@map dependency val targetModulePublication = (dependencyTargetComponent as? KotlinTargetComponentWithPublication)?.publicationDelegate
val rootModulePublication = dependencyProjectKotlinExtension.rootSoftwareComponent.publicationDelegate
// During Gradle POM generation, a project dependency is already written as the root module's coordinates. In the
// dependencies mapping, map the root module to the target's module:
val rootModule = project.dependencies.module(
listOf(
rootModulePublication?.groupId ?: dependency.group,
rootModulePublication?.artifactId ?: dependencyProject.name,
rootModulePublication?.version ?: dependency.version
).joinToString(":")
) as ModuleDependency
rootModule to project.dependencies.module(
listOf(
targetModulePublication?.groupId ?: dependency.group,
targetModulePublication?.artifactId ?: dependencyTargetComponent.defaultArtifactId,
targetModulePublication?.version ?: dependency.version
).joinToString(":")
) as ModuleDependency
} }
else -> {
val resolvedDependency = resolvedDependencies[Triple(dependency.group, dependency.name, dependency.version)]
?: return@associate dependency to dependency
val publicationDelegate = (dependencyTargetComponent as? KotlinTargetComponentWithPublication)?.publicationDelegate if (resolvedDependency.moduleArtifacts.isEmpty() && resolvedDependency.children.size == 1) {
// This is a dependency on a module that resolved to another module; map the original dependency to the target module
val targetModule = resolvedDependency.children.single()
dependency to project.dependencies.module(
listOf(
targetModule.moduleGroup,
targetModule.moduleName,
targetModule.moduleVersion
).joinToString(":")
) as ModuleDependency
dependencies.module( } else {
listOf( dependency to dependency
publicationDelegate?.groupId ?: dependency.group, }
publicationDelegate?.artifactId ?: dependencyTargetComponent.defaultArtifactId, }
publicationDelegate?.version ?: dependency.version
).joinToString(":")
) as ModuleDependency
} }
} }.mapKeys { (key, _) -> Triple(key.group, key.name, key.version) }
}.toSet()
return moduleDependencies.associate { dependency ->
val key = Triple(dependency.group, dependency.name, dependency.version)
val value = resolvedModulesByRootModuleCoordinates[key] ?: dependency
dependency to value
}
} }
internal fun KotlinTargetComponent.findUsageContext(configurationName: String): UsageContext? { internal fun KotlinTargetComponent.findUsageContext(configurationName: String): UsageContext? {
@@ -95,9 +95,6 @@ abstract class AbstractKotlinTarget(
componentName: String, componentName: String,
artifactNameAppendix: String artifactNameAppendix: String
): Set<DefaultKotlinUsageContext> { ): Set<DefaultKotlinUsageContext> {
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) val sourcesJarArtifact = sourcesJarArtifact(producingCompilation, componentName, artifactNameAppendix)
// Here, `JAVA_API` and `JAVA_RUNTIME_JARS` are used intentionally as Gradle needs this for // Here, `JAVA_API` and `JAVA_RUNTIME_JARS` are used intentionally as Gradle needs this for
@@ -111,7 +108,6 @@ abstract class AbstractKotlinTarget(
producingCompilation, producingCompilation,
project.usageByName(usageName), project.usageByName(usageName),
dependenciesConfigurationName, dependenciesConfigurationName,
publishWithKotlinMetadata,
sourcesJarArtifact sourcesJarArtifact
) )
} }
@@ -295,9 +291,6 @@ open class KotlinAndroidTarget(
classifierPrefix = artifactClassifier classifierPrefix = artifactClassifier
) )
val publishWithKotlinMetadata = (project.kotlinExtension as? KotlinMultiplatformExtension)?.isGradleMetadataAvailable
?: true // In non-MPP project, these usage contexts do not get published anyway
val variantName = getVariantName(variant) val variantName = getVariantName(variant)
val outputTask = getLibraryOutputTask(variant) ?: return emptySet() val outputTask = getLibraryOutputTask(variant) ?: return emptySet()
val artifact = run { val artifact = run {
@@ -325,7 +318,6 @@ open class KotlinAndroidTarget(
compilation, compilation,
project.usageByName(usageName), project.usageByName(usageName),
dependencyConfigurationName, dependencyConfigurationName,
publishWithKotlinMetadata,
sourcesJarArtifact, sourcesJarArtifact,
overrideConfigurationArtifacts = setOf(artifact) overrideConfigurationArtifacts = setOf(artifact)
) )
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.gradle.plugin.mpp package org.jetbrains.kotlin.gradle.plugin.mpp
import org.gradle.api.Project
import org.gradle.api.artifacts.ExcludeRule import org.gradle.api.artifacts.ExcludeRule
import org.gradle.api.artifacts.ModuleDependency import org.gradle.api.artifacts.ModuleDependency
import org.gradle.api.artifacts.ModuleIdentifier import org.gradle.api.artifacts.ModuleIdentifier
@@ -26,30 +27,35 @@ internal interface KotlinTargetComponentWithPublication : KotlinTargetComponent
var publicationDelegate: MavenPublication? var publicationDelegate: MavenPublication?
} }
private interface KotlinTargetComponentWithCoordinatesAndPublication : internal fun getCoordinatesFromPublicationDelegateAndProject(
KotlinTargetComponentWithPublication, publication: MavenPublication?,
ComponentWithCoordinates /* Gradle 4.7+ API, don't use with older versions */ project: Project,
{ target: KotlinTarget?
override fun getCoordinates() = object : ModuleVersionIdentifier { ): ModuleVersionIdentifier =
private val project get() = target.project object : ModuleVersionIdentifier {
private val moduleName: String private val moduleName: String
get() = get() =
publicationDelegate?.artifactId ?: "${project.name}-${target.name.toLowerCase()}" publication?.artifactId ?: lowerSpinalCaseName(project.name, target?.name)
private val moduleGroup: String private val moduleGroup: String
get() = get() =
publicationDelegate?.groupId ?: project.group.toString() publication?.groupId ?: project.group.toString()
override fun getGroup() = moduleGroup override fun getGroup() = moduleGroup
override fun getName() = moduleName override fun getName() = moduleName
override fun getVersion() = publicationDelegate?.version ?: project.version.toString() override fun getVersion() = publication?.version ?: project.version.toString()
override fun getModule(): ModuleIdentifier = object : ModuleIdentifier { override fun getModule(): ModuleIdentifier = object : ModuleIdentifier {
override fun getGroup(): String = moduleGroup override fun getGroup(): String = moduleGroup
override fun getName(): String = moduleName override fun getName(): String = moduleName
} }
} }
private interface KotlinTargetComponentWithCoordinatesAndPublication :
KotlinTargetComponentWithPublication,
ComponentWithCoordinates /* Gradle 4.7+ API, don't use with older versions */
{
override fun getCoordinates() = getCoordinatesFromPublicationDelegateAndProject(publicationDelegate, target.project, target)
} }
open class KotlinVariant( open class KotlinVariant(