From dbc8007c63966750e45d6ad04d3b9b20ca24d679 Mon Sep 17 00:00:00 2001 From: Sergey Igushkin Date: Tue, 25 Jun 2019 19:19:16 +0300 Subject: [PATCH] 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 --- .../kotlin/gradle/HierarchicalMppIT.kt | 159 ++++++++++++++++-- .../mpp/GranularMetadataTransformation.kt | 74 ++++---- .../plugin/mpp/SourceSetVisibilityProvider.kt | 53 +++--- .../mpp/TransformKotlinGranularMetadata.kt | 14 +- .../KotlinMetadataTargetConfigurator.kt | 32 ++-- 5 files changed, 243 insertions(+), 89 deletions(-) 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 4cb7313b000..b75db9af831 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 @@ -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) -> 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().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, + val newVisibleSourceSets: Set // 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() + ) + } + } + } } \ No newline at end of file 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 3b6f3ec0123..454b6ea125f 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 @@ -11,6 +11,8 @@ import org.gradle.api.file.FileCollection import org.jetbrains.kotlin.gradle.dsl.kotlinExtension import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet +import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet.Companion.COMMON_MAIN_SOURCE_SET_NAME +import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet.Companion.COMMON_TEST_SOURCE_SET_NAME import org.jetbrains.kotlin.gradle.plugin.sources.KotlinDependencyScope import org.jetbrains.kotlin.gradle.plugin.sources.getSourceSetHierarchy import org.jetbrains.kotlin.gradle.plugin.sources.sourceSetDependencyConfigurationByScope @@ -71,6 +73,9 @@ private typealias ModuleId = Pair // group ID, artifact ID private val ResolvedDependency.moduleId: ModuleId get() = moduleGroup to moduleName +private val Dependency.moduleId: ModuleId + get() = group to name + internal class GranularMetadataTransformation( val project: Project, val kotlinSourceSet: KotlinSourceSet, @@ -78,6 +83,7 @@ internal class GranularMetadataTransformation( 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 parentTransformations: Lazy> ) { val metadataDependencyResolutions: Iterable by lazy { doTransform() } @@ -115,48 +121,50 @@ internal class GranularMetadataTransformation( return result } - private fun getRequestedDependencies(kotlinSourceSet: KotlinSourceSet): Set { - val hierarchy = kotlinSourceSet.getSourceSetHierarchy().toMutableSet() - // This is an ad-hoc mechanism for exposing the commonMain dependencies to test source sets as well: - // TODO once a general production-test visibility mechanism is implemented, replace this workaround with the general solution - if (hierarchy.any { it.name == KotlinSourceSet.COMMON_TEST_SOURCE_SET_NAME }) { - hierarchy += project.kotlinExtension.sourceSets.getByName(KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME).getSourceSetHierarchy() - } - - return hierarchy.flatMapTo(mutableSetOf()) { sourceSet -> - sourceSetRequestedScopes.flatMap { scope -> + private val requestedDependencies: Iterable by lazy { + fun collectScopedDependenciesFromSourceSet(sourceSet: KotlinSourceSet): Set = + sourceSetRequestedScopes.flatMapTo(mutableSetOf()) { scope -> project.sourceSetDependencyConfigurationByScope(sourceSet, scope).allDependencies } - } + + val ownDependencies = collectScopedDependenciesFromSourceSet(kotlinSourceSet) + val parentDependencies = parentTransformations.value.flatMapTo(mutableSetOf()) { it.requestedDependencies } + + ownDependencies + parentDependencies + } + + private val resolvedConfigurations: Iterable by lazy { + allSourceSetsConfigurations.map { it.resolvedConfiguration.lenientConfiguration } } private fun doTransform(): Iterable { val result = mutableListOf() - val resolvedDependenciesFromAllSourceSets = allSourceSetsConfigurations.map { it.resolvedConfiguration.lenientConfiguration } + val parentResolutions = + parentTransformations.value.flatMap { it.metadataDependencyResolutions }.groupBy { it.dependency.moduleId } - val visitedDependencies = mutableSetOf() + val allRequestedDependencies = requestedDependencies - val directRequestedDependencies = getRequestedDependencies(kotlinSourceSet) - - val directRequestedModules: Set = directRequestedDependencies.mapTo(mutableSetOf()) { it.group to it.name } - - val allModuleDependencies = resolvedDependenciesFromAllSourceSets.flatMapTo(mutableSetOf()) { it.allModuleDependencies } + val allModuleDependencies = resolvedConfigurations.flatMap { it.allModuleDependencies } val knownProjectDependencies = collectProjectDependencies( - directRequestedDependencies.filterIsInstance(), + allRequestedDependencies.filterIsInstance(), allModuleDependencies ) val resolvedDependencyQueue: Queue = ArrayDeque().apply { + val requestedModules: Set = allRequestedDependencies.mapTo(mutableSetOf()) { it.moduleId } + addAll( - resolvedDependenciesFromAllSourceSets.flatMap { it.firstLevelModuleDependencies } - .filter { it.moduleId in directRequestedModules } + resolvedConfigurations.flatMap { it.firstLevelModuleDependencies } + .filter { it.moduleId in requestedModules } .map { ResolvedDependencyWithParent(it, null) } ) } + val visitedDependencies = mutableSetOf() + while (resolvedDependencyQueue.isNotEmpty()) { val (resolvedDependency, parent: ResolvedDependency?) = resolvedDependencyQueue.poll() @@ -164,7 +172,13 @@ internal class GranularMetadataTransformation( visitedDependencies.add(resolvedDependency) - val dependencyResult = processDependency(resolvedDependency, parent, projectDependency) + val dependencyResult = processDependency( + resolvedDependency, + parentResolutions[resolvedDependency.moduleId].orEmpty(), + parent, + projectDependency + ) + result.add(dependencyResult) val transitiveDependenciesToVisit = when (dependencyResult) { @@ -209,6 +223,7 @@ internal class GranularMetadataTransformation( */ private fun processDependency( module: ResolvedDependency, + parentResolutionsForModule: Iterable, parent: ResolvedDependency?, projectDependency: ProjectDependency? ): MetadataDependencyResolution { @@ -220,13 +235,10 @@ internal class GranularMetadataTransformation( } val projectStructureMetadata = mppDependencyMetadataExtractor?.getProjectStructureMetadata() + ?: return MetadataDependencyResolution.KeepOriginalDependency(module, projectDependency) - if (projectStructureMetadata == null) { - return MetadataDependencyResolution.KeepOriginalDependency(module, projectDependency) - } - - val (allVisibleSourceSets, visibleByParents) = - SourceSetVisibilityProvider(project).getVisibleSourceSets( + val allVisibleSourceSets = + SourceSetVisibilityProvider(project).getVisibleSourceSetNames( kotlinSourceSet, sourceSetRequestedScopes, parent ?: module, @@ -234,6 +246,10 @@ internal class GranularMetadataTransformation( projectDependency?.dependencyProject ) + val sourceSetsVisibleInParents = parentResolutionsForModule + .filterIsInstance() + .flatMapTo(mutableSetOf()) { it.allVisibleSourceSetNames } + // Keep only the transitive dependencies requested by the visible source sets: // Visit the transitive dependencies visible by parents, too (i.e. allVisibleSourceSets), as this source set might get a more // concrete view on them: @@ -250,7 +266,7 @@ internal class GranularMetadataTransformation( (it.moduleId) in requestedTransitiveDependencies } - val visibleSourceSetsExcludingDependsOn = allVisibleSourceSets.filterTo(mutableSetOf()) { it !in visibleByParents } + val visibleSourceSetsExcludingDependsOn = allVisibleSourceSets.filterTo(mutableSetOf()) { it !in sourceSetsVisibleInParents } return object : MetadataDependencyResolution.ChooseVisibleSourceSets( module, diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/SourceSetVisibilityProvider.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/SourceSetVisibilityProvider.kt index 0f0657298e1..517edc13728 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/SourceSetVisibilityProvider.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/SourceSetVisibilityProvider.kt @@ -15,43 +15,29 @@ import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet import org.jetbrains.kotlin.gradle.plugin.sources.KotlinDependencyScope import org.jetbrains.kotlin.gradle.targets.metadata.getPublishedPlatformCompilations -internal data class DependencySourceSetVisibilityResult( - val sourceSetsVisibleByThisSourceSet: Set, - val sourceSetsVisibleThroughDependsOn: Set -) - internal class SourceSetVisibilityProvider( private val project: Project ) { - fun getVisibleSourceSets( + /** + * Determine which source sets of the [resolvedMppDependency] are visible in the [visibleFrom] source set. + * + * This requires resolving dependencies of the compilations which [visibleFrom] takes part in, in order to find which variants the + * [resolvedMppDependency] got resolved to for those compilations. The [resolvedMppDependency] should therefore be the dependency + * on the 'root' module of the MPP (such as 'com.example:lib-foo', not 'com.example:lib-foo-metadata'). + * + * Once the variants are known, they are checked against the [dependencyProjectStructureMetadata], and the + * source sets of the dependency are determined that are compiled for all those variants and thus should be visible here. + * + * If the [resolvedMppDependency] is a project dependency, its project should be passed as [resolvedToOtherProject], as + * the Gradle API for dependency variants behaves differently for project dependencies and published ones. + */ + @Suppress("UnstableApiUsage") + fun getVisibleSourceSetNames( visibleFrom: KotlinSourceSet, dependencyScopes: Iterable, resolvedMppDependency: ResolvedDependency, dependencyProjectStructureMetadata: KotlinProjectStructureMetadata, resolvedToOtherProject: Project? - ): DependencySourceSetVisibilityResult { - val visibleByThisSourceSet = - getVisibleSourceSetsImpl( - visibleFrom, dependencyScopes, resolvedMppDependency, dependencyProjectStructureMetadata, resolvedToOtherProject - ) - - val visibleByParents = visibleFrom.dependsOn - .flatMapTo(mutableSetOf()) { - getVisibleSourceSetsImpl( - it, dependencyScopes, resolvedMppDependency, dependencyProjectStructureMetadata, resolvedToOtherProject - ) - } - - return DependencySourceSetVisibilityResult(visibleByThisSourceSet, visibleByParents) - } - - @Suppress("UnstableApiUsage") - private fun getVisibleSourceSetsImpl( - visibleFrom: KotlinSourceSet, - dependencyScopes: Iterable, - mppDependency: ResolvedDependency, - dependencyProjectMetadata: KotlinProjectStructureMetadata, - otherProject: Project? ): Set { val compilations = CompilationSourceSetUtil.compilationsBySourceSets(project).getValue(visibleFrom) @@ -66,7 +52,8 @@ internal class SourceSetVisibilityProvider( // Resolve the configuration but don't trigger artifacts download, only download component metadata: configuration.incoming.resolutionResult.allComponents .find { - it.moduleVersion?.group == mppDependency.moduleGroup && it.moduleVersion?.name == mppDependency.moduleName + it.moduleVersion?.group == resolvedMppDependency.moduleGroup && + it.moduleVersion?.name == resolvedMppDependency.moduleName } ?.variant?.displayName } @@ -76,15 +63,15 @@ internal class SourceSetVisibilityProvider( return emptySet() } - if (otherProject != null) { - val publishedVariants = getPublishedPlatformCompilations(otherProject).keys + if (resolvedToOtherProject != null) { + val publishedVariants = getPublishedPlatformCompilations(resolvedToOtherProject).keys visiblePlatformVariantNames = visiblePlatformVariantNames.mapTo(mutableSetOf()) { configurationName -> publishedVariants.first { it.dependencyConfigurationName == configurationName }.name } } - return dependencyProjectMetadata.sourceSetNamesByVariantName + return dependencyProjectStructureMetadata.sourceSetNamesByVariantName .filterKeys { it in visiblePlatformVariantNames } .values.let { if (it.isEmpty()) emptySet() else it.reduce { acc, item -> acc intersect item } } } 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 aeb912ecc81..91543e9b7d6 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 @@ -15,6 +15,7 @@ import org.jetbrains.kotlin.gradle.plugin.sources.KotlinDependencyScope.API_SCOP import org.jetbrains.kotlin.gradle.plugin.sources.KotlinDependencyScope.IMPLEMENTATION_SCOPE import org.jetbrains.kotlin.gradle.plugin.sources.getSourceSetHierarchy import org.jetbrains.kotlin.gradle.targets.metadata.ALL_COMPILE_METADATA_CONFIGURATION_NAME +import org.jetbrains.kotlin.gradle.targets.metadata.KotlinMetadataTargetConfigurator import java.io.File import javax.inject.Inject @@ -64,13 +65,22 @@ open class TransformKotlinGranularMetadata .allDependencies.map { listOf(it.group, it.name, it.version) }.toSet() } - private val transformation = + @get:Internal + internal val transformation: GranularMetadataTransformation by lazy { GranularMetadataTransformation( project, kotlinSourceSet, listOf(API_SCOPE, IMPLEMENTATION_SCOPE), - listOf(project.configurations.getByName(ALL_COMPILE_METADATA_CONFIGURATION_NAME)) + listOf(project.configurations.getByName(ALL_COMPILE_METADATA_CONFIGURATION_NAME)), + lazy { + KotlinMetadataTargetConfigurator.dependsOnWithInterCompilationDependencies(project, kotlinSourceSet).map { + project.tasks.withType(TransformKotlinGranularMetadata::class.java) + .getByName(KotlinMetadataTargetConfigurator.transformGranularMetadataTaskName(it)) + .transformation + } + } ) + } @get:Internal internal val metadataDependencyResolutions: Iterable diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/metadata/KotlinMetadataTargetConfigurator.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/metadata/KotlinMetadataTargetConfigurator.kt index f12879198e6..08b0e35a321 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/metadata/KotlinMetadataTargetConfigurator.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/metadata/KotlinMetadataTargetConfigurator.kt @@ -43,6 +43,17 @@ class KotlinMetadataTargetConfigurator(kotlinPluginVersion: String) : companion object { internal const val ALL_METADATA_JAR_NAME = "allMetadataJar" + + internal fun transformGranularMetadataTaskName(sourceSet: KotlinSourceSet) = + lowerCamelCaseName("transform", sourceSet.name, "DependenciesMetadata") + + // TODO generalize once a general production-test and other kinds of inter-compilation visibility are supported + // Currently, this is a temporary ad-hoc mechanism for exposing the commonMain dependencies to the test source sets + internal fun dependsOnWithInterCompilationDependencies(project: Project, sourceSet: KotlinSourceSet): Set = + sourceSet.dependsOn.toMutableSet().apply { + if (sourceSet.name == KotlinSourceSet.COMMON_TEST_SOURCE_SET_NAME) + add(project.kotlinExtension.sourceSets.getByName(KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME)) + } } private val KotlinOnlyTarget.apiElementsConfiguration: Configuration @@ -97,9 +108,6 @@ class KotlinMetadataTargetConfigurator(kotlinPluginVersion: String) : } } - private fun transformGranularMetadataTaskName(sourceSet: KotlinSourceSet) = - lowerCamelCaseName("transform", sourceSet.name, "DependenciesMetadata") - private fun setupDependencyTransformationForCommonSourceSets(target: KotlinMetadataTarget) { target.project.whenEvaluated { val publishedCommonSourceSets: Set = getPublishedCommonSourceSets(project) @@ -201,18 +209,22 @@ class KotlinMetadataTargetConfigurator(kotlinPluginVersion: String) : isSourceSetPublished: Boolean ) { KotlinDependencyScope.values().forEach { scope -> - val allMetadataConfigurations = mutableListOf().apply { - if (scope != KotlinDependencyScope.COMPILE_ONLY_SCOPE) - add(project.configurations.getByName(ALL_RUNTIME_METADATA_CONFIGURATION_NAME)) - if (scope != KotlinDependencyScope.RUNTIME_ONLY_SCOPE) - add(project.configurations.getByName(ALL_COMPILE_METADATA_CONFIGURATION_NAME)) - } + val allMetadataConfiguration = project.configurations.getByName( + when (scope) { + KotlinDependencyScope.RUNTIME_ONLY_SCOPE -> ALL_RUNTIME_METADATA_CONFIGURATION_NAME + else -> ALL_COMPILE_METADATA_CONFIGURATION_NAME + } + ) val granularMetadataTransformation = GranularMetadataTransformation( project, sourceSet, listOf(scope), - allMetadataConfigurations + allMetadataConfiguration, + lazy { + dependsOnWithInterCompilationDependencies(project, sourceSet).filterIsInstance() + .map { checkNotNull(it.dependencyTransformations[scope]) } + } ) (sourceSet as DefaultKotlinSourceSet).dependencyTransformations[scope] = granularMetadataTransformation