深入Task
task作為Gradle構建的最小原子工作烙常,可以通過task之間的相互依賴靈活的定義一個項目的構建生闲。
task包含一下屬性
- task自身的相關屬性成艘,包含所有的getter及舍、setter方法
- extentsions屬性
- conventions屬性
- extra屬性
Task配置
定義Task
可以為Project定義一系列的任務項醉蚁,每個任務都會有自己一系列的Action,可以通過doFirst盒蟆、doLast添加action
//創(chuàng)建默認task
tasks.register("hello") {
//配置階段的代碼
group = "custiom"
doLast {
//執(zhí)行階段的代碼
println("hello")
}
}
//創(chuàng)建基于Task模板的task
tasks.register<Copy>("copy") {
//配置拷貝源
from(file("srcDir"))
//配置拷貝目的
into(buildDir)
}
獲取Task
可以獲取已經定義的task踏烙,獲取task的相關配置或者對其進行再次配置
//通過名稱獲取task
println(tasks.named("hello").get().name)
//通過名稱獲取指定類型的task
println(tasks.named<Copy>("copy").get().destinationDir)
//根據(jù)類型獲取task
tasks.withType<Copy>().configureEach {
group = "customCopy"
}
我們可以在TaskContainer源碼中查看更多關于獲取task的api
Task依賴及排序
一個task可能有依賴另外一個task,也可能需要被放在某個task之后執(zhí)行历等,Gradle確保在執(zhí)行任務時遵守所有的任務依賴關系和排序規(guī)則讨惩,使用dependsOn來操作task的依賴,使用mustRunAfter寒屯、shouldRunAfter來操作task的執(zhí)行順序荐捻。
通過以下對象來指定task依賴或排序
- task字符串路徑
- Task對象
- TaskDenpendcy對象
- TaskRefrence對象
- RegularFileProperty黍少、File、DirectoryProperty對象
- 包含上述類型返回值的Provider對象
- 包含上述類型的集合
- 包含上述類型的閉包
- 依賴其他項目的task
project("project-a") {
//依賴項目b的taskY
tasks.register("taskX") {
//task的路徑通過:分割
dependsOn(":project-b:taskY")
doLast {
println("taskX")
}
}
}
project("project-b") {
tasks.register("taskY") {
doLast {
println("taskY")
}
}
}
執(zhí)行taskX之前會執(zhí)行taskY
- 依賴一個閉包
val taskX by tasks.registering {
doLast {
println("taskX")
}
}
//依賴一個閉包
taskX {
dependsOn(provider {
tasks.filter { task -> task.name.startsWith("lib") }
})
}
tasks.register("lib1") {
doLast {
println("lib1")
}
}
tasks.register("lib2") {
doLast {
println("lib2")
}
}
tasks.register("notALib") {
doLast {
println("notALib")
}
}
執(zhí)行taskX之前會執(zhí)行l(wèi)ib1处面、lib2
- 對task的執(zhí)行流程進行排序
val taskX by tasks.registering {
doLast {
println("taskX")
}
}
val taskY by tasks.registering {
doLast {
println("taskY")
}
}
taskY {
mustRunAfter(taskX)
}
執(zhí)行以下命令的輸出情況
> gradle -q taskY taskX
taskX
taskY
> gradle -q taskY
taskY
可以看出來依賴與順序的區(qū)別
當一個task依賴其他task時厂置,會優(yōu)先執(zhí)行依賴的task
task的執(zhí)行順序,并不意味著作為參照的task將被執(zhí)行魂角,只是在需要一起執(zhí)行時昵济,按照約定的先后順序執(zhí)行
- 當task已經有依賴流程了,會忽略排序流程
val taskX by tasks.registering {
doLast {
println("taskX")
}
}
val taskY by tasks.registering {
doLast {
println("taskY")
}
}
val taskZ by tasks.registering {
doLast {
println("taskZ")
}
}
taskX { dependsOn(taskY) }
taskY { dependsOn(taskZ) }
taskZ { shouldRunAfter(taskX) }
> gradle -q taskX
taskZ
taskY
taskX
跳過task
-
通過判斷條件
val hello by tasks.registering { doLast { println("hello world") } } hello { onlyIf { !project.hasProperty("skipHello") } }
通過異常野揪,拋出StopExecutionException
-
設置不可用
val disableMe by tasks.registering { doLast { println("This should not be printed if the task is disabled.") } } disableMe { enabled = false }
-
設置task超時時間
tasks.register("hangingTask") { doLast { Thread.sleep(100000) } timeout.set(Duration.ofMillis(500)) }
給TaskContainer添加Rule
TaskContainer繼承自NamedDomainObjectCollection访忿,它可以添加一個規(guī)則,當給定一個未知命名的domain object時囱挑,會應用改規(guī)則醉顽,你可以進行忽略,或者自行創(chuàng)建該命名的domain object
tasks.addRule("Pattern: ping<ID>") {
val taskName = this
if (startsWith("ping")) {
task(taskName) {
doLast {
println("Pinging: " + (taskName.replace("ping", "")))
}
}
}
}
自定義Task模版
定義簡單的task Class
open class GreetingTask : DefaultTask() {
var greeting = "hello from GreetingTask"
//TaskAction中寫task的具體執(zhí)行邏輯平挑,此方法是在執(zhí)行階段執(zhí)行
@TaskAction
fun greet() {
println(greeting)
}
}
tasks.register<GreetingTask>("hello")
通過setter方法配置task
tasks.register<GreetingTask>("greeting") {
//配置greeting參數(shù)
greeting = "greetings from GreetingTask"
}
通過構造方法配置task
open class GreetingTask() : DefaultTask() {
var greeting = "hello from GreetingTask"
@javax.inject.Inject
constructor(greeting: String) : this() {
this.greeting = greeting
}
@TaskAction
fun greet() {
println(greeting)
}
}
//直接傳遞構造函數(shù)的參數(shù)
tasks.register<GreetingTask>("greeting", "hello gradle")
通過命令行選項配置task
open class GreetingTask() : DefaultTask() {
@Option(option = "m", description = "配置greeting文本")
var greeting = "hello from GreetingTask"
@TaskAction
fun greet() {
println(greeting)
}
}
tasks.register<GreetingTask>("greeting")
//執(zhí)行命令gradlew greeting -m hellogradle
增量構建
為了提升Gradle的構建效率游添,避免進行重復的工作,Gradle引入了增量構建的概念通熄。
在大多數(shù)情況下唆涝,task一般都會包含輸入和輸出,以Gradle通關系列(三)中的ZipResTask為例唇辨,資源文件就是輸入廊酣,打包的zip文件就是輸出。 如果多次執(zhí)行一個Task時的輸入和輸出是一樣的赏枚,那么我們便可以認為這樣的Task是沒有必要重復執(zhí)行的 亡驰。 每個Task都擁有inputs和outputs屬性,他們的類型分別為TaskInputs和TaskOutputs饿幅。 在增量式構建中凡辱,我們可以為每個Task定義輸入(inputs)和輸入(outputs),如果在執(zhí)行一個Task時栗恩,如果它的輸入和輸出與前一次執(zhí)行時沒有發(fā)生變化透乾,那么Gradle便會認為該Task是最新的(UP-TO-DATE),因此Gradle將不予執(zhí)行磕秤。一個Task的inputs和outputs可以是一個或多個文件乳乌,可以是文件夾,還可以是Project的某個Property市咆,甚至可以是某個閉包所定義的條件 汉操。
改造ZipResTask為增量構建
//custom_build.gradle.kts
import org.gradle.kotlin.dsl.support.zipTo
open class ZipResExtensions {
var resPath: String = ""
var outputPath: String = ""
}
extensions.create<ZipResExtensions>("zipRes")
abstract class ZipResTask : DefaultTask() {
@get:InputDirectory
abstract val resDir: Property<File>
@get:OutputFile
abstract val outputFile: Property<File>
@TaskAction
fun zipRes() {
zipTo(outputFile.get(), resDir.get())
}
}
tasks.register("zipRes", ZipResTask::class)
afterEvaluate {
tasks.named("zipRes", ZipResTask::class) {
val zipResExtensions = project.extensions.getByName<ZipResExtensions>("zipRes")
resDir.set(file(zipResExtensions.resPath))
outputFile.set(file(zipResExtensions.outputPath))
}
}
執(zhí)行zipRes的輸出情況
第一次執(zhí)行
16:39:11: Executing task 'zipRes'...
> Task :zipRes
BUILD SUCCESSFUL in 88ms
1 actionable task: 1 executed
16:39:11: Task execution finished 'zipRes'.
第二次執(zhí)行
16:39:57: Executing task 'zipRes'...
> Task :zipRes UP-TO-DATE
BUILD SUCCESSFUL in 83ms
1 actionable task: 1 up-to-date
16:39:57: Task execution finished 'zipRes'.
沒有改動就是直接跳過該task的執(zhí)行,后面標記UP-TO-DATE
總結
task是Gradle構建的最小原子工作蒙兰,我們需要會創(chuàng)建task磷瘤、并配置它其弊、調整各種task之間的依賴來完成我們的構建