Gradle入門教程(三):Gradle構建腳本基礎

注:閱讀本章前建議先回顧第一章中的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

除了給它們添加依賴,還可以通過doFirstdoLast函數添加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

參考資料

UserGuide

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章