Fix transitive deps on MPP with host-specific source sets (KT-41083)
A consumer could not resolve the host-specific metadata artifact of the transitive dependency because the transitive dependency's parent in the platform dependencies graph did not include the dependency on it in the variant that is chosen for host-specific source sets metadata: the dependencies of that variant were empty. Export the dependencies of the Kotlin/Native target in the host-specific metadata variant as well. Issue #KT-41083 Fixed
This commit is contained in:
committed by
Sergey Igushkin
parent
9097d0918c
commit
5c88eb722d
+61
@@ -40,6 +40,67 @@ class KlibBasedMppIT : BaseGradleIT() {
|
||||
publishProjectDepAndAddDependency(validateHostSpecificPublication = false)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testHostSpecificSourceSetsInTransitiveDependencies() = with(Project("common-klib-lib-and-app")) {
|
||||
// KT-41083
|
||||
// Publish a lib with host specific source sets depending on another lib with host-specific source sets
|
||||
setupWorkingDir()
|
||||
val projectDepName = "dependency"
|
||||
val publishedGroup = "published"
|
||||
val producerProjectName = "producer"
|
||||
embedProject(this, renameTo = projectDepName)
|
||||
projectDir.resolve("$projectDepName/src").walkTopDown().filter { it.extension == "kt" }.forEach { ktFile ->
|
||||
// Avoid FQN duplicates between producer & consumer
|
||||
ktFile.modify { it.replace("package com.h0tk3y.hmpp.klib.demo", "package com.h0tk3y.hmpp.klib.lib") }
|
||||
}
|
||||
|
||||
gradleBuildScript(projectDepName).appendText(
|
||||
"""
|
||||
${"\n"}
|
||||
group = "$publishedGroup"
|
||||
""".trimIndent()
|
||||
)
|
||||
gradleBuildScript().modify {
|
||||
transformBuildScriptWithPluginsDsl(it) +
|
||||
"""
|
||||
${"\n"}
|
||||
dependencies { "commonMainImplementation"(project(":$projectDepName")) }
|
||||
group = "$publishedGroup"
|
||||
""".trimIndent()
|
||||
}
|
||||
gradleSettingsScript().appendText("\nrootProject.name = \"$producerProjectName\"")
|
||||
|
||||
build("publish") {
|
||||
assertSuccessful()
|
||||
}
|
||||
|
||||
// Then consume the published project. To do that, rename the modules so that Gradle chooses the published ones given the original
|
||||
// Maven coordinates and doesn't resolve them as project dependencies.
|
||||
|
||||
val localGroup = "local"
|
||||
gradleBuildScript(projectDepName).appendText("""${"\n"}group = "$localGroup"""")
|
||||
gradleBuildScript().appendText(
|
||||
"""
|
||||
${"\n"}
|
||||
repositories { maven("${'$'}rootDir/repo") }
|
||||
dependencies { "commonMainImplementation"("$publishedGroup:$producerProjectName:1.0") }
|
||||
group = "$localGroup"
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
// The consumer should correctly receive the klibs of the host-specific source sets
|
||||
|
||||
checkTaskCompileClasspath(
|
||||
"compile${hostSpecificSourceSet.capitalize()}KotlinMetadata",
|
||||
listOf(
|
||||
"published-producer-metadata-$hostSpecificSourceSet.klib",
|
||||
"published-producer-metadata-commonMain.klib",
|
||||
"published-dependency-metadata-$hostSpecificSourceSet.klib",
|
||||
"published-dependency-metadata-commonMain.klib"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testBuildWithPublishedDependency() = testBuildWithDependency {
|
||||
publishProjectDepAndAddDependency(validateHostSpecificPublication = true)
|
||||
|
||||
+35
-35
@@ -21,8 +21,6 @@ class KotlinSpecificDependenciesIT : BaseGradleIT() {
|
||||
override fun defaultBuildOptions(): BuildOptions =
|
||||
super.defaultBuildOptions().copy(androidGradlePluginVersion = AGPVersion.v3_6_0, androidHome = KotlinTestUtils.findAndroidSdk())
|
||||
|
||||
private var testBuildRunId = 0
|
||||
|
||||
private fun Project.prepare() { // call this when reusing a project after a test, too, in order to remove any added dependencies
|
||||
setupWorkingDir()
|
||||
gradleSettingsScript().takeIf { it.exists() }?.modify(::transformBuildScriptWithPluginsDsl)
|
||||
@@ -286,42 +284,44 @@ class KotlinSpecificDependenciesIT : BaseGradleIT() {
|
||||
val expression = """configurations["$configurationName"].toList()"""
|
||||
checkPrintedItems(subproject, expression, checkModulesInResolutionResult, checkModulesNotInResolutionResult)
|
||||
}
|
||||
}
|
||||
|
||||
private fun Project.checkTaskCompileClasspath(
|
||||
taskPath: String,
|
||||
checkModulesInClasspath: List<String> = emptyList(),
|
||||
checkModulesNotInClasspath: List<String> = emptyList()
|
||||
) {
|
||||
val subproject = taskPath.substringBeforeLast(":").takeIf { it.isNotEmpty() && it != taskPath }
|
||||
val taskName = taskPath.removePrefix(subproject.orEmpty())
|
||||
val expression = """(tasks.getByName("$taskName") as AbstractCompile).classpath.toList()"""
|
||||
checkPrintedItems(subproject, expression, checkModulesInClasspath, checkModulesNotInClasspath)
|
||||
}
|
||||
private var testBuildRunId = 0
|
||||
|
||||
private fun Project.checkPrintedItems(
|
||||
subproject: String?,
|
||||
itemsExpression: String,
|
||||
checkAnyItemsContains: List<String>,
|
||||
checkNoItemContains: List<String>
|
||||
) {
|
||||
setupWorkingDir()
|
||||
val printingTaskName = "printItems${testBuildRunId++}"
|
||||
gradleBuildScript(subproject).appendText(
|
||||
"""
|
||||
${'\n'}
|
||||
tasks.create("$printingTaskName") {
|
||||
doLast {
|
||||
println("###$printingTaskName" + $itemsExpression)
|
||||
}
|
||||
fun BaseGradleIT.Project.checkTaskCompileClasspath(
|
||||
taskPath: String,
|
||||
checkModulesInClasspath: List<String> = emptyList(),
|
||||
checkModulesNotInClasspath: List<String> = emptyList()
|
||||
) {
|
||||
val subproject = taskPath.substringBeforeLast(":").takeIf { it.isNotEmpty() && it != taskPath }
|
||||
val taskName = taskPath.removePrefix(subproject.orEmpty())
|
||||
val expression = """(tasks.getByName("$taskName") as AbstractCompile).classpath.toList()"""
|
||||
checkPrintedItems(subproject, expression, checkModulesInClasspath, checkModulesNotInClasspath)
|
||||
}
|
||||
|
||||
private fun BaseGradleIT.Project.checkPrintedItems(
|
||||
subproject: String?,
|
||||
itemsExpression: String,
|
||||
checkAnyItemsContains: List<String>,
|
||||
checkNoItemContains: List<String>
|
||||
) = with(testCase) {
|
||||
setupWorkingDir()
|
||||
val printingTaskName = "printItems${testBuildRunId++}"
|
||||
gradleBuildScript(subproject).appendText(
|
||||
"""
|
||||
${'\n'}
|
||||
tasks.create("$printingTaskName") {
|
||||
doLast {
|
||||
println("###$printingTaskName" + $itemsExpression)
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
build("${subproject?.prependIndent(":").orEmpty()}:$printingTaskName") {
|
||||
assertSuccessful()
|
||||
val itemsLine = output.lines().single { "###$printingTaskName" in it }.substringAfter(printingTaskName)
|
||||
val items = itemsLine.removeSurrounding("[", "]").split(", ").toSet()
|
||||
checkAnyItemsContains.forEach { pattern -> assertTrue { items.any { pattern in it } } }
|
||||
checkNoItemContains.forEach { pattern -> assertFalse { items.any { pattern in it } } }
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
build("${subproject?.prependIndent(":").orEmpty()}:$printingTaskName") {
|
||||
assertSuccessful()
|
||||
val itemsLine = output.lines().single { "###$printingTaskName" in it }.substringAfter(printingTaskName)
|
||||
val items = itemsLine.removeSurrounding("[", "]").split(", ").toSet()
|
||||
checkAnyItemsContains.forEach { pattern -> assertTrue { items.any { pattern in it } } }
|
||||
checkNoItemContains.forEach { pattern -> assertFalse { items.any { pattern in it } } }
|
||||
}
|
||||
}
|
||||
+2
@@ -88,6 +88,8 @@ open class KotlinNativeTarget @Inject constructor(
|
||||
project.artifacts.add(configuration.name, hostSpecificMetadataJar) { artifact ->
|
||||
artifact.classifier = "metadata"
|
||||
}
|
||||
|
||||
configuration.extendsFrom(*configurations.getByName(apiElementsConfigurationName).extendsFrom.toTypedArray())
|
||||
}
|
||||
|
||||
val metadataAttributes =
|
||||
|
||||
Reference in New Issue
Block a user