Gradle-任務

来源:https://www.cnblogs.com/skymxc/archive/2019/09/06/gradle-task.html
-Advertisement-
Play Games

任務結果標簽 當 Gradle 執行一個任務時,它會在控制台和 Tooling API 根據任務結果給任務打標簽。 這些標簽是根據任務是否有操作,是否應該執行操作,是否執行了操作以及這些操作做了哪些改變 來標記的。 下麵是 Gradle 的標簽以及對應的條件 (無標簽)或者 EXECUTED 任務執 ...


思維導圖

任務結果標簽

當 Gradle 執行一個任務時,它會在控制台和 Tooling API 根據任務結果給任務打標簽。

這些標簽是根據任務是否有操作,是否應該執行操作,是否執行了操作以及這些操作做了哪些改變 來標記的。

下麵是 Gradle 的標簽以及對應的條件

  • (無標簽)或者 EXECUTED

    任務執行了它的操作。
    • 任務有操作並且 Gradle 已經決定作為構建的一部分來執行
    • 任務沒有操作但有些依賴,並且執行了某些依賴項。參考下麵的生命周期任務。
  • UP-TO-DATE

    任務輸出沒有變化
    • 任務有輸入和輸出,但沒有改變。另行參考增量構建
    • 任務有操作,但是沒有改變它的輸出。
    • 任務沒有操作但是有些依賴,但所有的依賴都是最新的,忽略的或者來自緩存。參考下麵的生命周期任務
  • FROM-CACHE

    任務的輸出能夠在先前的任務執行中被找到
    • 任務輸出能夠從緩存中恢復。具體的可另行參考 構建緩存
  • SKIPPED

    任務沒有執行它的操作
    • 任務已經明確的在命令行中被排除。
    • 任務的 onlyIf 返回 false
  • NO-SOURCE

    任務不需要執行它的操作
    • 任務有輸出和輸入,但是沒有來源。例如輸入是空的。

在Android studio 的 build console 就可以看到這個標簽
Android studio Build console

Tooling API 是指 Gradle 提供的編程 API,通常給 IDE,CI 伺服器使用的

創建任務

最簡單方便的定義方式:定義一個 hello 任務

這是使用 DSL 的語法

task hello {
    doLast {
        println 'Hello world!'
    }
}

這裡的 task 看著像一個關鍵字,實際上是一個方法,這個方法的原型是 TaskContainer.create()

任務的創建就是使用這個方法給 Project 添加一個 Task 類型的屬性,名字就是我們的任務名字;所以才能使用任務名字引用一些API,例如為任務添加額外的屬性。

也可以使用一個字元串當做任務名稱,指定一個類型

task('hello') {
    doLast {
        println "hello"
    }
}

task('copy', type: Copy) {
    from(file('srcDir'))
    into(buildDir)
}

或者是直接使用 tasks.create 方法; tasks 是 Project的一屬性 類型是 TaskContainer

tasks.create('hello') {
    doLast {
        println "hello"
    }
}

tasks.create('copy', Copy) {
    from(file('srcDir'))
    into(buildDir)
}

任務配置

在創建任務的的時候,可以傳入參數對任務進行配置,比如 任務分組,任務描述等等

task(hello,group:'Hello',description:'這是一個 Hello。'){
    doLast{
        println "name:$name; group:$group; description:$description;"
    }
}

輸出如下

> Task :hello
name:hello; group:Hello; description:這是一個 Hello。;

BUILD SUCCESSFUL in 694ms

也可以在閉包中對任務進行配置

task taskX{
    group 'Hello'
    description '在閉包里配置'
    dependsOn hello
}

tasks.create('taskY',Copy){
    description '使用 tasks.create()'
    dependsOn hello
    group 'Hello'
}

使用 gradle help --task 任務名 查看任務詳情

任務詳情

可以配置的參數如下

配置項 描述 預設值
type 基於一個存在的 Task 來創建,和我們的類繼承差不多 DefaultTask
dependsOn 用於配置任務的依賴 []
action 添加到任務的一個 Action 或者一個閉包 null
description 任務描述 null
group 任務分組 null

任務訪問

通常是在配置任務或者是使用任務依賴的時候訪問任務。

下麵是幾種獲取任務引用的方式

使用 DSL 語法的方式

task hello
task copy(type: Copy)

