一个任务代表构建执行的某个独立工作单元,例如编译类、创建 JAR、生成 Javadoc 或将归档文件发布到仓库。

writing tasks 1

在阅读本章之前,建议您首先阅读学习基础知识并完成教程

列出任务

项目中所有可用的任务都来自 Gradle 插件和构建脚本。

您可以通过在终端中运行以下命令来列出项目中的所有可用任务

$ ./gradlew tasks

让我们以一个非常基础的 Gradle 项目为例。项目结构如下:

gradle-project
├── app
│   ├── build.gradle.kts    // empty file - no build logic
│   └── ...                 // some java code
├── settings.gradle.kts     // includes app subproject
├── gradle
│   └── ...
├── gradlew
└── gradlew.bat
gradle-project
├── app
│   ├── build.gradle    // empty file - no build logic
│   └── ...             // some java code
├── settings.gradle     // includes app subproject
├── gradle
│   └── ...
├── gradlew
└── gradlew.bat

Settings 文件包含以下内容:

settings.gradle.kts
rootProject.name = "gradle-project"
include("app")
settings.gradle
rootProject.name = 'gradle-project'
include('app')

目前,app 子项目的构建文件是空的。

要查看 app 子项目中可用的任务,请运行 ./gradlew :app:tasks

$ ./gradlew :app:tasks

> Task :app:tasks

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

Help tasks
----------
buildEnvironment - Displays all buildscript dependencies declared in project ':app'.
dependencies - Displays all dependencies declared in project ':app'.
dependencyInsight - Displays the insight into a specific dependency in project ':app'.
help - Displays a help message.
javaToolchains - Displays the detected java toolchains.
kotlinDslAccessorsReport - Prints the Kotlin code for accessing the currently available project extensions and conventions.
outgoingVariants - Displays the outgoing variants of project ':app'.
projects - Displays the sub-projects of project ':app'.
properties - Displays the properties of project ':app'.
resolvableConfigurations - Displays the configurations that can be resolved in project ':app'.
tasks - Displays the tasks runnable from project ':app'.

我们注意到目前只有少量帮助任务可用。这是因为 Gradle 核心只提供分析构建的任务。其他任务,例如构建项目或编译代码的任务,由插件添加。

让我们通过向 app 构建脚本添加 Gradle 核心 base 插件来探索这一点

app/build.gradle.kts
plugins {
    id("base")
}
app/build.gradle
plugins {
    id('base')
}

base 插件添加了核心生命周期任务。现在当我们运行 ./gradlew app:tasks 时,可以看到 assemblebuild 任务可用

$ ./gradlew :app:tasks

> Task :app:tasks

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

Build tasks
-----------
assemble - Assembles the outputs of this project.
build - Assembles and tests this project.
clean - Deletes the build directory.

Help tasks
----------
buildEnvironment - Displays all buildscript dependencies declared in project ':app'.
dependencies - Displays all dependencies declared in project ':app'.
dependencyInsight - Displays the insight into a specific dependency in project ':app'.
help - Displays a help message.
javaToolchains - Displays the detected java toolchains.
outgoingVariants - Displays the outgoing variants of project ':app'.
projects - Displays the sub-projects of project ':app'.
properties - Displays the properties of project ':app'.
resolvableConfigurations - Displays the configurations that can be resolved in project ':app'.
tasks - Displays the tasks runnable from project ':app'.

Verification tasks
------------------
check - Runs all checks.

任务结果

当 Gradle 执行任务时,它会通过控制台显示任务的结果标签。

author tasks 1

这些标签基于任务是否有要执行的操作以及 Gradle 是否执行了它们。操作包括但不限于编译代码、压缩文件和发布归档文件。

(无标签)EXECUTED

任务执行了其操作。

  • 任务有操作且 Gradle 执行了它们。

  • 任务没有操作但有一些依赖项,并且 Gradle 执行了一个或多个依赖项。另请参阅生命周期任务

UP-TO-DATE

任务的输出没有变化。

  • 任务有输出和输入,但它们没有改变。参阅增量构建

  • 任务有操作,但任务告诉 Gradle 它没有改变其输出。

  • 任务没有操作但有一些依赖项,但所有依赖项都是 UP-TO-DATESKIPPEDFROM-CACHE。参阅生命周期任务

  • 任务没有操作也没有依赖项。

