Gradle 可以做的事情實在太多了,導致初學者很容易被其強大又神祕的架構嚇到。因此筆者覺得有必要把官方文件上的這一段翻譯並整理出來,讓大家在學習 Gradle 前有正確的認知架構。
Gradle 可以幫我們建置幾乎任何軟體,因為它對我們的專案內容及要如何完成幾乎沒有任何限制。唯一的限制就是目前相依管理只支援 Maven 及 Ivy 兼容的程式碼儲存庫和檔案系統。
這樣聽起來好像我們會需要做很多前置才能建立建置工作?其實不會!
Gradle 可以透過外掛將常用的慣例作法或預建構功能載入,讓建置常見類型的專案(例如 Java/Kotlin 函式庫)變得容易。我們甚至可以建立和發佈客製化外掛來封裝特定慣例和建置功能。
Gradle 的運作核心由一連串的任務(工作單元)所組成,也就是說當要進行建置時,Gradle 會將所有要進行的任務依照它們的相依性連結在一起成為 Directed Acyclic Graphs (DAGs)。當任務圖建立後,Gradle 會確定哪些任務需要以何種順序運行,然後執行它們。
下圖顯示了兩個任務圖範例,一個是抽象的,另一個是具體的,任務之間的相依關係用箭頭表示:
幾乎任何建置都可以用這種方式繪製出任務圖,這也是 Gradle 很靈活的原因。任務圖可以由外掛和客製化的建置腳本定義,任務通過任務相依機制連接在一起。一個任務包括:
事實上,根據任務的內容,上述所有元素都是選擇性的,比方說標準的生命週期任務甚至沒有任何操作。有時將這些任務組合在一起只是為了方便。
值得一提的是,Gradle 的累加組建(Incremental Build)是堅固且可靠的,因此除非你確實想要執行清理任務,否則不需執行清理任務,如此可以確保建置的運行速度。
Gradle 有 3 個評估和執行建置腳本階段,這些階段組成了一個 Gradle 的建置生命週期:
良好設計的建置腳本是由聲明式設定而不是命令式邏輯組成。在設定階段評估該設定是可以理解的,即便如此,許多此類建置也有任務操作,例如透過 doLast {}
和 doFirst {}
在執行階段進行評估。這點很重要,因為在配定階段評估的程式碼不會看到在執行階段發生的更改。
設定階段的另一個重點是每次建置運行時都會評估其中涉及的所有內容,這就是為什麼在設定階段的最佳實踐是要避開昂貴的工作。建置掃描(Build Scan)可以幫助您識別此類 Hotspot 等。
假如可以只用 Gradle 內建的建置指令來建置專案就太好了!可惜大多數的專案都有一些特殊需求需要另外寫一些客製化的建置邏輯。Gradle 提供 5 種擴充機制,包括:
客製化任務類型:當你希望建置完成一些現有任務無法完成的工作時,你可以簡單地編寫自己的任務類型。一般來說,最好將客製化任務類型的原始碼放在 buildSrc
資料夾或封裝成外掛。然後,你可以像使用任何 Gradle 提供的任務類型一樣使用客製化任務類型。
客製化任務操作:你可以透過 Task.doFirst()
和 Task.doLast()
方法附加在任務之前或之後執行的客製化建置邏輯。
專案和任務的額外屬性:Gradle 允許你將自己的屬性增加到專案或任務中,然後你可以從自己的客製化操作或任何其他建置邏輯中使用這些屬性。額外的屬性甚至可以應用於不是由你顯式建立的任務,例如由 Gradle 的核心外掛建立的任務。
客製化慣例:慣例是一種簡化建置的有效方式,這讓使用者可以更輕鬆地理解和使用它們。這可以從使用標準專案結構和命名約定的建置中看出,例如 Java/Kotlin 建置。我們可以編寫自己的外掛來提供慣例,它們只需要為建置相關方面設定預設值。
客製化 Model:Gradle 允許我們將新概念引入建置任務、檔案和相依設定。你可以在許多語言外掛中看到這一點,它們將 Source Set 的概念加到建置中。建置過程的適當建模可以大大提高建置的易用性和效率。
把 Gradle 的建置腳本視為可執行的程式碼很容易,因為它們的確就是這樣。但這裡有一個實作細節:精心設計的建置腳本描述了建置軟體所需的步驟,而不是這些步驟應該如何完成工作。這是客製化任務類型和外掛的工作。
然而,有一個領域可以將建置腳本視為可執行的程式碼:理解建置腳本的語法如何映射到 Gradle 的 API。 API 文件是由 Groovy DSL 和 Javadocs 組成,其列出了方法和屬性,並引用了 Closure 和操作。這些在建置腳本的脈絡中代表著什麼?你可以查詢 Groovy Build Script Primer 以了解這個問題的答案,如何就能有效地使用 API 文件。