Rewrite project dependencies when publishing a multimodule MPP
When a new MPP is published without Gradle metadata, the project dependencies should be replaced with specific module dependencies, e.g. `sample-lib` with `sample-lib-jvm`, because a consumer won't be able to resolve `sample-lib` in a variant-aware way.
This commit is contained in:
+55
@@ -774,4 +774,59 @@ class NewMultiplatformIT : BaseGradleIT() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testPublishMultimoduleProjectWithNoMetadata() {
|
||||
val libProject = Project("sample-lib", gradleVersion, "new-mpp-lib-and-app")
|
||||
val appProject = Project("sample-app", gradleVersion, "new-mpp-lib-and-app")
|
||||
|
||||
with(libProject) {
|
||||
setupWorkingDir()
|
||||
appProject.setupWorkingDir()
|
||||
appProject.projectDir.copyRecursively(projectDir.resolve("sample-app"))
|
||||
|
||||
projectDir.resolve("settings.gradle").writeText("include 'sample-app'") // also disables feature preview 'GRADLE_METADATA'
|
||||
gradleBuildScript("sample-app").modify {
|
||||
it.replace("'com.example:sample-lib:1.0'", "project(':')") + "\n" + """
|
||||
apply plugin: 'maven-publish'
|
||||
group = "com.exampleapp"
|
||||
version = "1.0"
|
||||
publishing {
|
||||
repositories {
|
||||
maven { url "file://${'$'}{rootProject.projectDir.absolutePath.replace('\\', '/')}/repo" }
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
}
|
||||
|
||||
gradleBuildScript().appendText("\n" + """
|
||||
publishing {
|
||||
publications {
|
||||
jvm6 {
|
||||
groupId = "foo"
|
||||
artifactId = "bar"
|
||||
version = "42"
|
||||
}
|
||||
}
|
||||
}
|
||||
""".trimIndent())
|
||||
|
||||
build("publish") {
|
||||
assertSuccessful()
|
||||
assertFileContains(
|
||||
"repo/com/exampleapp/sample-app-nodejs/1.0/sample-app-nodejs-1.0.pom",
|
||||
"<groupId>com.example</groupId>",
|
||||
"<artifactId>sample-lib-nodejs</artifactId>",
|
||||
"<version>1.0</version>"
|
||||
)
|
||||
assertFileContains(
|
||||
"repo/com/exampleapp/sample-app-jvm8/1.0/sample-app-jvm8-1.0.pom",
|
||||
"<groupId>foo</groupId>",
|
||||
"<artifactId>bar</artifactId>",
|
||||
"<version>42</version>"
|
||||
)
|
||||
assertFileExists("repo/foo/bar/42/bar-42.jar")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-2
@@ -135,7 +135,7 @@ class KotlinMultiplatformPlugin(
|
||||
}
|
||||
}
|
||||
(this as MavenPublicationInternal).publishWithOriginalFileName()
|
||||
artifactId = "${project.name}-${variant.target.name.toLowerCase()}"
|
||||
artifactId = variant.target.defaultArtifactId
|
||||
groupId = project.group.toString()
|
||||
version = project.version.toString()
|
||||
}
|
||||
@@ -234,4 +234,6 @@ class KotlinMultiplatformPlugin(
|
||||
companion object {
|
||||
const val METADATA_TARGET_NAME = "metadata"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal val KotlinTarget.defaultArtifactId get() = "${project.name}-${name.toLowerCase()}"
|
||||
+72
-7
@@ -6,26 +6,25 @@
|
||||
package org.jetbrains.kotlin.gradle.plugin.mpp
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.artifacts.Configuration
|
||||
import org.gradle.api.artifacts.DependencyConstraint
|
||||
import org.gradle.api.artifacts.ModuleDependency
|
||||
import org.gradle.api.artifacts.PublishArtifact
|
||||
import org.gradle.api.artifacts.*
|
||||
import org.gradle.api.attributes.AttributeContainer
|
||||
import org.gradle.api.attributes.Usage
|
||||
import org.gradle.api.capabilities.Capability
|
||||
import org.gradle.api.component.ComponentWithVariants
|
||||
import org.gradle.api.internal.component.SoftwareComponentInternal
|
||||
import org.gradle.api.internal.component.UsageContext
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilationToRunnableFiles
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinTargetComponent
|
||||
import org.jetbrains.kotlin.gradle.plugin.usageByName
|
||||
import org.jetbrains.kotlin.utils.ifEmpty
|
||||
|
||||
class KotlinSoftwareComponent(
|
||||
private val project: Project,
|
||||
private val name: String,
|
||||
private val kotlinTargets: Iterable<KotlinTarget>
|
||||
) : SoftwareComponentInternal, ComponentWithVariants {
|
||||
|
||||
|
||||
override fun getUsages(): Set<UsageContext> = emptySet()
|
||||
|
||||
override fun getVariants(): Set<KotlinTargetComponent> =
|
||||
@@ -60,7 +59,10 @@ internal class KotlinPlatformUsageContext(
|
||||
get() = project.configurations.getByName(dependencyConfigurationName)
|
||||
|
||||
override fun getDependencies(): MutableSet<out ModuleDependency> =
|
||||
configuration.incoming.dependencies.withType(ModuleDependency::class.java)
|
||||
if (publishWithGradleMetadata)
|
||||
configuration.incoming.dependencies.withType(ModuleDependency::class.java)
|
||||
else
|
||||
rewriteMppDependenciesToTargetModuleDependencies(this, configuration).toMutableSet()
|
||||
|
||||
override fun getDependencyConstraints(): MutableSet<out DependencyConstraint> =
|
||||
configuration.incoming.dependencyConstraints
|
||||
@@ -77,3 +79,66 @@ internal class KotlinPlatformUsageContext(
|
||||
// FIXME this is a stub for a function that is not present in the Gradle API that we compile against
|
||||
fun getGlobalExcludes(): Set<Any> = emptySet()
|
||||
}
|
||||
|
||||
private fun rewriteMppDependenciesToTargetModuleDependencies(
|
||||
context: KotlinPlatformUsageContext,
|
||||
configuration: Configuration
|
||||
): Set<ModuleDependency> = with(context.kotlinTarget.project) {
|
||||
val target = context.kotlinTarget
|
||||
val moduleDependencies = configuration.incoming.dependencies.withType(ModuleDependency::class.java).ifEmpty { return emptySet() }
|
||||
|
||||
val targetMainCompilation = target.compilations.findByName(KotlinCompilation.MAIN_COMPILATION_NAME)
|
||||
?: return moduleDependencies // Android is not yet supported
|
||||
|
||||
val targetCompileDependenciesConfiguration = project.configurations.getByName(
|
||||
when (context.dependencyConfigurationName) {
|
||||
target.apiElementsConfigurationName -> targetMainCompilation.compileDependencyConfigurationName
|
||||
target.runtimeElementsConfigurationName ->
|
||||
(targetMainCompilation as KotlinCompilationToRunnableFiles).runtimeDependencyConfigurationName
|
||||
else -> error("unexpected configuration")
|
||||
}
|
||||
)
|
||||
|
||||
val resolvedCompileDependencies by lazy { // don't resolve if no project dependencies on MPP projects are found
|
||||
targetCompileDependenciesConfiguration.resolvedConfiguration.lenientConfiguration.allModuleDependencies.associateBy {
|
||||
Triple(it.moduleGroup, it.moduleName, it.moduleVersion)
|
||||
}
|
||||
}
|
||||
|
||||
moduleDependencies.map { dependency ->
|
||||
when (dependency) {
|
||||
!is ProjectDependency -> dependency
|
||||
else -> {
|
||||
val dependencyProject = dependency.dependencyProject
|
||||
val dependencyProjectKotlinExtension = dependencyProject.multiplatformExtension
|
||||
?: return@map dependency
|
||||
|
||||
if (dependencyProjectKotlinExtension.isGradleMetadataAvailable)
|
||||
return@map dependency
|
||||
|
||||
val resolved = resolvedCompileDependencies[Triple(dependency.group, dependency.name, dependency.version)]
|
||||
?: return@map dependency
|
||||
|
||||
val resolvedToConfiguration = resolved.configuration
|
||||
|
||||
val dependencyTarget = dependencyProjectKotlinExtension.targets.singleOrNull {
|
||||
resolvedToConfiguration in setOf(
|
||||
it.apiElementsConfigurationName,
|
||||
it.runtimeElementsConfigurationName,
|
||||
it.defaultConfigurationName
|
||||
)
|
||||
} ?: return@map dependency
|
||||
|
||||
val publicationDelegate = (dependencyTarget.component as KotlinVariant).publicationDelegate
|
||||
|
||||
dependencies.module(
|
||||
listOf(
|
||||
publicationDelegate?.groupId ?: dependency.group,
|
||||
publicationDelegate?.artifactId ?: dependencyTarget.defaultArtifactId,
|
||||
publicationDelegate?.version ?: dependency.version
|
||||
).joinToString(":")
|
||||
) as ModuleDependency
|
||||
}
|
||||
}
|
||||
}.toSet()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user