FROM-CACHE

任务的输出可以从之前的执行中找到。

  • 任务的输出已从构建缓存中恢复。参阅构建缓存

SKIPPED

任务未执行其操作。

NO-SOURCE

任务不需要执行其操作。

  • 任务有输入和输出,但没有源(即未找到输入)。

任务组和描述

任务组和描述用于组织和描述任务。

任务组用于对任务进行分类。当您运行 ./gradlew tasks 时,任务会列在其各自的组下,从而更容易理解它们的用途以及与其他任务的关系。使用 group 属性设置组。

描述

描述提供了对任务功能的简要说明。当您运行 ./gradlew tasks 时,描述会显示在每个任务旁边,帮助您理解其用途和如何使用它。使用 description 属性设置描述。

让我们以一个基础的 Java 应用为例。构建包含一个名为 app 的子项目。

让我们列出目前 app 中可用的任务

$ ./gradlew :app:tasks

> Task :app:tasks

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

Application tasks
-----------------
run - Runs this project as a JVM application.

Build tasks
-----------
assemble - Assembles the outputs of this project.

在这里,:run 任务属于 Application 组,描述为 将此项目作为 JVM 应用运行。在代码中,它看起来像这样:

app/build.gradle.kts
tasks.register("run") {
    group = "Application"
    description = "Runs this project as a JVM application."
}
app/build.gradle
tasks.register("run") {
    group = "Application"
    description = "Runs this project as a JVM application."
}

私有任务和隐藏任务

Gradle 不支持将任务标记为私有

然而,只有当 task.group 被设置或没有其他任务依赖它时,任务才会出现在运行 :tasks 的结果中。

例如,以下任务在运行 ./gradlew :app:tasks 时不会出现,因为它没有组;它被称为隐藏任务

app/build.gradle.kts
tasks.register("helloTask") {
    println("Hello")
}
app/build.gradle
tasks.register("helloTask") {
    println 'Hello'
}

尽管 helloTask 未被列出,但它仍然可以由 Gradle 执行

$ ./gradlew :app:tasks

> Task :app:tasks

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

Application tasks
-----------------
run - Runs this project as a JVM application

Build tasks
-----------
assemble - Assembles the outputs of this project.

让我们为同一个任务添加一个组

app/build.gradle.kts
tasks.register("helloTask") {
    group = "Other"
    description = "Hello task"
    println("Hello")
}
app/build.gradle
tasks.register("helloTask") {
    group = "Other"
    description = "Hello task"
    println 'Hello'
}

添加组后,任务就可见了

$ ./gradlew :app:tasks

> Task :app:tasks

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

Application tasks
-----------------
run - Runs this project as a JVM application

Build tasks
-----------
assemble - Assembles the outputs of this project.

Other tasks
-----------
helloTask - Hello task

相反,./gradlew tasks --all 会显示所有任务;会列出隐藏可见的任务。

对任务分组

如果您想自定义列出任务时向用户显示哪些任务,您可以对任务进行分组并设置每个组的可见性。

请记住,即使您隐藏了任务,它们仍然可用,并且 Gradle 仍然可以运行它们。

让我们从一个由 Gradle init 为具有多个子项目的 Java 应用构建的示例开始。项目结构如下:

gradle-project
├── app
│   ├── build.gradle.kts
│   └── src                 // some java code
│       └── ...
├── utilities
│   ├── build.gradle.kts
│   └── src                 // some java code
│       └── ...
├── list
│   ├── build.gradle.kts
│   └── src                 // some java code
│       └── ...
├── buildSrc
│   ├── build.gradle.kts
│   ├── settings.gradle.kts
│   └── src                 // common build logic
│       └── ...
├── settings.gradle.kts
├── gradle
├── gradlew
└── gradlew.bat
gradle-project
├── app
│   ├── build.gradle
│   └── src             // some java code
│       └── ...
├── utilities
│   ├── build.gradle
│   └── src             // some java code
│       └── ...
├── list
│   ├── build.gradle
│   └── src             // some java code
│       └── ...
├── buildSrc
│   ├── build.gradle
│   ├── settings.gradle
│   └── src             // common build logic
│       └── ...
├── settings.gradle
├── gradle
├── gradlew
└── gradlew.bat

运行 app:tasks 查看 app 子项目中可用的任务

$ ./gradlew :app:tasks

> Task :app:tasks

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

Application tasks
-----------------
run - Runs this project as a JVM application

Build tasks
-----------
assemble - Assembles the outputs of this project.
build - Assembles and tests this project.
buildDependents - Assembles and tests this project and all projects that depend on it.
buildNeeded - Assembles and tests this project and all projects it depends on.
classes - Assembles main classes.
clean - Deletes the build directory.
jar - Assembles a jar archive containing the classes of the 'main' feature.
testClasses - Assembles test classes.

Distribution tasks
------------------
assembleDist - Assembles the main distributions
distTar - Bundles the project as a distribution.
distZip - Bundles the project as a distribution.
installDist - Installs the project as a distribution as-is.

Documentation tasks
-------------------
javadoc - Generates Javadoc API documentation for the 'main' feature.

Help tasks
----------
buildEnvironment - Displays all buildscript dependencies declared in project ':app'.
dependencies - Displays all dependencies declared in project ':app'.
dependencyInsight - Displays the insight into a specific dependency in project ':app'.
help - Displays a help message.
javaToolchains - Displays the detected java toolchains.
kotlinDslAccessorsReport - Prints the Kotlin code for accessing the currently available project extensions and conventions.
outgoingVariants - Displays the outgoing variants of project ':app'.
projects - Displays the sub-projects of project ':app'.
properties - Displays the properties of project ':app'.
resolvableConfigurations - Displays the configurations that can be resolved in project ':app'.
tasks - Displays the tasks runnable from project ':app'.

Verification tasks
------------------
check - Runs all checks.
test - Runs the test suite.

如果我们查看可用任务列表,即使对于一个标准的 Java 项目,它也很庞大。其中许多任务很少被直接使用构建的开发人员需要。

我们可以配置 :tasks 任务,将显示的任务限制在某个组。

让我们创建自己的组,通过更新 app 构建脚本使所有任务默认隐藏

app/build.gradle.kts
val myBuildGroup = "my app build"               // Create a group name

tasks.register<TaskReportTask>("tasksAll") {    // Register the tasksAll task
group = myBuildGroup
description = "Show additional tasks."
setShowDetail(true)
}

tasks.named<TaskReportTask>("tasks") {          // Move all existing tasks to the group
displayGroup = myBuildGroup
}
app/build.gradle
def myBuildGroup = "my app build"               // Create a group name

tasks.register("tasksAll", TaskReportTask) {    // Register the tasksAll task
    group = myBuildGroup
    description = "Show additional tasks."
    setShowDetail(true)
}

tasks.named("tasks", TaskReportTask) {          // Move all existing tasks to the group
    displayGroup = myBuildGroup
}

现在,当我们列出 app 中可用的任务时,列表变短了

$ ./gradlew :app:tasks

> Task :app:tasks

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

My app build tasks
------------------
tasksAll - Show additional tasks.

任务类别

Gradle 区分两种任务类别

  1. 生命周期任务

  2. 可执行任务

生命周期任务定义了您可以调用的目标,例如 :build 项目。生命周期任务不为 Gradle 提供操作。它们必须被关联到可执行任务。base Gradle 插件只添加生命周期任务。

可执行任务定义了 Gradle 要执行的操作,例如 :compileJava,它编译项目的 Java 代码。操作包括创建 JAR、压缩文件、发布归档文件等等。像java-library 插件这样的插件添加了可执行任务。

让我们更新之前示例的构建脚本,该脚本目前是一个空文件,以便我们的 app 子项目成为一个 Java 库

app/build.gradle.kts
plugins {
    id("java-library")
}
app/build.gradle
plugins {
    id('java-library')
}

再次,我们列出可用任务,看看有哪些新任务可用

$ ./gradlew :app:tasks

> Task :app:tasks

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

Build tasks
-----------
assemble - Assembles the outputs of this project.
build - Assembles and tests this project.
buildDependents - Assembles and tests this project and all projects that depend on it.
buildNeeded - Assembles and tests this project and all projects it depends on.
classes - Assembles main classes.
clean - Deletes the build directory.
jar - Assembles a jar archive containing the classes of the 'main' feature.
testClasses - Assembles test classes.

Documentation tasks
-------------------
javadoc - Generates Javadoc API documentation for the 'main' feature.

