任务有两种类型:可执行任务(actionable tasks)和生命周期任务(lifecycle tasks)。

Gradle 中的可执行任务是执行实际工作的任务,例如编译代码。生命周期任务本身不执行工作。这些任务没有操作,相反,它们捆绑可执行任务并作为构建的目标

writing tasks 6

良好组织的生命周期任务设置可以增强构建对新用户的易用性,并简化与 CI 的集成。

生命周期任务

生命周期任务对于区分用户或机器(CI 与本地)之间的工作特别有用。例如,本地机器上的开发者可能不想在每次更改时都运行整个构建。

让我们以应用了 base 插件的标准 app 为例。

Gradle base 插件定义了几个生命周期任务,包括 buildassemblecheck

我们通过向 app 构建脚本添加以下几行来对 buildcheck 任务和 run 任务进行分组

app/build.gradle.kts
tasks.build {
    group = myBuildGroup
}

tasks.check {
    group = myBuildGroup
    description = "Runs checks (including tests)."
}

tasks.named("run") {
    group = myBuildGroup
}
app/build.gradle
tasks.build {
    group = myBuildGroup
}

tasks.check {
    group = myBuildGroup
    description = "Runs checks (including tests)."
}

tasks.named('run') {
    group = myBuildGroup
}

如果我们现在查看 app:tasks 列表,可以看到这三个任务是可用的

$ ./gradlew :app:tasks

> Task :app:tasks

------------------------------------------------------------
Tasks runnable from project ':app'
------------------------------------------------------------

My app build tasks
------------------
build - Assembles and tests this project.
check - Runs checks (including tests).
run - Runs this project as a JVM application
tasksAll - Show additional tasks.

如果标准生命周期任务足够,这已经很有用。调整分组有助于明确你期望在构建中使用的任务。

在许多情况下,你会想解决更具体的需求。一个常见的场景是运行质量检查而不运行测试。目前,:check 任务会运行测试和代码质量检查。相反,我们希望一直运行代码质量检查,但不运行耗时的测试。

为了添加质量检查生命周期任务,我们引入了一个额外的生命周期任务 qualityCheck 和一个名为 spotbugs 的插件。

要添加一个生命周期任务,使用 tasks.register()。你只需提供一个名称。将此任务放入我们的分组中,并使用 dependsOn() 方法连接属于此新生命周期任务的可执行任务

app/build.gradle.kts
plugins {
    id("com.github.spotbugs") version "6.0.7"           // spotbugs plugin
}

tasks.register("qualityCheck") {                        // qualityCheck task
    group = myBuildGroup                                // group
    description = "Runs checks (excluding tests)."      // description
    dependsOn(tasks.classes, tasks.spotbugsMain)        // dependencies
    dependsOn(tasks.testClasses, tasks.spotbugsTest)    // dependencies
}
app/build.gradle
plugins {
    id 'com.github.spotbugs' version '6.0.7'            // spotbugs plugin
}

tasks.register('qualityCheck') {                        // qualityCheck task
    group = myBuildGroup                                // group
    description = 'Runs checks (excluding tests).'      // description
    dependsOn tasks.classes, tasks.spotbugsMain         // dependencies
    dependsOn tasks.testClasses, tasks.spotbugsTest     // dependencies
}

请注意,你无需列出 Gradle 将执行的所有任务。只需在此处指定你想要收集的目标。Gradle 会确定需要调用哪些其他任务来达成这些目标。

在示例中,我们添加了 classes 任务(一个用于编译所有生产代码的生命周期任务)和 spotbugsMain 任务(用于检查我们的生产代码)。

我们还添加了一个描述,它将显示在任务列表中,有助于更好地区分这两个检查任务。

现在,如果运行 './gradlew :app:tasks',我们可以看到新的 qualityCheck 生命周期任务可用

$ ./gradlew :app:tasks

> Task :app:tasks