// Access tasks using Groovy dynamic properties on Project

println hello.name
println project.hello.name

println copy.destinationDir
println project.copy.destinationDir

使用 tasks 集合訪問

task hello
task copy(type: Copy)

println tasks.hello.name
println tasks.named('hello').get().name

println tasks.copy.destinationDir
println tasks.named('copy').get().destinationDir

使用 tasks.getByPath() 方法

可以在構建文件的任何地方使用這個方法,它接受任務名字,任務相對路徑或者絕對路徑。

project(':projectA') {
    task hello
}

task hello

println tasks.getByPath('hello').path
println tasks.getByPath(':hello').path
println tasks.getByPath('projectA:hello').path
println tasks.getByPath(':projectA:hello').path

輸出如下

gradle -q hello

:hello
:hello
:projectA:hello
:projectA:hello

當我們拿到這個任務的引用的時候,就可以按照我們的任務邏輯去操作它,比如配置任務依賴,配置任務的一些屬性,調用方法等。

添加操作

可以使用 Task.doLast 和 Task.doFirst 為任務添加操作

其中 doFirst 是在任務之前執行,doLast 在任務之後執行


task taskZ

taskZ.configure {
    group 'Custom'
    description '添加操作的實驗'
}

taskZ.doFirst {
    println 'add doFirst. first'
}

taskZ.doLast {
    println 'add doLast. first'
}
taskZ.doLast {
    println 'add doLast. second'
}
taskZ.doFirst {
    println 'add doFirst. second'
}

執行任務 gradle taskZ

gradle taskZ

> Task :taskZ
 add doFirst. second
 add doFirst. first
 add doLast. first
 add doLast. second

 BUILD SUCCESSFUL in 608ms

從輸出可以看到是先執行 doFirst 然後是 doLast 。

執行分析

任務的執行其實就是執行它的 actions 列表。

這個列表保存在 Task 對象實例中的 actions 成員變數中,其類型是一個 List

 private List<InputChangesAwareTaskAction> actions;

現在我們把 Task 之前執行、Task 本身執行以及 Task 之後執行分別稱為 doFirst,doSelf,doLast。下麵做個演示

class CustomTask extends DefaultTask{

    @TaskAction
    def doSelf(){
        println 'Task 自己本身在執行 in doSelf.'
    }
}

task taskA(type:CustomTask)

taskA.doFirst {
    println "do First. Task 之前執行"
}

taskA.doLast {
    println "do Last. Task 之後執行"
}

例子中定義了一個 CustomTask 並聲明瞭一個 doSelf 方法,使用 @TaskAction 標註,意思是這是任務本身要執行的方法。

執行 taskA 輸出

> Task :taskA
  do First. Task 之前執行
  Task 自己本身在執行 in doSelf.
  do Last. Task 之後執行

BUILD SUCCESSFUL in 726ms

前面說過任務執行就是執行它的 actions List。那麼要達到這種 doFirst、doSelf、doLast 順序的目的,就必須把 doFirst 的 actions 放在 actions List 的前面,把 doSelf 的 actions 放在 List 的中間,把 doLast 的 actions 放在 List 最後面,這樣才能達到按約定順序執行的目的。

當我們使用 task 方法創建 taskA 這個任務的時候,Gradle 會解析其帶有 Task Action 標註的方法作為其 Task 執行的 Action,然後通過 Task 的 prependParallelSafeAction 方法把該 Action 添加到 actions List 里。

   @Override
    public void prependParallelSafeAction(final Action<? super Task> action) {
        if (action == null) {
            throw new InvalidUserDataException("Action must not be null!");
        }
        getTaskActions().add(0, wrap(action));
    }

這個時候任務剛剛被創建,不會有 doFirst 的 Action, actions List 一般是空的。

只有在創建任務時,傳入了配置參數中的 action 選項配置的時候才會有。(上面配置任務有提到)

這個時候 actions List 就有了任務本身的 Action了。

再來看 doFirst 和 doLast 兩個方法的實現代碼

    @Override
    public Task doFirst(final Action<? super Task> action) {
        return doFirst("doFirst {} action", action);
    }

    @Override
    public Task doFirst(final String actionName, final Action<? super Task> action) {
        hasCustomActions = true;
        if (action == null) {
            throw new InvalidUserDataException("Action must not be null!");
        }
        taskMutator.mutate("Task.doFirst(Action)", new Runnable() {
            @Override
            public void run() {
                getTaskActions().add(0, wrap(action, actionName));
            }
        });
        return this;
    }

    @Override
    public Task doLast(final Action<? super Task> action) {
        return doLast("doLast {} action", action);
    }

    @Override
    public Task doLast(final String actionName, final Action<? super Task> action) {
        hasCustomActions = true;
        if (action == null) {
            throw new InvalidUserDataException("Action must not be null!");
        }
        taskMutator.mutate("Task.doLast(Action)", new Runnable() {
            @Override
            public void run() {
                getTaskActions().add(wrap(action, actionName));
            }
        });
        return this;
    }

看最重要的 actions.add 這部分

doFirst 永遠都是在 actions List 第一位添加,保證其添加的 Action 在現有的 actions List 的最前面。

doLast 永遠都是在 actions List 末尾添加,保證其添加的 Action 在現有的 actions List 元素的最後面。

一個往最前面添加,一個往最後面添加,最後這個 actions List 就形成了 doFirst,doSelf,doLast三部分的 actions.

也就保證了 doFirst,doSelf,doLast三部分的 Action 順序執行的目的。

這個任務執行分析在 《Android Gradle 權威指南》 中有很詳細的解釋。

任務排序

任務依賴也能夠達到讓任務排序的目的,但是還是有些區別的。

主要區別是排序不影響任務執行,隻影響執行順序。

有兩種排序規則 “must run after” and “should run after”.

taskA.mustRunAfter(taskB) 表示 taskA 必須總是在 TaskB 之後運行。

taskA.shouldRunAfter(taskB) 表示 taskA 應該在 taskB 之後運行,並不一定必須運行,沒有那麼嚴格。

should run after 不會被執行通常是在兩個場景下

  1. 陷入順序迴圈
  2. 在執行任務依賴的時候如果滿足了這個規則,將不會再次執行了。例如 再執行 taskB 的依賴時將 TaskA 給執行了,那麼在 taskB 完成後將不會再執行 taskA。

must run after

task taskX {
    doLast {
        println 'taskX'
    }
}
task taskY {
    doLast {
        println 'taskY'
    }
}
taskY.mustRunAfter taskX
> gradle -q taskY taskX
 taskX
 taskY

should run after

task taskX {
    doLast {
        println 'taskX'
    }
}
task taskY {
    doLast {
        println 'taskY'
    }
}
taskY.shouldRunAfter taskX
 > gradle -q taskY taskX
  taskX
  taskY

如果只執行一個任務的話順序規則就沒用了

gradle -q taskY

   taskY

任務跳過

Gradle 提供了多種方式跳過任務,任務被跳過將不會執行。

使用斷言 onlyIf

這個方法接收一個閉包參數,閉包返回 false 就不會執行,返回 true 將執行任務

這個方法是在執行任務前被調用的,不是在配置階段。

task hello {
    doLast {
        println 'hello world'
    }
}

hello.onlyIf { !project.hasProperty('skipHello') }

gradle hello -PskipHello

  > Task :hello SKIPPED

  BUILD SUCCESSFUL in 0s

使用 StopExecutionException

如果斷言不能滿足你的話,就可以使用這個 StopExecutionException ,使用這個異常可以執行時根據邏輯進行判斷。

這個異常可以在一個操作中拋出,拋出後直接跳過這個任務進行下一個任務。

task compile {
    doLast {
        println 'We are doing the compile.'
    }
}

compile.doFirst {
    // Here you would put arbitrary conditions in real life.
    // But this is used in an integration test so we want defined behavior.
    if (true) { throw new StopExecutionException() }
}
task myTask {
    dependsOn('compile')
    doLast {
        println 'I am not affected'
    }
}

gradle -q myTask

  I am not affected

使用 enabled = false

每個任務都有一個 enabled 標誌,預設是 true,如果設置為 false 這個任務將會被標記為 SKIPPED,直接跳過

task disableMe {
    doLast {
        println 'This should not be printed if the task is disabled.'
    }
}
disableMe.enabled = false

執行 gradle disableMe

gradle disableMe

 Task :disableMe SKIPPED

 BUILD SUCCESSFUL in 0s

使用 超時時間

每個任務都有一個 timeout 屬性用來限制執行時間。

