Deprecate KotlinSourceSet.getSourceSetHierarchy in favour of 'resolveAllDependsOnSourceSets' and general purpose 'transitiveClosure'
This commit is contained in:
committed by
Space
parent
4e07dcf2d8
commit
7da7dd92d6
+89
@@ -0,0 +1,89 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2021 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Associate compilations are not yet supported by the IDE. KT-34102 */
|
||||||
|
@file:Suppress("invisible_reference", "invisible_member", "FunctionName", "DuplicatedCode")
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.gradle
|
||||||
|
|
||||||
|
import org.gradle.api.Project
|
||||||
|
import org.gradle.testfixtures.ProjectBuilder
|
||||||
|
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
|
||||||
|
import org.jetbrains.kotlin.gradle.plugin.sources.resolveAllDependsOnSourceSets
|
||||||
|
import org.jetbrains.kotlin.gradle.plugin.sources.resolveAllSourceSetsDependingOn
|
||||||
|
import org.junit.Test
|
||||||
|
import kotlin.test.BeforeTest
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
class ResolveKotlinSourceSetsTest {
|
||||||
|
private lateinit var project: Project
|
||||||
|
private lateinit var kotlin: KotlinMultiplatformExtension
|
||||||
|
|
||||||
|
@BeforeTest
|
||||||
|
fun setup() {
|
||||||
|
project = ProjectBuilder.builder().build()
|
||||||
|
project.plugins.apply("kotlin-multiplatform")
|
||||||
|
kotlin = project.extensions.getByName("kotlin") as KotlinMultiplatformExtension
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun resolveAllSourceSetsDependingOn() {
|
||||||
|
val commonMain = kotlin.sourceSets.getByName("commonMain")
|
||||||
|
val nativeMain = kotlin.sourceSets.create("nativeMain")
|
||||||
|
val linuxMain = kotlin.sourceSets.create("linuxMain")
|
||||||
|
val macosMain = kotlin.sourceSets.create("macosMain")
|
||||||
|
val jvmMain = kotlin.sourceSets.create("jvmMain")
|
||||||
|
|
||||||
|
jvmMain.dependsOn(commonMain)
|
||||||
|
nativeMain.dependsOn(commonMain)
|
||||||
|
linuxMain.dependsOn(nativeMain)
|
||||||
|
macosMain.dependsOn(nativeMain)
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
setOf(nativeMain, linuxMain, macosMain, jvmMain),
|
||||||
|
kotlin.resolveAllSourceSetsDependingOn(commonMain)
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
setOf(linuxMain, macosMain),
|
||||||
|
kotlin.resolveAllSourceSetsDependingOn(nativeMain)
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEquals(emptySet(), kotlin.resolveAllSourceSetsDependingOn(linuxMain))
|
||||||
|
assertEquals(emptySet(), kotlin.resolveAllSourceSetsDependingOn(macosMain))
|
||||||
|
assertEquals(emptySet(), kotlin.resolveAllSourceSetsDependingOn(jvmMain))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun resolveAllDependsOnSourceSets() {
|
||||||
|
val commonMain = kotlin.sourceSets.getByName("commonMain")
|
||||||
|
val nativeMain = kotlin.sourceSets.create("nativeMain")
|
||||||
|
val linuxMain = kotlin.sourceSets.create("linuxMain")
|
||||||
|
val macosMain = kotlin.sourceSets.create("macosMain")
|
||||||
|
val jvmMain = kotlin.sourceSets.create("jvmMain")
|
||||||
|
|
||||||
|
jvmMain.dependsOn(commonMain)
|
||||||
|
nativeMain.dependsOn(commonMain)
|
||||||
|
linuxMain.dependsOn(nativeMain)
|
||||||
|
macosMain.dependsOn(nativeMain)
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
setOf(nativeMain, commonMain), linuxMain.resolveAllDependsOnSourceSets()
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
setOf(commonMain), nativeMain.resolveAllDependsOnSourceSets()
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
emptySet(), commonMain.resolveAllDependsOnSourceSets()
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
setOf(commonMain), jvmMain.resolveAllDependsOnSourceSets()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
+18
-10
@@ -11,16 +11,14 @@ import org.gradle.api.Project
|
|||||||
import org.gradle.api.file.SourceDirectorySet
|
import org.gradle.api.file.SourceDirectorySet
|
||||||
import org.gradle.util.ConfigureUtil
|
import org.gradle.util.ConfigureUtil
|
||||||
import org.jetbrains.kotlin.build.DEFAULT_KOTLIN_SOURCE_FILES_EXTENSIONS
|
import org.jetbrains.kotlin.build.DEFAULT_KOTLIN_SOURCE_FILES_EXTENSIONS
|
||||||
|
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
|
||||||
import org.jetbrains.kotlin.gradle.plugin.KotlinDependencyHandler
|
import org.jetbrains.kotlin.gradle.plugin.KotlinDependencyHandler
|
||||||
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
|
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
|
||||||
import org.jetbrains.kotlin.gradle.plugin.LanguageSettingsBuilder
|
import org.jetbrains.kotlin.gradle.plugin.LanguageSettingsBuilder
|
||||||
import org.jetbrains.kotlin.gradle.plugin.mpp.*
|
import org.jetbrains.kotlin.gradle.plugin.mpp.*
|
||||||
import org.jetbrains.kotlin.gradle.plugin.mpp.GranularMetadataTransformation
|
|
||||||
import org.jetbrains.kotlin.gradle.plugin.mpp.MetadataDependencyResolution
|
|
||||||
import org.jetbrains.kotlin.gradle.plugin.whenEvaluated
|
import org.jetbrains.kotlin.gradle.plugin.whenEvaluated
|
||||||
import org.jetbrains.kotlin.gradle.utils.*
|
import org.jetbrains.kotlin.gradle.utils.*
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.lang.reflect.Constructor
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
const val METADATA_CONFIGURATION_NAME_SUFFIX = "DependenciesMetadata"
|
const val METADATA_CONFIGURATION_NAME_SUFFIX = "DependenciesMetadata"
|
||||||
@@ -72,7 +70,7 @@ class DefaultKotlinSourceSet(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun languageSettings(configure: LanguageSettingsBuilder.() -> Unit): LanguageSettingsBuilder =
|
override fun languageSettings(configure: LanguageSettingsBuilder.() -> Unit): LanguageSettingsBuilder =
|
||||||
languageSettings.apply { configure(this) }
|
languageSettings.apply { configure(this) }
|
||||||
|
|
||||||
override fun getName(): String = displayName
|
override fun getName(): String = displayName
|
||||||
|
|
||||||
@@ -232,6 +230,12 @@ internal fun KotlinSourceSet.disambiguateName(simpleName: String): String {
|
|||||||
private fun createDefaultSourceDirectorySet(project: Project, name: String?): SourceDirectorySet =
|
private fun createDefaultSourceDirectorySet(project: Project, name: String?): SourceDirectorySet =
|
||||||
project.objects.sourceDirectorySet(name, name)
|
project.objects.sourceDirectorySet(name, name)
|
||||||
|
|
||||||
|
|
||||||
|
@Deprecated(
|
||||||
|
"Use 'getAllDependsOnSourceSets' instead",
|
||||||
|
level = DeprecationLevel.WARNING,
|
||||||
|
replaceWith = ReplaceWith("getAllDependsOnSourceSets")
|
||||||
|
)
|
||||||
internal fun KotlinSourceSet.getSourceSetHierarchy(): Set<KotlinSourceSet> {
|
internal fun KotlinSourceSet.getSourceSetHierarchy(): Set<KotlinSourceSet> {
|
||||||
val result = mutableSetOf<KotlinSourceSet>()
|
val result = mutableSetOf<KotlinSourceSet>()
|
||||||
|
|
||||||
@@ -245,10 +249,14 @@ internal fun KotlinSourceSet.getSourceSetHierarchy(): Set<KotlinSourceSet> {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun KotlinSourceSet.resolveAllDependsOnSourceSets(): Set<KotlinSourceSet> {
|
||||||
|
return transitiveClosure { dependsOn }
|
||||||
|
}
|
||||||
|
|
||||||
private fun <T> Class<T>.constructorOrNull(vararg parameterTypes: Class<*>): Constructor<T>? =
|
internal fun Iterable<KotlinSourceSet>.resolveAllDependsOnSourceSets(): Set<KotlinSourceSet> {
|
||||||
try {
|
return flatMapTo(mutableSetOf()) { it.resolveAllDependsOnSourceSets() }
|
||||||
getConstructor(*parameterTypes)
|
}
|
||||||
} catch (e: NoSuchMethodException) {
|
|
||||||
null
|
internal fun KotlinMultiplatformExtension.resolveAllSourceSetsDependingOn(sourceSet: KotlinSourceSet): Set<KotlinSourceSet> {
|
||||||
}
|
return sourceSet.transitiveClosure { sourceSets.filter { otherSourceSet -> this in otherSourceSet.dependsOn } }
|
||||||
|
}
|
||||||
|
|||||||
+39
@@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2021 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.utils
|
||||||
|
|
||||||
|
/**
|
||||||
|
* General purpose implementation of a transitive closure
|
||||||
|
* - Recursion free
|
||||||
|
* - Predictable amount of allocations
|
||||||
|
* - Handles loops and self references gracefully
|
||||||
|
* @param edges: Producer function from one node to all its children. This implementation can handle loops and self references gracefully.
|
||||||
|
* @return Note: No guarantees given about the order ot this [Set]
|
||||||
|
*/
|
||||||
|
internal inline fun <reified T> T.transitiveClosure(edges: T.() -> Iterable<T>): Set<T> {
|
||||||
|
// Fast path when initial edges are empty
|
||||||
|
val initialEdges = edges()
|
||||||
|
if (initialEdges is Collection && initialEdges.isEmpty()) return emptySet()
|
||||||
|
|
||||||
|
val queue = deque<T>()
|
||||||
|
val results = mutableSetOf<T>()
|
||||||
|
queue.addAll(initialEdges)
|
||||||
|
while (queue.isNotEmpty()) {
|
||||||
|
// ArrayDeque implementation will optimize this call to 'removeFirst'
|
||||||
|
val resolved = queue.removeAt(0)
|
||||||
|
if (resolved != this && results.add(resolved)) {
|
||||||
|
queue.addAll(resolved.edges())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return results.toSet()
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalStdlibApi::class)
|
||||||
|
private inline fun <reified T> deque(): MutableList<T> {
|
||||||
|
return if (KotlinVersion.CURRENT.isAtLeast(1, 4)) ArrayDeque()
|
||||||
|
else mutableListOf()
|
||||||
|
}
|
||||||
+60
@@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2021 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.utils
|
||||||
|
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
class TransitiveClosureTest {
|
||||||
|
private class Node(val value: String, val children: MutableList<Node> = mutableListOf()) {
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (other !is Node) return false
|
||||||
|
return other.value == value
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
return value.hashCode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Node.transitiveClosure() = transitiveClosure { children }
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `transitiveClosure does not include root node`() {
|
||||||
|
val closure = Node("a", mutableListOf(Node("b"), Node("c"))).transitiveClosure()
|
||||||
|
assertEquals(setOf(Node("b"), Node("c")), closure, "Expected transitiveClosure to not include root node")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `transitiveClosure handles loop and self references`() {
|
||||||
|
val nodeA = Node("a")
|
||||||
|
val nodeB = Node("b")
|
||||||
|
val nodeC = Node("c")
|
||||||
|
val nodeD = Node("d")
|
||||||
|
|
||||||
|
// a -> b -> c -> d
|
||||||
|
nodeA.children.add(nodeB)
|
||||||
|
nodeB.children.add(nodeC)
|
||||||
|
nodeC.children.add(nodeD)
|
||||||
|
|
||||||
|
// add self reference to b
|
||||||
|
nodeB.children.add(nodeB)
|
||||||
|
|
||||||
|
// add loop from c -> a
|
||||||
|
nodeC.children.add(nodeA)
|
||||||
|
|
||||||
|
val closure = nodeA.transitiveClosure()
|
||||||
|
assertEquals(
|
||||||
|
setOf(nodeB, nodeC, nodeD), closure,
|
||||||
|
"Expected transitiveClosure to be robust against loops and self references"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user