如何優雅地使用Gradle

Compiling

Gradle是一個基於Apache Ant和Apache Maven概念的JVM項目自動化建構工具。 有別於傳統的不忍卒讀的XML項目設置語言,它使用基於Groovy或者Kotlin的DSL(領域專用語言)來配置項目構建流程,大大提升了可讀性和易用性。Spring Framework、Hibernate等著名的開源框架都在使用Gradle,固然還包括Gradle自己。java

固然Gradle自己也是有學習曲線的,不少開發者對其的理解和使用可能只停留在gradle testgradle build上。隨着項目代碼的增長,項目依賴也愈來愈多,自定義的構建步驟也開始出現,整個項目的構建速度漸漸變慢,進而使得持續集成的速度也相繼變慢。編程

本文將基於Gradle 5.5介紹幾個很是實用但不爲人知的Gradle使用技巧,幫助讀者優雅地使用Gradle。api

使用Gradle Wrapper

Gradle Wrapper是官方推薦的使用Gradle的方式,由於它能夠簡單地聲明要使用的Gradle的版本,而後在項目構建中使用那個指定的版原本跑各個構建任務。這樣,無論你用的是命令行仍是IDE,無論你用的是Windows仍是Linux, 你用的都是同一個命令和版本,好比./gradlew build,此處./gradlew 便是所說的Gradle Wrapper。緩存

生成Gradle Wrapper,你只須要在項目根目錄下運行gradle wrapper --gradle-version 5.4.1, 你能夠根據須要選擇適合你的gradle-version。網絡

--parallel

隨着項目的發展,產品代碼和測試代碼與日俱增,然而項目的總體構建速度一般都會不斷變慢。變慢的緣由有不少種,好比有不可避免的編譯時間增加,也有能夠改善的測試運行方式。Gradle 5.x 版本中已經作了不少優化,好比默認的incremental build(增量編譯)和build cache(構建緩存)。此外,如今大部分電腦都配置了多核CPU,Gradle提供了--parallel 功能來幫助你基於多核CPU並行運行Gradle的任務。好比你的項目裏有三個無交叉依賴Gradle子模塊,當你在一臺六核的機器上跑時,三個子模塊能夠並行編譯和運行測試,理論上你最多能夠節省2/3的構建時間。架構

若是你想把這個選項做爲人和人跑任何Gradle命令時的默認選項,你能夠在你項目根目錄的gradle.properties 文件裏添加下面一行app

org.gradle.parallel=true
複製代碼

--fail-fast

在CI(持續集成)構建項目時,必不可少的一個環節是跑自動化測試,好比單元測試和集成測試。大型的項目每每有成百上千個自動化測試,若是CI系統沒有很好的並行化機制的話, 所有跑完至少要個五到十分鐘,甚至更久。萬一你的分支裏有測試掛了,默認狀況下你必需要等到全部測試跑完才能獲得反饋(好比收到提醒)。假設那個測試是在第二十秒時就掛了,意味着咱們浪費了以後五到十分鐘的機器資源。框架

--fail-fast 正是爲了解決這個問題而設計的,它的做用就是一旦有測試掛掉就直接終止,還沒跑的測試就被跳過了。這樣咱們的CI環境資源利用率就能有所提升,對於那種有數十個甚至上百個工程師在同時工做的代碼庫,--fail-fast的優化效果就很明顯了。ide

--fail-fast 只適用於Gradle Test類別的構建任務,好比 ./gradlew test --fail-fast。用於其餘任務則會報錯,好比./gradlew build --fail-fast,由於build不是Test類別的任務。除了用於命令行,你還能夠在Gradle腳本里設置:函數

test {
    failFast = true
}
複製代碼

-t, --continuous

--continuous 幫助開發者在本地開發時更高效地驗證他們的changes。好比你在寫一個類的單元測試,你已經有如下的兩個文件

# Foo.java
public class Foo {
    public int sum(int a, int b) {
        return a + b;
    }
}

# FooTest.java
public class FooTest {
    @Test
    public void testSum() {
        // TODO: implement me
    }
}
複製代碼

你能夠在命令行運行./gradlew test --tests FooTest --continuous 或者縮減版./gradlew test --tests FooTest -t,當你改變Foo.java的實現或者FooTest.java的測試代碼時,Gradle會檢測到文件內容改變,從而從新編譯運行你的測試,無需你人工執行一樣的命令。

這個功能很是適合那些須要快速反饋、迭代、驗證修復的任務,好比編譯、測試、重構、代碼風格錯誤修復等。

-x, --exclude-task

當你想執行一系列Gradle構建任務但又想跳過某些很慢的或者是不相關的任務時,-x就派上用場了。好比Gradle有個任務叫check, 它每每是開發者push本地commits前跑的一個任務,能夠當作一個快速代碼正確性校驗的綜合任務,包含編譯、代碼靜態分析、自動化測試等。若是此時你不想跑集成測試,由於集成測試很慢,你就能夠在命令行跑./gradlew check -x integrationTest, 這樣它就會跑除了集成測試以外的相關校驗任務。

-m, --dry-run

當你修改了一些Gradle腳本,或者想快速驗證你的Gradle配置是正確的,合理地應用--dry-run能幫你節省很多時間。好比你想看看check任務到底會跑哪些相關任務時,你能夠用./gradlew check -m, 不出幾秒Gradle就會在命令行打印出須要執行的任務名(但不執行)。

--offline

設想你在火車或者飛機上很無聊,忽然靈光乍現想到一個優化你如今系統的方法,你火燒眉毛地打開筆記本啪啪啪啪敲完了代碼,結果在命令行一跑Gradle報錯說沒有網絡鏈接,沒法下載一些項目依賴。再有沒有比這個更讓一個工程師痛苦的事了吧。其實Gradle大部分時間都在本地緩存了全部的項目依賴,只是它習慣性地會去網上從新更新校對下依賴版本等信息。此時--offline就能幫助你強制Gradle開啓離線工做模式!

api / implementation / compile

不少構建系統都會碰到一個棘手的問題:如何合理解決Transitive Dependency(依賴傳遞)? 比方說如今有以下依賴關係App -> Lib A -> Lib B, 若是_Lib B_ 只在_Lib A_的函數內部使用到,理想的狀況下_App_的代碼是不能直接調用_Lib B_的,由於_Lib B_只是_Lib A_的內部實現方式(internal implementation)。 然而大部分狀況下你會發現_App_的代碼居然能調用_Lib B_ 😱🤔

以上就是在Gradle腳本里使用compile關鍵詞的反作用。不要覺得這僅僅是一個內部實現暴露的問題,它還會形成依賴版本解決出錯致使應用層出錯,還會減慢項目代碼的總體編譯速度。

Gradle從3.4版本起其實就提供了一個解決方案。它把compile語義拆分紅兩類,apiimplementationapi等價於被_deprecated_的compile。至於implementation, 以上面的例子爲基礎

# build.gradle of App
dependencies {
   api project('LibA')
}

# build.gradle of Lib A
dependencies {
   api project('models')
   implementation 'com.google.guava:guava:18.0'
}
複製代碼

這樣_Guava:18_這個內部依賴就不能滲透到_App_那裏。若是_App_恰好也須要使用_Guava_, 它就須要顯示定義_Guava_爲它的依賴,並且它能夠自由選擇_Guava_的版本而不用擔憂版本誤用或者衝突。

參考文章

本文原載於: 【技術博客】如何優雅地使用Gradle


藝與術公衆號(various__artists): 分享關於編程、軟件工程、系統架構、前沿技術的藝與術的思考。

wechat account
相關文章
相關標籤/搜索