第15章 More about Tasks 更多關(guān)于任務(wù)

2018-02-24 15:56 更新

在入門教程中(Chapter 06. Build Script Basics 構(gòu)建腳本的基礎(chǔ)識(shí)),已經(jīng)學(xué)到了如何創(chuàng)建簡(jiǎn)單 task。之后您還學(xué)習(xí)了如何將其他行為添加到這些 task 中,同時(shí)你已經(jīng)學(xué)會(huì)了如何創(chuàng)建 task 之間的依賴。這都是簡(jiǎn)單的 task 。但 Gradle 讓 task 的概念更深遠(yuǎn)。Gradle 支持增強(qiáng)的task,也就是,有自己的屬性和方法的 task 。這是真正的與你所使用的 Ant target(目標(biāo))的不同之處。這種增強(qiáng)的任務(wù)可以由你提供,或由 Gradle 構(gòu)建。

15.1. Defining tasks 定義任務(wù)

在(Chapter 06. Build Script Basics 構(gòu)建腳本的基礎(chǔ)識(shí))中我們已經(jīng)看到如何通過(guò)關(guān)鍵字這種風(fēng)格來(lái)定義 task 。在某些情況中,你可能需要使用這種關(guān)鍵字風(fēng)格的幾種不同的變式。例如,在表達(dá)式中不能用這種關(guān)鍵字風(fēng)格。

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)
}

你可能更愿意使用另外一種替代的語(yǔ)法來(lái)定義任務(wù):

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.2. Locating tasks 定位任務(wù)

你經(jīng)常需要在構(gòu)建文件中查找你所定義的 task,例如,為了去配置或是使用它們作為依賴。對(duì)這樣的情況,有很多種方法。首先,每個(gè) task 都可作為 project 的一個(gè)屬性,并且使用 task 名稱作為這個(gè)屬性名稱:

Example 15.4. Accessing tasks as properties

build.gradle

task hello

println hello.name
println project.hello.name

task 也可以通過(guò) task 集合來(lái)訪問(wèn)

Example 15.5. Accessing tasks via tasks collection

build.gradle

task hello

println tasks.hello.name
println tasks['hello'].name

您可以從任何 project 中,使用 tasks.getByPath() 方法獲取 task 路徑并且通過(guò)這個(gè)路徑來(lái)訪問(wèn) task。你可以用 task 名稱,相對(duì)路徑或者是絕對(duì)路徑作為參數(shù)調(diào)用 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

執(zhí)行 gradle -q hello

> gradle -q hello
:hello
:hello
:projectA:hello
:projectA:hello

詳見?TaskContainer?關(guān)于更多定位 task 的選項(xiàng)

15.3. Configuring tasks 配置任務(wù)

作為一個(gè)例子,讓我們看看由 Gradle 提供的 Copy task。若要?jiǎng)?chuàng)建Copy task ,您可以在構(gòu)建腳本中聲明:

Example 15.7. Creating a copy task

build.gradle

task myCopy(type: Copy)

上面的代碼創(chuàng)建了一個(gè)什么都沒(méi)做的復(fù)制 task ??梢允褂盟?API 來(lái)配置這個(gè)任務(wù) (見Copy)。下面的示例演示了幾種不同的方式來(lái)實(shí)現(xiàn)相同的配置。

要明白,意識(shí)到這項(xiàng)任務(wù)的名稱是 “myCopy”,但它的類型是“Copy”。你可以有多個(gè)同一類型的 task ,但具有不同的名稱。你會(huì)發(fā)現(xiàn)這給你一個(gè)很大的權(quán)力來(lái)實(shí)現(xiàn)橫切關(guān)注點(diǎn)在一個(gè)特定類型的所有 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 中配置對(duì)象的方式。您必須在每一次的配置語(yǔ)句重復(fù)上下文 (myCopy)。這顯得很冗余并且很不好讀。

還有另一種配置任務(wù)的方式。它也保留了上下文,且可以說(shuō)是可讀性最強(qiáng)的。它是我們通常最喜歡的方式。

Example 15.9. Configuring a task - with closure

build.gradle

task myCopy(type: Copy)

myCopy {
   from 'resources'
   into 'target'
   include('**/*.txt', '**/*.xml', '**/*.properties')
}

這種方式適用于任何任務(wù)。該例子的第 3 行只是 tasks.getByName() 方法的簡(jiǎn)潔寫法。特別要注意的是,如果您向 getByName() 方法傳入一個(gè)閉包,這個(gè)閉包的應(yīng)用是在配置這個(gè)任務(wù)的時(shí)候,而不是任務(wù)執(zhí)行的時(shí)候。

您也可以在定義一個(gè)任務(wù)的時(shí)候使用一個(gè)配置閉包。

Example 15.10. Defining a task with closure

build.gradle

task copy(type: Copy) {
   from 'resources'
   into 'target'
   include('**/*.txt', '**/*.xml', '**/*.properties')
}

15.4. Adding dependencies to a task 給任務(wù)添加依賴

定義任務(wù)的依賴關(guān)系有幾種方法。在第 6.5 章節(jié),"任務(wù)依賴"中,已經(jīng)向你介紹了使用任務(wù)名稱來(lái)定義依賴。任務(wù)的名稱可以指向同一個(gè)項(xiàng)目中的任務(wù),或者其他項(xiàng)目中的任務(wù)。要引用另一個(gè)項(xiàng)目中的任務(wù),你需要把它所屬的項(xiàng)目的路徑作為前綴加到它的名字中。下面是一個(gè)示例,添加了從projectA:taskX 到 projectB:taskY 的依賴關(guān)系:

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'
    }
}

執(zhí)行 gradle -q taskX

> gradle -q taskX
taskY
taskX

您可以使用一個(gè) Task 對(duì)象而不是任務(wù)名稱來(lái)定義依賴,如下:

Example 15.12. Adding dependency using task object

build.gradle

task taskX << {
    println 'taskX'
}

task taskY << {
    println 'taskY'
}

taskX.dependsOn taskY

執(zhí)行 gradle -q taskX

> gradle -q taskX
taskY
taskX

對(duì)于更高級(jí)的用法,您可以使用閉包來(lái)定義 task 依賴。在計(jì)算依賴時(shí),閉包會(huì)被傳入正在計(jì)算依賴的任務(wù)。這個(gè)閉包應(yīng)該返回一個(gè) Task 對(duì)象或是Task 對(duì)象的集合,返回值會(huì)被作為這個(gè) task 的依賴項(xiàng)。下面的示例是從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'
}

執(zhí)行 gradle -q taskX

> gradle -q taskX
lib1
lib2
taskX

更多關(guān)于 task 依賴 ,見?Task?API

15.5. Ordering tasks 排序任務(wù)

任務(wù)排序還是一個(gè)孵化中的功能。請(qǐng)注意此功能在以后的 Gradle 版本中可能會(huì)改變。

在某些情況下,控制兩個(gè)任務(wù)的執(zhí)行的順序,而不引入這些任務(wù)之間的顯式依賴,是很有用的。任務(wù)排序和任務(wù)依賴之間的主要區(qū)別是,排序規(guī)則不會(huì)影響那些任務(wù)的執(zhí)行,而僅將執(zhí)行的順序。

任務(wù)排序在許多情況下可能很有用:

  • 強(qiáng)制任務(wù)順序執(zhí)行: 如,'build' 永遠(yuǎn)不會(huì)在 'clean' 前面執(zhí)行。
  • 在構(gòu)建中盡早進(jìn)行構(gòu)建驗(yàn)證:如,驗(yàn)證在開始發(fā)布的工作前有一個(gè)正確的證書。
  • 通過(guò)在長(zhǎng)久驗(yàn)證前運(yùn)行快速驗(yàn)證以得到更快的反饋:如,單元測(cè)試應(yīng)在集成測(cè)試之前運(yùn)行。
  • 一個(gè)任務(wù)聚合了某一特定類型的所有任務(wù)的結(jié)果:如,測(cè)試報(bào)告任務(wù)結(jié)合了所有執(zhí)行的測(cè)試任務(wù)的輸出。

有兩種排序規(guī)則是可用的:"必須在之后運(yùn)行"和"應(yīng)該在之后運(yùn)行"。

通過(guò)使用 “必須在之后運(yùn)行”的排序規(guī)則,您可以指定 taskB 必須總是運(yùn)行在 taskA 之后,無(wú)論 taskA 和 taskB 這兩個(gè)任務(wù)在什么時(shí)候被調(diào)度執(zhí)行。這被表示為 taskB.mustRunAfter(taskA) ?!皯?yīng)該在之后運(yùn)行”的排序規(guī)則與其類似,但沒(méi)有那么嚴(yán)格,因?yàn)樗趦煞N情況下會(huì)被忽略。首先是如果使用這一規(guī)則引入了一個(gè)排序循環(huán)。其次,當(dāng)使用并行執(zhí)行,并且一個(gè)任務(wù)的所有依賴項(xiàng)除了任務(wù)應(yīng)該在之后運(yùn)行之外所有條件已滿足,那么這個(gè)任務(wù)將會(huì)運(yùn)行,不管它的“應(yīng)該在之后運(yùn)行”的依賴項(xiàng)是否已經(jīng)運(yùn)行了。當(dāng)傾向于更快的反饋時(shí),會(huì)使用“應(yīng)該在之后運(yùn)行”的規(guī)則,因?yàn)檫@種排序很有幫助但要求不嚴(yán)格。

目前使用這些規(guī)則仍有可能出現(xiàn) taskA 執(zhí)行而 taskB 沒(méi)有執(zhí)行,或者taskB 執(zhí)行而 taskA 沒(méi)有執(zhí)行。

Example 15.14. Adding a 'must run after' task ordering

build.gradle

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

執(zhí)行 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

執(zhí)行 gradle -q taskY taskX

> gradle -q taskY taskX
taskX
taskY

在上面的例子中,它仍有可能執(zhí)行 taskY 而不會(huì)導(dǎo)致 taskX 也運(yùn)行:

Example 15.16. Task ordering does not imply task execution

執(zhí)行 gradle -q taskY

> gradle -q taskY
taskY

如果想指定兩個(gè)任務(wù)之間的“必須在之后運(yùn)行”和“應(yīng)該在之后運(yùn)行”排序,可以使用?Task.mustRunAfter()?和Task.shouldRunAfter()?方法。這些方法接受一個(gè)任務(wù)實(shí)例、 任務(wù)名稱或?Task.dependsOn()?所接受的任何其他輸入作為參數(shù)。

請(qǐng)注意"B.mustRunAfter(A)"或"B.shouldRunAfter(A)"并不意味著這些任務(wù)之間的任何執(zhí)行上的依賴關(guān)系:

它是可以獨(dú)立地執(zhí)行任務(wù) A 和 B 的。

  • 排序規(guī)則僅在這兩項(xiàng)任務(wù)計(jì)劃執(zhí)行時(shí)起作用。
  • 當(dāng)--continue參數(shù)運(yùn)行時(shí),可能會(huì)是 A 執(zhí)行失敗后 B 執(zhí)行了。

如之前所述,如果“應(yīng)該在之后運(yùn)行”的排序規(guī)則引入了排序循環(huán),那么它將會(huì)被忽略。

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

執(zhí)行 gradle -q taskX

> gradle -q taskX
taskZ
taskY
taskX

15.6. Adding a description to a task 給任務(wù)添加描述

可以給任務(wù)添加描述,這個(gè)描述將會(huì)在 task 執(zhí)行時(shí)顯示。

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. Replacing tasks 替換任務(wù)

有時(shí)您想要替換一個(gè)任務(wù)。例如,您想要把通過(guò) Java 插件添加的一個(gè)任務(wù)與不同類型的一個(gè)自定義任務(wù)進(jìn)行交換。你可以這樣實(shí)現(xiàn):

Example 15.19. Overwriting a task

build.gradle

task copy(type: Copy)

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

執(zhí)行 gradle -q copy

> gradle -q copy
I am the new one.

在這里我們用一個(gè)簡(jiǎn)單的任務(wù)替換 Copy 類型的任務(wù)。當(dāng)創(chuàng)建這個(gè)簡(jiǎn)單的任務(wù)時(shí),您必須將 overwrite 屬性設(shè)置為 true。否則 Gradle 將拋出異常,說(shuō)這種名稱的任務(wù)已經(jīng)存在。

15.8. Skipping tasks 跳過(guò)任務(wù)

Gradle 提供多種方式來(lái)跳過(guò)任務(wù)的執(zhí)行。

15.8.1. Using a predicate 使用斷言

你可以使用 onlyIf() 方法將斷言附加到一項(xiàng)任務(wù)中。如果斷言結(jié)果為 true,才會(huì)執(zhí)行任務(wù)的操作。你可以用一個(gè)閉包來(lái)實(shí)現(xiàn)斷言。閉包會(huì)作為一個(gè)參數(shù)傳給任務(wù),并且任務(wù)應(yīng)該執(zhí)行時(shí)返回 true,或任務(wù)應(yīng)該跳過(guò)時(shí)返回false。斷言只在任務(wù)要執(zhí)行前才計(jì)算。

Example 15.20. Skipping a task using a predicate

build.gradle

task hello << {
    println 'hello world'
}

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

執(zhí)行 gradle hello -PskipHello

> gradle hello -PskipHello
:hello SKIPPED

BUILD SUCCESSFUL

Total time: 1 secs

15.8.2. Using StopExecutionException

如果跳過(guò)任務(wù)的規(guī)則不能與斷言同時(shí)表達(dá),您可以使用StopExecutionException。如果一個(gè)操作(action)拋出了此異常,那么這個(gè)操作(action)接下來(lái)的行為和這個(gè)任務(wù)的其他 操作(action)都會(huì)被跳過(guò)。構(gòu)建會(huì)繼續(xù)執(zhí)行下一個(gè)任務(wù)。

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 提供的任務(wù),那么此功能將非常有用。它允許您向一個(gè)任務(wù)的內(nèi)置操作中添加執(zhí)行條件。(你可能會(huì)想,為什么既不導(dǎo)入StopExecutionException 也沒(méi)有通過(guò)其完全限定名來(lái)訪問(wèn)它。原因是,Gradle 會(huì)向您的腳本添加默認(rèn)的一些導(dǎo)入。這些導(dǎo)入是可自定義的 (見Appendix E. Existing IDE Support and how to cope without it 支持的 IDE 以及如何應(yīng)對(duì)沒(méi)有它)。)

15.8.3. Enabling and disabling tasks 啟用和禁用任務(wù)

每一項(xiàng)任務(wù)有一個(gè)默認(rèn)值為 true 的 enabled 標(biāo)記。將它設(shè)置為 false,可以不讓這個(gè)任務(wù)的任何操作執(zhí)行。

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

執(zhí)行 gradle disableMe

> gradle disableMe
:disableMe SKIPPED

BUILD SUCCESSFUL

Total time: 1 secs

15.9. Skipping tasks that are up-to-date 跳過(guò)處于最新狀態(tài)的任務(wù)

如果您使用 Gradle 自帶的任務(wù),如 Java 插件所添加的任務(wù)的話,你可能已經(jīng)注意到 Gradle 將跳過(guò)處于最新狀態(tài)的任務(wù)。這種行在您自己定義的任務(wù)上也有效,而不僅僅是內(nèi)置任務(wù)。

15.9.1. Declaring a task's inputs and outputs 聲明一個(gè)任務(wù)的輸入和輸出

讓我們來(lái)看一個(gè)例子。在這里我們的任務(wù)從一個(gè) XML 源文件生成多個(gè)輸出文件。讓我們運(yùn)行它幾次。

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"
        }
    }
}

