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