projects 和 tasks是 Gradle 中最重要的兩個(gè)概念。
任何一個(gè) Gradle 構(gòu)建都是由一個(gè)或多個(gè) projects 組成。每個(gè) project 包括許多可構(gòu)建組成部分。 這完全取決于你要構(gòu)建些什么。舉個(gè)例子,每個(gè) project 或許是一個(gè) jar 包或者一個(gè) web 應(yīng)用,它也可以是一個(gè)由許多其他項(xiàng)目中產(chǎn)生的 jar 構(gòu)成的 zip 壓縮包。一個(gè) project 不必描述它只能進(jìn)行構(gòu)建操作。它也可以部署你的應(yīng)用或搭建你的環(huán)境。不要擔(dān)心它像聽上去的那樣龐大。 Gradle 的 build-by-convention 可以讓您來具體定義一個(gè) project 到底該做什么。
每個(gè) project 都由多個(gè) tasks 組成。每個(gè) task 都代表了構(gòu)建執(zhí)行過程中的一個(gè)原子性操作。如編譯,打包,生成 javadoc,發(fā)布到某個(gè)倉庫等操作。
到目前為止,可以發(fā)現(xiàn)我們可以在一個(gè) project 中定義一些簡單任務(wù),后續(xù)章節(jié)將會(huì)闡述多項(xiàng)目構(gòu)建和多項(xiàng)目多任務(wù)的內(nèi)容。
你可以通過在命令行運(yùn)行 gradle 命令來執(zhí)行構(gòu)建,gradle 命令會(huì)從當(dāng)前目錄下尋找 build.gradle 文件來執(zhí)行構(gòu)建。我們稱 build.gradle 文件為構(gòu)建腳本。嚴(yán)格來說這其實(shí)是一個(gè)構(gòu)建配置腳本,后面你會(huì)了解到這個(gè)構(gòu)建腳本定義了一個(gè) project 和一些默認(rèn)的 task。
要嘗試這一點(diǎn),請(qǐng)創(chuàng)建以下名為 build.gradle 的構(gòu)建腳本。
build.gradle
task hello {
doLast {
println 'Hello world!'
}
}
然后在該文件所在目錄執(zhí)行 ?gradle -q hello
?
?
-q
? 參數(shù)的作用是什么?該文檔的示例中很多地方在調(diào)用 gradle 命令時(shí)都加了?
-q
?參數(shù)。該參數(shù)用來控制 gradle 的日志級(jí)別,可以保證只輸出我們需要的內(nèi)容。具體可參閱本文檔第十八章 日志來了解更多參數(shù)和信息。
Output of gradle -q hello
> gradle -q hello
Hello world!
上面的腳本定義了一個(gè)叫做 hello 的 task,并且給它添加了一個(gè)動(dòng)作。當(dāng)執(zhí)行 gradle hello 的時(shí)候, Gralde 便會(huì)去調(diào)用 hello 這個(gè)任務(wù)來執(zhí)行給定操作。這些操作其實(shí)就是一個(gè)用 groovy 書寫的閉包。
如果你覺得它看上去跟 Ant 中的 targets 很像,沒錯(cuò)確實(shí)是這樣。Gradle 的 tasks 就相當(dāng)于 Ant 中的 targets。不過你會(huì)發(fā)現(xiàn)他功能更加強(qiáng)大。我們只是換了一個(gè)比 target 更形象的另外一個(gè)術(shù)語。不幸的是這恰巧與 Ant 中的術(shù)語有些沖突。ant 命令中有諸如 javac、copy、tasks。所以當(dāng)該文檔中提及 tasks 時(shí),除非特別指明 ant task。否則指的均是指 Gradle 中的 tasks。
用一種更簡潔的方式來定義上面的 hello 任務(wù)。
build.gradle
task hello {
println 'Hello world!'
}
上面的腳本又一次采用閉包的方式來定義了一個(gè)叫做 hello 的任務(wù),本文檔后續(xù)章節(jié)中我們將會(huì)更多的采用這種風(fēng)格來定義任務(wù)。
注:<<在Gradle4.x中被棄用,在Gradle 5.0中被移除,詳情見:Gradle 4.x官網(wǎng)
例:task <<{ println 'Hello world!'} 解決方法:直接去掉或使用doLast解決。
Gradle 腳本采用 Groovy 書寫,作為開胃菜,看下下面這個(gè)例子。
build.gradle
task upper << {
String someString = 'mY_nAmE'
println "Original: " + someString
println "Upper case: " + someString.toUpperCase()
}
Output of gradle -q upper
> gradle -q upper
Original: mY_nAmE
Upper case: MY_NAME
或者
build.gradle
task count << {
4.times { print "$it " }
}
Output of gradle -q count
> gradle -q count
0 1 2 3
你可以按如下方式創(chuàng)建任務(wù)間的依賴關(guān)系
build.gradle
task hello << {
println 'Hello world!'
}
task intro(dependsOn: hello) << {
println "I'm Gradle"
}
?gradle -q intro
?的輸出結(jié)果
Output of gradle -q intro
\> gradle -q intro
Hello world!
I'm Gradle
添加依賴 task 也可以不必首先聲明被依賴的 task。
build.gradle
task taskX(dependsOn: 'taskY') << {
println 'taskX'
}
task taskY << {
println 'taskY'
}
輸出 ?gradle -q taskX
?
\> gradle -q taskX
taskY
taskX
可以看到,taskX 是 在 taskY 之前定義的,這在多項(xiàng)目構(gòu)建中非常有用。
注意:當(dāng)引用的任務(wù)尚未定義的時(shí)候不可使用短標(biāo)記法來運(yùn)行任務(wù)。
借助 Groovy 的強(qiáng)大不僅可以定義簡單任務(wù)還能做更多的事。例如,可以動(dòng)態(tài)定義任務(wù)。
build.gradle
4.times { counter ->
task "task$counter" << {
println "I'm task number $counter"
}
}
gradle -q task1 的輸出結(jié)果。
\> gradle -q task1
I'm task number 1
一旦任務(wù)被創(chuàng)建后,任務(wù)之間可以通過 API 進(jìn)行相互訪問。這也是與 Ant 的不同之處。比如可以增加一些依賴。
build.gradle
4.times { counter ->
task "task$counter" << {
println "I'm task number $counter"
}
}
task0.dependsOn task2, task3
gradle -q task0的輸出結(jié)果。
Output of gradle -q task0
\> gradle -q task0
I'm task number 2
I'm task number 3
I'm task number 0
為已存在的任務(wù)增加行為。
build.gradle
task hello << {
println 'Hello Earth'
}
hello.doFirst {
println 'Hello Venus'
}
hello.doLast {
println 'Hello Mars'
}
hello << {
println 'Hello Jupiter'
}
Output of gradle -q hello
> gradle -q hello
Hello Venus
Hello Earth
Hello Mars
Hello Jupiter
doFirst 和 doLast 可以進(jìn)行多次調(diào)用。他們分別被添加在任務(wù)的開頭和結(jié)尾。當(dāng)任務(wù)開始執(zhí)行時(shí)這些動(dòng)作會(huì)按照既定順序進(jìn)行。其中 << 操作符 是 doLast 的簡寫方式。
你早就注意到了吧,沒錯(cuò),每個(gè)任務(wù)都是一個(gè)腳本的屬性,你可以訪問它:
build.gradle
task hello << {
println 'Hello world!'
}
hello.doLast {
println "Greetings from the $hello.name task."
}
gradle -q hello 的輸出結(jié)果
Output of gradle -q hello
\> gradle -q hello
Hello world!
Greetings from the hello task.
對(duì)于插件提供的內(nèi)置任務(wù)。這尤其方便(例如:complie)
你可以為一個(gè)任務(wù)添加額外的屬性。例如,新增一個(gè)叫做 myProperty 的屬性,用 ext.myProperty 的方式給他一個(gè)初始值。這樣便增加了一個(gè)自定義屬性。
build.gradle
task myTask {
ext.myProperty = "myValue"
}
task printTaskProperties << {
println myTask.myProperty
}
gradle -q printTaskProperties 的輸出結(jié)果
Output of gradle -q printTaskProperties
\> gradle -q printTaskProperties
myValue
自定義屬性不僅僅局限于任務(wù)上,還可以做更多事情。
Ant 任務(wù)是 Gradle 中的一等公民。Gradle 借助 Groovy 對(duì) Ant 任務(wù)進(jìn)行了優(yōu)秀的整合。Gradle 自帶了一個(gè) AntBuilder,在 Gradle 中調(diào)用 Ant 任務(wù)比在 build.xml 中調(diào)用更加的方便和強(qiáng)大。 通過下面的例子你可以學(xué)到如何調(diào)用一個(gè) Ant 任務(wù)以及如何與 Ant 中的屬性進(jìn)行通信。
build.gradle
task loadfile << {
def files = file('../antLoadfileResources').listFiles().sort()
files.each { File file ->
if (file.isFile()) {
ant.loadfile(srcFile: file, property: file.name)
println " *** $file.name ***"
println "${ant.properties[file.name]}"
}
}
}
gradle -q loadfile 的輸出結(jié)果
Output of gradle -q loadfile
\> gradle -q loadfile
*** agile.manifesto.txt ***
Individuals and interactions over processes and tools
Working software over comprehensive documentation
Customer collaboration over contract negotiation
Responding to change over following a plan
*** gradle.manifesto.txt ***
Make the impossible possible, make the possible easy and make the easy elegant.
(inspired by Moshe Feldenkrais)
在你腳本里還可以利用 Ant 做更多的事情。想了解更多請(qǐng)參閱在 Gradle 中調(diào)用 Ant。
Gradle 的強(qiáng)大要看你如何編寫腳本邏輯。針對(duì)上面的例子,首先要做的就是要抽取方法。
build.gradle
task checksum << {
fileList('../antLoadfileResources').each {File file ->
ant.checksum(file: file, property: "cs_$file.name")
println "$file.name Checksum: ${ant.properties["cs_$file.name"]}"
}
}
task loadfile << {
fileList('../antLoadfileResources').each {File file ->
ant.loadfile(srcFile: file, property: file.name)
println "I'm fond of $file.name"
}
}
File[] fileList(String dir) {
file(dir).listFiles({file -> file.isFile() } as FileFilter).sort()
}
gradle -q loadfile 的輸出結(jié)果
Output of gradle -q loadfile
\> gradle -q loadfile
I'm fond of agile.manifesto.txt
I'm fond of gradle.manifesto.txt
在后面的章節(jié)你會(huì)看到類似出去出來的方法可以在多項(xiàng)目構(gòu)建中的子項(xiàng)目中調(diào)用。無論構(gòu)建邏輯多復(fù)雜,Gradle 都可以提供給你一種簡便的方式來組織它們。
Gradle 允許在腳本中定義多個(gè)默認(rèn)任務(wù)。
build.gradle
defaultTasks 'clean', 'run'
task clean << {
println 'Default Cleaning!'
}
task run << {
println 'Default Running!'
}
task other << {
println "I'm not a default task!"
}
gradle -q 的輸出結(jié)果。
Output of gradle -q
\> gradle -q
Default Cleaning!
Default Running!
這與直接調(diào)用 gradle clean run 效果是一樣的。在多項(xiàng)目構(gòu)建中,每個(gè)子項(xiàng)目都可以指定單獨(dú)的默認(rèn)任務(wù)。如果子項(xiàng)目未進(jìn)行指定將會(huì)調(diào)用父項(xiàng)目指定的的默認(rèn)任務(wù)。
稍后會(huì)對(duì) Gradle 的配置階段和運(yùn)行階段進(jìn)行詳細(xì)說明 配置階段后,Gradle 會(huì)了解所有要執(zhí)行的任務(wù) Gradle 提供了一個(gè)鉤子來捕獲這些信息。一個(gè)例子就是可以檢查已經(jīng)執(zhí)行的任務(wù)中有沒有被釋放。借由此,你可以為一些變量賦予不同的值。
在下面的例子中,為 distribution 和 release 任務(wù)賦予了不同的 version 值。
build.gradle
task distribution << {
println "We build the zip with version=$version"
}
task release(dependsOn: 'distribution') << {
println 'We release now'
}
gradle.taskGraph.whenReady {taskGraph ->
if (taskGraph.hasTask(release)) {
version = '1.0'
} else {
version = '1.0-SNAPSHOT'
}
}
gradle -q distribution 的輸出結(jié)果
Output of gradle -q distribution
\> gradle -q distribution
We build the zip with version=1.0-SNAPSHOT
gradle -q release 的輸出結(jié)果
Output of gradle -q release
\> gradle -q release
We build the zip with version=1.0
We release now
whenReady 會(huì)在已發(fā)布的任務(wù)之前影響到已發(fā)布任務(wù)的執(zhí)行。即使已發(fā)布的任務(wù)不是主要任務(wù)(也就是說,即使這個(gè)任務(wù)不是通過命令行直接調(diào)用)
在本章中,我們了解了什么是 task,但這還不夠詳細(xì)。欲知更多請(qǐng)參閱章節(jié)任務(wù)詳述。
另外,可以目錄繼續(xù)學(xué)習(xí)Java 構(gòu)建入門和依賴管理基礎(chǔ)。
更多建議: