深刻理解gradle中的taskjava
在以前的文章中,咱們講到了如何使用gradle建立一個簡單的task,以及task之間怎麼依賴,甚至使用了程序來建立task。在本文中,咱們會更加深刻的去了解一下gradle中的task。多線程
定義一個task能夠有不少種方式,好比下面的使用string做爲task的名字:ide
task('hello') { doLast { println "hello" } } task('copy', type: Copy) { from(file('srcDir')) into(buildDir) }
還可使用tasks容器來建立:函數
tasks.create('hello') { doLast { println "hello" } } tasks.create('copy', Copy) { from(file('srcDir')) into(buildDir) }
上面的例子中,咱們使用tasks.create方法,將新建立的task加到tasks集合中。工具
咱們還可使用groovy特有的語法來定義一個task:gradle
task(hello) { doLast { println "hello" } } task(copy, type: Copy) { from(file('srcDir')) into(buildDir) }
上面咱們在建立task的時候,使用了tasks集合類來建立task。ui
實際上,tasks集合類是一個很是有用的工具類,咱們可使用它來作不少事情。this
直接在build文件中使用tasks,其實是引用了TaskContainer的一個實例對象。咱們還可使用 Project.getTasks()
來獲取這個實例對象。線程
咱們看下TaskContainer的定義:code
public interface TaskContainer extends TaskCollection<Task>, PolymorphicDomainObjectContainer<Task>
從定義上,咱們能夠看出TaskContainer是一個task的集合和域對象的集合。
taskContainer中有四類很是重要的方法:
第一類是定位task的方法,有個分別是findByPath和getByPath。兩個方法的區別就是findByPath若是沒找到會返回null,而getByPath沒找到的話會拋出UnknownTaskException。
看下怎麼使用:
task hello println tasks.getByPath('hello').path println tasks.getByPath(':hello').path
輸出:
:hello :hello
第二類是建立task的方法create,create方法有多種實現,你能夠直接經過名字來建立一個task:
task('hello') { doLast { println "hello" } }
也能夠建立特定類型的task:
task('copy', type: Copy) { from(file('srcDir')) into(buildDir) }
還能夠建立帶參數的構造函數的task:
class CustomTask extends DefaultTask { final String message final int number @Inject CustomTask(String message, int number) { this.message = message this.number = number } }
上面咱們爲CustomTask建立了一個帶參數的構造函數,注意,這裏須要帶上@javax.inject.Inject註解,表示咱們後面能夠傳遞參數給這個構造函數。
咱們能夠這樣使用:
tasks.create('myTask', CustomTask, 'hello', 42)
也能夠這樣使用:
task myTask(type: CustomTask, constructorArgs: ['hello', 42])
第三類是register,register也是用來建立新的task的,不過register執行的是延遲建立。也就是說只有當task被須要使用的時候纔會被建立。
咱們先看一個register方法的定義:
TaskProvider<Task> register(String name, Action<? super Task> configurationAction) throws InvalidUserDataException
能夠看到register返回了一個TaskProvider,有點像java多線程中的callable,當咱們調用Provider.get()獲取task值的時候,纔會去建立這個task。
或者咱們調用TaskCollection.getByName(java.lang.String)的時候也會建立對應的task。
最後一類是replace方法:
Task replace(String name) <T extends Task> T replace(String name, Class<T> type)
replace的做用就是建立一個新的task,而且替換掉一樣名字的老的task。
task之間的依賴關係是經過task name來決定的。咱們能夠在同一個項目中作task之間的依賴:
task hello { doLast { println 'Hello www.flydean.com!' } } task intro { dependsOn hello doLast { println "I'm flydean" } }
也能夠跨項目進行task的依賴,若是是跨項目的task依賴的話,須要制定task的路徑:
project('project-a') { task taskX { dependsOn ':project-b:taskY' doLast { println 'taskX' } } } project('project-b') { task taskY { doLast { println 'taskY' } } }
或者咱們能夠在定義好task以後,再處理task之間的依賴關係:
task taskX { doLast { println 'taskX' } } task taskY { doLast { println 'taskY' } }
還能夠動態添加依賴關係:
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之間是有執行順序的,咱們稱之爲對task的排序ordering。
先看一下ordering和dependency有什麼區別。dependency表示的是一種強依賴關係,若是taskA依賴於taskB,那麼執行taskA的時候必定要先執行taskB。
而ordering則是一種並不太強列的順序關係。表示taskA須要在taskB以後執行,可是taskB不執行也能夠。
在gradle中有兩種order:分別是must run after和should run after。
taskA.mustRunAfter(taskB)表示必須遵照的順序關係,而taskA.shouldRunAfter(taskB)則不是必須的,在下面兩種狀況下能夠忽略這樣的順序關係:
第一種狀況是若是shouldRunAfter引入了order循環的時候。
第二種狀況是若是在並行執行的狀況下,task全部的依賴關係都已經知足了,那麼也會忽略這個順序。
咱們看下怎麼使用:
task taskX { doLast { println 'flydean.com' } } task taskY { doLast { println 'hello' } } taskY.mustRunAfter taskX //taskY.shouldRunAfter taskX
咱們能夠給task一些描述信息,這樣咱們在執行gradle tasks的時候,就能夠查看到:
task copy(type: Copy) { description 'Copies the resource directory to the target directory.' from 'resources' into 'target' include('**/*.txt', '**/*.xml', '**/*.properties') }
有時候咱們須要根據build文件中的某些屬性來判斷是否執行特定的task,咱們可使用onlyIf :
task hello { doLast { println 'www.flydean.com' } } hello.onlyIf { !project.hasProperty('skipHello') }
或者咱們能夠拋出StopExecutionException異常,若是遇到這個異常,那麼task後面的任務將不會被執行:
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超時,當超時的時候,執行task的線程將會被中斷,而且task將會被標記爲failed。
若是咱們想繼續執行,那麼可使用 --continue。
注意, 只有可以響應中斷的task,timeout纔有用。
task hangingTask() { doLast { Thread.sleep(100000) } timeout = Duration.ofMillis(500) }
若是咱們想要給某些task定義一些規則,那麼可使用tasks.addRule:
tasks.addRule("Pattern: ping<ID>") { String taskName -> if (taskName.startsWith("ping")) { task(taskName) { doLast { println "Pinging: " + (taskName - 'ping') } } } }
上咱們定義了一個rule,若是taskName是以ping開頭的話,那麼將會輸出對應的內容。
看下運行結果:
> gradle -q pingServer1 Pinging: Server1
我還能夠將這些rules做爲依賴項引入:
task groupPing { dependsOn pingServer1, pingServer2 }
和java中的finally同樣,task也能夠指定對應的finalize task:
task taskX { doLast { println 'taskX' } } task taskY { doLast { println 'taskY' } } taskX.finalizedBy taskY > gradle -q taskX taskX taskY
finalize task是必定會被執行的,即便上面的taskX中拋出了異常。
以上就是gradle中task的詳解,但願你們可以喜歡。
本文已收錄於 http://www.flydean.com/gradle-task-in-depth/
最通俗的解讀,最深入的乾貨,最簡潔的教程,衆多你不知道的小技巧等你來發現!
歡迎關注個人公衆號:「程序那些事」,懂技術,更懂你!