Fix visibility for MPP dependencies from non-root source sets, KT-32204
Fix incorrect source sets visibility when a dependency is added to a non-root source set and its visible source sets are mistakenly treated as already visible by the dependsOn parents and are thus not extracted from the metadata package. To simplify the logic, reuse the GranularMetadataTransformation instances from the dependsOn parent source sets: they can provide the requested dependencies sets for the parents and their transformation results, so that excluding the dependency source sets which are seen the parents gets simpler. The issue is then fixed by naturally excluding only the source sets which are mentioned in the parents' transformation results. Issue #KT-32204 Fixed
This commit is contained in:
+144
-15
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.gradle.internals.MULTIPLATFORM_PROJECT_METADATA_FILE
|
||||
import org.jetbrains.kotlin.gradle.internals.parseKotlinSourceSetMetadataFromXml
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinProjectStructureMetadata
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.ModuleDependencyIdentifier
|
||||
import org.jetbrains.kotlin.gradle.plugin.sources.DefaultKotlinSourceSet
|
||||
import org.jetbrains.kotlin.gradle.util.modify
|
||||
import java.io.File
|
||||
import java.util.zip.ZipFile
|
||||
@@ -24,13 +25,7 @@ class HierarchicalMppIT : BaseGradleIT() {
|
||||
|
||||
@Test
|
||||
fun testPublishedModules() {
|
||||
Project("third-party-lib", gradleVersion, "hierarchical-mpp-published-modules").run {
|
||||
setupWorkingDir()
|
||||
gradleBuildScript().modify(::transformBuildScriptWithPluginsDsl)
|
||||
build("publish") {
|
||||
assertSuccessful()
|
||||
}
|
||||
}
|
||||
publishThirdPartyLib(withGranularMetadata = false)
|
||||
|
||||
Project("my-lib-foo", gradleVersion, "hierarchical-mpp-published-modules").run {
|
||||
setupWorkingDir()
|
||||
@@ -59,13 +54,7 @@ class HierarchicalMppIT : BaseGradleIT() {
|
||||
|
||||
@Test
|
||||
fun testProjectDependencies() {
|
||||
Project("third-party-lib", gradleVersion, "hierarchical-mpp-project-dependency").run {
|
||||
setupWorkingDir()
|
||||
gradleBuildScript().modify(::transformBuildScriptWithPluginsDsl)
|
||||
build("publish") {
|
||||
assertSuccessful()
|
||||
}
|
||||
}
|
||||
publishThirdPartyLib(withGranularMetadata = false)
|
||||
|
||||
with(Project("hierarchical-mpp-project-dependency", gradleVersion)) {
|
||||
setupWorkingDir()
|
||||
@@ -79,6 +68,20 @@ class HierarchicalMppIT : BaseGradleIT() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun publishThirdPartyLib(withGranularMetadata: Boolean): Project =
|
||||
Project("third-party-lib", gradleVersion, "hierarchical-mpp-published-modules").apply {
|
||||
setupWorkingDir()
|
||||
gradleBuildScript().modify(::transformBuildScriptWithPluginsDsl)
|
||||
|
||||
if (withGranularMetadata) {
|
||||
projectDir.resolve("gradle.properties").appendText("kotlin.mpp.enableGranularSourceSetsMetadata=true")
|
||||
}
|
||||
|
||||
build("publish") {
|
||||
assertSuccessful()
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkMyLibFoo(compiledProject: CompiledProject, subprojectPrefix: String? = null) = with(compiledProject) {
|
||||
assertSuccessful()
|
||||
assertTasksExecuted(expectedTasks(subprojectPrefix))
|
||||
@@ -273,7 +276,7 @@ class HierarchicalMppIT : BaseGradleIT() {
|
||||
"compileKotlinMetadata",
|
||||
"compileJvmAndJsMainKotlinMetadata",
|
||||
"compileLinuxAndJsMainKotlinMetadata"
|
||||
).map { subprojectPrefix?.let { ":$it" }.orEmpty() + ":" + it }
|
||||
).map { task -> subprojectPrefix?.let { ":$it" }.orEmpty() + ":" + task }
|
||||
|
||||
// the projects used in these tests are similar and only the dependencies differ:
|
||||
private fun expectedProjectStructureMetadata(
|
||||
@@ -317,4 +320,130 @@ class HierarchicalMppIT : BaseGradleIT() {
|
||||
.use { inputStream -> documentBuilder.parse(inputStream) }
|
||||
return checkNotNull(parseKotlinSourceSetMetadataFromXml(document))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testProcessingDependencyDeclaredInNonRootSourceSet() {
|
||||
publishThirdPartyLib(withGranularMetadata = true)
|
||||
|
||||
Project("my-lib-foo", gradleVersion, "hierarchical-mpp-published-modules").run {
|
||||
setupWorkingDir()
|
||||
gradleBuildScript().modify(::transformBuildScriptWithPluginsDsl)
|
||||
|
||||
val intermediateMetadataCompileTask = ":compileJvmAndJsMainKotlinMetadata"
|
||||
|
||||
build(intermediateMetadataCompileTask) {
|
||||
assertSuccessful()
|
||||
|
||||
checkNamesOnCompileClasspath(
|
||||
intermediateMetadataCompileTask,
|
||||
shouldInclude = listOf(
|
||||
"third-party-lib" to "commonMain"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDependenciesInNonPublishedSourceSets() {
|
||||
publishThirdPartyLib(withGranularMetadata = true)
|
||||
|
||||
Project("my-lib-foo", gradleVersion, "hierarchical-mpp-published-modules").run {
|
||||
setupWorkingDir()
|
||||
gradleBuildScript().modify(::transformBuildScriptWithPluginsDsl)
|
||||
|
||||
testDependencyTransformations { reports ->
|
||||
reports.single {
|
||||
it.sourceSetName == "jvmAndJsMain" && it.scope == "api" && it.groupAndModule.startsWith("com.example")
|
||||
}.let {
|
||||
assertEquals(setOf("commonMain"), it.allVisibleSourceSets)
|
||||
assertEquals(setOf("commonMain"), it.newVisibleSourceSets)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Project.testDependencyTransformations(
|
||||
subproject: String? = null,
|
||||
check: CompiledProject.(reports: Iterable<DependencyTransformationReport>) -> Unit
|
||||
) {
|
||||
setupWorkingDir()
|
||||
val buildGradleKts = gradleBuildScript(subproject)
|
||||
assert(buildGradleKts.extension == "kts") { "Only Kotlin scripts are supported." }
|
||||
|
||||
val testTaskName = "reportDependencyTransformationsForTest"
|
||||
|
||||
if (testTaskName !in buildGradleKts.readText()) {
|
||||
buildGradleKts.modify {
|
||||
"import ${DefaultKotlinSourceSet::class.qualifiedName}\n" + it + "\n" + """
|
||||
val $testTaskName by tasks.creating {
|
||||
doFirst {
|
||||
for (scope in listOf("api", "implementation", "compileOnly", "runtimeOnly")) {
|
||||
println("========\n${'$'}scope\n")
|
||||
|
||||
kotlin.sourceSets.withType<DefaultKotlinSourceSet>().forEach { sourceSet ->
|
||||
println("--------\n${'$'}{sourceSet.name}")
|
||||
|
||||
sourceSet
|
||||
.getDependenciesTransformation(
|
||||
"${'$'}{sourceSet.name}${'$'}{scope.capitalize()}DependenciesMetadata"
|
||||
).forEach {
|
||||
val line = listOf(
|
||||
"${DependencyTransformationReport.TEST_OUTPUT_MARKER}",
|
||||
sourceSet.name,
|
||||
scope,
|
||||
it.groupId + ":" + it.moduleName,
|
||||
it.allVisibleSourceSets.joinToString(","),
|
||||
it.useFilesForSourceSets.keys.joinToString(",")
|
||||
)
|
||||
|
||||
println(" " + line.joinToString(" :: "))
|
||||
}
|
||||
println()
|
||||
}
|
||||
println()
|
||||
}
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
build(":${subproject?.plus(":").orEmpty()}$testTaskName") {
|
||||
assertSuccessful()
|
||||
|
||||
val reports = output.lines()
|
||||
.filter { DependencyTransformationReport.TEST_OUTPUT_MARKER in it }
|
||||
.map { DependencyTransformationReport.parseTestOutputLine(it) }
|
||||
|
||||
check(this, reports)
|
||||
}
|
||||
}
|
||||
|
||||
private data class DependencyTransformationReport(
|
||||
val sourceSetName: String,
|
||||
val scope: String,
|
||||
val groupAndModule: String,
|
||||
val allVisibleSourceSets: Set<String>,
|
||||
val newVisibleSourceSets: Set<String> // those which the dependsOn parents don't see
|
||||
) {
|
||||
val isExcluded: Boolean get() = allVisibleSourceSets.isEmpty()
|
||||
|
||||
companion object {
|
||||
const val TEST_OUTPUT_MARKER = "###transformation"
|
||||
const val TEST_OUTPUT_COMPONENT_SEPARATOR = " :: "
|
||||
const val TEST_OUTPUT_ITEMS_SEPARATOR = ","
|
||||
|
||||
fun parseTestOutputLine(line: String): DependencyTransformationReport {
|
||||
val tail = line.substringAfter(TEST_OUTPUT_MARKER + TEST_OUTPUT_COMPONENT_SEPARATOR)
|
||||
val (sourceSetName, scope, groupAndModule, allVisibleSourceSets, newVisibleSourceSets) =
|
||||
tail.split(TEST_OUTPUT_COMPONENT_SEPARATOR)
|
||||
return DependencyTransformationReport(
|
||||
sourceSetName, scope, groupAndModule,
|
||||
allVisibleSourceSets.split(TEST_OUTPUT_ITEMS_SEPARATOR).filter { it.isNotEmpty() }.toSet(),
|
||||
newVisibleSourceSets.split(TEST_OUTPUT_ITEMS_SEPARATOR).filter { it.isNotEmpty() }.toSet()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user