Help tasks
----------
buildEnvironment - Displays all buildscript dependencies declared in project ':app'.
dependencies - Displays all dependencies declared in project ':app'.
dependencyInsight - Displays the insight into a specific dependency in project ':app'.
help - Displays a help message.
javaToolchains - Displays the detected java toolchains.
outgoingVariants - Displays the outgoing variants of project ':app'.
projects - Displays the sub-projects of project ':app'.
properties - Displays the properties of project ':app'.
resolvableConfigurations - Displays the configurations that can be resolved in project ':app'.
tasks - Displays the tasks runnable from project ':app'.

Verification tasks
------------------
check - Runs all checks.
test - Runs the test suite.

我们看到许多新任务可用,例如 jartestClasses

此外,java-library 插件将可执行任务关联到了生命周期任务。如果我们调用 :build 任务,可以看到已经执行了几个任务,包括 :app:compileJava 任务。

$./gradlew :app:build

> Task :app:compileJava
> Task :app:processResources NO-SOURCE
> Task :app:classes
> Task :app:jar
> Task :app:assemble
> Task :app:compileTestJava
> Task :app:processTestResources NO-SOURCE
> Task :app:testClasses
> Task :app:test
> Task :app:check
> Task :app:build

可执行任务 :compileJava 关联到了生命周期任务 :build

增量任务

Gradle 任务的一个关键特性是其增量性质。

Gradle 可以重用之前构建的结果。因此,如果之前构建过项目,并且只做了少量修改,重新运行 :build 将不需要 Gradle 执行大量工作。

例如,如果我们只修改项目的测试代码,而生产代码保持不变,执行构建将只重新编译测试代码。Gradle 会将生产代码的任务标记为 UP-TO-DATE,表示自上次成功构建以来未发生变化。

$./gradlew :app:build

gradle@MacBook-Pro temp1 % ./gradlew :app:build
> Task :app:compileJava UP-TO-DATE
> Task :app:processResources NO-SOURCE
> Task :app:classes UP-TO-DATE
> Task :app:jar UP-TO-DATE
> Task :app:assemble UP-TO-DATE
> Task :app:compileTestJava
> Task :app:processTestResources NO-SOURCE
> Task :app:testClasses
> Task :app:test
> Task :app:check UP-TO-DATE
> Task :app:build UP-TO-DATE

缓存任务

Gradle 可以使用构建缓存重用过去构建的结果。

要启用此功能,请使用 --build-cache 命令行参数或在您的 gradle.properties 文件中设置 org.gradle.caching=true 来激活构建缓存。

此优化有可能显著加速您的构建

$./gradlew :app:clean :app:build --build-cache

> Task :app:compileJava FROM-CACHE
> Task :app:processResources NO-SOURCE
> Task :app:classes UP-TO-DATE
> Task :app:jar
> Task :app:assemble
> Task :app:compileTestJava FROM-CACHE
> Task :app:processTestResources NO-SOURCE
> Task :app:testClasses UP-TO-DATE
> Task :app:test FROM-CACHE
> Task :app:check UP-TO-DATE
> Task :app:build

当 Gradle 可以从缓存中获取任务的输出时,它会将任务标记为 FROM-CACHE

如果您经常在分支之间切换,构建缓存会非常方便。Gradle 支持本地和远程构建缓存。

开发任务

开发 Gradle 任务时,您有两种选择

  1. 使用现有的 Gradle 任务类型,例如 ZipCopyDelete

  2. 创建您自己的 Gradle 任务类型,例如 MyResolveTaskCustomTaskUsingToolchains

任务类型就是 Gradle Task 类的子类。

对于 Gradle 任务,需要考虑三个状态

  1. 注册任务 - 在构建逻辑中使用任务(由您实现或 Gradle 提供)。

  2. 配置任务 - 为已注册的任务定义输入和输出。

  3. 实现任务 - 创建自定义任务类(即自定义类类型)。

注册通常使用 register() 方法完成。
配置任务通常使用 named() 方法完成。
实现任务通常通过继承 Gradle 的 DefaultTask 类来完成

tasks.register<Copy>("myCopy")                              (1)

tasks.named<Copy>("myCopy") {                               (2)
    from("resources")
    into("target")
    include("**/*.txt", "**/*.xml", "**/*.properties")
}

abstract class MyCopyTask : DefaultTask() {                 (3)
    @TaskAction
    fun copyFiles() {
        val sourceDir = File("sourceDir")
        val destinationDir = File("destinationDir")
        sourceDir.listFiles()?.forEach { file ->
            if (file.isFile && file.extension == "txt") {
                file.copyTo(File(destinationDir, file.name))
            }
        }
    }
}
1 注册类型为 CopymyCopy 任务,让 Gradle 知道我们打算在构建逻辑中使用它。
2 根据 Copy 任务的 API,配置已注册的 myCopy 任务所需的输入和输出。
3 实现一个名为 MyCopyTask 的自定义任务类型,它继承 DefaultTask 并定义 copyFiles 任务操作。
tasks.register("myCopy", Copy)                              (1)

tasks.named("myCopy", Copy) {                               (2)
    from "resources"
    into "target"
    include "**/*.txt", "**/*.xml", "**/*.properties"
}

abstract class MyCopyTask extends DefaultTask {             (3)
    @TaskAction
    void copyFiles() {
        fileTree('sourceDir').matching {
            include '**/*.txt'
        }.forEach { file ->
            file.copyTo(file.path.replace('sourceDir', 'destinationDir'))
        }
    }
}
1 注册类型为 CopymyCopy 任务,让 Gradle 知道我们打算在构建逻辑中使用它。
2 根据 Copy 任务的 API,配置已注册的 myCopy 任务所需的输入和输出。
3 实现一个名为 MyCopyTask 的自定义任务类型,它继承 DefaultTask 并定义 copyFiles 任务操作。

1. 注册任务

您可以通过在构建脚本或插件中注册任务来定义 Gradle 要执行的操作。

使用字符串定义任务名称来定义任务

build.gradle.kts
tasks.register("hello") {
    doLast {
        println("hello")
    }
}
build.gradle
tasks.register('hello') {
    doLast {
        println 'hello'
    }
}

在上面的示例中,使用 TaskContainer 中的 register() 方法将任务添加到 TasksCollection 中。

2. 配置任务

必须配置 Gradle 任务才能成功完成其操作。如果任务需要 ZIP 文件,则必须配置文件名和位置。您可以参考 Gradle Zip 任务的 API 来了解如何正确配置它。

让我们以 Gradle 提供的 Copy 任务为例。我们首先在构建脚本中注册一个名为 myCopy、类型为 Copy 的任务

build.gradle.kts
tasks.register<Copy>("myCopy")
build.gradle
tasks.register('myCopy', Copy)

这注册了一个没有默认行为的复制任务。由于该任务是 Copy 类型,这是一种 Gradle 支持的任务类型,因此可以使用其 API 进行配置。

以下示例显示了几种实现相同配置的方法

1. 使用 named() 方法:

使用 named() 配置在其他地方注册的现有任务

build.gradle.kts
tasks.named<Copy>("myCopy") {
    from("resources")
    into("target")
    include("**/*.txt", "**/*.xml", "**/*.properties")
}
build.gradle
tasks.named('myCopy') {
    from 'resources'
    into 'target'
    include('**/*.txt', '**/*.xml', '**/*.properties')
}

2. 使用配置块:

注册任务后立即使用一个块来配置任务

build.gradle.kts
tasks.register<Copy>("copy") {
   from("resources")
   into("target")
   include("**/*.txt", "**/*.xml", "**/*.properties")
}
build.gradle
tasks.register('copy', Copy) {
   from 'resources'
   into 'target'
   include('**/*.txt', '**/*.xml', '**/*.properties')
}

3. 将名称方法作为调用:

仅在 Groovy 中支持的一种流行选项是简写记法

copy {
    from("resources")
    into("target")
    include("**/*.txt", "**/*.xml", "**/*.properties")
}
此选项破坏了任务配置避免,不建议使用!

无论选择哪种方法,任务都会配置要复制的文件名和文件位置。

3. 实现任务

Gradle 提供了许多任务类型,包括 DeleteJavadocCopyExecTarPmd。如果 Gradle 没有提供满足您构建逻辑需求的任务类型,您可以实现自定义任务类型。

要创建自定义任务类,您可以继承 DefaultTask 并使继承类成为抽象类

app/build.gradle.kts
abstract class MyCopyTask : DefaultTask() {

}
app/build.gradle
abstract class MyCopyTask extends DefaultTask {

}