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