From 5c88eb722dc09804766655b009d389a48904e360 Mon Sep 17 00:00:00 2001 From: Sergey Igushkin Date: Thu, 27 Aug 2020 14:37:28 +0400 Subject: [PATCH] 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 --- .../jetbrains/kotlin/gradle/KlibBasedMppIT.kt | 61 ++++++++++++++++ .../gradle/KotlinSpecificDependenciesIT.kt | 70 +++++++++---------- .../targets/native/KotlinNativeTarget.kt | 2 + 3 files changed, 98 insertions(+), 35 deletions(-) diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/KlibBasedMppIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/KlibBasedMppIT.kt index 41f613a1264..9c7ef7539bf 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/KlibBasedMppIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/KlibBasedMppIT.kt @@ -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) diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/KotlinSpecificDependenciesIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/KotlinSpecificDependenciesIT.kt index a205fcc75d6..d8081040590 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/KotlinSpecificDependenciesIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/KotlinSpecificDependenciesIT.kt @@ -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 = emptyList(), - checkModulesNotInClasspath: List = 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, - checkNoItemContains: List - ) { - setupWorkingDir() - val printingTaskName = "printItems${testBuildRunId++}" - gradleBuildScript(subproject).appendText( - """ - ${'\n'} - tasks.create("$printingTaskName") { - doLast { - println("###$printingTaskName" + $itemsExpression) - } +fun BaseGradleIT.Project.checkTaskCompileClasspath( + taskPath: String, + checkModulesInClasspath: List = emptyList(), + checkModulesNotInClasspath: List = 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, + checkNoItemContains: List +) = 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 } } } } } \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeTarget.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeTarget.kt index c0718fda756..869d96ac3bf 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeTarget.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeTarget.kt @@ -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 =