Gradle 2.0 用户指南翻译——第十五章. 任务详述

简介: 翻译项目请关注Github上的地址:https://github.com/msdx/gradledoc本文翻译所在分支:https://github.com/msdx/gradledoc/tree/2.0 。
翻译项目请关注Github上的地址:
https://github.com/msdx/gradledoc
本文翻译所在分支:
https://github.com/msdx/gradledoc/tree/2.0 。


第十五章. 任务详述

Chapter 15. More about Tasks

在入门教程(《15.1. 定义任务

15.1. Defining tasks

在《

示例 15.1. 定义任务 - Example 15.1. Defining tasks

build.gradle

task(hello) << {
    println "hello"
}

task(copy, type: Copy) {
    from(file('srcDir'))
    into(buildDir)
}

你还可以使用字符串作为任务名称:
You can also use strings for the task names:

示例 15.2. 定义任务 —— 使用字符串作为任务名称 - Example 15.2. Defining tasks - using strings for task names

build.gradle

task('hello') <<
{
    println "hello"
}

task('copy', type: Copy) {
    from(file('srcDir'))
    into(buildDir)
}

有一种用于定义任务的替代语法,你可能更希望使用:
There is an alternative syntax for defining tasks, which you may prefer to use:

示例 15.3. 使用替代语法定义任务 - Example 15.3. Defining tasks with alternative syntax

build.gradle

tasks.create(name: 'hello') << {
    println "hello"
}

tasks.create(name: 'copy', type: Copy) {
    from(file('srcDir'))
    into(buildDir)
}

示例 15.16. 任务排序并不意味着任务执行 - Example 15.16. Task ordering does not imply task execution

gradle -q taskY 的输出结果
Output of gradle -q taskY

> gradle -q taskY
taskY

如果想指定两个任务之间的“必须在之后运行”或“应该在之后运行”的排序,可以使用Task.mustRunAfter()Task.shouldRunAfter()方法。这些方法接受一个任务实例、任务名称或Task.dependsOn()所接受的任何其他输入。 
To specify a "must run after" or "should run after" ordering between 2 tasks, you use the Task.mustRunAfter() and Task.shouldRunAfter() methods. These method accept a task instance, a task name or any other input accepted by Task.dependsOn().

请注意“B.mustRunAfter(A)”或“B.shouldRunAfter(A)”并不意味着这些任务之间有任何执行上的依赖关系: 
Note that "B.mustRunAfter(A)" or "B.shouldRunAfter(A)" does not imply any execution dependency between the tasks:

  • 任务AB可以独立执行。排序规则只有在两个任务都计划执行时才有作用。
    It is possible to execute tasks A and B independently. The ordering rule only has an effect when both tasks are scheduled for execution.
  • 当使用--continue参数运行时,可能会存在A执行失败后B执行了。
    When run with --continue, it is possible for B to execute in the event that A fails.

如前所述,如果“应该在之后运行”的排序规则引入了排序循环,那么这条规则将被忽略。
As mentioned before 'should run after' ordering rule will be ignored if it introduces an ordering cycle:

示例 15.17. 当引入循环时,“应该在之后运行”的任务排序会被忽略 - Example 15.17. A 'should run after' task ordering is ignored if it introduces an ordering cycle

build.gradle

task taskX << {
    println 'taskX'
}
task taskY << {
    println 'taskY'
}
task taskZ << {
    println 'taskZ'
}
taskX.dependsOn taskY
taskY.dependsOn taskZ
taskZ.shouldRunAfter taskX

gradle -q taskX的输出结果
Output of gradle -q taskX

> gradle -q taskX
taskZ
taskY
taskX

15.6. 向任务添加描述

15.6. Adding a description to a task

你可以向你的任务添加描述。例如在执行gradle tasks时显示该描述。 
You can add a description to your task. This description is for example displayed when executing gradle tasks.

示例 15.18. 向任务添加描述 - Example 15.18. Adding a description to a task

build.gradle

task copy(type: Copy) {
   description 'Copies the resource directory to the target directory.'
   from 'resources'
   into 'target'
   include('**/*.txt', '**/*.xml', '**/*.properties')
}

15.7. 替换任务

15.7. Replacing tasks

有时你会想要替换一个任务。例如,你想要将Java插件添加的任务替换成一个不同类型的自定义任务。你可以通过以下方式实现: 
Sometimes you want to replace a task. For example if you want to exchange a task added by the Java plugin with a custom task of a different type. You can achieve this with:

示例 15.19. 重写任务 - Example 15.19. Overwriting a task

build.gradle

task copy(type: Copy)

task copy(overwrite: true) << {
    println('I am the new one.')
}

gradle -q copy 的输出结果
Output of gradle -q copy

> gradle -q copy
I am the new one.

这里我们把一个Copy类型的任务替换为一个简单的任务。当创建这个简单任务时,你必须把overwrite属性设置为true。否则,Gradle会抛出一个异常,称这个名称的任务已经存在。 
Here we replace a task of type Copy with a simple task. When creating the simple task, you have to set the overwrite property to true. Otherwise Gradle throws an exception, saying that a task with such a name already exists.

15.8. 跳过任务

15.8. Skipping tasks

Gradle 提供了多种方式来跳过任务的执行。
Gradle offers multiple ways to skip the execution of a task.

15.8.1. 使用断言

15.8.1. Using a predicate

你可以使用onlyIf()方法将断言附加到任务中。只有当断言结果为true时,才会执行该任务的操作。你可以用闭包来实现断言。闭包会作为一个参数传给任务,如果任务应该执行,应该返回true,如果任务应该被跳过,则返回false。断言只会在任务执行之前才评估。 
You can use the onlyIf() method to attach a predicate to a task. The task's actions are only executed if the predicate evaluates to true. You implement the predicate as a closure. The closure is passed the task as a parameter, and should return true if the task should execute and false if the task should be skipped. The predicate is evaluated just before the task is due to be executed.

示例 15.20. 使用断言跳过一个任务 - Example 15.20. Skipping a task using a predicate

build.gradle

task hello << {
    println 'hello world'
}

hello.onlyIf { !project.hasProperty('skipHello') }

gradle hello -PskipHello的输出结果
Output of gradle hello -PskipHello

> gradle hello -PskipHello
:hello SKIPPED

BUILD SUCCESSFUL

Total time: 1 secs

15.8.2. 使用 StopExecutionException

15.8.2. Using StopExecutionException

如果跳过任务的规则不能用断言来表达,你可以使用StopExecutionException。如果一个操作抛出此异常,则此操作的接下来的执行以及该任务的任何后续操作都会跳过。构建会继续执行下一个任务。 
If the rules for skipping a task can't be expressed with predicate, you can use the StopExecutionException. If this exception is thrown by an action, the further execution of this action as well as the execution of any following action of this task is skipped. The build continues with executing the next task.

示例 15.21. 使用 StopExecutionException 跳过任务 - Example 15.21. Skipping tasks with StopExecutionException

build.gradle

task compile << {
    println 'We are doing the compile.'
}

compile.doFirst {
    // Here you would put arbitrary conditions in real life. But we use this as an integration test, so we want defined behavior.
    if (true) { throw new StopExecutionException() }
}
task myTask(dependsOn: 'compile') << {
   println 'I am not affected'
}

gradle -q myTask 的输出结果
Output of gradle -q myTask

> gradle -q myTask
I am not affected

如果你使用Gradle提供的任务,那么此功能将非常有用。它允许你向一个任务的内置操作添加执行条件。 [7] 
This feature is helpful if you work with tasks provided by Gradle. It allows you to add conditional execution of the built-in actions of such a task. [7]

15.8.3. 启用和禁用任务

15.8.3. Enabling and disabling tasks

每一项任务有一个默认值为trueenabled标记。将它设置为false,可以不让这个任务的任何操作执行。 
Every task has also an enabled flag which defaults to true. Setting it to false prevents the execution of any of the task's actions.

示例 15.22. 启用和禁用任务 - Example 15.22. Enabling and disabling tasks

build.gradle

task disableMe << {
    println 'This should not be printed if the task is disabled.'
}
disableMe.enabled = false

Gradle disableMe的输出结果
Output of gradle disableMe

> gradle disableMe
:disableMe SKIPPED

BUILD SUCCESSFUL

Total time: 1 secs

15.9. 跳过最新的任务

15.9. Skipping tasks that are up-to-date

如果你使用 Gradle 自带的任务,如 Java 插件所添加的任务的话,你可能已经注意到 Gradle 将跳过最新的任务。这种行为也可以用于你的任务,而不仅仅是内置任务 
If you are using one of the tasks that come with Gradle, such as a task added by the Java plugin, you might have noticed that Gradle will skip tasks that are up-to-date. This behaviour is also available for your tasks, not just for built-in tasks.

15.9.1. 声明一个任务的输入和输出

15.9.1. Declaring a task's inputs and outputs

我们来看一个例子。在这里,我们的任务从一个 XML 源文件生成了多个输出文件。 
Let's have a look at an example. Here our task generates several output files from a source XML file. Let's run it a couple of times.

示例 15.23. 一个生成任务 - Example 15.23. A generator task

build.gradle

task transform {
    ext.srcFile = file('mountains.xml')
    ext.destDir = new File(buildDir, 'generated')
    doLast {
        println "Transforming source file."
        destDir.mkdirs()
        def mountains = new XmlParser().parse(srcFile)
        mountains.mountain.each { mountain ->
            def name = mountain.name[0].text()
            def height = mountain.height[0].text()
            def destFile = new File(destDir, "${name}.txt")
            destFile.text = "$name -> ${height}\n"
        }
    }
}

gradle transform的输出结果
Output of gradle transform

> gradle transform
:transform
Transforming source file.

gradle transform的输出结果
Output of gradle transform

> gradle transform
:transform
Transforming source file.

请注意,Gradle第二次执行此任务的时候,即使没有任何变化也不会跳过该任务。我们的示例任务是使用一个动作闭包定义的。 Gradle不知道这个闭包做了什么,不能自动判断这个任务是否是最新的。要使用Gradle的最新检查,你需要声明任务的输入和输出。 
Notice that Gradle executes this task a second time, and does not skip the task even though nothing has changed. Our example task was defined using an action closure. Gradle has no idea what the closure does and cannot automatically figure out whether the task is up-to-date or not. To use Gradle's up-to-date checking, you need to declare the inputs and outputs of the task.

每一个任务都有一个inputsoutputs属性,用来声明这个任务的输入和输出。下面,我们修改了我们的示例,声明它将 XML 源文件作为输入,并产生到目标目录的输出。让我们运行几次。 
Each task has an inputs and outputs property, which you use to declare the inputs and outputs of the task. Below, we have changed our example to declare that it takes the source XML file as an input and produces output to a destination directory. Let's run it a couple of times.

示例 15.24. 声明一个任务的输入和输出 - Example 15.24. Declaring the inputs and outputs of a task

build.gradle

task transform {
    ext.srcFile = file('mountains.xml')
    ext.destDir = new File(buildDir, 'generated')
    inputs.file srcFile
    outputs.dir destDir
    doLast {
        println "Transforming source file."
        destDir.mkdirs()
        def mountains = new XmlParser().parse(srcFile)
        mountains.mountain.each { mountain ->
            def name = mountain.name[0].text()
            def height = mountain.height[0].text()
            def destFile = new File(destDir, "${name}.txt")
            destFile.text = "$name -> ${height}\n"
        }
    }
}

gradle transform的输出结果
Output of gradle transform

> gradle transform
:transform
Transforming source file.

gradle transform的输出结果
Output of gradle transform

> gradle transform
:transform UP-TO-DATE

现在,Gradle 知道要检查哪些文件,以确定任务是否为最新的。
Now, Gradle knows which files to check to determine whether the task is up-to-date or not.

任务的 inputs 属性是 TaskInputs类型。任务的 outputs 属性是 TaskOutputs类型。 
The task's inputs property is of type TaskInputs. The task's outputs property is of type TaskOutputs.

一个任务如果没有定义输出,那么它将永远不会被当作是最新的。对于任务的输出不是文件或是更复杂的场景, TaskOutputs.upToDateWhen()方法允许你以编程方式计算任务的输出是否应该被视为最新的。 
A task with no defined outputs will never be considered up-to-date. For scenarios where the outputs of a task are not files, or for more complex scenarios, the TaskOutputs.upToDateWhen() method allows you to calculate programmatically if the tasks outputs should be considered up to date.

一个只定义了输出的任务,如果自上次构建以来它的输出没变,那么它会被视为最新的。 
A task with only outputs defined will be considered up-to-date if those outputs are unchanged since the previous build.

15.9.2. 它是怎么实现的?

15.9.2. How does it work?

在第一次执行任务之前,Gradle会获取输入的快照。此快照包含一组输入文件和每个文件的内容的散列。然后Gradle执行任务。如果任务成功完成,则Gradle会对输出进行快照。此快照包含一组输出文件和每个文件的内容的散列。 Gradle会保存这两个快照,用于这个任务下一次的执行。 
Before a task is executed for the first time, Gradle takes a snapshot of the inputs. This snapshot contains the set of input files and a hash of the contents of each file. Gradle then executes the task. If the task completes successfully, Gradle takes a snapshot of the outputs. This snapshot contains the set of output files and a hash of the contents of each file. Gradle persists both snapshots for next time the task is executed.

在这之后每一次执行任务之前,Gradle将输入和输出进行新的快照。如果新快照与以前的快照相同,则Gradle会假定输出是最新的,并跳过任务。如果它们不一样,则Gradle会执行任务。Gradle会保存这两个快照,用于这个任务的下一次执行。 
Each time after that, before the task is executed, Gradle takes a new snapshot of the inputs and outputs. If the new snapshots are the same as the previous snapshots, Gradle assumes that the outputs are up to date and skips the task. If they are not the same, Gradle executes the task. Gradle persists both snapshots for next time the task is executed.

请注意,如果一个任务有指定的输出目录,在它上一次执行之后添加到该目录的所有文件都将被忽略,并且不会使这个任务过期。这样,那些不相关的任务之间就可以共用一个输出目录,而不会产生互相干扰。如果因为一些原因你不想要这样的行为,请考虑使用TaskOutputs.upToDateWhen()。 
Note that if a task has an output directory specified, any files added to that directory since the last time it was executed are ignored and will NOT cause the task to be out of date. This is so unrelated tasks may share an output directory without interfering with each other. If this is not the behaviour you want for some reason, consider using TaskOutputs.upToDateWhen()

15.10. 任务规则

15.10. Task rules

有时你想要有一个任务,它的行为依赖于一个大的或是无限的数值范围的参数。任务规则是提供这样的任务的一种很好的表达方式: 
Sometimes you want to have a task whose behavior depends on a large or infinite number value range of parameters. A very nice and expressive way to provide such tasks are task rules:

示例 15.25. 任务规则 - Example 15.25. Task rule

build.gradle

tasks.addRule("Pattern: ping") { String taskName ->
    if (taskName.startsWith("ping")) {
        task(taskName) << {
            println "Pinging: " + (taskName - 'ping')
        }
    }
}

gradle -q task1的输出结果
Output of gradle -q pingServer1

> gradle -q pingServer1
Pinging: Server1

这个String参数用作规则的描述,当对这个例子运行gradle tasks时会显示这个描述。 
The String parameter is used as a description for the rule. This description is shown when running for example gradle tasks.

规则不只是从命令行调用任务才起作用,你也可以对基于规则的任务创建依赖关系: 
Rules not just work when calling tasks from the command line. You can also create dependsOn relations on rule based tasks:

示例 15.26. 依赖于基于规则的任务 - Example 15.26. Dependency on rule based tasks

build.gradle

tasks.addRule("Pattern: ping") { String taskName ->
    if (taskName.startsWith("ping")) {
        task(taskName) << {
            println "Pinging: " + (taskName - 'ping')
        }
    }
}

task groupPing {
    dependsOn pingServer1, pingServer2
}

Gradle q groupPing的输出结果
Output of gradle -q groupPing

> gradle -q groupPing
Pinging: Server1
Pinging: Server2

15.11. 析构器任务

15.11. Finalizer tasks

build.gradle

task taskX << {
    println 'taskX'
}
task taskY << {
    println 'taskY'
}

taskX.finalizedBy taskY

gradle -q taskX的输出结果
Output of gradle -q taskX

> gradle -q taskX
taskX
taskY

即使最终的任务执行失败,析构器任务也会被执行。
Finalizer task will be executed even if the finalized task fails.

示例 15.28. 执行失败的任务的任务析构器 - Example 15.28. Task finalizer for a failing task

build.gradle

task taskX << {
    println 'taskX'
    throw new RuntimeException()
}
task taskY << {
    println 'taskY'
}

taskX.finalizedBy taskY

gradle -q taskX的输出结果
Output of gradle -q taskX

> gradle -q taskX
taskX
taskY

另一方面,如果最终的任务什么都没执行,比如因为失败的任务依赖或如果它被认为是最新的话,析构任务不会执行。
On the other hand, finalizer tasks are not executed if the finalized task didn't do any work, for example due to failed task dependency or if it's considered up to date.

在不管构建是否成功都必须清理所创建的资源的情况下,析构任务很有用。这种资源的一个例子是,在集成测试任务前开始启动的Web容器,即使有些测试失败也始终应该关闭。
Finalizer tasks are useful in situations where build creates a resource that has to be cleaned up regardless of the build failing or succeeding. An example of such resource is a web container started before an integration test task and which should be always shut down, even if some of the tests fail.

你可以使用Task.finalizedBy()方法来指定一个析构器任务。这个方法接受一个任务实例,任务名称或Task.dependsOn()所能接受的任何其他输入。 
To specify a finalizer task you use the Task.finalizedBy() method. This method accepts a task instance, a task name or any other input accepted by Task.dependsOn().

15.12. 总结

15.12. Summary

如果你是从 Ant 转过来的,像Copy这种增强的Gradle任务,看起来就像一个Ant目标和Ant任务之间的混合。实际上确实是这样子。Gradle 不像Ant那样对任务和目标进行分离。简单的Gradle任务就像Ant的目标,而增强的Gradle任务也包含了Ant任务方面的内容。所有的Gradle任务共享一个通用的API,你可以创建它们之间的依赖关系。这样的任务可能比Ant任务更好配置,它充分利用了类型系统,更具表现力也更易于维护。 
If you are coming from Ant, such an enhanced Gradle task as Copy looks like a mixture between an Ant target and an Ant task. And this is actually the case. The separation that Ant does between tasks and targets is not done by Gradle. The simple Gradle tasks are like Ant's targets and the enhanced Gradle tasks also include the Ant task aspects. All of Gradle's tasks share a common API and you can create dependencies between them. Such a task might be nicer to configure than an Ant task. It makes full use of the type system, is more expressive and easier to maintain.



目录
相关文章
|
Web App开发 存储 数据可视化
跟踪Gradle任务执行
跟踪Gradle任务执行
102 0
|
4月前
|
Android开发
Android gradle task任务检查各个module之间资源文件冲突.md
Android gradle task任务检查各个module之间资源文件冲突.md
Android gradle task任务检查各个module之间资源文件冲突.md
|
测试技术 Android开发 索引
第四章 Gradle任务
上一章我们已经介绍了Gradle脚本的基础,在其中我们也强调了Gradle中最要的Projects和Tasks这两个概念,尤其是Tasks,我们的所有Gradle的构建工作都是由Tasks组合完成的,那么这一章我们就详细的介绍下Tasks--任务。
193 0
第四章 Gradle任务
|
Java 测试技术
Gradle 1.12 翻译——第九章 Groovy快速入门
由于时间关系,没办法同时做笔记和翻译,关于Gradle的用户指南,本博客不再做相关笔记,而只对未翻译章节进行翻译并在此发表。 有关其他已翻译的章节请关注Github上的项目:https://github.
1141 0
|
Java 容器
翻译:Gradle之 Java插件
原文地址 http://www.gradle.org/docs/current/userguide/java_plugin.html 23.1. Usage用法 要使用Java插件,在脚本里加入: Example 23.1. Using the Java plugin build.gradle apply plugin: 'java' 23.2. Source sets源集 Java插件引入了一个概念:源集(source set),一个源集就是一组被一起编译一起执行的源文件。
803 0
|
Java API
翻译:Gradle之构建脚本编写
原文地址 http://www.gradle.org/docs/current/userguide/writing_build_scripts.html 13.1. The Gradle build language构建语言 Gradle提供了一种“领域专用语言”(domain specific language) 或者 DSL对构建进行描述。
961 0
|
Java 数据库连接 Maven
翻译:Gradle之依赖管理
  原文地址 http://www.gradle.org/docs/current/userguide/artifact_dependencies_tutorial.html   8.1. What is dependency management?何谓?为何? 依赖管理大致有两块:首先Gradle需要找到你工程需要的东西,这些东西就是所谓的“依赖”。
844 0
|
API
翻译:Gradle之构建脚本入门
原文地址http://www.gradle.org/docs/current/userguide/tutorial_using_tasks.html 6.1. Projects and tasks工程和任务 Gradle中的任何东西都基于俩概念: projects 工程 和 tasks 任务。
848 0
|
Java 测试技术 API
翻译--Gradle之Java工程入门
原文地址 http://www.gradle.org/docs/current/userguide/tutorial_java_projects.html   7.1. The Java plugin插件 我们已经看到Gradle是一个通用构建工具,它可以完成相当多的任务,只要你能在脚本里定义好。
1266 0
|
人工智能 移动开发 Java
Android Studio插件版本与Gradle 版本对应关系
Android Studio插件版本与Gradle 版本对应关系
2706 0
Android Studio插件版本与Gradle 版本对应关系