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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-1
@@ -135,7 +135,7 @@ class KotlinMultiplatformPlugin(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
(this as MavenPublicationInternal).publishWithOriginalFileName()
|
(this as MavenPublicationInternal).publishWithOriginalFileName()
|
||||||
artifactId = "${project.name}-${variant.target.name.toLowerCase()}"
|
artifactId = variant.target.defaultArtifactId
|
||||||
groupId = project.group.toString()
|
groupId = project.group.toString()
|
||||||
version = project.version.toString()
|
version = project.version.toString()
|
||||||
}
|
}
|
||||||
@@ -235,3 +235,5 @@ class KotlinMultiplatformPlugin(
|
|||||||
const val METADATA_TARGET_NAME = "metadata"
|
const val METADATA_TARGET_NAME = "metadata"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal val KotlinTarget.defaultArtifactId get() = "${project.name}-${name.toLowerCase()}"
|
||||||
+71
-6
@@ -6,19 +6,18 @@
|
|||||||
package org.jetbrains.kotlin.gradle.plugin.mpp
|
package org.jetbrains.kotlin.gradle.plugin.mpp
|
||||||
|
|
||||||
import org.gradle.api.Project
|
import org.gradle.api.Project
|
||||||
import org.gradle.api.artifacts.Configuration
|
import org.gradle.api.artifacts.*
|
||||||
import org.gradle.api.artifacts.DependencyConstraint
|
|
||||||
import org.gradle.api.artifacts.ModuleDependency
|
|
||||||
import org.gradle.api.artifacts.PublishArtifact
|
|
||||||
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.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.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.KotlinTarget
|
||||||
import org.jetbrains.kotlin.gradle.plugin.KotlinTargetComponent
|
import org.jetbrains.kotlin.gradle.plugin.KotlinTargetComponent
|
||||||
import org.jetbrains.kotlin.gradle.plugin.usageByName
|
import org.jetbrains.kotlin.utils.ifEmpty
|
||||||
|
|
||||||
class KotlinSoftwareComponent(
|
class KotlinSoftwareComponent(
|
||||||
private val project: Project,
|
private val project: Project,
|
||||||
@@ -60,7 +59,10 @@ internal class KotlinPlatformUsageContext(
|
|||||||
get() = project.configurations.getByName(dependencyConfigurationName)
|
get() = project.configurations.getByName(dependencyConfigurationName)
|
||||||
|
|
||||||
override fun getDependencies(): MutableSet<out ModuleDependency> =
|
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> =
|
override fun getDependencyConstraints(): MutableSet<out DependencyConstraint> =
|
||||||
configuration.incoming.dependencyConstraints
|
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
|
// 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()
|
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