注:閱讀本章前建議先回顧第一章中的Projects和tasks概念
3.1 初識build.gradle
在第二章中分析項目結構時我們說過build.gradle
這個文件是構建腳本文件,它的本質是在其中定義了一個project
和若干tasks
當我們在命令行中輸入gradle build
(或gradlew build)命令進行項目構建時,Gradle會自動在當前目錄下去尋找build.gradle
文件,按照裏面定義的腳本進行構建。
以一個小demo爲例,我們在一個空目錄中新建一個文件build.gradle
task hello {
doLast {
println 'Hello world!'
}
}
在當前目錄下打開命令行,輸入gradle -q hello
,會輸出以下結果(注:-q參數是爲了不讓Gradle的日誌打印出來,只讓我們輸出的字符串顯示)
> gradle -q hello
Hello world!
在這個demo中,我們在build.gradle中定義了一個任務hello,並在其中添加了一個動作——打印"Hello wold!",然後當我們在命令行執行gradle -q hello
命令時就會執行這個task。
3.2 更小的單位——Action
前文說過,一個項目可以有多個Project、每個Project中又有多個Task,Task是原子性的操作。
但一個Task又由多個Action組成,多個Action組成一個Action List ,按順序執行。
3.1中的doLast
函數就是往Task的Action List的末端插入一個Action,相應的還有doFirst
函數——往Action List的前端插入一個Action。doLast、doFirst
都可以被調用多次
Action從語法的角度來說就是閉包,doLast和doFirst接收的參數也是閉包
3.3 Groovy與Kotlin
Gradle的構建腳本完全支持Groovy和Kotlin兩種語言,當用Groovy書寫構建腳本時,文件名爲build.gradle
;用kotlin書寫時,文件名爲build.gradle.kts
以下是兩個例子,分別用兩種語言實現同一個功能:
//build.gradle
task upper {
doLast {
String someString = 'mY_nAmE'
println "Original: $someString"
println "Upper case: ${someString.toUpperCase()}"
}
}
//build.gradle.kts
tasks.register("upper") {
doLast {
val someString = "mY_nAmE"
println("Original: $someString")
println("Upper case: ${someString.toUpperCase()}")
}
}
本文的示例默認用Groovy語言表述
3.4 tasks之間的依賴
有些任務之間可能會有先後關係,這時候就可以用tasks之間的依賴關係來表示:
//build.gradle
task taskX {
dependsOn 'taskY'
doLast {
println 'taskX'
}
}
task taskY {
doLast {
println 'taskY'
}
}
//build.gradle.kts
tasks.register("taskX") {
dependsOn("taskY")
doLast {
println("taskX")
}
}
tasks.register("taskY") {
doLast {
println("taskY")
}
}
我們在命令行中輸入命令gradle -q taskX
得到以下結果:
> gradle -q taskX
taskY
taskX
由結果我們可以看出,Gradle會先執行被依賴的taskY,再去接着執行taskX
3.5 動態tasks
Groovy和Kotlin所提供的語法特性可以定義多個tasks,比如下面動態創建tasks的例子:
//build.gradle
4.times { counter ->
task "task$counter" {
doLast {
println "I'm task number $counter"
}
}
}
//build.gradle.kts
repeat(4) { counter ->
tasks.register("task$counter") {
doLast {
println("I'm task number $counter")
}
}
}
運行結果:
> gradle -q task1
I'm task number 1
3.6 在聲明之外操控Task
當一個task聲明完之後,可以拿到它們的引用對它們進行操控。
//build.gradle
4.times { counter ->
task "task$counter" {
doLast {
println "I'm task number $counter"
}
}
}
task0.dependsOn task2, task3
//build.gradle.kts
repeat(4) { counter ->
tasks.register("task$counter") {
doLast {
println("I'm task number $counter")
}
}
}
tasks.named("task0") { dependsOn("task2", "task3") }
運行結果:
> gradle -q task0
I'm task number 2
I'm task number 3
I'm task number 0
除了給它們添加依賴,還可以通過doFirst
、doLast
函數添加Action。
3.7 Task的額外屬性
你可以給一個Task添加一些額外的屬性
//build.gradle.kts
task myTask {
ext.myProperty = "myValue"
}
task printTaskProperties {
doLast {
println myTask.myProperty
}
}
//build.gradle.kts
tasks.register("myTask") {
extra["myProperty"] = "myValue"
}
tasks.register("printTaskProperties") {
doLast {
println(tasks["myTask"].extra["myProperty"])
}
}
運行結果:
> gradle -q printTaskProperties
myValue
3.8 默認Task
Gradle允許添加默認Task,當執行gradle
命令而不指定task時就會執行這些默認的Tasks。
**注意:**當gradle
命令指定了task時,默認的Task除非被依賴,否則不會執行。
//build.gradle
defaultTasks 'clean', 'run'
task clean {
doLast {
println 'Default Cleaning!'
}
}
task run {
doLast {
println 'Default Running!'
}
}
task other {
doLast {
println "I'm not a default task!"
}
}
//build.gradle.kts
task("clean") {
doLast {
println("Default Cleaning!")
}
}
tasks.register("run") {
doLast {
println("Default Running!")
}
}
tasks.register("other") {
doLast {
println("I'm not a default task!")
}
}
執行結果:
> gradle -q
Default Cleaning!
Default Running!
> gradle -q other
I'm not a default task!
3.9 配置階段與執行階段
Gradle運行構建腳本時有配置(Configuration)階段和執行(Execution)階段,先配置後執行。
當配置階段執行完了之後,Gradle就知道哪些tasks將要被執行,Gradle給我們提供了一個hook的能力,在兩個階段之間執行一些操作。
下面的demo將根據release這個task是否將會被執行做出不同操作,代表我們在開發時debug和release的兩種情況。
//build.gradle
task distribution {
doLast {
println "We build the zip with version=$version"
}
}
task release {
dependsOn 'distribution'
doLast {
println 'We release now'
}
}
gradle.taskGraph.whenReady { taskGraph ->
if (taskGraph.hasTask(":release")) {
version = '1.0'
} else {
version = '1.0-SNAPSHOT'
}
}
tasks.register("distribution") {
doLast {
println("We build the zip with version=$version")
}
}
tasks.register("release") {
dependsOn("distribution")
doLast {
println("We release now")
}
}
gradle.taskGraph.whenReady {
version =
if (hasTask(":release")) "1.0"
else "1.0-SNAPSHOT"
}
執行結果:
> gradle -q distribution
We build the zip with version=1.0-SNAPSHOT
> gradle -q release
We build the zip with version=1.0
We release now
3.10 外部依賴
如果你的構建腳本需要使用一些外部的依賴,比如說需要一些開源庫來執行某些操作時,可以將它們的classpath添加至腳本中,使用buildscript
方法
//build.gradle
import org.apache.commons.codec.binary.Base64
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath group: 'commons-codec', name: 'commons-codec', version:'1.2'
}
}
task encode {
doLast {
def byte[] encodedString = new Base64().encode('hello world\n').getBytes()
println new String(encodedString)
}
}
//build.gradle.kts
import org.apache.commons.codec.binary.Base64
buildscript {
repositories {
mavenCentral()
}
dependencies {
"classpath"(group = "commons-codec", name = "commons-codec", version= "1.2")
}
}
tasks.register("encode") {
doLast {
val encodedString = Base64().encode("hello world\n".toByteArray())
println(String(encodedString))
}
}
輸出結果:
> gradle -q encode
aGVsbG8gd29ybGQK