diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/KT31459JvmMainClassesTest.kt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/KT31459JvmMainClassesTest.kt index 66fdf09a708..1d1c48c5a4e 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/KT31459JvmMainClassesTest.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/KT31459JvmMainClassesTest.kt @@ -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 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 get() = taskDependencies + .getDependencies(null) .map { it.name } .toSet() } \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/assertions.kt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/assertions.kt index 7b116cc4e18..b1b8c342f3d 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/assertions.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/assertions.kt @@ -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 + ) + + val visited = mutableSetOf() + 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) { assertEquals( dependencies, this.taskDependencies.getDependencies(null),