Gradle通關(guān)系列(四)-深入Task

深入Task

task作為Gradle構(gòu)建的最小原子工作,可以通過task之間的相互依賴靈活的定義一個項目的構(gòu)建。

task包含一下屬性

  • task自身的相關(guān)屬性,包含所有的getter、setter方法
  • extentsions屬性
  • conventions屬性
  • extra屬性

Task配置

定義Task

可以為Project定義一系列的任務(wù)項,每個任務(wù)都會有自己一系列的Action,可以通過doFirst、doLast添加action

//創(chuàng)建默認(rèn)task
tasks.register("hello") {
    //配置階段的代碼
    group = "custiom"
    doLast {
        //執(zhí)行階段的代碼
        println("hello")
    }
}
//創(chuàng)建基于Task模板的task
tasks.register<Copy>("copy") {
    //配置拷貝源
    from(file("srcDir"))
    //配置拷貝目的
    into(buildDir)
}

獲取Task

可以獲取已經(jīng)定義的task,獲取task的相關(guān)配置或者對其進行再次配置

//通過名稱獲取task
println(tasks.named("hello").get().name)
//通過名稱獲取指定類型的task
println(tasks.named<Copy>("copy").get().destinationDir)
//根據(jù)類型獲取task
tasks.withType<Copy>().configureEach {
    group = "customCopy"
}

我們可以在TaskContainer源碼中查看更多關(guān)于獲取task的api

Task依賴及排序

一個task可能有依賴另外一個task,也可能需要被放在某個task之后執(zhí)行,Gradle確保在執(zhí)行任務(wù)時遵守所有的任務(wù)依賴關(guān)系和排序規(guī)則,使用dependsOn來操作task的依賴,使用mustRunAfter、shouldRunAfter來操作task的執(zhí)行順序。

通過以下對象來指定task依賴或排序

  • task字符串路徑
  • Task對象
  • TaskDenpendcy對象
  • TaskRefrence對象
  • RegularFileProperty、File、DirectoryProperty對象
  • 包含上述類型返回值的Provider對象
  • 包含上述類型的集合
  • 包含上述類型的閉包
  1. 依賴其他項目的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

  1. 依賴一個閉包
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

  1. 對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ū)別

當(dāng)一個task依賴其他task時,會優(yōu)先執(zhí)行依賴的task

task的執(zhí)行順序,并不意味著作為參照的task將被執(zhí)行,只是在需要一起執(zhí)行時,按照約定的先后順序執(zhí)行

  1. 當(dāng)task已經(jīng)有依賴流程了,會忽略排序流程
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

  1. 通過判斷條件

    val hello by tasks.registering {
        doLast {
            println("hello world")
        }
    }
    
    hello {
        onlyIf { !project.hasProperty("skipHello") }
    }
    
  2. 通過異常,拋出StopExecutionException

  3. 設(shè)置不可用

    val disableMe by tasks.registering {
        doLast {
            println("This should not be printed if the task is disabled.")
        }
    }
    
    disableMe {
        enabled = false
    }
    
    
  4. 設(shè)置task超時時間

    tasks.register("hangingTask") {
        doLast {
            Thread.sleep(100000)
        }
        timeout.set(Duration.ofMillis(500))
    }
    

給TaskContainer添加Rule

TaskContainer繼承自NamedDomainObjectCollection,它可以添加一個規(guī)則,當(dāng)給定一個未知命名的domain object時,會應(yīng)用改規(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"
}

通過構(gòu)造方法配置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)
    }
}
//直接傳遞構(gòu)造函數(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

增量構(gòu)建

為了提升Gradle的構(gòu)建效率,避免進行重復(fù)的工作,Gradle引入了增量構(gòu)建的概念。

在大多數(shù)情況下,task一般都會包含輸入和輸出,以Gradle通關(guān)系列(三)中的ZipResTask為例,資源文件就是輸入,打包的zip文件就是輸出。 如果多次執(zhí)行一個Task時的輸入和輸出是一樣的,那么我們便可以認(rèn)為這樣的Task是沒有必要重復(fù)執(zhí)行的 。 每個Task都擁有inputs和outputs屬性,他們的類型分別為TaskInputs和TaskOutputs。 在增量式構(gòu)建中,我們可以為每個Task定義輸入(inputs)和輸入(outputs),如果在執(zhí)行一個Task時,如果它的輸入和輸出與前一次執(zhí)行時沒有發(fā)生變化,那么Gradle便會認(rèn)為該Task是最新的(UP-TO-DATE),因此Gradle將不予執(zhí)行。一個Task的inputs和outputs可以是一個或多個文件,可以是文件夾,還可以是Project的某個Property,甚至可以是某個閉包所定義的條件 。

改造ZipResTask為增量構(gòu)建

//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í)行,后面標(biāo)記UP-TO-DATE

總結(jié)

task是Gradle構(gòu)建的最小原子工作,我們需要會創(chuàng)建task、并配置它、調(diào)整各種task之間的依賴來完成我們的構(gòu)建

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容