當任務執行超時,任務執行線程就會被終止,任務將會被標記失敗。

如果使用了 --continues 其他任務將會繼續執行。

如果任務不能響應超時,任務將不會被終止。

Gradle 所有的內置任務都會響應超時。

task hangingTask() {
    doLast {
        Thread.sleep(100000)
    }
    timeout = Duration.ofMillis(500)
}

任務規則

正常情況下在使用 gradle 執行任務時,如果任務不存在就會拋出異常。

而任務規則就是在 Gradle 找不到任務時應用的規則,例如我們可以在找不到任務時列印一句話或者執行其他操作。

任務規則的添加和創建一樣都是由 TaskContainer 完成,其實這個實在 NamedDomainObjectCollection 介面中的,不過 TaskContainer 繼承了這個介面。

tasks.addRule("這就是一個任務規則描述"){ taskName ->
    task (taskName){
         println "$taskName 不存在"
    }
}

這個 方法有多個重載,詳情可查看 API。
API 傳送門

生命周期任務

生命周期任務通常是沒有操作的,通常是表達一個概念,例如下麵幾個:

  • 一個步驟,例如 check 檢查,build 構建;
  • 一個可構建的東西,例如一個可執行文件
  • 一個組合了多個邏輯任務的空殼任務,例如 使用 compileAll 任務執行所有編譯任務

Base Plugin 中定義了標準生命周期任務:

  • check
  • assemble
  • build

幾乎所有的核心語言插件都應用了 Base Plugin, 例如 Java Plugin。

所以它的生命周期任務幾乎所有語言插件都有。

除非生命周期任務有操作,否則它的結果標記應該是由它的依賴的執行結果決定的。

如果所有的依賴都被執行了,那麼就應該標記 EXECUTED

如果所有的依賴都是最新的,跳過的或來自緩存,那麼就應該被標記為 UP-TO-DATE


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • oracle資料庫前言 oracle sqllesson1 Selecting Rowslesson2 Sorting & Limiting Selected Rowslesson3 Single Row Functionslesson4 Displaying Data from Multiple ...
  • 1.查找錯誤日誌文件路徑 mysql> show variables like "log_error"; 2.查找日誌文件路徑 mysql> show variables like "general_log_file"; 3.查找慢查詢日誌文件路徑 mysql> show variables lik... ...
  • [mysqld] log_bin = mysql-bin binlog_format = mixed expire_logs_days = 7 # 超過7天的binlog刪除 slow_query_log = 1 long_query_time = 3 # 慢查詢時間 超過3秒則為慢查詢 slow-... ...
  • 實現批處理的技術許許多多,從各種關係型資料庫的sql處理,到大數據領域的MapReduce,Hive,Spark等等。這些都是處理有限數據流的經典方式。而Flink專註的是無限流處理,那麼他是怎麼做到批處理的呢? 無限流處理:輸入數據沒有盡頭;數據處理從當前或者過去的某一個時間 點開始,持續不停地進 ...
  • 1、下載安裝包 官網下載地址:https://redisdesktop.com/pricing 官網下載需要付費使用 再此附上一個免費的破解版本,綠色安全可用 鏈接:https://pan.baidu.com/s/1K5Yd1OQ8nAofCl79Hp8r1A 提取碼:528k 下載完後後直接打開e ...
  • Ethink組件取值手冊一、取值Sql查詢配置中取值方式:所有可以對外過濾的組件都可以用id.output取值 就是取組件setOutput()里輸出的值 ,具體分為以下兩種:1)$p{OBJ_1.output}—用於sql語句中根據值的類型加引號 如:’安徽’2)$p[OBJ_1.output]—... ...
  • 修改so庫中的依賴名 在 "ArchLinuxArm" 上有一些針對 aarch64, arm, armeabi v7a 等Android常用架構的so庫可以下載,有時候可以省去很多編譯時間,且都是編譯optimize比較好的版本,而且基本都是最新版本的代碼編譯的。比如SuiteSparse就是例子 ...
  • 版權聲明:本文為xing_star原創文章,轉載請註明出處! 本文同步自http://javaexception.com/archives/207 ImageView設置rounded corner 到2019年,現在的圖片載入框架,幾乎都是Glide了。 最近的需求又碰到了要給圖片設置圓角,發現之 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...