[Gradle] Add circular dependency check for KT-31459

^KT-31459
This commit is contained in:
Anton Lakotka
2022-09-06 15:25:28 +02:00
committed by Space
parent 4861aa3381
commit a4cfb99de4
2 changed files with 74 additions and 6 deletions
@@ -5,9 +5,14 @@
package org.jetbrains.kotlin.gradle
import org.gradle.api.Project
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.assertEquals
import kotlin.test.fail
class KT31459JvmMainClassesTest {
@@ -27,15 +32,57 @@ class KT31459JvmMainClassesTest {
assertEquals(
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
.getDependencies(this)
@Test
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 }
.toSet()
}
@@ -7,10 +7,8 @@ package org.jetbrains.kotlin.gradle
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.Dependency
import org.jetbrains.kotlin.gradle.plugin.getKotlinPluginVersion
import org.jetbrains.kotlin.gradle.utils.`is`
import kotlin.test.assertEquals
import kotlin.test.assertTrue
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>) {
assertEquals(
dependencies, this.taskDependencies.getDependencies(null),