在入門教程中([Chapter 06\. Build Script Basics 構建腳本的基礎識](https://github.com/waylau/Gradle-2-User-Guide/blob/master/Chapter%2006.%20Build%20Script%20Basics%20%E6%9E%84%E5%BB%BA%E8%84%9A%E6%9C%AC%E7%9A%84%E5%9F%BA%E7%A1%80%E8%AF%86.md)),已經學到了如何創建簡單 task。之后您還學習了如何將其他行為添加到這些 task 中,同時你已經學會了如何創建 task 之間的依賴。這都是簡單的 task 。但 Gradle 讓 task 的概念更深遠。Gradle 支持增強的task,也就是,有自己的屬性和方法的 task 。這是真正的與你所使用的 Ant target(目標)的不同之處。這種增強的任務可以由你提供,或由 Gradle 構建。
## [](https://github.com/waylau/Gradle-2-User-Guide/blob/master/Chapter%2015.%20More%20about%20Tasks%20%E6%9B%B4%E5%A4%9A%E5%85%B3%E4%BA%8E%E4%BB%BB%E5%8A%A1.md#151-defining-tasks-定義任務)15.1\. Defining tasks 定義任務
在([Chapter 06\. Build Script Basics 構建腳本的基礎識](https://github.com/waylau/Gradle-2-User-Guide/blob/master/Chapter%2006.%20Build%20Script%20Basics%20%E6%9E%84%E5%BB%BA%E8%84%9A%E6%9C%AC%E7%9A%84%E5%9F%BA%E7%A1%80%E8%AF%86.md))中我們已經看到如何通過關鍵字這種風格來定義 task 。在某些情況中,你可能需要使用這種關鍵字風格的幾種不同的變式。例如,在表達式中不能用這種關鍵字風格。
Example 15.1\. Defining tasks
build.gradle
~~~
task(hello) << {
println "hello"
}
task(copy, type: Copy) {
from(file('srcDir'))
into(buildDir)
}
~~~
也可以使用字符串作為 task 名稱
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)
}
~~~
你可能更愿意使用另外一種替代的語法來定義任務:
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)
}
~~~
## [](https://github.com/waylau/Gradle-2-User-Guide/blob/master/Chapter%2015.%20More%20about%20Tasks%20%E6%9B%B4%E5%A4%9A%E5%85%B3%E4%BA%8E%E4%BB%BB%E5%8A%A1.md#152-locating-tasks-定位任務)15.2\. Locating tasks 定位任務
你經常需要在構建文件中查找你所定義的 task,例如,為了去配置或是使用它們作為依賴。對這樣的情況,有很多種方法。首先,每個 task 都可作為 project 的一個屬性,并且使用 task 名稱作為這個屬性名稱:
Example 15.4\. Accessing tasks as properties
build.gradle
~~~
task hello
println hello.name
println project.hello.name
~~~
task 也可以通過 task 集合來訪問
Example 15.5\. Accessing tasks via tasks collection
build.gradle
~~~
task hello
println tasks.hello.name
println tasks['hello'].name
~~~
您可以從任何 project 中,使用 tasks.getByPath() 方法獲取 task 路徑并且通過這個路徑來訪問 task。你可以用 task 名稱,相對路徑或者是絕對路徑作為參數調用 getByPath() 方法。
Example 15.6\. Accessing tasks by path
build.gradle
~~~
project(':projectA') {
task hello
}
task hello
println tasks.getByPath('hello').path
println tasks.getByPath(':hello').path
println tasks.getByPath('projectA:hello').path
println tasks.getByPath(':projectA:hello').path
~~~
執行 gradle -q hello
~~~
> gradle -q hello
:hello
:hello
:projectA:hello
:projectA:hello
~~~
詳見?[TaskContainer](http://www.gradle.org/docs/current/javadoc/org/gradle/api/tasks/TaskContainer.html)?關于更多定位 task 的選項
## [](https://github.com/waylau/Gradle-2-User-Guide/blob/master/Chapter%2015.%20More%20about%20Tasks%20%E6%9B%B4%E5%A4%9A%E5%85%B3%E4%BA%8E%E4%BB%BB%E5%8A%A1.md#153-configuring-tasks-配置任務)15.3\. Configuring tasks 配置任務
作為一個例子,讓我們看看由 Gradle 提供的 Copy task。若要創建Copy task ,您可以在構建腳本中聲明:
Example 15.7\. Creating a copy task
build.gradle
~~~
task myCopy(type: Copy)
~~~
上面的代碼創建了一個什么都沒做的復制 task 。可以使用它的 API 來配置這個任務 (見[Copy](http://www.gradle.org/docs/current/dsl/org.gradle.api.tasks.Copy.html))。下面的示例演示了幾種不同的方式來實現相同的配置。
要明白,意識到這項任務的名稱是 “myCopy”,但它的類型是“Copy”。你可以有多個同一類型的 task ,但具有不同的名稱。你會發現這給你一個很大的權力來實現橫切關注點在一個特定類型的所有 task。
Example 15.8\. Configuring a task - various ways
build.gradle
~~~
Copy myCopy = task(myCopy, type: Copy)
myCopy.from 'resources'
myCopy.into 'target'
myCopy.include('**/*.txt', '**/*.xml', '**/*.properties')
~~~
這類似于我們通常在 Java 中配置對象的方式。您必須在每一次的配置語句重復上下文 (myCopy)。這顯得很冗余并且很不好讀。
還有另一種配置任務的方式。它也保留了上下文,且可以說是可讀性最強的。它是我們通常最喜歡的方式。
Example 15.9\. Configuring a task - with closure
build.gradle
~~~
task myCopy(type: Copy)
myCopy {
from 'resources'
into 'target'
include('**/*.txt', '**/*.xml', '**/*.properties')
}
~~~
這種方式適用于任何任務。該例子的第 3 行只是 tasks.getByName() 方法的簡潔寫法。特別要注意的是,如果您向 getByName() 方法傳入一個閉包,這個閉包的應用是在配置這個任務的時候,而不是任務執行的時候。
您也可以在定義一個任務的時候使用一個配置閉包。
Example 15.10\. Defining a task with closure
build.gradle
~~~
task copy(type: Copy) {
from 'resources'
into 'target'
include('**/*.txt', '**/*.xml', '**/*.properties')
}
~~~
## [](https://github.com/waylau/Gradle-2-User-Guide/blob/master/Chapter%2015.%20More%20about%20Tasks%20%E6%9B%B4%E5%A4%9A%E5%85%B3%E4%BA%8E%E4%BB%BB%E5%8A%A1.md#154-adding-dependencies-to-a-task-給任務添加依賴)15.4\. Adding dependencies to a task 給任務添加依賴
定義任務的依賴關系有幾種方法。在第 6.5 章節,"任務依賴"中,已經向你介紹了使用任務名稱來定義依賴。任務的名稱可以指向同一個項目中的任務,或者其他項目中的任務。要引用另一個項目中的任務,你需要把它所屬的項目的路徑作為前綴加到它的名字中。下面是一個示例,添加了從projectA:taskX 到 projectB:taskY 的依賴關系:
Example 15.11\. Adding dependency on task from another project
build.gradle
~~~
project('projectA') {
task taskX(dependsOn: ':projectB:taskY') << {
println 'taskX'
}
}
project('projectB') {
task taskY << {
println 'taskY'
}
}
~~~
執行 gradle -q taskX
~~~
> gradle -q taskX
taskY
taskX
~~~
您可以使用一個 Task 對象而不是任務名稱來定義依賴,如下:
Example 15.12\. Adding dependency using task object
build.gradle
~~~
task taskX << {
println 'taskX'
}
task taskY << {
println 'taskY'
}
taskX.dependsOn taskY
~~~
執行 gradle -q taskX
~~~
> gradle -q taskX
taskY
taskX
~~~
對于更高級的用法,您可以使用閉包來定義 task 依賴。在計算依賴時,閉包會被傳入正在計算依賴的任務。這個閉包應該返回一個 Task 對象或是Task 對象的集合,返回值會被作為這個 task 的依賴項。下面的示例是從taskX 加入了 project 中所有名稱以 lib 開頭的 task 的依賴
Example 15.13\. Adding dependency using closure
build.gradle
~~~
task taskX << {
println 'taskX'
}
taskX.dependsOn {
tasks.findAll { task -> task.name.startsWith('lib') }
}
task lib1 << {
println 'lib1'
}
task lib2 << {
println 'lib2'
}
task notALib << {
println 'notALib'
}
~~~
執行 gradle -q taskX
~~~
> gradle -q taskX
lib1
lib2
taskX
~~~
更多關于 task 依賴 ,見?[Task](http://www.gradle.org/docs/current/dsl/org.gradle.api.Task.html)?API
## [](https://github.com/waylau/Gradle-2-User-Guide/blob/master/Chapter%2015.%20More%20about%20Tasks%20%E6%9B%B4%E5%A4%9A%E5%85%B3%E4%BA%8E%E4%BB%BB%E5%8A%A1.md#155-ordering-tasks-排序任務)15.5\. Ordering tasks 排序任務
任務排序還是一個[孵化中](https://github.com/waylau/Gradle-2-User-Guide/blob/master/Appendix%20C.%20The%20Feature%20Lifecycle%20%E7%89%B9%E6%80%A7%E7%9A%84%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F.md)的功能。請注意此功能在以后的 Gradle 版本中可能會改變。
在某些情況下,控制兩個任務的執行的順序,而不引入這些任務之間的顯式依賴,是很有用的。任務排序和任務依賴之間的主要區別是,排序規則不會影響那些任務的執行,而僅將執行的順序。
任務排序在許多情況下可能很有用:
* 強制任務順序執行: 如,'build' 永遠不會在 'clean' 前面執行。
* 在構建中盡早進行構建驗證:如,驗證在開始發布的工作前有一個正確的證書。
* 通過在長久驗證前運行快速驗證以得到更快的反饋:如,單元測試應在集成測試之前運行。
* 一個任務聚合了某一特定類型的所有任務的結果:如,測試報告任務結合了所有執行的測試任務的輸出。
有兩種排序規則是可用的:"必須在之后運行"和"應該在之后運行"。
通過使用 “必須在之后運行”的排序規則,您可以指定 taskB 必須總是運行在 taskA 之后,無論 taskA 和 taskB 這兩個任務在什么時候被調度執行。這被表示為 taskB.mustRunAfter(taskA) 。“應該在之后運行”的排序規則與其類似,但沒有那么嚴格,因為它在兩種情況下會被忽略。首先是如果使用這一規則引入了一個排序循環。其次,當使用并行執行,并且一個任務的所有依賴項除了任務應該在之后運行之外所有條件已滿足,那么這個任務將會運行,不管它的“應該在之后運行”的依賴項是否已經運行了。當傾向于更快的反饋時,會使用“應該在之后運行”的規則,因為這種排序很有幫助但要求不嚴格。
目前使用這些規則仍有可能出現 taskA 執行而 taskB 沒有執行,或者taskB 執行而 taskA 沒有執行。
Example 15.14\. Adding a 'must run after' task ordering
build.gradle
~~~
task taskX << {
println 'taskX'
}
task taskY << {
println 'taskY'
}
taskY.mustRunAfter taskX
~~~
執行 gradle -q taskY taskX
~~~
> gradle -q taskY taskX
taskX
taskY
~~~
Example 15.15\. Adding a 'should run after' task ordering
build.gradle
~~~
task taskX << {
println 'taskX'
}
task taskY << {
println 'taskY'
}
taskY.shouldRunAfter taskX
~~~
執行 gradle -q taskY taskX
~~~
> gradle -q taskY taskX
taskX
taskY
~~~
在上面的例子中,它仍有可能執行 taskY 而不會導致 taskX 也運行:
Example 15.16\. Task ordering does not imply task execution
執行 gradle -q taskY
~~~
> gradle -q taskY
taskY
~~~
如果想指定兩個任務之間的“必須在之后運行”和“應該在之后運行”排序,可以使用?[Task.mustRunAfter()](http://www.gradle.org/docs/current/dsl/org.gradle.api.Task.html#org.gradle.api.Task:mustRunAfter(java.lang.Object%5B%5D))?和[Task.shouldRunAfter()](http://www.gradle.org/docs/current/javadoc/org/gradle/api/Task.html#shouldRunAfter(java.lang.Object%5B%5D))?方法。這些方法接受一個任務實例、 任務名稱或?[Task.dependsOn()](http://www.gradle.org/docs/current/dsl/org.gradle.api.Task.html#org.gradle.api.Task:dependsOn(java.lang.Object%5B%5D))?所接受的任何其他輸入作為參數。
請注意"B.mustRunAfter(A)"或"B.shouldRunAfter(A)"并不意味著這些任務之間的任何執行上的依賴關系:
它是可以獨立地執行任務 A 和 B 的。
* 排序規則僅在這兩項任務計劃執行時起作用。
* 當--continue參數運行時,可能會是 A 執行失敗后 B 執行了。
如之前所述,如果“應該在之后運行”的排序規則引入了排序循環,那么它將會被忽略。
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
~~~
> gradle -q taskX
taskZ
taskY
taskX
~~~
## [](https://github.com/waylau/Gradle-2-User-Guide/blob/master/Chapter%2015.%20More%20about%20Tasks%20%E6%9B%B4%E5%A4%9A%E5%85%B3%E4%BA%8E%E4%BB%BB%E5%8A%A1.md#156-adding-a-description-to-a-task-給任務添加描述)15.6\. Adding a description to a task 給任務添加描述
可以給任務添加描述,這個描述將會在 task 執行時顯示。
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')
}
~~~
## [](https://github.com/waylau/Gradle-2-User-Guide/blob/master/Chapter%2015.%20More%20about%20Tasks%20%E6%9B%B4%E5%A4%9A%E5%85%B3%E4%BA%8E%E4%BB%BB%E5%8A%A1.md#157-replacing-tasks-替換任務)15.7\. Replacing tasks 替換任務
有時您想要替換一個任務。例如,您想要把通過 Java 插件添加的一個任務與不同類型的一個自定義任務進行交換。你可以這樣實現:
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
~~~
> gradle -q copy
I am the new one.
~~~
在這里我們用一個簡單的任務替換 Copy 類型的任務。當創建這個簡單的任務時,您必須將 overwrite 屬性設置為 true。否則 Gradle 將拋出異常,說這種名稱的任務已經存在。
## [](https://github.com/waylau/Gradle-2-User-Guide/blob/master/Chapter%2015.%20More%20about%20Tasks%20%E6%9B%B4%E5%A4%9A%E5%85%B3%E4%BA%8E%E4%BB%BB%E5%8A%A1.md#158-skipping-tasks-跳過任務)15.8\. Skipping tasks 跳過任務
Gradle 提供多種方式來跳過任務的執行。
### [](https://github.com/waylau/Gradle-2-User-Guide/blob/master/Chapter%2015.%20More%20about%20Tasks%20%E6%9B%B4%E5%A4%9A%E5%85%B3%E4%BA%8E%E4%BB%BB%E5%8A%A1.md#1581-using-a-predicate-使用斷言)15.8.1\. Using a predicate 使用斷言
你可以使用 onlyIf() 方法將斷言附加到一項任務中。如果斷言結果為 true,才會執行任務的操作。你可以用一個閉包來實現斷言。閉包會作為一個參數傳給任務,并且任務應該執行時返回 true,或任務應該跳過時返回false。斷言只在任務要執行前才計算。
Example 15.20\. Skipping a task using a predicate
build.gradle
~~~
task hello << {
println 'hello world'
}
hello.onlyIf { !project.hasProperty('skipHello') }
~~~
執行 gradle hello -PskipHello
~~~
> gradle hello -PskipHello
:hello SKIPPED
BUILD SUCCESSFUL
Total time: 1 secs
~~~
### [](https://github.com/waylau/Gradle-2-User-Guide/blob/master/Chapter%2015.%20More%20about%20Tasks%20%E6%9B%B4%E5%A4%9A%E5%85%B3%E4%BA%8E%E4%BB%BB%E5%8A%A1.md#1582-using-stopexecutionexception)15.8.2\. Using StopExecutionException
如果跳過任務的規則不能與斷言同時表達,您可以使用[StopExecutionException](http://www.gradle.org/docs/current/javadoc/org/gradle/api/tasks/StopExecutionException.html)。如果一個操作(action)拋出了此異常,那么這個操作(action)接下來的行為和這個任務的其他 操作(action)都會被跳過。構建會繼續執行下一個任務。
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 this is used in an integration test so we want defined behavior.
if (true) { throw new StopExecutionException() }
}
task myTask(dependsOn: 'compile') << {
println 'I am not affected'
}
~~~
Output of gradle -q myTask
~~~
> gradle -q myTask
I am not affected
~~~
如果您使用由 Gradle 提供的任務,那么此功能將非常有用。它允許您向一個任務的內置操作中添加執行條件。(你可能會想,為什么既不導入StopExecutionException 也沒有通過其完全限定名來訪問它。原因是,Gradle 會向您的腳本添加默認的一些導入。這些導入是可自定義的 (見[Appendix E. Existing IDE Support and how to cope without it 支持的 IDE 以及如何應對沒有它](https://github.com/waylau/Gradle-2-User-Guide/blob/master/Appendix%20E.%20Existing%20IDE%20Support%20and%20how%20to%20cope%20without%20it%20%E6%94%AF%E6%8C%81%E7%9A%84%20IDE%20%E4%BB%A5%E5%8F%8A%E5%A6%82%E4%BD%95%E5%BA%94%E5%AF%B9%E6%B2%A1%E6%9C%89%E5%AE%83.md))。)
### [](https://github.com/waylau/Gradle-2-User-Guide/blob/master/Chapter%2015.%20More%20about%20Tasks%20%E6%9B%B4%E5%A4%9A%E5%85%B3%E4%BA%8E%E4%BB%BB%E5%8A%A1.md#1583-enabling-and-disabling-tasks-啟用和禁用任務)15.8.3\. Enabling and disabling tasks 啟用和禁用任務
每一項任務有一個默認值為 true 的 enabled 標記。將它設置為 false,可以不讓這個任務的任何操作執行。
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
~~~
> gradle disableMe
:disableMe SKIPPED
BUILD SUCCESSFUL
Total time: 1 secs
~~~
## [](https://github.com/waylau/Gradle-2-User-Guide/blob/master/Chapter%2015.%20More%20about%20Tasks%20%E6%9B%B4%E5%A4%9A%E5%85%B3%E4%BA%8E%E4%BB%BB%E5%8A%A1.md#159-skipping-tasks-that-are-up-to-date-跳過處于最新狀態的任務)15.9\. Skipping tasks that are up-to-date 跳過處于最新狀態的任務
如果您使用 Gradle 自帶的任務,如 Java 插件所添加的任務的話,你可能已經注意到 Gradle 將跳過處于最新狀態的任務。這種行在您自己定義的任務上也有效,而不僅僅是內置任務。
### [](https://github.com/waylau/Gradle-2-User-Guide/blob/master/Chapter%2015.%20More%20about%20Tasks%20%E6%9B%B4%E5%A4%9A%E5%85%B3%E4%BA%8E%E4%BB%BB%E5%8A%A1.md#1591-declaring-a-tasks-inputs-and-outputs-聲明一個任務的輸入和輸出)15.9.1\. Declaring a task's inputs and outputs 聲明一個任務的輸入和輸出
讓我們來看一個例子。在這里我們的任務從一個 XML 源文件生成多個輸出文件。讓我們運行它幾次。
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
~~~
> gradle transform
:transform
Transforming source file.
~~~
執行 gradle transform
~~~
> gradle transform
:transform
Transforming source file.
~~~
請注意 Gradle 第二次執行執行這項任務時,即使什么都未作改變,也沒有跳過該任務。我們的示例任務被用一個操作(action)閉包來定義。Gradle 不知道這個閉包做了什么,也無法自動判斷這個任務是否為最新狀態。若要使用 Gradle 的最新狀態(up-to-date)檢查,您需要聲明這個任務的輸入和輸出。
每個任務都有一個 inputs 和 outputs 的屬性,用來聲明任務的輸入和輸出。下面,我們修改了我們的示例,聲明它將 XML 源文件作為輸入,并產生輸出到一個目標目錄。讓我們運行它幾次。
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
~~~
> gradle transform
:transform
Transforming source file.
~~~
執行 gradle transform
~~~
> gradle transform
:transform UP-TO-DATE
~~~
現在,Gradle 知道哪些文件要檢查以確定任務是否為最新狀態。
任務的 inputs 屬性是?[TaskInputs](http://www.gradle.org/docs/current/javadoc/org/gradle/api/tasks/TaskInputs.html)類型。任務的 outputs 屬性是?[TaskOutputs](http://www.gradle.org/docs/current/javadoc/org/gradle/api/tasks/TaskOutputs.html)?類型。
一個沒有定義輸出的任務將永遠不會被當作是最新的。對于任務的輸出并不是文件的場景,或者是更復雜的場景,[TaskOutputs.upToDateWhen()?](http://www.gradle.org/docs/current/javadoc/org/gradle/api/tasks/TaskOutputs.html#upToDateWhen(groovy.lang.Closure))方法允許您以編程方式計算任務的輸出是否應該被判斷為最新狀態。
一個只定義了輸出的任務,如果自上一次構建以來它的輸出沒有改變,那么它會被判定為最新狀態。
### [](https://github.com/waylau/Gradle-2-User-Guide/blob/master/Chapter%2015.%20More%20about%20Tasks%20%E6%9B%B4%E5%A4%9A%E5%85%B3%E4%BA%8E%E4%BB%BB%E5%8A%A1.md#1592-how-does-it-work-它是怎么實現的)15.9.2\. How does it work 它是怎么實現的?
在第一次執行任務之前,Gradle 對輸入進行一次快照。這個快照包含了輸入文件集和每個文件的內容的哈希值。然后 Gradle 執行該任務。如果任務成功完成,Gradle 將對輸出進行一次快照。該快照包含輸出文件集和每個文件的內容的哈希值。Gradle 會保存這兩個快照,直到任務的下一次執行。
之后每一次,在執行任務之前,Gradle 會對輸入和輸出進行一次新的快照。如果新的快照和前一次的快照一樣,Gradle 會假定這些輸出是最新狀態的并跳過該任務。如果它們不一則, Gradle 則會執行該任務。Gradle 會保存這兩個快照,直到任務的下一次執行。
請注意,如果一個任務有一個指定的輸出目錄,在它上一次執行之后添加到該目錄的所有文件都將被忽略,并且不會使這個任務成為過時狀態。這是不相關的任務可以在不互相干擾的情況下共用一個輸出目錄。如果你因為一些理由而不想這樣,請考慮使用?[TaskOutputs.upToDateWhen](http://www.gradle.org/docs/current/javadoc/org/gradle/api/tasks/TaskOutputs.html#upToDateWhen(groovy.lang.Closure))
## [](https://github.com/waylau/Gradle-2-User-Guide/blob/master/Chapter%2015.%20More%20about%20Tasks%20%E6%9B%B4%E5%A4%9A%E5%85%B3%E4%BA%8E%E4%BB%BB%E5%8A%A1.md#1510-task-rules-任務規則)15.10\. Task rules 任務規則
有時你想要有這樣一項任務,它的行為依賴于參數數值范圍的一個大數或是無限的數字。任務規則是提供此類任務的一個很好的表達方式:
Example 15.25\. Task rule
build.gradle
~~~
tasks.addRule("Pattern: ping<ID>") { String taskName ->
if (taskName.startsWith("ping")) {
task(taskName) << {
println "Pinging: " + (taskName - 'ping')
}
}
}
~~~
執行 gradle -q pingServer1
~~~
> gradle -q pingServer1
Pinging: Server1
~~~
這個字符串參數被用作這條規則的描述。當對這個例子運行 gradle tasks 的時候,這個描述會被顯示。
規則不只是從命令行調用任務才起作用。你也可以對基于規則的任務創建依賴關系
Example 15.26\. Dependency on rule based tasks
build.gradle
~~~
tasks.addRule("Pattern: ping<ID>") { String taskName ->
if (taskName.startsWith("ping")) {
task(taskName) << {
println "Pinging: " + (taskName - 'ping')
}
}
}
task groupPing {
dependsOn pingServer1, pingServer2
}
~~~
執行 gradle -q groupPing
~~~
> gradle -q groupPing
Pinging: Server1
Pinging: Server2
~~~
執行 gradle -q tasks 你找不到 “pingServer1” 或 “pingServer2” 任務,但腳本執行邏輯是基于請求執行這些任務的
## [](https://github.com/waylau/Gradle-2-User-Guide/blob/master/Chapter%2015.%20More%20about%20Tasks%20%E6%9B%B4%E5%A4%9A%E5%85%B3%E4%BA%8E%E4%BB%BB%E5%8A%A1.md#1511-finalizer-tasks-析構器任務)15.11\. Finalizer tasks 析構器任務
析構器任務是一個 孵化中 的功能 (請參閱[Appendix C. The Feature Lifecycle 特性的生命周期](https://github.com/waylau/Gradle-2-User-Guide/blob/master/Appendix%20C.%20The%20Feature%20Lifecycle%20%E7%89%B9%E6%80%A7%E7%9A%84%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F.md)?C.1.2 章節, “Incubating”)。
當最終的任務準備運行時,析構器任務會自動地添加到任務圖中。
Example 15.27\. Adding a task finalizer
build.gradle
~~~
task taskX << {
println 'taskX'
}
task taskY << {
println 'taskY'
}
taskX.finalizedBy taskY
~~~
執行 gradle -q taskX
~~~
> gradle -q taskX
taskX
taskY
~~~
即使最終的任務執行失敗,析構器任務也會被執行。
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
~~~
> gradle -q taskX
taskX
taskY
~~~
另一方面,如果最終的任務什么都不做的話,比如由于失敗的任務依賴項或如果它被認為是最新的狀態,析構任務不會執行。
在不管構建成功或是失敗,都必須清理創建的資源的情況下,析構認為是很有用的。這樣的資源的一個例子是,一個 web 容器會在集成測試任務前開始,并且在之后關閉,即使有些測試失敗。
你可以使用 [Task.finalizedBy()]([http://www.gradle.org/docs/current/dsl/org.gradle.api.Task.html#org.gradle.api.Task:finalizedBy(java.lang.Object[]))](http://www.gradle.org/docs/current/dsl/org.gradle.api.Task.html#org.gradle.api.Task:finalizedBy(java.lang.Object%5B%5D)%EF%BC%89)?方法指定一個析構器任務。這個方法接受一個任務實例、 任務名稱或[Task.dependsOn()](http://www.gradle.org/docs/current/dsl/org.gradle.api.Task.html#org.gradle.api.Task:dependsOn(java.lang.Object%5B%5D))所接受的任何其他輸入作為參數。
## [](https://github.com/waylau/Gradle-2-User-Guide/blob/master/Chapter%2015.%20More%20about%20Tasks%20%E6%9B%B4%E5%A4%9A%E5%85%B3%E4%BA%8E%E4%BB%BB%E5%8A%A1.md#1512-summary-總結)15.12\. Summary 總結
如果你是從 Ant 轉過來的,像 Copy 這種增強的 Gradle 任務,看起來就像是一個 Ant target(目標)和一個 Ant task (任務)之間的混合物。實際上確實是這樣子。Gradle 沒有像 Ant 那樣對任務和目標進行分離。簡單的 Gradle 任務就像 Ant 的目標,而增強的 Gradle 任務還包括 Ant 任務方面的內容。Gradle 的所有任務共享一個公共 API,您可以創建它們之間的依賴性。這樣的一個任務可能會比一個 Ant 任務更好配置。它充分利用了類型系統,更具有表現力而且易于維護。
- 關于
- 第1章 Introduction 介紹
- 第2章 Overview 總覽
- 第3章 Tutorials 教程
- 第4章 Installing Gradle 安裝
- 第5章 Troubleshooting 問題解決
- 第6章 Build Script Basics 構建腳本的基礎識
- 第7章 Java Quickstart 快速開始 Java
- 第8章 Dependency Management Basics 依賴管理的基礎知識
- 第9章 Groovy Quickstart 快速開始 Groovy
- 第10章 Web Application Quickstart 快速開始 Web 應用
- 第11章 Using the Gradle Command-Line 使用 Gradle 命令行
- 第12章 Using the Gradle Graphical User Interface 使用 Gradle 圖形化用戶界面
- 第13章 Writing Build Scripts 編寫構建腳本
- 第14章 Tutorial - 'This and That' 教程-這個那個
- 第15章 More about Tasks 更多關于任務
- 第16章 Working With Files 跟文件工作
- 第17章 Using Ant from Gradle 從 Gradle 使用 Ant
- 第18章 Logging 日志.md
- 第19章 The Gradle Daemon 守護進程
- 第20章 The Build Environment 構建環境
- 第21章 Gradle Plugins 插件
- 第22章 Standard Gradle plugins 標準 Gradle 插件
- 附錄E Existing IDE Support and how to cope without it 支持的 IDE 以及如何應對沒有它