[Gradle] Add circular dependency check for KT-31459
^KT-31459
This commit is contained in:
+51
-4
@@ -5,9 +5,14 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.gradle
|
package org.jetbrains.kotlin.gradle
|
||||||
|
|
||||||
|
import org.gradle.api.Project
|
||||||
import org.gradle.api.Task
|
import org.gradle.api.Task
|
||||||
|
import org.gradle.api.file.FileCollection
|
||||||
|
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
|
||||||
|
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
import kotlin.test.fail
|
||||||
|
|
||||||
class KT31459JvmMainClassesTest {
|
class KT31459JvmMainClassesTest {
|
||||||
|
|
||||||
@@ -27,15 +32,57 @@ class KT31459JvmMainClassesTest {
|
|||||||
|
|
||||||
assertEquals(
|
assertEquals(
|
||||||
setOf("compileKotlinJvm", "compileJava", "jvmProcessResources"),
|
setOf("compileKotlinJvm", "compileJava", "jvmProcessResources"),
|
||||||
task.allDependencies
|
task.directDependencies
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns names of all tasks that given task depends on
|
* This mechanism used by `kotlinx-atomicfu` gradle plugin. It replaces compilation classes to transformed classes dir
|
||||||
*/
|
*/
|
||||||
private val Task.allDependencies: Set<String> get() = taskDependencies
|
@Test
|
||||||
.getDependencies(this)
|
fun `it should be possible to replace compilation output classes`() {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Code taken from `kotlinx-atomicfu` gradle plugin
|
||||||
|
*/
|
||||||
|
fun Project.addClassesTransformationTask(target: KotlinTarget) {
|
||||||
|
val compilation = target.compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME)
|
||||||
|
val classesDirs = compilation.output.classesDirs
|
||||||
|
|
||||||
|
// make copy of original classes directory
|
||||||
|
val originalClassesDirs: FileCollection =
|
||||||
|
project.files(classesDirs.from.toTypedArray())
|
||||||
|
val transformedClassesDir = project.buildDir.resolve("classes/atomicfu/${target.name}/${compilation.name}")
|
||||||
|
val transformTask = project.tasks.create("transformTask") {
|
||||||
|
it.dependsOn(compilation.compileAllTaskName)
|
||||||
|
it.inputs.files(originalClassesDirs)
|
||||||
|
it.outputs.files(transformedClassesDir)
|
||||||
|
}
|
||||||
|
classesDirs.setFrom(transformedClassesDir)
|
||||||
|
classesDirs.builtBy(transformTask)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Given MPP Project with JVM target
|
||||||
|
val project = buildProjectWithMPP {
|
||||||
|
kotlin {
|
||||||
|
val target = jvm {}
|
||||||
|
|
||||||
|
// And classes transformation task applied
|
||||||
|
addClassesTransformationTask(target)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
project.evaluate()
|
||||||
|
|
||||||
|
val jvmMainClasses = project.tasks.getByName("jvmMainClasses")
|
||||||
|
jvmMainClasses.assertNoCircularTaskDependencies()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns names of all tasks that given task directly depends on
|
||||||
|
*/
|
||||||
|
private val Task.directDependencies: Set<String> get() = taskDependencies
|
||||||
|
.getDependencies(null)
|
||||||
.map { it.name }
|
.map { it.name }
|
||||||
.toSet()
|
.toSet()
|
||||||
}
|
}
|
||||||
+23
-2
@@ -7,10 +7,8 @@ package org.jetbrains.kotlin.gradle
|
|||||||
|
|
||||||
import org.gradle.api.Project
|
import org.gradle.api.Project
|
||||||
import org.gradle.api.Task
|
import org.gradle.api.Task
|
||||||
import org.gradle.api.artifacts.Configuration
|
|
||||||
import org.gradle.api.artifacts.Dependency
|
import org.gradle.api.artifacts.Dependency
|
||||||
import org.jetbrains.kotlin.gradle.plugin.getKotlinPluginVersion
|
import org.jetbrains.kotlin.gradle.plugin.getKotlinPluginVersion
|
||||||
import org.jetbrains.kotlin.gradle.utils.`is`
|
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.assertTrue
|
import kotlin.test.assertTrue
|
||||||
import kotlin.test.fail
|
import kotlin.test.fail
|
||||||
@@ -31,6 +29,29 @@ fun Task.assertNotDependsOn(other: Task) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Task.assertNoCircularTaskDependencies() {
|
||||||
|
data class TaskAndDependants(
|
||||||
|
val task: Task,
|
||||||
|
val dependants: List<Task>
|
||||||
|
)
|
||||||
|
|
||||||
|
val visited = mutableSetOf<Task>()
|
||||||
|
val queue = ArrayDeque(taskDependencies.getDependencies(this).map { TaskAndDependants(it, listOf(this)) })
|
||||||
|
|
||||||
|
while(queue.isNotEmpty()) {
|
||||||
|
val (task, dependants) = queue.removeFirst()
|
||||||
|
if (task in visited) {
|
||||||
|
val dependencyChain = dependants.joinToString(" -> ") { it.name }
|
||||||
|
fail("Task $name has circular dependency: $dependencyChain")
|
||||||
|
}
|
||||||
|
visited.add(task)
|
||||||
|
|
||||||
|
val dependencies = task.taskDependencies.getDependencies(null)
|
||||||
|
queue.addAll(dependencies.map { TaskAndDependants(it, dependants + task) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
fun Task.assertTaskDependenciesEquals(dependencies: Set<Task>) {
|
fun Task.assertTaskDependenciesEquals(dependencies: Set<Task>) {
|
||||||
assertEquals(
|
assertEquals(
|
||||||
dependencies, this.taskDependencies.getDependencies(null),
|
dependencies, this.taskDependencies.getDependencies(null),
|
||||||
|
|||||||
Reference in New Issue
Block a user