深入理解gradle中的task
文章目錄
- 簡介
- 定義task
- tasks 集合類
- Task 之間的依賴
- 定義task之間的順序
- 給task一些描述
- task的條件執(zhí)行
- task rule
- Finalizer tasks
- 總結(jié)
簡介
在之前的文章中,我們講到了如何使用gradle創(chuàng)建一個(gè)簡單的task,以及task之間怎么依賴,甚至使用了程序來創(chuàng)建task。在本文中,我們會更加深入的去了解一下gradle中的task。
定義task
定義一個(gè)task可以有很多種方式,比如下面的使用string作為task的名字:
task('hello') {doLast {println "hello"} }task('copy', type: Copy) {from(file('srcDir'))into(buildDir) }還可以使用tasks容器來創(chuàng)建:
tasks.create('hello') {doLast {println "hello"} }tasks.create('copy', Copy) {from(file('srcDir'))into(buildDir) }上面的例子中,我們使用tasks.create方法,將新創(chuàng)建的task加到tasks集合中。
我們還可以使用groovy特有的語法來定義一個(gè)task:
task(hello) {doLast {println "hello"} }task(copy, type: Copy) {from(file('srcDir'))into(buildDir) }tasks 集合類
上面我們在創(chuàng)建task的時(shí)候,使用了tasks集合類來創(chuàng)建task。
實(shí)際上,tasks集合類是一個(gè)非常有用的工具類,我們可以使用它來做很多事情。
直接在build文件中使用tasks,實(shí)際上是引用了TaskContainer的一個(gè)實(shí)例對象。我們還可以使用 Project.getTasks() 來獲取這個(gè)實(shí)例對象。
我們看下TaskContainer的定義:
public interface TaskContainer extends TaskCollection<Task>, PolymorphicDomainObjectContainer<Task>從定義上,我們可以看出TaskContainer是一個(gè)task的集合和域?qū)ο蟮募稀?/p>
taskContainer中有四類非常重要的方法:
第一類是定位task的方法,有個(gè)分別是findByPath和getByPath。兩個(gè)方法的區(qū)別就是findByPath如果沒找到會返回null,而getByPath沒找到的話會拋出UnknownTaskException。
看下怎么使用:
task helloprintln tasks.getByPath('hello').path println tasks.getByPath(':hello').path輸出:
:hello :hello第二類是創(chuàng)建task的方法create,create方法有多種實(shí)現(xiàn),你可以直接通過名字來創(chuàng)建一個(gè)task:
task('hello') {doLast {println "hello"} }也可以創(chuàng)建特定類型的task:
task('copy', type: Copy) {from(file('srcDir'))into(buildDir) }還可以創(chuàng)建帶參數(shù)的構(gòu)造函數(shù)的task:
class CustomTask extends DefaultTask {final String messagefinal int number@InjectCustomTask(String message, int number) {this.message = messagethis.number = number} }上面我們?yōu)镃ustomTask創(chuàng)建了一個(gè)帶參數(shù)的構(gòu)造函數(shù),注意,這里需要帶上@javax.inject.Inject注解,表示我們后面可以傳遞參數(shù)給這個(gè)構(gòu)造函數(shù)。
我們可以這樣使用:
tasks.create('myTask', CustomTask, 'hello', 42)也可以這樣使用:
task myTask(type: CustomTask, constructorArgs: ['hello', 42])第三類是register,register也是用來創(chuàng)建新的task的,不過register執(zhí)行的是延遲創(chuàng)建。也就是說只有當(dāng)task被需要使用的時(shí)候才會被創(chuàng)建。
我們先看一個(gè)register方法的定義:
TaskProvider<Task> register?(String name,Action<? super Task> configurationAction)throws InvalidUserDataException可以看到register返回了一個(gè)TaskProvider,有點(diǎn)像java多線程中的callable,當(dāng)我們調(diào)用Provider.get()獲取task值的時(shí)候,才會去創(chuàng)建這個(gè)task。
或者我們調(diào)用TaskCollection.getByName(java.lang.String)的時(shí)候也會創(chuàng)建對應(yīng)的task。
最后一類是replace方法:
Task replace?(String name) <T extends Task> T replace?(String name,Class<T> type)replace的作用就是創(chuàng)建一個(gè)新的task,并且替換掉同樣名字的老的task。
Task 之間的依賴
task之間的依賴關(guān)系是通過task name來決定的。我們可以在同一個(gè)項(xiàng)目中做task之間的依賴:
task hello {doLast {println 'Hello www.flydean.com!'} } task intro {dependsOn hellodoLast {println "I'm flydean"} }也可以跨項(xiàng)目進(jìn)行task的依賴,如果是跨項(xiàng)目的task依賴的話,需要制定task的路徑:
project('project-a') {task taskX {dependsOn ':project-b:taskY'doLast {println 'taskX'}} }project('project-b') {task taskY {doLast {println 'taskY'}} }或者我們可以在定義好task之后,再處理task之間的依賴關(guān)系:
task taskX {doLast {println 'taskX'} }task taskY {doLast {println 'taskY'} }還可以動態(tài)添加依賴關(guān)系:
task taskX {doLast {println 'taskX'} }// Using a Groovy Closure taskX.dependsOn {tasks.findAll { task -> task.name.startsWith('lib') } }task lib1 {doLast {println 'lib1'} }task lib2 {doLast {println 'lib2'} }task notALib {doLast {println 'notALib'} }定義task之間的順序
有時(shí)候我們的task之間是有執(zhí)行順序的,我們稱之為對task的排序ordering。
先看一下ordering和dependency有什么區(qū)別。dependency表示的是一種強(qiáng)依賴關(guān)系,如果taskA依賴于taskB,那么執(zhí)行taskA的時(shí)候一定要先執(zhí)行taskB。
而ordering則是一種并不太強(qiáng)列的順序關(guān)系。表示taskA需要在taskB之后執(zhí)行,但是taskB不執(zhí)行也可以。
在gradle中有兩種order:分別是must run after和should run after。
taskA.mustRunAfter(taskB)表示必須遵守的順序關(guān)系,而taskA.shouldRunAfter(taskB)則不是必須的,在下面兩種情況下可以忽略這樣的順序關(guān)系:
第一種情況是如果shouldRunAfter引入了order循環(huán)的時(shí)候。
第二種情況是如果在并行執(zhí)行的情況下,task所有的依賴關(guān)系都已經(jīng)滿足了,那么也會忽略這個(gè)順序。
我們看下怎么使用:
task taskX {doLast {println 'flydean.com'} } task taskY {doLast {println 'hello'} } taskY.mustRunAfter taskX //taskY.shouldRunAfter taskX給task一些描述
我們可以給task一些描述信息,這樣我們在執(zhí)行g(shù)radle tasks的時(shí)候,就可以查看到:
task copy(type: Copy) {description 'Copies the resource directory to the target directory.'from 'resources'into 'target'include('**/*.txt', '**/*.xml', '**/*.properties') }task的條件執(zhí)行
有時(shí)候我們需要根據(jù)build文件中的某些屬性來判斷是否執(zhí)行特定的task,我們可以使用onlyIf :
task hello {doLast {println 'www.flydean.com'} }hello.onlyIf { !project.hasProperty('skipHello') }或者我們可以拋出StopExecutionException異常,如果遇到這個(gè)異常,那么task后面的任務(wù)將不會被執(zhí)行:
task compile {doLast {println 'We are doing the compile.'} }compile.doFirst {if (true) { throw new StopExecutionException() } } task myTask {dependsOn('compile')doLast {println 'I am not affected'} }我們還可以啟動和禁用task:
myTask.enabled = false最后我們還可以讓task超時(shí),當(dāng)超時(shí)的時(shí)候,執(zhí)行task的線程將會被中斷,并且task將會被標(biāo)記為failed。
如果我們想繼續(xù)執(zhí)行,那么可以使用 --continue。
注意, 只有能夠響應(yīng)中斷的task,timeout才有用。
task hangingTask() {doLast {Thread.sleep(100000)}timeout = Duration.ofMillis(500) }task rule
如果我們想要給某些task定義一些規(guī)則,那么可以使用tasks.addRule:
tasks.addRule("Pattern: ping<ID>") { String taskName ->if (taskName.startsWith("ping")) {task(taskName) {doLast {println "Pinging: " + (taskName - 'ping')}}} }上我們定義了一個(gè)rule,如果taskName是以ping開頭的話,那么將會輸出對應(yīng)的內(nèi)容。
看下運(yùn)行結(jié)果:
> gradle -q pingServer1 Pinging: Server1我還可以將這些rules作為依賴項(xiàng)引入:
task groupPing {dependsOn pingServer1, pingServer2 }Finalizer tasks
和java中的finally一樣,task也可以指定對應(yīng)的finalize task:
task taskX {doLast {println 'taskX'} } task taskY {doLast {println 'taskY'} }taskX.finalizedBy taskY> gradle -q taskX taskX taskYfinalize task是一定會被執(zhí)行的,即使上面的taskX中拋出了異常。
總結(jié)
以上就是gradle中task的詳解,希望大家能夠喜歡。
本文已收錄于 http://www.flydean.com/gradle-task-in-depth/
最通俗的解讀,最深刻的干貨,最簡潔的教程,眾多你不知道的小技巧等你來發(fā)現(xiàn)!
歡迎關(guān)注我的公眾號:「程序那些事」,懂技術(shù),更懂你!
總結(jié)
以上是生活随笔為你收集整理的深入理解gradle中的task的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: gradle中的build script
- 下一篇: gradle中的增量构建