------------------------------------------------------------
Tasks runnable from project ':app'
------------------------------------------------------------

My app build tasks
------------------
build - Assembles and tests this project.
check - Runs checks (including tests).
qualityCheck - Runs checks (excluding tests).
run - Runs this project as a JVM application
tasksAll - Show additional tasks.

如果我们运行它,可以看到它运行了 checkstyle 但没有运行测试

$ ./gradlew :app:qualityCheck

> Task :buildSrc:checkKotlinGradlePluginConfigurationErrors
> Task :buildSrc:generateExternalPluginSpecBuilders UP-TO-DATE
> Task :buildSrc:extractPrecompiledScriptPluginPlugins UP-TO-DATE
> Task :buildSrc:compilePluginsBlocks UP-TO-DATE
> Task :buildSrc:generatePrecompiledScriptPluginAccessors UP-TO-DATE
> Task :buildSrc:generateScriptPluginAdapters UP-TO-DATE
> Task :buildSrc:compileKotlin UP-TO-DATE
> Task :buildSrc:compileJava NO-SOURCE
> Task :buildSrc:compileGroovy NO-SOURCE
> Task :buildSrc:pluginDescriptors UP-TO-DATE
> Task :buildSrc:processResources UP-TO-DATE
> Task :buildSrc:classes UP-TO-DATE
> Task :buildSrc:jar UP-TO-DATE
> Task :app:processResources NO-SOURCE
> Task :app:processTestResources NO-SOURCE
> Task :list:compileJava UP-TO-DATE
> Task :utilities:compileJava UP-TO-DATE
> Task :app:compileJava
> Task :app:classes
> Task :app:compileTestJava
> Task :app:testClasses
> Task :app:spotbugsTest
> Task :app:spotbugsMain
> Task :app:qualityCheck

BUILD SUCCESSFUL in 1s
16 actionable tasks: 5 executed, 11 up-to-date

至此,我们已经了解了单个子项目中的任务,这对于在某个子项目中进行本地开发时非常有用。

有了这个设置,开发者只需要知道他们可以通过 :subproject-name:tasks 调用 Gradle 来查看哪些任务可用且对他们有用。

全局生命周期任务

另一个调用生命周期任务的地方是根构建中;这对于持续集成(CI)特别有用。

Gradle 任务在 CI 或 CD 系统中扮演着关键角色,这些系统中编译所有代码、运行测试或构建和打包完整应用程序等活动是典型的。为了便于此操作,你可以包含跨多个子项目的生命周期任务。

Gradle 已经存在很长时间了,你会经常看到根目录中的构建文件服务于各种目的。在旧版本的 Gradle 中,许多任务是在根 Gradle 构建文件中定义的,这导致了各种问题。因此,在确定此文件的内容时要谨慎。

少数应该放在根构建文件中的元素之一是全局生命周期任务。

让我们继续以 Gradle init Java 应用程序多项目为例。

这次,我们在根项目中包含了构建脚本。我们将为全局生命周期任务建立两个分组:一个用于与本地开发相关的任务(例如运行所有检查),另一个专用于我们的 CI 系统。

我们再次将列出的任务范围缩小到我们特定的分组

build.gradle.kts
val globalBuildGroup = "My global build"
val ciBuildGroup = "My CI build"

tasks.named<TaskReportTask>("tasks") {
    displayGroups = listOf<String>(globalBuildGroup, ciBuildGroup)
}
build.gradle
def globalBuildGroup = "My global build"
def ciBuildGroup = "My CI build"

tasks.named(TaskReportTask, "tasks") {
    displayGroups = [globalBuildGroup, ciBuildGroup]
}

如果需要,可以通过更新 displayGroups 来隐藏 CI 任务。

当前,根项目没有暴露任何任务

$ ./gradlew :tasks

> Task :tasks

------------------------------------------------------------
Tasks runnable from root project 'gradle-project'
------------------------------------------------------------

