Gradle: improve tasks registration API, fix test tasks registration
Previously test tasks may be created inside other registration task lambda which may be called lazily. This is illegal in Gradle. Also let introduce Project.createOrRegisterTask on Project receiver and reified type parameter.
This commit is contained in:
+19
-14
@@ -25,6 +25,7 @@ import org.gradle.language.jvm.tasks.ProcessResources
|
||||
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.*
|
||||
import org.jetbrains.kotlin.gradle.targets.jvm.tasks.KotlinJvmTest
|
||||
import org.jetbrains.kotlin.gradle.tasks.createOrRegisterTask
|
||||
import org.jetbrains.kotlin.gradle.testing.internal.registerTestTask
|
||||
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
|
||||
import java.util.concurrent.Callable
|
||||
@@ -272,7 +273,7 @@ abstract class AbstractKotlinTargetConfigurator<KotlinTargetType : KotlinTarget>
|
||||
isVisible = false
|
||||
isCanBeResolved = true // Needed for IDE import
|
||||
description =
|
||||
"Runtime dependencies for $compilation (deprecated, use '${compilation.runtimeOnlyConfigurationName} ' instead)."
|
||||
"Runtime dependencies for $compilation (deprecated, use '${compilation.runtimeOnlyConfigurationName} ' instead)."
|
||||
}
|
||||
|
||||
val runtimeOnlyConfiguration = configurations.maybeCreate(compilation.runtimeOnlyConfigurationName).apply {
|
||||
@@ -361,23 +362,27 @@ abstract class KotlinTargetConfigurator<KotlinCompilationType : KotlinCompilatio
|
||||
}
|
||||
|
||||
override fun configureTest(target: KotlinOnlyTarget<KotlinCompilationType>) {
|
||||
val testCompilation = target.compilations.findByName(KotlinCompilation.TEST_COMPILATION_NAME) as? KotlinCompilationToRunnableFiles<*>
|
||||
?: return // Otherwise, there is no runtime classpath
|
||||
val testCompilation =
|
||||
target.compilations.findByName(KotlinCompilation.TEST_COMPILATION_NAME) as? KotlinCompilationToRunnableFiles<*>
|
||||
?: return // Otherwise, there is no runtime classpath
|
||||
|
||||
target.project.tasks.create(lowerCamelCaseName(target.disambiguationClassifier, testTaskNameSuffix), KotlinJvmTest::class.java).apply {
|
||||
registerTestTask(this)
|
||||
val testTaskName = lowerCamelCaseName(target.disambiguationClassifier, testTaskNameSuffix)
|
||||
val testTask = target.project.createOrRegisterTask<KotlinJvmTest>(testTaskName) { testTask ->
|
||||
testTask.targetName = target.disambiguationClassifier
|
||||
}
|
||||
|
||||
targetName = target.disambiguationClassifier
|
||||
|
||||
project.afterEvaluate {
|
||||
// use afterEvaluate to override the JavaPlugin defaults for Test tasks
|
||||
conventionMapping.map("testClassesDirs") { testCompilation.output.classesDirs }
|
||||
conventionMapping.map("classpath") { testCompilation.runtimeDependencyFiles }
|
||||
description = "Runs the unit tests."
|
||||
group = JavaBasePlugin.VERIFICATION_GROUP
|
||||
target.project.tasks.findByName(JavaBasePlugin.CHECK_TASK_NAME)?.dependsOn(this@apply)
|
||||
testTask.project.afterEvaluate {
|
||||
// use afterEvaluate to override the JavaPlugin defaults for Test tasks
|
||||
testTask.configure { testTask ->
|
||||
testTask.conventionMapping.map("testClassesDirs") { testCompilation.output.classesDirs }
|
||||
testTask.conventionMapping.map("classpath") { testCompilation.runtimeDependencyFiles }
|
||||
testTask.description = "Runs the unit tests."
|
||||
testTask.group = JavaBasePlugin.VERIFICATION_GROUP
|
||||
testTask.project.tasks.findByName(JavaBasePlugin.CHECK_TASK_NAME)?.dependsOn(testTask)
|
||||
}
|
||||
}
|
||||
|
||||
registerTestTask(testTask)
|
||||
}
|
||||
|
||||
private fun addJar(configuration: Configuration, jarArtifact: PublishArtifact) {
|
||||
|
||||
+8
-1
@@ -5,9 +5,16 @@
|
||||
|
||||
package org.jetbrains.kotlin.gradle.plugin
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.Task
|
||||
|
||||
class LegacyTaskHolder<T : Task>(private val task: T) : TaskHolder<T> {
|
||||
class LegacyTaskHolder<out T : Task>(private val task: T) : TaskHolder<T> {
|
||||
override val project: Project
|
||||
get() = task.project
|
||||
|
||||
override val name: String
|
||||
get() = task.name
|
||||
|
||||
override fun doGetTask() = task
|
||||
|
||||
override fun getTaskOrProvider(): Any = task
|
||||
|
||||
+3
-1
@@ -24,7 +24,9 @@ import org.gradle.api.Project
|
||||
* Reference to a org.gradle.api.Task or org.gradle.api.TaskProvider necessary in order to support flexible creation of tasks.
|
||||
* For gradle versions < 4.9 tasks are created meanwhile for gradle with version >= 4.9 tasks are registered
|
||||
*/
|
||||
interface TaskHolder<T : Task> {
|
||||
interface TaskHolder<out T : Task> {
|
||||
val project: Project
|
||||
val name: String
|
||||
|
||||
/**
|
||||
* Returns Task itself if task was created or TaskProvider<Task> if task was registered.
|
||||
|
||||
+4
-3
@@ -16,14 +16,15 @@
|
||||
|
||||
package org.jetbrains.kotlin.gradle.plugin
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.Task
|
||||
import org.gradle.api.tasks.TaskProvider
|
||||
|
||||
class TaskProviderHolder<T : Task>(
|
||||
private val name: String,
|
||||
class TaskProviderHolder<out T : Task>(
|
||||
override val name: String,
|
||||
override val project: Project,
|
||||
private val provider: TaskProvider<T>
|
||||
) : TaskHolder<T> {
|
||||
|
||||
override fun getTaskOrProvider(): Any = provider
|
||||
|
||||
override fun doGetTask(): T = provider.get()
|
||||
|
||||
+4
-3
@@ -5,7 +5,7 @@ import org.jetbrains.kotlin.gradle.plugin.KotlinCompilationToRunnableFiles
|
||||
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsPlugin
|
||||
import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTest
|
||||
import org.jetbrains.kotlin.gradle.tasks.Kotlin2JsCompile
|
||||
import org.jetbrains.kotlin.gradle.tasks.registerTask
|
||||
import org.jetbrains.kotlin.gradle.tasks.createOrRegisterTask
|
||||
import org.jetbrains.kotlin.gradle.testing.internal.configureConventions
|
||||
import org.jetbrains.kotlin.gradle.testing.internal.registerTestTask
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
@@ -40,7 +40,7 @@ internal class KotlinJsCompilationTestsConfigurator(
|
||||
// apply plugin (cannot do it lazy)
|
||||
val nodeJs = NodeJsPlugin[target.project]
|
||||
|
||||
registerTask(project, testTaskName, KotlinJsTest::class.java) { testJs ->
|
||||
val testJs = project.createOrRegisterTask<KotlinJsTest>(testTaskName) { testJs ->
|
||||
testJs.group = LifecycleBasePlugin.VERIFICATION_GROUP
|
||||
|
||||
testJs.dependsOn(compileTestKotlin2Js, nodeJs.nodeJsSetupTask)
|
||||
@@ -54,7 +54,8 @@ internal class KotlinJsCompilationTestsConfigurator(
|
||||
testJs.nodeModulesToLoad.add(compileTestKotlin2Js.outputFile.name)
|
||||
|
||||
testJs.configureConventions()
|
||||
registerTestTask(testJs)
|
||||
}
|
||||
|
||||
registerTestTask(testJs)
|
||||
}
|
||||
}
|
||||
+12
-13
@@ -129,24 +129,23 @@ open class KotlinNativeTargetConfigurator(
|
||||
val taskName = binary.runTaskName ?: return
|
||||
|
||||
if (binary.isDefaultTestExecutable) {
|
||||
tasks.create(taskName, KotlinNativeTest::class.java).apply {
|
||||
tasks.maybeCreate(LifecycleBasePlugin.CHECK_TASK_NAME).dependsOn(this)
|
||||
val testTask = createOrRegisterTask<KotlinNativeTest>(taskName) { testTask ->
|
||||
testTask.group = LifecycleBasePlugin.VERIFICATION_GROUP
|
||||
testTask.description = "Executes Kotlin/Native unit tests for target ${binary.target.name}."
|
||||
testTask.targetName = binary.compilation.target.targetName
|
||||
|
||||
group = LifecycleBasePlugin.VERIFICATION_GROUP
|
||||
description = "Executes Kotlin/Native unit tests for target ${binary.target.name}."
|
||||
targetName = binary.compilation.target.targetName
|
||||
testTask.enabled = binary.target.konanTarget.isCurrentHost
|
||||
|
||||
enabled = binary.target.konanTarget.isCurrentHost
|
||||
testTask.executable = binary.outputFile
|
||||
testTask.workingDir = project.projectDir.absolutePath
|
||||
|
||||
executable = binary.outputFile
|
||||
workingDir = project.projectDir.absolutePath
|
||||
testTask.onlyIf { binary.outputFile.exists() }
|
||||
testTask.dependsOn(binary.linkTaskName)
|
||||
|
||||
onlyIf { binary.outputFile.exists() }
|
||||
dependsOn(binary.linkTaskName)
|
||||
configureConventions()
|
||||
|
||||
registerTestTask(this)
|
||||
testTask.configureConventions()
|
||||
}
|
||||
|
||||
registerTestTask(testTask)
|
||||
} else {
|
||||
tasks.create(taskName, Exec::class.java).apply {
|
||||
group = RUN_GROUP
|
||||
|
||||
+16
-7
@@ -31,9 +31,17 @@ internal val canLocateTask = org.jetbrains.kotlin.gradle.utils.isGradleVersionAt
|
||||
* Registers the task with [name] and [type] and initialization script [body]
|
||||
* If Gradle with version <4.9 is used the task will be created
|
||||
*/
|
||||
internal fun <T : Task> registerTask(project: Project, name: String, type: Class<T>, body: (T) -> (Unit)): TaskHolder<T> {
|
||||
@JvmName("registerTaskOld")
|
||||
@Deprecated("please use Project.createOrRegisterTask", ReplaceWith("project.createOrRegisterTask(name, body)"))
|
||||
internal fun <T : Task> registerTask(project: Project, name: String, type: Class<T>, body: (T) -> (Unit)): TaskHolder<T> =
|
||||
project.createOrRegisterTask(name, type, body)
|
||||
|
||||
internal inline fun <reified T : Task> Project.createOrRegisterTask(name: String, noinline body: (T) -> (Unit)): TaskHolder<T> =
|
||||
createOrRegisterTask(name, T::class.java, body)
|
||||
|
||||
internal fun <T : Task> Project.createOrRegisterTask(name: String, type: Class<T>, body: (T) -> (Unit)): TaskHolder<T> {
|
||||
return if (useLazyTaskConfiguration) {
|
||||
TaskProviderHolder(name, project.tasks.register(name, type) { with(it, body) })
|
||||
TaskProviderHolder(name, project, project.tasks.register(name, type) { with(it, body) })
|
||||
} else {
|
||||
val result = LegacyTaskHolder(project.tasks.create(name, type))
|
||||
with(result.doGetTask(), body)
|
||||
@@ -41,13 +49,14 @@ internal fun <T : Task> registerTask(project: Project, name: String, type: Class
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Locates a task by [name] and [type], without triggering its creation or configuration.
|
||||
*/
|
||||
internal fun <T : Task> locateTask(project: Project, name: String, type: Class<T>): TaskHolder<T>? =
|
||||
if (canLocateTask) {
|
||||
try {
|
||||
TaskProviderHolder(name, project.tasks.named(name, type))
|
||||
TaskProviderHolder(name, project, project.tasks.named(name, type))
|
||||
} catch (e: UnknownTaskException) {
|
||||
null
|
||||
}
|
||||
@@ -89,7 +98,7 @@ internal open class KotlinTasksProvider(val targetName: String) {
|
||||
name: String,
|
||||
compilation: KotlinCompilation<*>,
|
||||
configureAction: (Kotlin2JsCompile) -> Unit
|
||||
): TaskHolder<out Kotlin2JsCompile> {
|
||||
): TaskHolder<Kotlin2JsCompile> {
|
||||
val properties = PropertiesProvider(project)
|
||||
val taskClass = taskOrWorkersTask<Kotlin2JsCompile, Kotlin2JsCompileWithWorkers>(properties)
|
||||
val result = registerTask(project, name, taskClass) {
|
||||
@@ -104,7 +113,7 @@ internal open class KotlinTasksProvider(val targetName: String) {
|
||||
name: String,
|
||||
compilation: KotlinCompilation<*>,
|
||||
configureAction: (KotlinCompileCommon) -> (Unit)
|
||||
): TaskHolder<out KotlinCompileCommon> {
|
||||
): TaskHolder<KotlinCompileCommon> {
|
||||
val properties = PropertiesProvider(project)
|
||||
val taskClass = taskOrWorkersTask<KotlinCompileCommon, KotlinCompileCommonWithWorkers>(properties)
|
||||
val result = registerTask(project, name, taskClass) {
|
||||
@@ -115,7 +124,7 @@ internal open class KotlinTasksProvider(val targetName: String) {
|
||||
}
|
||||
|
||||
open fun configure(
|
||||
kotlinTaskHolder: TaskHolder<out AbstractKotlinCompile<*>>,
|
||||
kotlinTaskHolder: TaskHolder<AbstractKotlinCompile<*>>,
|
||||
project: Project,
|
||||
propertiesProvider: PropertiesProvider,
|
||||
compilation: KotlinCompilation<*>
|
||||
@@ -149,7 +158,7 @@ internal class AndroidTasksProvider(targetName: String) : KotlinTasksProvider(ta
|
||||
RegexTaskToFriendTaskMapper.Android(targetName)
|
||||
|
||||
override fun configure(
|
||||
kotlinTaskHolder: TaskHolder<out AbstractKotlinCompile<*>>,
|
||||
kotlinTaskHolder: TaskHolder<AbstractKotlinCompile<*>>,
|
||||
project: Project,
|
||||
propertiesProvider: PropertiesProvider,
|
||||
compilation: KotlinCompilation<*>
|
||||
|
||||
+22
-19
@@ -34,31 +34,34 @@ private val Project.allTestsTask: TaskHolder<AggregateTestReport>
|
||||
private fun cleanTaskName(taskName: String) = "clean" + taskName.capitalize()
|
||||
|
||||
@Suppress("UnstableApiUsage")
|
||||
internal fun registerTestTask(task: AbstractTestTask) {
|
||||
val project = task.project
|
||||
internal fun registerTestTask(taskHolder: TaskHolder<AbstractTestTask>) {
|
||||
val project = taskHolder.project
|
||||
val allTests = project.allTestsTask.doGetTask()
|
||||
|
||||
project.tasks.maybeCreate(LifecycleBasePlugin.CHECK_TASK_NAME).dependsOn(task)
|
||||
project.tasks.maybeCreate(cleanTaskName(allTests.name)).dependsOn(cleanTaskName(task.name))
|
||||
val tasks = project.tasks
|
||||
tasks.maybeCreate(LifecycleBasePlugin.CHECK_TASK_NAME).dependsOn(taskHolder.name)
|
||||
tasks.maybeCreate(cleanTaskName(allTests.name)).dependsOn(cleanTaskName(taskHolder.name))
|
||||
allTests.dependsOn(taskHolder.name)
|
||||
|
||||
allTests.dependsOn(task)
|
||||
allTests.registerTestTask(task)
|
||||
taskHolder.configure { task ->
|
||||
allTests.registerTestTask(task)
|
||||
|
||||
project.gradle.taskGraph.whenReady {
|
||||
if (it.hasTask(allTests)) {
|
||||
// when [allTestsTask] task enabled, test failure should be reported only on [allTestsTask],
|
||||
// not at individual target's test tasks. To do that, we need:
|
||||
// - disable all reporting in test tasks
|
||||
// - enable [checkFailedTests] on [allTestsTask]
|
||||
project.gradle.taskGraph.whenReady {
|
||||
if (it.hasTask(allTests)) {
|
||||
// when [allTestsTask] task enabled, test failure should be reported only on [allTestsTask],
|
||||
// not at individual target's test tasks. To do that, we need:
|
||||
// - disable all reporting in test tasks
|
||||
// - enable [checkFailedTests] on [allTestsTask]
|
||||
|
||||
task.ignoreFailures = true
|
||||
task.reports.html.isEnabled = false
|
||||
task.reports.junitXml.isEnabled = false
|
||||
task.ignoreFailures = true
|
||||
task.reports.html.isEnabled = false
|
||||
task.reports.junitXml.isEnabled = false
|
||||
|
||||
allTests.checkFailedTests = true
|
||||
allTests.ignoreFailures = false
|
||||
allTests.checkFailedTests = true
|
||||
allTests.ignoreFailures = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ijListenTestTask(task)
|
||||
ijListenTestTask(task)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user