diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/HierarchicalMppIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/HierarchicalMppIT.kt index b75db9af831..c0a19ab3ce8 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/HierarchicalMppIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/HierarchicalMppIT.kt @@ -10,6 +10,7 @@ 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.checkedReplace import org.jetbrains.kotlin.gradle.util.modify import java.io.File import java.util.zip.ZipFile @@ -52,6 +53,77 @@ class HierarchicalMppIT : BaseGradleIT() { } } + @Test + fun testDependenciesInTests() { + publishThirdPartyLib(withGranularMetadata = true) + + Project("my-lib-foo", gradleVersion, "hierarchical-mpp-published-modules").run { + setupWorkingDir() + gradleBuildScript().modify(::transformBuildScriptWithPluginsDsl) + + testDependencyTransformations { reports -> + val testApiTransformationReports = + reports.filter { report -> + report.groupAndModule.startsWith("com.example.thirdparty") && + report.sourceSetName.let { it == "commonTest" || it == "jvmAndJsTest" } + } + + testApiTransformationReports.forEach { + assertTrue("$it") { it.isExcluded } // should not be visible in test source sets + } + } + + // --- Move the dependency from jvmAndJsMain to commonMain, expect that it is now propagated to commonTest: + gradleBuildScript().modify { + it.checkedReplace("api(\"com.example.thirdparty:third-party-lib:1.0\")", "//") + "\n" + """ + dependencies { + "commonMainApi"("com.example.thirdparty:third-party-lib:1.0") + } + """.trimIndent() + } + + testDependencyTransformations { reports -> + val testApiTransformationReports = + reports.filter { report -> + report.groupAndModule.startsWith("com.example.thirdparty") && + report.sourceSetName.let { it == "commonTest" || it == "jvmAndJsTest" } && + report.scope == "api" + } + + testApiTransformationReports.forEach { + assertEquals(setOf("commonMain"), it.allVisibleSourceSets, "$it") + assertEquals(emptySet(), it.newVisibleSourceSets, "$it") + } + } + + // --- Remove the dependency from commonMain, add it to commonTest to check that it is correctly picked from a non-published + // source set: + gradleBuildScript().modify { + it.checkedReplace("\"commonMainApi\"(\"com.example.thirdparty:third-party-lib:1.0\")", "//") + "\n" + """ + dependencies { + "commonTestApi"("com.example.thirdparty:third-party-lib:1.0") + } + """.trimIndent() + } + + testDependencyTransformations { reports -> + reports.single { + it.sourceSetName == "commonTest" && it.scope == "api" && it.groupAndModule.startsWith("com.example.thirdparty") + }.let { + assertEquals(setOf("commonMain"), it.allVisibleSourceSets) + assertEquals(setOf("commonMain"), it.newVisibleSourceSets) + } + + reports.single { + it.sourceSetName == "jvmAndJsTest" && it.scope == "api" && it.groupAndModule.startsWith("com.example.thirdparty") + }.let { + assertEquals(setOf("commonMain"), it.allVisibleSourceSets) + assertEquals(emptySet(), it.newVisibleSourceSets) + } + } + } + } + @Test fun testProjectDependencies() { publishThirdPartyLib(withGranularMetadata = false) diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/GranularMetadataTransformation.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/GranularMetadataTransformation.kt index 454b6ea125f..8ff0afef16e 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/GranularMetadataTransformation.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/GranularMetadataTransformation.kt @@ -82,7 +82,7 @@ internal class GranularMetadataTransformation( /** A list of scopes that the dependencies from [kotlinSourceSet] are treated as requested dependencies. */ val sourceSetRequestedScopes: List, /** A configuration that holds the dependencies of the appropriate scope for all Kotlin source sets in the project */ - val allSourceSetsConfigurations: Iterable + val allSourceSetsConfiguration: Configuration, val parentTransformations: Lazy> ) { val metadataDependencyResolutions: Iterable by lazy { doTransform() } @@ -134,8 +134,24 @@ internal class GranularMetadataTransformation( ownDependencies + parentDependencies } - private val resolvedConfigurations: Iterable by lazy { - allSourceSetsConfigurations.map { it.resolvedConfiguration.lenientConfiguration } + private val resolvedConfiguration: LenientConfiguration by lazy { + /** If [kotlinSourceSet] is not a published source set, its dependencies are not included in [allSourceSetsConfiguration]. + * In that case, to resolve the dependencies of the source set in a way that is consistent with the published source sets, + * we need to create a new configuration with the dependencies from both [allSourceSetsConfiguration] and the + * input configuration(s) of the source set. */ + var modifiedConfiguration: Configuration? = null + + val originalDependencies = allSourceSetsConfiguration.allDependencies + + requestedDependencies.forEach { dependency -> + if (dependency !in originalDependencies) { + modifiedConfiguration = (modifiedConfiguration ?: allSourceSetsConfiguration.copyRecursive()).apply { + dependencies.add(dependency) + } + } + } + + (modifiedConfiguration ?: allSourceSetsConfiguration).resolvedConfiguration.lenientConfiguration } private fun doTransform(): Iterable { @@ -146,7 +162,7 @@ internal class GranularMetadataTransformation( val allRequestedDependencies = requestedDependencies - val allModuleDependencies = resolvedConfigurations.flatMap { it.allModuleDependencies } + val allModuleDependencies = resolvedConfiguration.allModuleDependencies val knownProjectDependencies = collectProjectDependencies( allRequestedDependencies.filterIsInstance(), @@ -157,7 +173,7 @@ internal class GranularMetadataTransformation( val requestedModules: Set = allRequestedDependencies.mapTo(mutableSetOf()) { it.moduleId } addAll( - resolvedConfigurations.flatMap { it.firstLevelModuleDependencies } + resolvedConfiguration.firstLevelModuleDependencies .filter { it.moduleId in requestedModules } .map { ResolvedDependencyWithParent(it, null) } ) diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/TransformKotlinGranularMetadata.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/TransformKotlinGranularMetadata.kt index 91543e9b7d6..35ebc8a261e 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/TransformKotlinGranularMetadata.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/TransformKotlinGranularMetadata.kt @@ -71,7 +71,7 @@ open class TransformKotlinGranularMetadata project, kotlinSourceSet, listOf(API_SCOPE, IMPLEMENTATION_SCOPE), - listOf(project.configurations.getByName(ALL_COMPILE_METADATA_CONFIGURATION_NAME)), + project.configurations.getByName(ALL_COMPILE_METADATA_CONFIGURATION_NAME), lazy { KotlinMetadataTargetConfigurator.dependsOnWithInterCompilationDependencies(project, kotlinSourceSet).map { project.tasks.withType(TransformKotlinGranularMetadata::class.java)