執(zhí)行 gradle transform

> gradle transform
:transform
Transforming source file.

執(zhí)行 gradle transform

> gradle transform
:transform
Transforming source file.

請(qǐng)注意 Gradle 第二次執(zhí)行執(zhí)行這項(xiàng)任務(wù)時(shí),即使什么都未作改變,也沒(méi)有跳過(guò)該任務(wù)。我們的示例任務(wù)被用一個(gè)操作(action)閉包來(lái)定義。Gradle 不知道這個(gè)閉包做了什么,也無(wú)法自動(dòng)判斷這個(gè)任務(wù)是否為最新狀態(tài)。若要使用 Gradle 的最新狀態(tài)(up-to-date)檢查,您需要聲明這個(gè)任務(wù)的輸入和輸出。

每個(gè)任務(wù)都有一個(gè) inputs 和 outputs 的屬性,用來(lái)聲明任務(wù)的輸入和輸出。下面,我們修改了我們的示例,聲明它將 XML 源文件作為輸入,并產(chǎn)生輸出到一個(gè)目標(biāo)目錄。讓我們運(yùn)行它幾次。

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"
        }
    }
}

執(zhí)行 gradle transform

> gradle transform
:transform
Transforming source file.

執(zhí)行 gradle transform

> gradle transform
:transform UP-TO-DATE

現(xiàn)在,Gradle 知道哪些文件要檢查以確定任務(wù)是否為最新狀態(tài)。

任務(wù)的 inputs 屬性是?TaskInputs類型。任務(wù)的 outputs 屬性是?TaskOutputs?類型。

一個(gè)沒(méi)有定義輸出的任務(wù)將永遠(yuǎn)不會(huì)被當(dāng)作是最新的。對(duì)于任務(wù)的輸出并不是文件的場(chǎng)景,或者是更復(fù)雜的場(chǎng)景,TaskOutputs.upToDateWhen()?方法允許您以編程方式計(jì)算任務(wù)的輸出是否應(yīng)該被判斷為最新狀態(tài)。

一個(gè)只定義了輸出的任務(wù),如果自上一次構(gòu)建以來(lái)它的輸出沒(méi)有改變,那么它會(huì)被判定為最新狀態(tài)。

15.9.2. How does it work 它是怎么實(shí)現(xiàn)的?

在第一次執(zhí)行任務(wù)之前,Gradle 對(duì)輸入進(jìn)行一次快照。這個(gè)快照包含了輸入文件集和每個(gè)文件的內(nèi)容的哈希值。然后 Gradle 執(zhí)行該任務(wù)。如果任務(wù)成功完成,Gradle 將對(duì)輸出進(jìn)行一次快照。該快照包含輸出文件集和每個(gè)文件的內(nèi)容的哈希值。Gradle 會(huì)保存這兩個(gè)快照,直到任務(wù)的下一次執(zhí)行。

之后每一次,在執(zhí)行任務(wù)之前,Gradle 會(huì)對(duì)輸入和輸出進(jìn)行一次新的快照。如果新的快照和前一次的快照一樣,Gradle 會(huì)假定這些輸出是最新狀態(tài)的并跳過(guò)該任務(wù)。如果它們不一則, Gradle 則會(huì)執(zhí)行該任務(wù)。Gradle 會(huì)保存這兩個(gè)快照,直到任務(wù)的下一次執(zhí)行。

請(qǐng)注意,如果一個(gè)任務(wù)有一個(gè)指定的輸出目錄,在它上一次執(zhí)行之后添加到該目錄的所有文件都將被忽略,并且不會(huì)使這個(gè)任務(wù)成為過(guò)時(shí)狀態(tài)。這是不相關(guān)的任務(wù)可以在不互相干擾的情況下共用一個(gè)輸出目錄。如果你因?yàn)橐恍├碛啥幌脒@樣,請(qǐng)考慮使用?TaskOutputs.upToDateWhen

15.10. Task rules 任務(wù)規(guī)則

