在入門教程構(gòu)建基礎(chǔ)中,你已經(jīng)學(xué)習(xí)了如何創(chuàng)建簡(jiǎn)單的任務(wù)。之后您還學(xué)習(xí)了如何將其他行為添加到這些任務(wù)中。并且你已經(jīng)學(xué)會(huì)了如何創(chuàng)建任務(wù)之間的依賴。這都是簡(jiǎn)單的任務(wù)。但 Gradle 讓任務(wù)的概念更深遠(yuǎn)。Gradle 支持增強(qiáng)的任務(wù),也就是,有自己的屬性和方法的任務(wù)。這是真正的與你所使用的 Ant 目標(biāo)(target)的不同之處。這種增強(qiáng)的任務(wù)可以由你提供,或由 Gradle 提供。
在構(gòu)建基礎(chǔ)中我們已經(jīng)看到如何通過(guò)關(guān)鍵字這種風(fēng)格來(lái)定義任務(wù)。在某些情況中,你可能需要使用這種關(guān)鍵字風(fēng)格的幾種不同的變式。例如,在表達(dá)式中不能用這種關(guān)鍵字風(fēng)格。
定義任務(wù)
build.gradle
task(hello) << {
println "hello"
}
task(copy, type: Copy) {
from(file('srcDir'))
into(buildDir)
}
您還可以使用字符串作為任務(wù)名稱:
定義任務(wù) — — 使用字符串作為任務(wù)名稱
build.gradle
task('hello') <<
{
println "hello"
}
task('copy', type: Copy) {
from(file('srcDir'))
into(buildDir)
}
對(duì)于定義任務(wù),有一種替代的語(yǔ)法你可能更愿意使用:
使用替代語(yǔ)法定義任務(wù)
build.gradle
tasks.create(name: 'hello') << {
println "hello"
}
tasks.create(name: 'copy', type: Copy) {
from(file('srcDir'))
into(buildDir)
}
在這里我們將任務(wù)添加到 tasks 集合。關(guān)于 create() 方法的更多變化可以看看 TaskContainer。
你經(jīng)常需要在構(gòu)建文件中查找你所定義的任務(wù),例如,為了去配置或是依賴它們。對(duì)這樣的情況,有很多種方法。首先,每個(gè)任務(wù)都可作為項(xiàng)目的一個(gè)屬性,并且使用任務(wù)名稱作為這個(gè)屬性名稱:
以屬性方式訪問(wèn)任務(wù)
build.gradle
task hello
println hello.name
println project.hello.name
任務(wù)也可以通過(guò) tasks 集合來(lái)訪問(wèn)。
通過(guò) tasks 集合訪問(wèn)任務(wù)
build.gradle
task hello
println tasks.hello.name
println tasks['hello'].name
您可以從任何項(xiàng)目中,使用 tasks.getByPath() 方法獲取任務(wù)路徑并且通過(guò)這個(gè)路徑來(lái)訪問(wèn)任務(wù)。你可以用任務(wù)名稱,相對(duì)路徑或者是絕對(duì)路徑作為參數(shù)調(diào)用 getByPath() 方法。
通過(guò)路徑訪問(wèn)任務(wù)
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的輸出結(jié)果
> gradle -q hello
:hello
:hello
:projectA:hello
:projectA:hello
有關(guān)查找任務(wù)的更多選項(xiàng),可以看一下 TaskContainer。
作為一個(gè)例子,讓我們看看由 Gradle 提供的 Copy 任務(wù)。若要?jiǎng)?chuàng)建 Copy 任務(wù),您可以在構(gòu)建腳本中聲明:
創(chuàng)建一個(gè)復(fù)制任務(wù)
build.gradle
task myCopy(type: Copy)
上面的代碼創(chuàng)建了一個(gè)什么都沒(méi)做的復(fù)制任務(wù)。可以使用它的 API 來(lái)配置這個(gè)任務(wù)(見(jiàn) Copy)。下面的示例演示了幾種不同的方式來(lái)實(shí)現(xiàn)相同的配置。
配置任務(wù)的幾種方式
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)的。它是我們通常最喜歡的方式。
配置任務(wù)-使用閉包
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è)配置閉包。
使用閉包定義任務(wù)
build.gradle
task copy(type: Copy) {
from 'resources'
into 'target'
include('**/*.txt', '**/*.xml', '**/*.properties')
}
定義任務(wù)的依賴關(guān)系有幾種方法。在依賴管理基礎(chǔ)中,已經(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)系:
從另一個(gè)項(xiàng)目的任務(wù)上添加依賴
build.gradle
project('projectA') {
task taskX(dependsOn: ':projectB:taskY') << {
println 'taskX'
}
}
project('projectB') {
task taskY << {
println 'taskY'
}
}
gradle -q taskX 的輸出結(jié)果
> gradle -q taskX
taskY
taskX
您可以使用一個(gè) Task 對(duì)象而不是任務(wù)名稱來(lái)定義依賴,如下:
使用 task 對(duì)象添加依賴
build.gradle
task taskX << {
println 'taskX'
}
task taskY << {
println 'taskY'
}
taskX.dependsOn taskY
gradle -q taskX的輸出結(jié)果
> gradle -q taskX
taskY
taskX
對(duì)于更高級(jí)的用法,您可以使用閉包來(lái)定義任務(wù)依賴。在計(jì)算依賴時(shí),閉包會(huì)被傳入正在計(jì)算依賴的任務(wù)。這個(gè)閉包應(yīng)該返回一個(gè) Task 對(duì)象或是 Task 對(duì)象的集合,返回值會(huì)被作為這個(gè)任務(wù)的依賴項(xiàng)。下面的示例是從 taskX 加入了項(xiàng)目中所有名稱以 lib 開頭的任務(wù)的依賴:
使用閉包添加依賴
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 的輸出結(jié)果
> gradle -q taskX
lib1
lib2
taskX
有關(guān)任務(wù)依賴的詳細(xì)信息,請(qǐng)參閱 Task 的 API。
任務(wù)排序還是一個(gè)孵化中的功能。請(qǐng)注意此功能在以后的 Gradle 版本中可能會(huì)改變。
在某些情況下,控制兩個(gè)任務(wù)的執(zhí)行的順序,而不引入這些任務(wù)之間的顯式依賴,是很有用的。任務(wù)排序和任務(wù)依賴之間的主要區(qū)別是,排序規(guī)則不會(huì)影響那些任務(wù)的執(zhí)行,而僅將執(zhí)行的順序。
任務(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í)行。
添加 '必須在之后運(yùn)行 ' 的任務(wù)排序
build.gradle
task taskX << {
println 'taskX'
}
task taskY << {
println 'taskY'
}
taskY.mustRunAfter taskX
gradle -q taskY taskX 的輸出結(jié)果
> gradle -q taskY taskX
taskX
taskY
添加 '應(yīng)該在之后運(yùn)行 ' 的任務(wù)排序
build.gradle
task taskX << {
println 'taskX'
}
task taskY << {
println 'taskY'
}
taskY.shouldRunAfter taskX
gradle -q taskY taskX 的輸出結(jié)果
> gradle -q taskY taskX
taskX
taskY
在上面的例子中,它仍有可能執(zhí)行 taskY 而不會(huì)導(dǎo)致 taskX 也運(yùn)行:
任務(wù)排序并不意味著任務(wù)執(zhí)行
gradle -q taskY 的輸出結(jié)果
> 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)系:
如之前所述,如果“應(yīng)該在之后運(yùn)行”的排序規(guī)則引入了排序循環(huán),那么它將會(huì)被忽略。
當(dāng)引入循環(huán)時(shí),“應(yīng)該在其之后運(yùn)行”的任務(wù)排序會(huì)被忽略
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 的輸出結(jié)果
> gradle -q taskX
taskZ
taskY
taskX
你可以向你的任務(wù)添加描述。例如,當(dāng)執(zhí)行 gradle tasks 時(shí)顯示這個(gè)描述。
向任務(wù)添加描述
build.gradle
task copy(type: Copy) {
description 'Copies the resource directory to the target directory.'
from 'resources'
into 'target'
include('**/*.txt', '**/*.xml', '**/*.properties')
}
有時(shí)您想要替換一個(gè)任務(wù)。例如,您想要把通過(guò) Java 插件添加的一個(gè)任務(wù)與不同類型的一個(gè)自定義任務(wù)進(jìn)行交換。你可以這樣實(shí)現(xiàn):
重寫任務(wù)
build.gradle
task copy(type: Copy)
task copy(overwrite: true) << {
println('I am the new one.')
}
gradle -q copy 的輸出結(jié)果
> 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)存在。
Gradle 提供多種方式來(lái)跳過(guò)任務(wù)的執(zhí)行。
你可以使用 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ì)算。
使用斷言跳過(guò)一個(gè)任務(wù)
build.gradle
task hello << {
println 'hello world'
}
hello.onlyIf { !project.hasProperty('skipHello') }
gradle hello -PskipHello 的輸出結(jié)果
> gradle hello -PskipHello
:hello SKIPPED
BUILD SUCCESSFUL
Total time: 1 secs
如果跳過(guò)任務(wù)的規(guī)則不能與斷言同時(shí)表達(dá),您可以使用 StopExecutionException。如果一個(gè)操作(action)拋出了此異常,那么這個(gè)操作(action)接下來(lái)的行為和這個(gè)任務(wù)的其他 操作(action)都會(huì)被跳過(guò)。構(gòu)建會(huì)繼續(xù)執(zhí)行下一個(gè)任務(wù)。
使用 StopExecutionException 跳過(guò)任務(wù)
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 的輸出結(jié)果
> gradle -q myTask
I am not affected
如果您使用由 Gradle 提供的任務(wù),那么此功能將非常有用。它允許您向一個(gè)任務(wù)的內(nèi)置操作中添加執(zhí)行條件。
每一項(xiàng)任務(wù)有一個(gè)默認(rèn)值為 true 的 enabled 標(biāo)記。將它設(shè)置為 false,可以不讓這個(gè)任務(wù)的任何操作執(zhí)行。
啟用和禁用任務(wù)
build.gradle
task disableMe << {
println 'This should not be printed if the task is disabled.'
}
disableMe.enabled = false
Gradle disableMe 的輸出結(jié)果
> gradle disableMe
:disableMe SKIPPED
BUILD SUCCESSFUL
Total time: 1 secs
如果您使用 Gradle 自帶的任務(wù),如 Java 插件所添加的任務(wù)的話,你可能已經(jīng)注意到 Gradle 將跳過(guò)處于最新?tīng)顟B(tài)的任務(wù)。這種行在您自己定義的任務(wù)上也有效,而不僅僅是內(nèi)置任務(wù)。
讓我們來(lái)看一個(gè)例子。在這里我們的任務(wù)從一個(gè) XML 源文件生成多個(gè)輸出文件。讓我們運(yùn)行它幾次。
一個(gè)生成任務(wù)
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 的輸出結(jié)果
> gradle transform
:transform
Transforming source file.
gradle transform的輸出結(jié)果
> 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īng)顟B(tài)。若要使用 Gradle 的最新?tīng)顟B(tài)(up-to-date)檢查,您需要聲明這個(gè)任務(wù)的輸入和輸出。
每個(gè)任務(wù)都有一個(gè) inputs 和 outputs 的屬性,用來(lái)聲明任務(wù)的輸入和輸出。下面,我們修改了我們的示例,聲明它將 XML 源文件作為輸入,并產(chǎn)生輸出到一個(gè)目標(biāo)目錄。讓我們運(yùn)行它幾次。
聲明一個(gè)任務(wù)的輸入和輸出
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 的輸出結(jié)果
> gradle transform
:transform
Transforming source file.
gradle transform 的輸出結(jié)果
> gradle transform
:transform UP-TO-DATE
現(xiàn)在,Gradle 知道哪些文件要檢查以確定任務(wù)是否為最新?tīng)顟B(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īng)顟B(tài)。
一個(gè)只定義了輸出的任務(wù),如果自上一次構(gòu)建以來(lái)它的輸出沒(méi)有改變,那么它會(huì)被判定為最新?tīng)顟B(tài)。
在第一次執(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īng)顟B(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()
有時(shí)你想要有這樣一項(xiàng)任務(wù),它的行為依賴于參數(shù)數(shù)值范圍的一個(gè)大數(shù)或是無(wú)限的數(shù)字。任務(wù)規(guī)則是提供此類任務(wù)的一個(gè)很好的表達(dá)方式:
任務(wù)規(guī)則
build.gradle
tasks.addRule("Pattern: ping<ID>") { String taskName ->
if (taskName.startsWith("ping")) {
task(taskName) << {
println "Pinging: " + (taskName - 'ping')
}
}
}
Gradle q pingServer1 的輸出結(jié)果
> 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)系:
基于規(guī)則的任務(wù)依賴
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 的輸出結(jié)果
> gradle -q groupPing
Pinging: Server1
Pinging: Server2
析構(gòu)器任務(wù)是一個(gè)孵化中的功能 。當(dāng)最終的任務(wù)準(zhǔn)備運(yùn)行時(shí),析構(gòu)器任務(wù)會(huì)自動(dòng)地添加到任務(wù)圖中。
添加一個(gè)析構(gòu)器任務(wù)
build.gradle
task taskX << {
println 'taskX'
}
task taskY << {
println 'taskY'
}
taskX.finalizedBy taskY
gradle -q taskX 的輸出結(jié)果
> gradle -q taskX
taskX
taskY
即使最終的任務(wù)執(zhí)行失敗,析構(gòu)器任務(wù)也會(huì)被執(zhí)行。
執(zhí)行失敗的任務(wù)的任務(wù)析構(gòu)器
build.gradle
task taskX << {
println 'taskX'
throw new RuntimeException()
}
task taskY << {
println 'taskY'
}
taskX.finalizedBy taskY
gradle -q taskX 的輸出結(jié)果
> 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ù)名稱或<a4><c5>Task.dependsOn()</c5></a4>
所接受的任何其他輸入作為參數(shù)。
如果你是從 Ant 轉(zhuǎn)過(guò)來(lái)的,像 Copy 這種增強(qiáng)的 Gradle 任務(wù),看起來(lái)就像是一個(gè) Ant 目標(biāo)(target)和一個(gè) Ant 任務(wù)(task)之間的混合物。實(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ù)。
更多建議: