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:
+69
-10
@@ -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 {
|
||||||
|
|||||||
+10
-3
@@ -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 =
|
||||||
|
|||||||
+66
-1
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+89
-45
@@ -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? {
|
||||||
|
|||||||
-8
@@ -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)
|
||||||
)
|
)
|
||||||
|
|||||||
+16
-10
@@ -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(
|
||||||
|
|||||||
Reference in New Issue
Block a user