projects 和 tasks是 Gradle 中最重要的兩個概念。
任何一個 Gradle 構(gòu)建都是由一個或多個 projects 組成。每個 project 包括許多可構(gòu)建組成部分。 這完全取決于你要構(gòu)建些什么。舉個例子,每個 project 或許是一個 jar 包或者一個 web 應(yīng)用,它也可以是一個由許多其他項目中產(chǎn)生的 jar 構(gòu)成的 zip 壓縮包。一個 project 不必描述它只能進行構(gòu)建操作。它也可以部署你的應(yīng)用或搭建你的環(huán)境。不要擔心它像聽上去的那樣龐大。 Gradle 的 build-by-convention 可以讓您來具體定義一個 project 到底該做什么。
每個 project 都由多個 tasks 組成。每個 task 都代表了構(gòu)建執(zhí)行過程中的一個原子性操作。如編譯,打包,生成 javadoc,發(fā)布到某個倉庫等操作。
到目前為止,可以發(fā)現(xiàn)我們可以在一個 project 中定義一些簡單任務(wù),后續(xù)章節(jié)將會闡述多項目構(gòu)建和多項目多任務(wù)的內(nèi)容。
你可以通過在命令行運行 gradle 命令來執(zhí)行構(gòu)建,gradle 命令會從當前目錄下尋找 build.gradle 文件來執(zhí)行構(gòu)建。我們稱 build.gradle 文件為構(gòu)建腳本。嚴格來說這其實是一個構(gòu)建配置腳本,后面你會了解到這個構(gòu)建腳本定義了一個 project 和一些默認的 task。
要嘗試這一點,請創(chuàng)建以下名為 build.gradle 的構(gòu)建腳本。
build.gradle
task hello {
doLast {
println 'Hello world!'
}
}
然后在該文件所在目錄執(zhí)行 ?gradle -q hello
?
?
-q
? 參數(shù)的作用是什么?該文檔的示例中很多地方在調(diào)用 gradle 命令時都加了?
-q
?參數(shù)。該參數(shù)用來控制 gradle 的日志級別,可以保證只輸出我們需要的內(nèi)容。具體可參閱本文檔第十八章 日志來了解更多參數(shù)和信息。
Output of gradle -q hello
> gradle -q hello
Hello world!
上面的腳本定義了一個叫做 hello 的 task,并且給它添加了一個動作。當執(zhí)行 gradle hello 的時候, Gralde 便會去調(diào)用 hello 這個任務(wù)來執(zhí)行給定操作。這些操作其實就是一個用 groovy 書寫的閉包。
如果你覺得它看上去跟 Ant 中的 targets 很像,沒錯確實是這樣。Gradle 的 tasks 就相當于 Ant 中的 targets。不過你會發(fā)現(xiàn)他功能更加強大。我們只是換了一個比 target 更形象的另外一個術(shù)語。不幸的是這恰巧與 Ant 中的術(shù)語有些沖突。ant 命令中有諸如 javac、copy、tasks。所以當該文檔中提及 tasks 時,除非特別指明 ant task。否則指的均是指 Gradle 中的 tasks。
用一種更簡潔的方式來定義上面的 hello 任務(wù)。
build.gradle
task hello {
println 'Hello world!'
}
上面的腳本又一次采用閉包的方式來定義了一個叫做 hello 的任務(wù),本文檔后續(xù)章節(jié)中我們將會更多的采用這種風格來定義任務(wù)。
注:<<在Gradle4.x中被棄用,在Gradle 5.0中被移除,詳情見:Gradle 4.x官網(wǎng)
例:task <<{ println 'Hello world!'} 解決方法:直接去掉或使用doLast解決。
Gradle 腳本采用 Groovy 書寫,作為開胃菜,看下下面這個例子。
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 之前定義的,這在多項目構(gòu)建中非常有用。
注意:當引用的任務(wù)尚未定義的時候不可使用短標記法來運行任務(wù)。
借助 Groovy 的強大不僅可以定義簡單任務(wù)還能做更多的事。例如,可以動態(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 進行相互訪問。這也是與 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 可以進行多次調(diào)用。他們分別被添加在任務(wù)的開頭和結(jié)尾。當任務(wù)開始執(zhí)行時這些動作會按照既定順序進行。其中 << 操作符 是 doLast 的簡寫方式。
你早就注意到了吧,沒錯,每個任務(wù)都是一個腳本的屬性,你可以訪問它:
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.
對于插件提供的內(nèi)置任務(wù)。這尤其方便(例如:complie)
你可以為一個任務(wù)添加額外的屬性。例如,新增一個叫做 myProperty 的屬性,用 ext.myProperty 的方式給他一個初始值。這樣便增加了一個自定義屬性。
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 對 Ant 任務(wù)進行了優(yōu)秀的整合。Gradle 自帶了一個 AntBuilder,在 Gradle 中調(diào)用 Ant 任務(wù)比在 build.xml 中調(diào)用更加的方便和強大。 通過下面的例子你可以學到如何調(diào)用一個 Ant 任務(wù)以及如何與 Ant 中的屬性進行通信。
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 做更多的事情。想了解更多請參閱在 Gradle 中調(diào)用 Ant。
Gradle 的強大要看你如何編寫腳本邏輯。針對上面的例子,首先要做的就是要抽取方法。
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é)你會看到類似出去出來的方法可以在多項目構(gòu)建中的子項目中調(diào)用。無論構(gòu)建邏輯多復雜,Gradle 都可以提供給你一種簡便的方式來組織它們。
Gradle 允許在腳本中定義多個默認任務(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 效果是一樣的。在多項目構(gòu)建中,每個子項目都可以指定單獨的默認任務(wù)。如果子項目未進行指定將會調(diào)用父項目指定的的默認任務(wù)。
稍后會對 Gradle 的配置階段和運行階段進行詳細說明 配置階段后,Gradle 會了解所有要執(zhí)行的任務(wù) Gradle 提供了一個鉤子來捕獲這些信息。一個例子就是可以檢查已經(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 會在已發(fā)布的任務(wù)之前影響到已發(fā)布任務(wù)的執(zhí)行。即使已發(fā)布的任務(wù)不是主要任務(wù)(也就是說,即使這個任務(wù)不是通過命令行直接調(diào)用)
在本章中,我們了解了什么是 task,但這還不夠詳細。欲知更多請參閱章節(jié)任務(wù)詳述。
另外,可以目錄繼續(xù)學習Java 構(gòu)建入門和依賴管理基礎(chǔ)。
更多建議: