1. 程式人生 > 實用技巧 >【譯】關於 Gradle 你需要知道的 5 件事

【譯】關於 Gradle 你需要知道的 5 件事

Gradle 非常靈活並且強大,以致於初學者很容易對其感到恐懼。但是,理解下面 5 個核心原則,會讓 Gradle 變得更易於理解。

1. Gradle 是一個通用的構建工具

Gradle 可以用於構建(build)任何軟體,因為它對你要構建的東西或構建方式幾乎不做任何假設。不過當前它最大的限制是,只支援相容 Maven 和 lvy 的倉庫和檔案系統。

這並不意味著你需要為構建做許多工作。Gradle 可以通過外掛(plugins新增一層約定(convention)以及預構建功能(prebuild functionality)來讓常見的專案型別,例如 Java 庫,更容易被構建。你甚至能將自己的約定和構建功能封裝成外掛來發布。

2. 核心模型基於 task

task 是 Gradle 的工作單元。Gradle 的構建模型就是一個 task 的定向無環圖(Directed Acyclic Graphs, DAGs)。也就是說,構建本質上是在配置一個由 task 組成的定向無環圖。task 之間根據它們的依賴關係相連。一旦 task 圖被建立,Gradle 就能確定該以何種順序執行 task。

這張圖顯示了兩個 task 圖的例子,一個是抽象的,一個是具體的,task 之間的依賴關係用箭頭表示:

圖 1. Gradle task 圖的兩個例子

幾乎所有的構建過程都可以通過這種方式建模為一個 task 圖,這也是 Gradle 靈活的原因之一。而且這個 task 圖可以由外掛和你的構建指令碼來定義,並通過

task 依賴機制將 task 連線起來。

一個 task 包括:

  • 動作(Actions)——執行某些工作。例如複製檔案或者編譯原始碼。

  • 輸入(Inputs)——給動作使用或操作的值、檔案和目錄

  • 輸出(Outputs)——由動作修改或生成的檔案和目錄

以上內容都是可選的,使用與否取決於實際需要。一些 task,比如標準生命週期 task(standard lifecycle tasks),甚至沒有任何動作。它們只是將多個任務聚合在一起,以方便使用。

你可以選擇你需要的 task 來執行。為了節約時間,請選擇剛好能滿足需要的 task。如果想執行單元測試,就選擇執行單元測試的 task——通常是 test

。如果想打包一個應用,大多數構建都提供一個 assemble task 以供使用。

最後,Gradle 的增量構建支援強大而又可靠,所以為了保持構建的執行速度,請避免執行 clean task,除非你確實想執行清理。

3. Gradle 的多個固定構建階段

Gradle 會在三個階段(phases)評估(evaluates)並執行(execute)構建指令碼。理解這三個階段非常重要。

  1. 初始化(Initialization)

    設定構建的環境,並明確哪些專案將參與其中。

  2. 配置(Configuration)

    構造並配置構建的 task 圖。然後根據使用者想要執行的 task,確定需要執行哪些任務,以及執行的順序。

  3. 執行(Execution)

    執行配置階段結束時選擇的 task。

這些階段組成了 Gradle 的構建生命週期(Build Lifecycle)

與 Apache Maven 術語的比較

Gradle 的“構建階段”與 Maven 的“階段”不同。Maven 的“階段”將構建執行劃分成了多個部分。它們的作用類似於 Gradle 的 task 圖,儘管沒有那麼靈活。

Maven 的構建生命週期概念與 Gradle 的生命週期 task 大致相似。

設計良好的構建指令碼主要由宣告式配置組成,而非命令式邏輯。容易理解的是,這些配置在配置階段就會被評估。但許多構建也有 task 動作(例如通過 doLast {}doFirst {} 新增的),它們在執行階段被評估。理解這一點非常重要,因為配置階段評估的程式碼無法感知到執行階段發生的變化。

配置階段的另一個重要方面是,每當構建執行時,都會對其中涉及的一切進行評估。因此要避免在配置階段做複雜的工作。除此之外,構建掃描(build scan)可以幫助你識別這樣的熱點。

4. Gradle 可以使用多種方式進行擴充套件

如果你能用 Gradle 內建的構建邏輯來構建你的專案,那再好不過了。然而事實往往沒有這麼順利。大多數構建都有一些特殊的要求,這就要求你能新增自定義構建邏輯。

Gradle 提供了多種機制來進行擴充套件,比如:

  • 自定義 task 型別

    當你想讓構建做一些現有 task 不能做的工作時,你可以簡單地編寫自己的 task 型別。通常最好把自定義 task 型別的原始檔放在 buildSrc 目錄或打包的外掛中。然後你就可以像使用任何 Gradle 內建的 task 型別一樣,使用這個自定義 task 型別。

  • 自定義 task 動作

    你可以通過 Task.doFirst()Task.doLast() 方法將自定義構建邏輯附加在 task 之前或之後執行。

  • 專案和 task 的額外屬性

    你可以將自定義屬性新增到專案或 task 中,並在自定義動作或任何其他構建邏輯中使用。額外的屬性甚至能被應用到那些不是由你明確建立的 task 上,比如由 Gradle 核心外掛建立的 task。

  • 自定義約定

    約定是簡化構建的有力方法,它可以讓使用者更容易理解和使用。這可以從標準專案結構和命名約定中看出,比如 Java 構建。你可以編寫你自己的外掛來提供約定,它們只需要為構建的相關方面配置預設值。

  • 自定義模型

    Gradle 允許你在構建中引入除了 task、檔案、依賴配置之外的新概念。你可以在大多數語言外掛中看到這一點,它們將源集(source sets)的概念新增到了構建之中。對構建過程進行適當的建模可以大大提高構建的易用性和效率。

5. 用構建指令碼操作 API

Gradle 的構建指令碼看起來像可執行程式碼,實際上它的確是。這裡有一個實現細節:設計良好的構建指令碼描述了構建軟體需要哪些(what)步驟,而不是這些步驟應該如何(how)完成工作。那是自定義任務型別和外掛的工作。

有一個普遍的誤解,認為 Gradle 的強大和靈活來自於它的構建指令碼是程式碼這一事實。這個觀點完全錯誤。實際上那是底層模型和 API 提供的力量。正如我們在最佳實踐中所建議的那樣,你應該避免在你的構建指令碼中放入過多的命令式邏輯

然而,有一個領域,“將構建指令碼視為可執行程式碼”在此領域是很有用的,即:理解構建指令碼的語法如何對映到 Gradle 的 API。API 文件(由 Groovy DSL 參考Javadocs 組成)中列出了方法、屬性並題及了閉包和動作。它們在構建指令碼的上下文中有什麼含義?請閱讀 Groovy 構建指令碼入門來獲得這個問題的答案。這能幫助你有效地使用 API 文件。

由於 Gradle 執行在 JVM 上,構建指令碼也可以使用標準的 Java API。Groovy 構建指令碼可以額外使用 Groovy API,而 Kotlin 構建指令碼可以使用 Kotlin 的。


本文於2020年6月30日由筆者翻譯自 Gradle 官方文件:《What is Gradle? 》文章的 Five things you need to know about Gradle 部分。

本文使用與原文相同的知識共享署名-非商業性使用-相同方式共享 4.0 國際協議 (CC BY-NC-SA 4.0) 進行許可。