[Gradle] k2 structure fix
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/dependencies/AssociatedCompilationsMetadataDependenciesContributor.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/dependencies/AssociatedCompilationsMetadataDependenciesContributor.kt
new file mode 100644
index 0000000..29d2574
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/dependencies/AssociatedCompilationsMetadataDependenciesContributor.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2010-2025 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.gradle.dependencies
+
+import org.gradle.api.file.FileCollection
+import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
+import org.jetbrains.kotlin.gradle.plugin.sources.InternalKotlinSourceSet
+import org.jetbrains.kotlin.gradle.targets.metadata.findMetadataCompilation
+
+internal class AssociatedCompilationMetadataDependencies(
+ val associatedSourceSet: KotlinSourceSet,
+ override val files: FileCollection
+) : KotlinSourceSetDependencies
+
+internal object AssociatedCompilationsMetadataDependenciesContributor :
+ KotlinSourceSetDependenciesContributor<AssociatedCompilationMetadataDependencies> {
+
+ override suspend fun invoke(sourceSet: InternalKotlinSourceSet): List<AssociatedCompilationMetadataDependencies>? {
+ val project = sourceSet.project
+ val associatedSourceSets = sourceSet.getAdditionalVisibleSourceSets()
+ return associatedSourceSets.mapNotNull { associatedSourceSet ->
+ val metadataCompilation = project.findMetadataCompilation(associatedSourceSet) ?: return@mapNotNull null
+ AssociatedCompilationMetadataDependencies(associatedSourceSet, metadataCompilation.output.classesDirs)
+ }
+ }
+}
\ No newline at end of file
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/dependencies/CommonizedNativeDistributionDependenciesContributor.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/dependencies/CommonizedNativeDistributionDependenciesContributor.kt
new file mode 100644
index 0000000..b0a04a2
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/dependencies/CommonizedNativeDistributionDependenciesContributor.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2010-2025 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.gradle.dependencies
+
+import org.gradle.api.file.FileCollection
+import org.jetbrains.kotlin.commonizer.CommonizerTarget
+import org.jetbrains.kotlin.gradle.plugin.sources.InternalKotlinSourceSet
+import org.jetbrains.kotlin.gradle.plugin.sources.internal
+import org.jetbrains.kotlin.gradle.targets.native.internal.commonizedNativeDistributionKlibsOrNull
+import org.jetbrains.kotlin.gradle.targets.native.internal.sharedCommonizerTarget
+
+internal class CommonizedNativeDistributionDependencies(
+ val commonizerTarget: CommonizerTarget,
+ override val files: FileCollection
+) : KotlinSourceSetDependencies {}
+
+internal object CommonizedNativeDistributionDependenciesContributor :
+ KotlinSourceSetDependenciesContributor<CommonizedNativeDistributionDependencies> {
+
+ override suspend fun invoke(sourceSet: InternalKotlinSourceSet): List<CommonizedNativeDistributionDependencies>? {
+ val project = sourceSet.project
+ val commonizerTarget = sourceSet.internal.sharedCommonizerTarget.await() ?: return null
+ val files = project.commonizedNativeDistributionKlibsOrNull(commonizerTarget) ?: return null
+ val fileCollection = project.files(files)
+ return listOf(
+ CommonizedNativeDistributionDependencies(commonizerTarget, fileCollection)
+ )
+ }
+}
\ No newline at end of file
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/dependencies/KotlinConfigurationDependenciesContributor.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/dependencies/KotlinConfigurationDependenciesContributor.kt
new file mode 100644
index 0000000..7ccb614
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/dependencies/KotlinConfigurationDependenciesContributor.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2010-2025 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.gradle.dependencies
+
+import org.gradle.api.file.FileCollection
+import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget
+import org.jetbrains.kotlin.gradle.plugin.sources.InternalKotlinSourceSet
+import org.jetbrains.kotlin.gradle.targets.metadata.singleKotlinCompilationOrNull
+
+internal class KotlinDependencyFromConfiguration(
+ val configurationName: String,
+ override val files: FileCollection,
+) : KotlinSourceSetDependencies
+
+internal object KotlinDependencyFromConfigurationContributor :
+ KotlinSourceSetDependenciesContributor<KotlinDependencyFromConfiguration> {
+
+ override suspend fun invoke(sourceSet: InternalKotlinSourceSet): List<KotlinDependencyFromConfiguration>? {
+ val compilation = sourceSet.singleKotlinCompilationOrNull() ?: return null
+
+ /** Exclude Android because its compileDependencyConfiguration can't be resolved directly */
+ if (compilation.target is KotlinAndroidTarget) return null
+ val project = sourceSet.project
+ val configuration = project.configurations.getByName(compilation.compileDependencyConfigurationName)
+ val files = configuration.incoming.files
+
+ return listOf(KotlinDependencyFromConfiguration(configuration.name, files))
+ }
+}
\ No newline at end of file
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/dependencies/KotlinDependsOnSourceSetMetadataDependenciesContributor.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/dependencies/KotlinDependsOnSourceSetMetadataDependenciesContributor.kt
new file mode 100644
index 0000000..84fe167
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/dependencies/KotlinDependsOnSourceSetMetadataDependenciesContributor.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2010-2025 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.gradle.dependencies
+
+import org.gradle.api.file.FileCollection
+import org.jetbrains.kotlin.gradle.plugin.sources.InternalKotlinSourceSet
+import org.jetbrains.kotlin.gradle.targets.metadata.findMetadataCompilation
+
+internal class KotlinDependsOnSourceSetMetadataDependencies(
+ val sourceSetName: String,
+ override val files: FileCollection
+): KotlinSourceSetDependencies
+
+internal object KotlinDependsOnSourceSetMetadataDependenciesContributor : KotlinSourceSetDependenciesContributor<KotlinDependsOnSourceSetMetadataDependencies> {
+ override suspend fun invoke(sourceSet: InternalKotlinSourceSet): List<KotlinDependsOnSourceSetMetadataDependencies>? {
+ val project = sourceSet.project
+ return sourceSet.dependsOnClosure.mapNotNull { dependsOnSourceSet ->
+ val metadataCompilation = project.findMetadataCompilation(dependsOnSourceSet) ?: return@mapNotNull null
+ KotlinDependsOnSourceSetMetadataDependencies(
+ sourceSetName = dependsOnSourceSet.name,
+ files = metadataCompilation.output.classesDirs
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/dependencies/KotlinNativePlatformDependenciesContributor.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/dependencies/KotlinNativePlatformDependenciesContributor.kt
new file mode 100644
index 0000000..34c5bf1
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/dependencies/KotlinNativePlatformDependenciesContributor.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2010-2025 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.gradle.dependencies
+
+import org.gradle.api.file.FileCollection
+import org.jetbrains.kotlin.gradle.plugin.mpp.AbstractKotlinNativeCompilation
+import org.jetbrains.kotlin.gradle.plugin.sources.InternalKotlinSourceSet
+import org.jetbrains.kotlin.gradle.targets.metadata.singleKotlinCompilationOrNull
+import org.jetbrains.kotlin.gradle.targets.native.internal.retrievePlatformDependencies
+
+internal class KotlinNativePlatformDependency(
+ val platformType: String,
+ override val files: FileCollection,
+) : KotlinSourceSetDependencies
+
+internal object KotlinNativePlatformDependenciesContributor :
+ KotlinSourceSetDependenciesContributor<KotlinNativePlatformDependency> {
+
+ override suspend fun invoke(sourceSet: InternalKotlinSourceSet): List<KotlinNativePlatformDependency>? {
+ val compilation = sourceSet.singleKotlinCompilationOrNull() ?: return null
+ if (compilation !is AbstractKotlinNativeCompilation) return null
+
+ val dependencies = compilation.retrievePlatformDependencies()
+ return listOf(KotlinNativePlatformDependency(
+ platformType = compilation.konanTarget.name,
+ files = dependencies
+ )
+ )
+ }
+}
\ No newline at end of file
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/dependencies/KotlinNativeStdlibDependencyContributor.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/dependencies/KotlinNativeStdlibDependencyContributor.kt
new file mode 100644
index 0000000..24f5bac
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/dependencies/KotlinNativeStdlibDependencyContributor.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2010-2025 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.gradle.dependencies
+
+import org.gradle.api.file.FileCollection
+import org.jetbrains.kotlin.commonizer.stdlib
+import org.jetbrains.kotlin.gradle.plugin.sources.InternalKotlinSourceSet
+import org.jetbrains.kotlin.gradle.targets.metadata.isNativeSourceSet
+import org.jetbrains.kotlin.gradle.utils.konanDistribution
+
+internal class KotlinNativeStdlibDependency(
+ override val files: FileCollection,
+) : KotlinSourceSetDependencies
+
+internal object KotlinNativeStdlibDependencyContributor :
+ KotlinSourceSetDependenciesContributor<KotlinNativeStdlibDependency> {
+
+ override suspend fun invoke(sourceSet: InternalKotlinSourceSet): List<KotlinNativeStdlibDependency>? {
+ if (!sourceSet.isNativeSourceSet.await()) return null
+ val project = sourceSet.project
+
+ return listOf(
+ KotlinNativeStdlibDependency(
+ project.files(project.konanDistribution.stdlib)
+ )
+ )
+ }
+}
\ No newline at end of file
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/dependencies/KotlinSourceSetDependenciesContributor.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/dependencies/KotlinSourceSetDependenciesContributor.kt
new file mode 100644
index 0000000..9dee9f1
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/dependencies/KotlinSourceSetDependenciesContributor.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2010-2025 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.gradle.dependencies
+
+import org.gradle.api.file.FileCollection
+import org.jetbrains.kotlin.gradle.plugin.KotlinGradlePluginExtensionPoint
+import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
+import org.jetbrains.kotlin.gradle.plugin.sources.InternalKotlinSourceSet
+import org.jetbrains.kotlin.gradle.plugin.sources.internal
+
+internal interface KotlinSourceSetDependenciesContributor<T: KotlinSourceSetDependencies> {
+ suspend operator fun invoke(sourceSet: InternalKotlinSourceSet): List<T>?
+
+ companion object {
+ val extensionPoint = KotlinGradlePluginExtensionPoint<KotlinSourceSetDependenciesContributor<*>>()
+
+ private val cache = mutableMapOf<InternalKotlinSourceSet, List<KotlinSourceSetDependencies>>()
+ suspend fun getOrEvaluate(sourceSet: InternalKotlinSourceSet): List<KotlinSourceSetDependencies> =
+ cache.getOrPut(sourceSet) {
+ val res = mutableListOf<KotlinSourceSetDependencies>()
+ extensionPoint[sourceSet.internal.project].forEach { contributor ->
+ val contribution = contributor(sourceSet) ?: emptyList()
+ res.addAll(contribution)
+ }
+ res
+ }
+ }
+}
+
+internal fun <T: KotlinSourceSetDependencies> KotlinSourceSetDependenciesContributor(
+ code: suspend (InternalKotlinSourceSet) -> List<T>?
+) = object : KotlinSourceSetDependenciesContributor<T> {
+ override suspend fun invoke(sourceSet: InternalKotlinSourceSet): List<T>? = code(sourceSet)
+}
+
+
+internal suspend fun KotlinSourceSet.dependencies(): List<KotlinSourceSetDependencies> =
+ KotlinSourceSetDependenciesContributor.getOrEvaluate(internal)
+
+sealed interface KotlinSourceSetDependencies {
+ val files: FileCollection
+}
\ No newline at end of file
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/dependencies/TransformedMetadataDependenciesContributor.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/dependencies/TransformedMetadataDependenciesContributor.kt
new file mode 100644
index 0000000..fe7aeca
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/dependencies/TransformedMetadataDependenciesContributor.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2010-2025 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.gradle.dependencies
+
+import org.gradle.api.file.FileCollection
+import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension
+import org.jetbrains.kotlin.gradle.plugin.KotlinProjectSetupCoroutine
+import org.jetbrains.kotlin.gradle.plugin.mpp.locateOrRegisterMetadataDependencyTransformationTask
+import org.jetbrains.kotlin.gradle.plugin.sources.InternalKotlinSourceSet
+import org.jetbrains.kotlin.gradle.plugin.sources.internal
+import org.jetbrains.kotlin.gradle.plugin.sources.isSharedSourceSet
+
+internal class TransformedMetadataDependencies(
+ override val files: FileCollection
+): KotlinSourceSetDependencies
+
+internal object TransformedMetadataDependenciesContributor : KotlinSourceSetDependenciesContributor<TransformedMetadataDependencies> {
+ override suspend fun invoke(sourceSet: InternalKotlinSourceSet): List<TransformedMetadataDependencies>? {
+ val project = sourceSet.project
+ if (!sourceSet.isSharedSourceSet()) return null
+
+ val transformationTask = project.locateOrRegisterMetadataDependencyTransformationTask(sourceSet)
+ val ownTransformedLibraries = transformationTask.map { it.ownTransformedLibraries() }
+ val fileCollection = project.files(ownTransformedLibraries)
+ return listOf(TransformedMetadataDependencies(fileCollection))
+ }
+}
+
+internal val CreateTransformedMetadataDependencies = KotlinProjectSetupCoroutine {
+ val sharedSourceSets = project.multiplatformExtension.awaitSourceSets().filter { it.internal.isSharedSourceSet() }
+ sharedSourceSets.forEach { sourceSet -> project.locateOrRegisterMetadataDependencyTransformationTask(sourceSet) }
+}
\ No newline at end of file
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/PropertiesProvider.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/PropertiesProvider.kt
index 76cd110..422863f 100644
--- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/PropertiesProvider.kt
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/PropertiesProvider.kt
@@ -137,7 +137,7 @@
),
key = "SeparateKmpCompilation"
)
- }.orElse(false)
+ }.orElse(true)
val incrementalJs: Boolean?
get() = booleanProperty("kotlin.incremental.js")
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinSourceSetDependencies.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinSourceSetDependencies.kt
new file mode 100644
index 0000000..f0c56b6
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinSourceSetDependencies.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2010-2025 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.gradle.plugin.mpp
+
+///**
+// * Single source of truth for dependencies of a Kotlin source set.
+// */
+//internal suspend fun KotlinSourceSet.allSourceSetDependencies(): List<KotlinSourceSetDependency> {
+// return listOfNotNull(
+//// gradleConfigurationDependencies(),
+//// nativeStdlibDependency(),
+//// transformedMetadataDependencies(),
+//// cinteropDependencies(),
+// ).flatten()
+// // types of KotlinSourceSets:
+// // 1. Common
+// // 2. Common Native
+// // 3. Native platform
+// // 4. JVM platform
+// // 5. Android platform
+// // 6. JS platform
+// // 8. Wasm platform
+//}
+
+//private suspend fun KotlinSourceSet.nonPublishableSourceSetMetadataDependencies(): List<KotlinSourceSetDependency> {
+// val platformCompilations = internal.awaitPlatformCompilations()
+// if (!platformCompilations.any { compilation -> !compilation.isMain() }) return emptyList()
+//
+//}
+//
+//internal sealed class KotlinSourceSetDependency {
+// data class ResolvedDependency(val resolvedArtifact: ResolvedArtifact) : KotlinSourceSetDependency()
+// data class KotlinNativeBundleStdlib(val provider: Provider<File>) : KotlinSourceSetDependency()
+// data class TransformedMetadataKlibDependency(val provider: Provider<File>): KotlinSourceSetDependency()
+//}
\ No newline at end of file
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/compilationImpl/KotlinCompilationK2MultiplatformConfigurator.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/compilationImpl/KotlinCompilationK2MultiplatformConfigurator.kt
index 496c938..caa26b7 100644
--- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/compilationImpl/KotlinCompilationK2MultiplatformConfigurator.kt
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/compilationImpl/KotlinCompilationK2MultiplatformConfigurator.kt
@@ -7,144 +7,133 @@
import org.gradle.api.Project
import org.gradle.api.file.FileCollection
-import org.jetbrains.kotlin.commonizer.stdlib
+import org.gradle.api.provider.SetProperty
+import org.jetbrains.kotlin.gradle.dependencies.KotlinSourceSetDependencies
+import org.jetbrains.kotlin.gradle.dependencies.dependencies
+import org.jetbrains.kotlin.gradle.dsl.KotlinDependencies
import org.jetbrains.kotlin.gradle.dsl.usesK2
+import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.Companion.kotlinPropertiesProvider
-import org.jetbrains.kotlin.gradle.plugin.mpp.AbstractKotlinNativeCompilation
-import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinSharedNativeCompilation
+import org.jetbrains.kotlin.gradle.plugin.launch
+import org.jetbrains.kotlin.gradle.plugin.mpp.InternalKotlinCompilation
+import org.jetbrains.kotlin.gradle.plugin.mpp.awaitAllKotlinSourceSets
import org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.factory.KotlinCompilationImplFactory
import org.jetbrains.kotlin.gradle.plugin.sources.android.androidSourceSetInfoOrNull
-import org.jetbrains.kotlin.gradle.plugin.sources.awaitPlatformCompilations
import org.jetbrains.kotlin.gradle.plugin.sources.internal
-import org.jetbrains.kotlin.gradle.plugin.sources.isSharedSourceSet
-import org.jetbrains.kotlin.gradle.targets.metadata.isNativeSourceSet
-import org.jetbrains.kotlin.gradle.targets.metadata.retrieveExternalDependencies
-import org.jetbrains.kotlin.gradle.targets.native.internal.retrievePlatformDependencies
import org.jetbrains.kotlin.gradle.tasks.K2MultiplatformCompilationTask
import org.jetbrains.kotlin.gradle.tasks.K2MultiplatformStructure
-import org.jetbrains.kotlin.gradle.utils.Future
-import org.jetbrains.kotlin.gradle.utils.filesProvider
-import org.jetbrains.kotlin.gradle.utils.future
-import org.jetbrains.kotlin.gradle.utils.konanDistribution
-import org.jetbrains.kotlin.gradle.utils.lazyFuture
-import org.jetbrains.kotlin.utils.topologicalSort
internal object KotlinCompilationK2MultiplatformConfigurator : KotlinCompilationImplFactory.PreConfigure {
- override fun configure(compilation: KotlinCompilationImpl) {
- val project = compilation.project
- compilation.project.tasks.configureEach { compileTask ->
- if (compileTask.name != compilation.compileKotlinTaskName) return@configureEach
- if (compileTask !is K2MultiplatformCompilationTask) return@configureEach
+ /**
+ * Returns fragment name of [this]
+ * by default it is name of [KotlinSourceSet] but for android it should name of compilation's default source set.
+ * i.e. all android-specific source sets (fragments) should be combined into one.
+ * See KT-62508 for detailed explanation
+ */
+ private fun KotlinSourceSet.fragmentName(): String =
+ if (androidSourceSetInfoOrNull != null) {
+ androidSourceSetInfoOrNull!!.androidVariantType.name
+ } else {
+ name
+ }
- /**
- * Returns fragment name of [this]
- * by default it is name of [KotlinSourceSet] but for android it should name of compilation's default source set.
- * i.e. all android-specific source sets (fragments) should be combined into one.
- * See KT-62508 for detailed explanation
- */
- fun KotlinSourceSet.fragmentName(): String =
- if (androidSourceSetInfoOrNull != null) {
- compilation.defaultSourceSet.name
- } else {
- name
- }
+ private fun <T : Any> K2MultiplatformCompilationTask.setIfK2Enabled(
+ propertyGetter: K2MultiplatformStructure.() -> SetProperty<T>,
+ value: () -> Iterable<T>,
+ ): SetProperty<T> {
+ val property = multiplatformStructure.propertyGetter()
+ property.set(compilerOptions.usesK2.map {
+ @Suppress("NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS") // looks like a bug
+ if (it) value() else null
+ })
+ return property
+ }
- compileTask.multiplatformStructure.refinesEdges.set(compilation.project.provider {
- if (!compileTask.compilerOptions.usesK2.get()) return@provider emptyList()
- compilation.allKotlinSourceSets.flatMap { sourceSet ->
- sourceSet.dependsOn.mapNotNull { dependsOn ->
- val from = sourceSet.fragmentName()
- val to = dependsOn.fragmentName()
- if (from == to) return@mapNotNull null
- K2MultiplatformStructure.RefinesEdge(from, to)
- }
- }
- })
-
- compileTask.multiplatformStructure.fragments.set(compilation.project.provider {
- if (!compileTask.compilerOptions.usesK2.get()) return@provider emptyList()
-
- val mostCommonFragmentPerNativePlatforms = project.lazyFuture {
- val refinementGraph = buildMap<String, MutableSet<String>> {
- compileTask.multiplatformStructure.refinesEdges.get().forEach { edge ->
- getOrPut(edge.fromFragmentName) { mutableSetOf() }.add(edge.toFragmentName)
- }
- }
-
- compilation.allKotlinSourceSets
- .filter { it.internal.isSharedSourceSet() }
- .filter { it.isNativeSourceSet.await() }
- .map { // fragment -> native platforms
- it.fragmentName() to it.internal.awaitPlatformCompilations().filterIsInstance<AbstractKotlinNativeCompilation>()
- .map { compilation -> compilation.konanTarget.name }.toSet()
- }
- .groupBy({ it.second }) { it.first } // native platforms -> fragments
- .mapValues { (_, fragments) -> fragments.toSet() }
- .mapValues { (_, fragments) -> // the most common fragments go first
- topologicalSort(fragments) {
- refinementGraph[this]?.filter { it in fragments } ?: emptySet()
- }.reversed()
- }
- .mapValues { (_, fragments) -> fragments.first() }
- }
-
- compilation.allKotlinSourceSets
- .groupBy { it.fragmentName() }
- .map { (fragmentName, sourceSets) ->
- val sourceFiles = sourceSets.map { it.kotlin.asFileTree }
- .reduce { acc, fileTree -> acc + fileTree }
- K2MultiplatformStructure.Fragment(
- fragmentName,
- sourceFiles,
- if (project.kotlinPropertiesProvider.separateKmpCompilation.get()) {
- compilation.project.retrieveFragmentDependencies(
- sourceSets,
- fragmentName,
- mostCommonFragmentPerNativePlatforms
- )
- } else {
- project.files()
- }
- )
- }
- })
-
- compileTask.multiplatformStructure.defaultFragmentName.set(compilation.defaultSourceSet.fragmentName())
+ private fun KotlinCompilationImpl.fetchRefinesEdges(): List<K2MultiplatformStructure.RefinesEdge> {
+ return allKotlinSourceSets.flatMap { sourceSet ->
+ sourceSet.dependsOn.mapNotNull { dependsOn ->
+ val from = sourceSet.fragmentName()
+ val to = dependsOn.fragmentName()
+ if (from == to) return@mapNotNull null
+ K2MultiplatformStructure.RefinesEdge(from, to)
+ }
}
}
- private fun Project.retrieveFragmentDependencies(
- sourceSets: List<KotlinSourceSet>,
- fragmentName: String,
- mostCommonFragmentPerNativePlatformsFuture: Future<Map<Set<String>, String>>,
- ): FileCollection = filesProvider {
- future {
- buildSet {
- for (sourceSet in sourceSets) {
- val internalSourceSet = sourceSet.internal
- if (!internalSourceSet.isSharedSourceSet()) continue
- if (internalSourceSet.isNativeSourceSet.await()) {
- val mostCommonFragmentPerNativePlatforms = mostCommonFragmentPerNativePlatformsFuture.await()
- val mostCommonNativeFragment = mostCommonFragmentPerNativePlatforms.maxBy { it.key.size }.value
- if (mostCommonNativeFragment == fragmentName) {
- add(project.konanDistribution.stdlib)
- }
- val metadataCompilation = internalSourceSet.compilations.filterIsInstance<KotlinSharedNativeCompilation>()
- .find { it.name == sourceSet.name }
- if (metadataCompilation != null) {
- val nativePlatforms = internalSourceSet.awaitPlatformCompilations()
- .filterIsInstance<AbstractKotlinNativeCompilation>()
- .map { compilation -> compilation.konanTarget.name }.toSet()
- if (mostCommonFragmentPerNativePlatforms[nativePlatforms] == fragmentName) {
- add(metadataCompilation.retrievePlatformDependencies())
- }
- }
- }
- // We do not need transitive dependencies defined on higher levels of the hierarchy here
- add(sourceSet.retrieveExternalDependencies(transitive = false))
- }
+ override fun configure(compilation: KotlinCompilationImpl) {
+ val project = compilation.project
+ project.launch {
+ val fragments = compilationFragments(compilation, project)
+ compilation.compileTaskProvider.configure configureTask@{ compileTask ->
+ if (compileTask !is K2MultiplatformCompilationTask) return@configureTask
+ compileTask.setIfK2Enabled(K2MultiplatformStructure::refinesEdges) { compilation.fetchRefinesEdges() }
+ compileTask.multiplatformStructure.fragments.set(fragments)
+ compileTask.multiplatformStructure.defaultFragmentName.set(compilation.defaultSourceSet.fragmentName())
}
- }.getOrThrow()
+ }
+ }
+
+ internal suspend fun compilationDependencies(compilation: InternalKotlinCompilation<*>): Map<KotlinSourceSet, Set<KotlinSourceSetDependencies>> {
+ val allSourceSets = compilation.awaitAllKotlinSourceSets()
+ val reversedDependsOnEdges = mutableMapOf<KotlinSourceSet, MutableSet<KotlinSourceSet>>()
+ allSourceSets.forEach { sourceSet ->
+ sourceSet.dependsOn.forEach { dependsOn ->
+ reversedDependsOnEdges.getOrPut(dependsOn) { mutableSetOf() }.add(sourceSet)
+ }
+ }
+
+ val rootSourceSets = allSourceSets.toMutableSet()
+ reversedDependsOnEdges.values.forEach { rootSourceSets.removeAll(it) }
+
+ class Node(
+ val sourceSet: KotlinSourceSet,
+ val allDependencies: Set<KotlinSourceSetDependencies>,
+ )
+
+ val queue = ArrayDeque<Node>()
+ rootSourceSets.forEach { queue.add(Node(it, mutableSetOf())) }
+
+ // bfs
+ val result = mutableMapOf<KotlinSourceSet, MutableSet<KotlinSourceSetDependencies>>()
+ while (queue.isNotEmpty()) {
+ val node = queue.removeFirst()
+ val nodeDependencies = result.getOrPut(node.sourceSet) { node.sourceSet.dependencies().toMutableSet() }
+ nodeDependencies.removeAll(node.allDependencies)
+
+ val nextNodes = reversedDependsOnEdges[node.sourceSet] ?: continue
+ val allDependencies = node.allDependencies + nodeDependencies
+ for (nextNode in nextNodes) {
+ queue.addLast(Node(nextNode, allDependencies))
+ }
+ }
+
+ return result
+ }
+
+ private suspend fun compilationFragments(
+ compilation: KotlinCompilationImpl,
+ project: Project,
+ ): List<K2MultiplatformStructure.Fragment> {
+ // val isSeparateKmpCompilation = project.kotlinPropertiesProvider.separateKmpCompilation.get()
+
+ //val compilationDependencies = compilationDependencies(compilation)
+ val compilationDependencies = compilation.awaitAllKotlinSourceSets().associateWith { it.dependencies() }
+
+ return compilation.awaitAllKotlinSourceSets()
+ .flatMap { it.internal.withDependsOnClosure }
+ .groupBy { it.fragmentName() }
+ .map { (fragmentName, sourceSets) ->
+ val sourceFiles = sourceSets.map { it.kotlin.asFileTree }.reduce { acc, fileTree -> acc + fileTree }
+ val dependencies = compilationDependencies[sourceSets.first()] ?: emptySet()
+
+ val fileDependencies = project.files()
+ dependencies.forEach { fileDependencies.from(it.files) }
+ K2MultiplatformStructure.Fragment(
+ fragmentName,
+ sourceFiles,
+ fileDependencies,
+ )
+ }
}
}
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/registerKotlinPluginExtensions.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/registerKotlinPluginExtensions.kt
index f5d78b5..8173d41 100644
--- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/registerKotlinPluginExtensions.kt
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/registerKotlinPluginExtensions.kt
@@ -7,7 +7,7 @@
import org.gradle.api.Project
import org.jetbrains.kotlin.gradle.artifacts.*
-import org.jetbrains.kotlin.gradle.plugin.mpp.uklibs.diagnostics.UklibPublicationDiagnosticsSetupAction
+import org.jetbrains.kotlin.gradle.dependencies.*
import org.jetbrains.kotlin.gradle.dsl.*
import org.jetbrains.kotlin.gradle.internal.CustomizeKotlinDependenciesSetupAction
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.Companion.kotlinPropertiesProvider
@@ -32,6 +32,7 @@
import org.jetbrains.kotlin.gradle.plugin.mpp.resources.publication.SetUpMultiplatformAndroidAssetsAndResourcesPublicationAction
import org.jetbrains.kotlin.gradle.plugin.mpp.resources.publication.SetUpMultiplatformJvmResourcesPublicationAction
import org.jetbrains.kotlin.gradle.plugin.mpp.uklibs.consumption.UklibConsumptionSetupAction
+import org.jetbrains.kotlin.gradle.plugin.mpp.uklibs.diagnostics.UklibPublicationDiagnosticsSetupAction
import org.jetbrains.kotlin.gradle.plugin.sources.KotlinMultiplatformSourceSetSetupAction
import org.jetbrains.kotlin.gradle.plugin.sources.LanguageSettingsSetupAction
import org.jetbrains.kotlin.gradle.plugin.statistics.FinalizeConfigurationFusMetricAction
@@ -39,8 +40,8 @@
import org.jetbrains.kotlin.gradle.scripting.internal.ScriptingGradleSubpluginSetupAction
import org.jetbrains.kotlin.gradle.targets.*
import org.jetbrains.kotlin.gradle.targets.js.npm.AddNpmDependencyExtensionProjectSetupAction
-import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmCompilationWireJavaSourcesSideEffect
import org.jetbrains.kotlin.gradle.targets.jvm.ConfigureJavaTestFixturesSideEffect
+import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmCompilationWireJavaSourcesSideEffect
import org.jetbrains.kotlin.gradle.targets.metadata.KotlinMetadataTargetSetupAction
import org.jetbrains.kotlin.gradle.targets.native.ConfigureFrameworkExportSideEffect
import org.jetbrains.kotlin.gradle.targets.native.CreateFatFrameworksSetupAction
@@ -101,6 +102,7 @@
register(project, SetUpMultiplatformAndroidAssetsAndResourcesPublicationAction)
register(project, SetUpSwiftExportAction)
register(project, ConfigureKotlinTopLevelDependenciesDSL)
+ register(project, CreateTransformedMetadataDependencies)
if (isKmpProjectIsolationEnabled) {
register(project, ProjectStructureMetadataForKMPSetupAction)
@@ -146,6 +148,7 @@
register(project, KotlinCreateNativeCInteropTasksSideEffect)
register(project, KotlinCreateCompilationArchivesTask)
register(project, KotlinJvmCompilationWireJavaSourcesSideEffect)
+// register(project, ConfigureK2MultiplatformStructureDumpTasks)
}
KotlinTargetArtifact.extensionPoint.apply {
@@ -196,6 +199,18 @@
register(project, NativeBinaryConfigurationChecker)
}
}
+
+ if (project.kotlinPropertiesProvider.separateKmpCompilation.get()) {
+ KotlinSourceSetDependenciesContributor.extensionPoint.apply {
+ register(project, KotlinDependencyFromConfigurationContributor)
+ register(project, KotlinNativeStdlibDependencyContributor)
+ register(project, KotlinNativePlatformDependenciesContributor)
+ register(project, CommonizedNativeDistributionDependenciesContributor)
+ register(project, TransformedMetadataDependenciesContributor)
+ register(project, AssociatedCompilationsMetadataDependenciesContributor)
+// register(project, KotlinDependsOnSourceSetMetadataDependenciesContributor)
+ }
+ }
}
private val Project.isKmpProjectIsolationEnabled get() = PropertiesProvider(project).kotlinKmpProjectIsolationEnabled
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/DefaultKotlinSourceSet.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/DefaultKotlinSourceSet.kt
index 6fda1f8..e1c3570 100644
--- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/DefaultKotlinSourceSet.kt
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/DefaultKotlinSourceSet.kt
@@ -138,7 +138,7 @@
return getDependenciesTransformation()
}
- fun getAdditionalVisibleSourceSets(): List<KotlinSourceSet> = getVisibleSourceSetsFromAssociateCompilations(this)
+ override fun getAdditionalVisibleSourceSets(): List<KotlinSourceSet> = getVisibleSourceSetsFromAssociateCompilations(this)
internal fun getDependenciesTransformation(): Iterable<MetadataDependencyTransformation> {
val metadataDependencyResolutionByModule =
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/InternalKotlinSourceSet.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/InternalKotlinSourceSet.kt
index 0b0ac15..695fb52 100644
--- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/InternalKotlinSourceSet.kt
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/InternalKotlinSourceSet.kt
@@ -23,6 +23,7 @@
val dependsOnClosure: ObservableSet<KotlinSourceSet>
val withDependsOnClosure: ObservableSet<KotlinSourceSet>
val compilations: MutableObservableSet<KotlinCompilation<*>>
+ fun getAdditionalVisibleSourceSets(): List<KotlinSourceSet>
}
internal suspend fun InternalKotlinSourceSet.awaitPlatformCompilations(): Set<KotlinCompilation<*>> {
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/metadata/KotlinMetadataTargetConfigurator.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/metadata/KotlinMetadataTargetConfigurator.kt
index 2d1c035d8..3a483d5 100644
--- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/metadata/KotlinMetadataTargetConfigurator.kt
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/metadata/KotlinMetadataTargetConfigurator.kt
@@ -276,6 +276,9 @@
internal fun isSingleKotlinTargetSourceSet(sourceSet: KotlinSourceSet): Boolean =
sourceSet.platformCompilations().map { it.target }.toSet().size == 1
+internal fun InternalKotlinSourceSet.singleKotlinCompilationOrNull(): KotlinCompilation<*>? =
+ platformCompilations().distinctBy { it.target }.singleOrNull()
+
internal fun isMultipleKotlinTargetSourceSet(sourceSet: KotlinSourceSet): Boolean =
sourceSet.platformCompilations().map { it.target }.toSet().size > 1
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/K2MultiplatformStructure.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/K2MultiplatformStructure.kt
index 058f2d4..f6f6189 100644
--- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/K2MultiplatformStructure.kt
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/K2MultiplatformStructure.kt
@@ -5,6 +5,8 @@
package org.jetbrains.kotlin.gradle.tasks
+import com.google.gson.GsonBuilder
+import com.google.gson.JsonObject
import org.gradle.api.file.FileCollection
import org.gradle.api.provider.ListProperty
import org.gradle.api.provider.Property
@@ -14,6 +16,7 @@
import org.gradle.work.Incremental
import org.gradle.work.NormalizeLineEndings
import org.jetbrains.kotlin.gradle.InternalKotlinGradlePluginApi
+import org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.KotlinCompilationSideEffect
import java.io.File
@InternalKotlinGradlePluginApi
@@ -109,3 +112,91 @@
"${edge.fromFragmentName}:${edge.toFragmentName}"
}.toTypedArray()
+//internal abstract class K2MultiplatformStructureDumpTask : DefaultTask() {
+// @get:Nested
+// abstract val structure: Property<K2MultiplatformStructure>
+//
+// @get:OutputFile
+// abstract val output: RegularFileProperty
+//
+// @TaskAction
+// fun action() {
+// val structure = structure.get()
+// val output = output.get().asFile
+// output.writeText(structure.toJson())
+// }
+//}
+
+internal val ConfigureK2MultiplatformStructureDumpTasks = KotlinCompilationSideEffect { compilation ->
+ val project = compilation.project
+ println("Compilation ${compilation.name} of target ${compilation.target.name} of project ${project.path}")
+ println("Has task name: ${compilation.compileTaskProvider.name}")
+ compilation.compileTaskProvider.configure { task ->
+ println("Configure Task: ${task.name} of project ${project.path}")
+ if (task !is K2MultiplatformCompilationTask) return@configure
+ val k2StructureOutput = project.layout.buildDirectory.file("kgp-debug-data/compilations/${task.name}-k2-structure.json")
+ val classpathOutput = project.layout.buildDirectory.file("kgp-debug-data/compilations/${task.name}-classpath.txt")
+
+ task.outputs.files(k2StructureOutput)
+ task.doLast {
+ if (task is KotlinCompileTool) {
+ println("Writing libraries classpath to ${classpathOutput.get().asFile.absolutePath}")
+ classpathOutput.get().asFile.writeText(task.libraries.files.joinToString("\n"))
+ }
+
+ println("Writing K2MultiplatformStructure to ${k2StructureOutput.get().asFile.absolutePath}")
+ k2StructureOutput.get().asFile.writeText(task.multiplatformStructure.toJson())
+ }
+ }
+
+// project.tasks.register(compilation.disambiguateName("K2MultiplatformStructureDump"), K2MultiplatformStructureDumpTask::class.java) {
+// val compileTaskProvider = compilation.compileTaskProvider
+// val structureProvider = compileTaskProvider.map { compileTask ->
+// (compileTask as K2MultiplatformCompilationTask).multiplatformStructure
+// }
+// it.dependsOn(compileTaskProvider)
+// it.structure.set(structureProvider)
+// it.output.set(project.layout.buildDirectory.file("k2MultiplatformStructure/${compilation.disambiguatedName}.json"))
+// }
+}
+
+private fun K2MultiplatformStructure.toJson(): String {
+ val root = JsonObject()
+
+ // Materialize defaultFragmentName if present
+ this.defaultFragmentName.orNull?.let { root.addProperty("defaultFragmentName", it) }
+
+ // Materialize fragments with sources and dependencies as lists of files
+ val fragmentsArray = com.google.gson.JsonArray()
+ for (fragment in this.fragments.get()) {
+ val fragmentObj = JsonObject()
+ fragmentObj.addProperty("fragmentName", fragment.fragmentName)
+
+ val sourcesArray = com.google.gson.JsonArray()
+ for (file in fragment.sources.files) {
+ sourcesArray.add(file.absolutePath)
+ }
+ fragmentObj.add("sources", sourcesArray)
+
+ val depsArray = com.google.gson.JsonArray()
+ for (file in fragment.dependencies.files) {
+ depsArray.add(file.absolutePath)
+ }
+ fragmentObj.add("dependencies", depsArray)
+
+ fragmentsArray.add(fragmentObj)
+ }
+ root.add("fragments", fragmentsArray)
+
+ // Materialize refines edges
+ val edgesArray = com.google.gson.JsonArray()
+ for (edge in this.refinesEdges.get()) {
+ val edgeObj = JsonObject()
+ edgeObj.addProperty("fromFragmentName", edge.fromFragmentName)
+ edgeObj.addProperty("toFragmentName", edge.toFragmentName)
+ edgesArray.add(edgeObj)
+ }
+ root.add("refinesEdges", edgesArray)
+
+ return GsonBuilder().setPrettyPrinting().create().toJson(root)
+}
\ No newline at end of file
diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/dependencyResolutionTests/MavenRepositoryMock.kt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/dependencyResolutionTests/MavenRepositoryMock.kt
index a8f3bdc..f8b95e4 100644
--- a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/dependencyResolutionTests/MavenRepositoryMock.kt
+++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/dependencyResolutionTests/MavenRepositoryMock.kt
@@ -6,9 +6,13 @@
package org.jetbrains.kotlin.gradle.dependencyResolutionTests
import org.gradle.api.Project
+import org.gradle.api.attributes.AttributeContainer
import org.gradle.kotlin.dsl.maven
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
+import org.jetbrains.kotlin.gradle.plugin.mpp.DefaultKotlinUsageContext
+import org.jetbrains.kotlin.gradle.plugin.mpp.internal
+import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget
import org.jetbrains.kotlin.gradle.utils.targets
import java.io.File
@@ -21,6 +25,11 @@
val asMavenNotation get() = moduleKey(group, name, version)
val dependencies: MutableSet<Module> = mutableSetOf()
+ fun artifactName(target: KotlinTarget, usageContext: DefaultKotlinUsageContext): String {
+ val extension = if (target is KotlinJvmTarget) "jar" else "klib"
+ return "$name-${usageContext.dependencyConfigurationName}-$version.$extension"
+ }
+
/**
* Limitation! All modules should be defined outside of the [variantDependencies] lambda
*/
@@ -40,6 +49,9 @@
fun applyToProject(project: Project, repositoryDir: File) {
project.allprojects { it.repositories.maven(repositoryDir) }
+ }
+
+ fun publishMocks(project: Project, repositoryDir: File) {
val targets = project.kotlinExtension.targets.toList()
declaredModules.values.forEach { module -> module.publishAsMockedLibrary(repositoryDir, targets) }
}
@@ -73,7 +85,7 @@
init: MavenRepositoryMockDsl.() -> Unit,
): MavenRepositoryMock {
val mock = mockMavenRepository(init)
- mock.applyToProject(this, repositoryDir)
+ mock.publishMocks(this, repositoryDir)
return mock
}
@@ -112,10 +124,18 @@
</project>
""".trimIndent()
)
+
+ kotlinTargets.forEach { target ->
+ target.publishableUsageContexts().forEach { usageContext ->
+ val artifactName = artifactName(target, usageContext)
+ val artifactFile = moduleRootDir.resolve(artifactName)
+ artifactFile.writeText("Mocked artifact content for $target / ${usageContext.name}")
+ }
+ }
}
private fun MavenRepositoryMock.Module.publishedMockedGradleMetadata(kotlinTargets: List<KotlinTarget>): String {
- val variants = kotlinTargets.joinToString(",") { variantJson(it) + "\n" }
+ val variants = kotlinTargets.flatMap { variantJsons(it) }.joinToString(",") { "$it\n" }
return """
{
"formatVersion": "1.1",
@@ -134,15 +154,31 @@
""".trimIndent()
}
-private fun MavenRepositoryMock.Module.variantJson(target: KotlinTarget): String {
- val apiElements = target.project.configurations.getByName(target.apiElementsConfigurationName)
- val attributesString = apiElements
- .attributes
+private fun KotlinTarget.publishableUsageContexts() = internal
+ .kotlinComponents.flatMap { component ->
+ if (!component.publishable) return@flatMap emptyList()
+ component.internal.usages.filterIsInstance<DefaultKotlinUsageContext>()
+ }
+
+private fun MavenRepositoryMock.Module.variantJsons(target: KotlinTarget): List<String> =
+ target.publishableUsageContexts().map { usageContext ->
+ val artifactName = artifactName(target, usageContext)
+ val variantDependencies = variantDependencies(target)
+ val variantName = usageContext.name
+ variantJsons(artifactName, variantDependencies, variantName, usageContext.attributes)
+ }
+
+private fun MavenRepositoryMock.Module.variantJsons(
+ artifactName: String,
+ variantDependencies: Iterable<MavenRepositoryMock.Module>,
+ variantName: String,
+ attributes: AttributeContainer
+): String {
+ val attributesString = attributes
.keySet()
- .map { it to apiElements.attributes.getAttribute(it) }
+ .map { it to attributes.getAttribute(it) }
.joinToString(",\n") { "\"${it.first.name}\": \"${it.second}\"" }
- val variantDependencies = target.variantDependencies()
val allDependencies = dependencies + variantDependencies
val dependenciesJson = allDependencies.joinToString(", \n") { moduleDependency ->
@@ -159,11 +195,17 @@
return """
{
- "name": "${target.name.ifEmpty { "apiElements" }}",
+ "name": "$variantName",
"attributes": {
$attributesString
},
- "dependencies": [ $dependenciesJson ]
+ "dependencies": [ $dependenciesJson ],
+ "files": [
+ {
+ "name": "$artifactName",
+ "url": "$artifactName"
+ }
+ ]
}
""".trimIndent()
}
\ No newline at end of file
diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/dependencyResolutionTests/SourceSetDependenciesResolution.kt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/dependencyResolutionTests/SourceSetDependenciesResolution.kt
index d000fd5..b2a3a35 100644
--- a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/dependencyResolutionTests/SourceSetDependenciesResolution.kt
+++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/dependencyResolutionTests/SourceSetDependenciesResolution.kt
@@ -7,22 +7,29 @@
import org.gradle.api.Project
import org.gradle.api.internal.project.ProjectInternal
+import org.jetbrains.kotlin.gradle.dependencies.KotlinSourceSetDependencies
+import org.jetbrains.kotlin.gradle.dependencies.KotlinSourceSetDependenciesContributor
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
import org.jetbrains.kotlin.gradle.dsl.platformTargets
+import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
import org.jetbrains.kotlin.gradle.plugin.mpp.resolvableMetadataConfiguration
import org.jetbrains.kotlin.gradle.plugin.sources.internal
+import org.jetbrains.kotlin.gradle.testing.prettyPrinted
import org.jetbrains.kotlin.gradle.util.*
+import org.jetbrains.kotlin.gradle.utils.future
import org.jetbrains.kotlin.gradle.utils.targets
import org.jetbrains.kotlin.test.KotlinTestUtils
import org.junit.Rule
import org.junit.rules.TemporaryFolder
+import java.io.File
+import kotlin.test.assertEquals
abstract class SourceSetDependenciesResolution {
@get:Rule
val tempFolder = TemporaryFolder()
class SourceSetDependenciesDsl(
- private val project: Project
+ val project: Project
) {
val mavenRepositoryMock = MavenRepositoryMock()
@@ -60,6 +67,25 @@
configure: SourceSetDependenciesDsl.(Project) -> Unit
) {
val repoRoot = tempFolder.newFolder()
+ val project = prepareProject(repoRoot, withProject, configure)
+
+ val actualResult = project.resolveAllSourceSetDependencies().sanitize()
+ val expectedFile = resourcesRoot
+ .resolve("dependenciesResolution")
+ .resolve(this.javaClass.simpleName)
+ .resolve(expectedFilePath)
+
+ KotlinTestUtils.assertEqualsToFile(expectedFile.toFile(), actualResult) {
+ // remove comment lines
+ it.replace("""\s+//.+""".toRegex(), "")
+ }
+ }
+
+ private fun prepareProject(
+ repoRoot: File,
+ withProject: ProjectInternal?,
+ configure: SourceSetDependenciesDsl.(Project) -> Unit,
+ ): Project {
val project = withProject ?: buildProject {
// Disable stdlib and kotlin-dom-api for default tests, as they just pollute dependencies dumps
enableDefaultStdlibDependency(false)
@@ -76,16 +102,29 @@
project.evaluate()
- val actualResult = project.resolveAllSourceSetDependencies().sanitize()
- val expectedFile = resourcesRoot
- .resolve("dependenciesResolution")
- .resolve(this.javaClass.simpleName)
- .resolve(expectedFilePath)
+ dsl.mavenRepositoryMock.publishMocks(project, repoRoot)
+ return project
+ }
- KotlinTestUtils.assertEqualsToFile(expectedFile.toFile(), actualResult) {
- // remove comment lines
- it.replace("""\s+//.+""".toRegex(), "")
+ internal fun <T : KotlinSourceSetDependencies> testKotlinSourceSetDependenciesContributor(
+ contributor: KotlinSourceSetDependenciesContributor<T>,
+ expectedDependencies: Map<String, Any>,
+ withProject: ProjectInternal? = null,
+ configure: SourceSetDependenciesDsl.(Project) -> Unit
+ ) {
+ val repoRoot = tempFolder.newFolder()
+ val project = prepareProject(repoRoot, withProject, configure)
+ val res: Map<KotlinSourceSet, List<T>?> = project.future {
+ project.kotlinExtension.sourceSets.associateWith { contributor.invoke(it.internal) }
+ }.getOrThrow()
+
+ val dependencies = res.mapKeys { (key, _) -> key.name }.mapValues { (_, value) ->
+ value?.map { it.files }
}
+ assertEquals(
+ expectedDependencies.prettyPrinted.toString(),
+ dependencies.prettyPrinted.toString()
+ )
}
private fun Project.resolveAllSourceSetDependencies(): String {
diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/dependencyResolutionTests/contributors/KotlinDependencyFromConfigurationContributorTest.kt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/dependencyResolutionTests/contributors/KotlinDependencyFromConfigurationContributorTest.kt
new file mode 100644
index 0000000..3f2ed11
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/dependencyResolutionTests/contributors/KotlinDependencyFromConfigurationContributorTest.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2010-2025 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+@file:Suppress("FunctionName")
+
+package org.jetbrains.kotlin.gradle.dependencyResolutionTests.contributors
+
+import org.gradle.api.Project
+import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
+import org.jetbrains.kotlin.gradle.dependencies.KotlinDependencyFromConfigurationContributor
+import org.jetbrains.kotlin.gradle.dependencyResolutionTests.SourceSetDependenciesResolution
+import org.jetbrains.kotlin.gradle.util.applyMultiplatformPlugin
+import org.jetbrains.kotlin.gradle.util.configureDefaults
+import org.jetbrains.kotlin.gradle.util.kotlin
+import org.jetbrains.kotlin.gradle.util.setMultiplatformAndroidSourceSetLayoutVersion
+import org.jetbrains.kotlin.gradle.utils.androidExtension
+import kotlin.test.Test
+
+@OptIn(ExperimentalWasmDsl::class)
+internal class KotlinDependencyFromConfigurationContributorTest : SourceSetDependenciesResolution() {
+ private val contributor = KotlinDependencyFromConfigurationContributor
+
+ private fun Project.defaultTargets() {
+ setMultiplatformAndroidSourceSetLayoutVersion(2)
+ applyMultiplatformPlugin()
+ plugins.apply("com.android.library")
+ androidExtension.configureDefaults()
+
+ kotlin {
+ jvm()
+ linuxX64()
+ js()
+ wasmJs()
+ wasmWasi()
+ androidTarget {
+ publishLibraryVariants("release")
+ }
+
+ applyDefaultHierarchyTemplate {
+ common {
+ group("jvmAndAndroid") {
+ withAndroidTarget()
+ withJvm()
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `resolves dependencies for jvm target`() {
+ val expected = mutableMapOf(
+ "x" to "x"
+ )
+ testKotlinSourceSetDependenciesContributor(contributor, expected) { project ->
+ project.defaultTargets()
+
+// api("commonMain", "test:lib-commonMain:1.0")
+// api("commonTest", "test:lib-commonTest:1.0")
+// api("jvmAndAndroidMain", "test:lib-jvmAndAndroidMain:1.0")
+// api("jvmAndAndroidTest", "test:lib-jvmAndAndroidTest:1.0")
+ api("jvmMain", "test:lib:1.0")
+// api("jvmTest", "test:lib-test:1.0")
+ }
+ }
+}
\ No newline at end of file
diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/uklibs/PrettyPrintTest.kt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/uklibs/PrettyPrintTest.kt
index 3132233..70f25b7 100644
--- a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/uklibs/PrettyPrintTest.kt
+++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/uklibs/PrettyPrintTest.kt
@@ -6,6 +6,8 @@
package org.jetbrains.kotlin.gradle.unitTests.uklibs
import org.jetbrains.kotlin.gradle.testing.prettyPrinted
+import org.jetbrains.kotlin.gradle.testing.prettyPrintedFileCollectionOf
+import org.jetbrains.kotlin.gradle.util.buildProject
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
@@ -84,6 +86,7 @@
val a: Int,
val b: String,
)
+
data class B(val m: Map<String, A>)
assertEquals(
@@ -109,6 +112,35 @@
}
@Test
+ fun testFileCollection() {
+ val project = buildProject {}
+ val fc = project.files("file1.txt", "dir/file2.txt")
+ assertEquals(
+ """
+ prettyPrintedFileCollectionOf(
+ "${project.projectDir}/file1.txt",
+ "${project.projectDir}/dir/file2.txt",
+ )
+ """.trimIndent(),
+ fc.prettyPrinted.toString(),
+ )
+ }
+
+ @Test
+ fun testPrettyPrintedFileCollectionOf() {
+ val project = buildProject {}
+ val fc = project.files("file1.txt", "dir/file2.txt")
+
+ assertEquals(
+ prettyPrintedFileCollectionOf(
+ "${project.projectDir}/file1.txt",
+ "${project.projectDir}/dir/file2.txt",
+ ).toString(),
+ fc.prettyPrinted.toString(),
+ )
+ }
+
+ @Test
fun equalityComparisonOfPrettyPrintedTypes() {
data class C(
val value: String
diff --git a/libraries/tools/kotlin-gradle-plugin/src/testFixtures/kotlin/org/jetbrains/kotlin/gradle/testing/PrettyPrint.kt b/libraries/tools/kotlin-gradle-plugin/src/testFixtures/kotlin/org/jetbrains/kotlin/gradle/testing/PrettyPrint.kt
index 59f1618..de91d7f 100644
--- a/libraries/tools/kotlin-gradle-plugin/src/testFixtures/kotlin/org/jetbrains/kotlin/gradle/testing/PrettyPrint.kt
+++ b/libraries/tools/kotlin-gradle-plugin/src/testFixtures/kotlin/org/jetbrains/kotlin/gradle/testing/PrettyPrint.kt
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.gradle.testing
+import org.gradle.api.file.FileCollection
import kotlin.reflect.full.memberProperties
/** Pretty print diffable by text and copypastable collection-like hierarchies */
@@ -17,6 +18,18 @@
val indentationSpace = " ".repeat(indentation)
val nextIndentationDepth = indentation + 2
val elements: Array<String> = when (value) {
+ is FileCollection -> {
+ val files = value.files.map { it.absolutePath }
+ arrayOf(
+ PrettyPrintedFileCollection(files).prettyPrinted(indentation).toString()
+ )
+ }
+ is PrettyPrintedFileCollection -> arrayOf(
+ "prettyPrintedFileCollectionOf(",
+ *value.map { twoSpaces + it.prettyPrinted(nextIndentationDepth) + "," }.toTypedArray(),
+ ")"
+ )
+
is Map<*, *> -> arrayOf(
"mutableMapOf(",
*value.map { it }.sortedBy { it.key.toString() }.map {
@@ -78,4 +91,7 @@
private fun Any.prettyPrinted(indentation: Int = 0): PrettyPrint<Any> = PrettyPrint(this, indentation)
}
-val <T : Any> T.prettyPrinted: PrettyPrint<T> get() = PrettyPrint(this, 0)
\ No newline at end of file
+val <T : Any> T.prettyPrinted: PrettyPrint<T> get() = PrettyPrint(this, 0)
+
+private class PrettyPrintedFileCollection(files: List<String>) : List<String> by files
+fun prettyPrintedFileCollectionOf(vararg files: String): List<String> = PrettyPrintedFileCollection(files.toList())
\ No newline at end of file