有時(shí)你想要有這樣一項(xiàng)任務(wù),它的行為依賴于參數(shù)數(shù)值范圍的一個(gè)大數(shù)或是無(wú)限的數(shù)字。任務(wù)規(guī)則是提供此類任務(wù)的一個(gè)很好的表達(dá)方式:

Example 15.25. Task rule

build.gradle

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

執(zhí)行 gradle -q pingServer1

> gradle -q pingServer1
Pinging: Server1

這個(gè)字符串參數(shù)被用作這條規(guī)則的描述。當(dāng)對(duì)這個(gè)例子運(yùn)行 gradle tasks 的時(shí)候,這個(gè)描述會(huì)被顯示。

規(guī)則不只是從命令行調(diào)用任務(wù)才起作用。你也可以對(duì)基于規(guī)則的任務(wù)創(chuàng)建依賴關(guān)系

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
}

執(zhí)行 gradle -q groupPing

> gradle -q groupPing
Pinging: Server1
Pinging: Server2

執(zhí)行 gradle -q tasks 你找不到 “pingServer1” 或 “pingServer2” 任務(wù),但腳本執(zhí)行邏輯是基于請(qǐng)求執(zhí)行這些任務(wù)的

15.11. Finalizer tasks 析構(gòu)器任務(wù)

析構(gòu)器任務(wù)是一個(gè) 孵化中 的功能 (請(qǐng)參閱Appendix C. The Feature Lifecycle 特性的生命周期?C.1.2 章節(jié), “Incubating”)。

當(dāng)最終的任務(wù)準(zhǔn)備運(yùn)行時(shí),析構(gòu)器任務(wù)會(huì)自動(dòng)地添加到任務(wù)圖中。

Example 15.27. Adding a task finalizer

build.gradle

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

taskX.finalizedBy taskY

執(zhí)行 gradle -q taskX

> gradle -q taskX
taskX
taskY

即使最終的任務(wù)執(zhí)行失敗,析構(gòu)器任務(wù)也會(huì)被執(zhí)行。

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

執(zhí)行 gradle -q taskX

> gradle -q taskX
taskX
taskY

另一方面,如果最終的任務(wù)什么都不做的話,比如由于失敗的任務(wù)依賴項(xiàng)或如果它被認(rèn)為是最新的狀態(tài),析構(gòu)任務(wù)不會(huì)執(zhí)行。

在不管構(gòu)建成功或是失敗,都必須清理創(chuàng)建的資源的情況下,析構(gòu)認(rèn)為是很有用的。這樣的資源的一個(gè)例子是,一個(gè) web 容器會(huì)在集成測(cè)試任務(wù)前開始,并且在之后關(guān)閉,即使有些測(cè)試失敗。

你可以使用 Task.finalizedBy()?方法指定一個(gè)析構(gòu)器任務(wù)。這個(gè)方法接受一個(gè)任務(wù)實(shí)例、 任務(wù)名稱或Task.dependsOn()所接受的任何其他輸入作為參數(shù)。

15.12. Summary 總結(jié)

如果你是從 Ant 轉(zhuǎn)過(guò)來(lái)的,像 Copy 這種增強(qiáng)的 Gradle 任務(wù),看起來(lái)就像是一個(gè) Ant target(目標(biāo))和一個(gè) Ant task (任務(wù))之間的混合物。實(shí)際上確實(shí)是這樣子。Gradle 沒(méi)有像 Ant 那樣對(duì)任務(wù)和目標(biāo)進(jìn)行分離。簡(jiǎn)單的 Gradle 任務(wù)就像 Ant 的目標(biāo),而增強(qiáng)的 Gradle 任務(wù)還包括 Ant 任務(wù)方面的內(nèi)容。Gradle 的所有任務(wù)共享一個(gè)公共 API,您可以創(chuàng)建它們之間的依賴性。這樣的一個(gè)任務(wù)可能會(huì)比一個(gè) Ant 任務(wù)更好配置。它充分利用了類型系統(tǒng),更具有表現(xiàn)力而且易于維護(hù)。

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)