No tasks
在这个文件中,我们不应用插件!

让我们添加一个 qualityCheckApp 任务来执行 app 子项目中的所有代码质量检查。类似地,出于 CI 的目的,我们实现一个运行所有测试的 checkAll 任务。

build.gradle.kts
tasks.register("qualityCheckApp") {
    group = globalBuildGroup
    description = "Runs checks on app (globally)"
    dependsOn(":app:qualityCheck" )
}

tasks.register("checkAll") {
    group = ciBuildGroup
    description = "Runs checks for all projects (CI)"
    dependsOn(subprojects.map { ":${it.name}:check" })
    dependsOn(gradle.includedBuilds.map { it.task(":checkAll") })
}
build.gradle
tasks.register("qualityCheckApp") {
    group = globalBuildGroup
    description = "Runs checks on app (globally)"
    dependsOn(":app:qualityCheck")
}

tasks.register("checkAll") {
    group = ciBuildGroup
    description = "Runs checks for all projects (CI)"
    dependsOn subprojects.collect { ":${it.name}:check" }
    dependsOn gradle.includedBuilds.collect { it.task(":checkAll") }
}

所以现在我们可以让 Gradle 显示根项目的任务,默认情况下它只会显示 qualityCheckAll 任务(以及可选的 checkAll 任务,取决于 displayGroups 的值)。

应该清楚用户在本地应该运行什么

$ ./gradlew :tasks

> Task :tasks

------------------------------------------------------------
Tasks runnable from root project 'gradle-project'
------------------------------------------------------------

My CI build tasks
-----------------
checkAll - Runs checks for all projects (CI)

My global build tasks
---------------------
qualityCheckApp - Runs checks on app (globally)

如果我们运行 :checkAll 任务,可以看到它编译了所有代码并运行了代码质量检查(包括 spotbug

$ ./gradlew :checkAll

> Task :buildSrc:checkKotlinGradlePluginConfigurationErrors
> Task :buildSrc:generateExternalPluginSpecBuilders UP-TO-DATE
> Task :buildSrc:extractPrecompiledScriptPluginPlugins UP-TO-DATE
> Task :buildSrc:compilePluginsBlocks UP-TO-DATE
> Task :buildSrc:generatePrecompiledScriptPluginAccessors UP-TO-DATE
> Task :buildSrc:generateScriptPluginAdapters UP-TO-DATE
> Task :buildSrc:compileKotlin UP-TO-DATE
> Task :buildSrc:compileJava NO-SOURCE
> Task :buildSrc:compileGroovy NO-SOURCE
> Task :buildSrc:pluginDescriptors UP-TO-DATE
> Task :buildSrc:processResources UP-TO-DATE
> Task :buildSrc:classes UP-TO-DATE
> Task :buildSrc:jar UP-TO-DATE
> Task :utilities:processResources NO-SOURCE
> Task :app:processResources NO-SOURCE
> Task :utilities:processTestResources NO-SOURCE
> Task :app:processTestResources NO-SOURCE
> Task :list:compileJava
> Task :list:processResources NO-SOURCE
> Task :list:classes
> Task :list:jar
> Task :utilities:compileJava
> Task :utilities:classes
> Task :utilities:jar
> Task :utilities:compileTestJava NO-SOURCE
> Task :utilities:testClasses UP-TO-DATE
> Task :utilities:test NO-SOURCE
> Task :utilities:check UP-TO-DATE
> Task :list:compileTestJava
> Task :list:processTestResources NO-SOURCE
> Task :list:testClasses
> Task :app:compileJava
> Task :app:classes
> Task :app:compileTestJava
> Task :app:testClasses
> Task :list:test
> Task :list:check
> Task :app:test
> Task :app:spotbugsTest
> Task :app:spotbugsMain
> Task :app:check
> Task :checkAll

BUILD SUCCESSFUL in 1s
21 actionable tasks: 12 executed, 9 up-to-date