--- title: 'Gradle 基礎 - Gradle、GradleWrapper' disqus: kyleAlien --- Gradle 基礎 - Gradle、GradleWrapper === ## OverView of Content [TOC] ## Gradle 概述 **Gradle 是一款==建構系統工具==**,它的 **DSL 基於 ++Groovy 實現++**,可以控制 DSL 來達到建構系統的目的 > Gradle 建構的大部分功能都是通過插件的方式來實現,當然若不符合需求可以自定義所需的插件 ### GPL & DSL 語言 1. `GPL` 的全名是 **General-purpose language** (通用用途語言),它是對語言的描述,代表了該類型語言是跨應用領域 > 又稱為第三等級語言:C/C++、Java 2. `DSL` 的全名是 **Domain-specific language** (區域特定語言),它描述的是針對特定應用領域使用的語言 (像是在編譯程式) > 又稱為第四等級語言:Gradle、make ### 個家專案管理 - 特點 * 一般來講,我們撰寫的 Source code 需要透過不同的工具編譯、聚集、打包、測試,這些工作最初都是由人工手動執行,如果是大項目很可能會造成花費過多時間在這裡 (出錯可能性也變大) * 藉此引入自動化工具 (Gradle 就是其中一個) 有以下好處 1. 項目自動化可以盡量避免手動操作所導致的錯誤 2. 項目自動化可以有效、有順序的執行編譯、測試、打工等等工作,因為這些工作都有固定的步驟 3. 自動化工具 **必定與操作平台無關**,不依賴指定 IDE、平台 * 在 Gradle 出現之前有三個基於 Java 開發的建構工具:^1.^ `Ant`、^2.^ `Gant`、^3.^ `Maven` | 建構工具 | 格式 | 特點 | 缺點 | | -------- | -------- | -------- | - | | Ant | XML (`build.xml`) | 主要由 Project、Target、Task 組成 | 須配合 Ivy 管理、無法獲取運行時訊息、項目大腳本難理解 (自由度高) | | Gant | Groovy (`build.gant`) | 基於 Ant | | | Maven | XML (`pom.xml`) | 繼承 Ant 項目建構功能 | 具有依賴管理、項目管理,提供下載的 Repository | :::success * **Ant** vs. **Maven** ? 1. Ant 必須每個項目都重新建構 (包括沒有改動到的檔案),而 Maven 每個階段的建構都是 **使用插件實現** 2. Ant 必須配合 Iny 作依賴項目管理,而 Maven 自身就可以依賴項目管理 3. Maven 有需多默認參數 (包括建構 src 的路徑等等),而 Ant 沒有預設值,必須全部寫在 XML 中 ::: :::warning * **Maven** 缺點 ? 1. Maven 提供默認的結構、生命週期,而部分項目可能無法適配 2. Maven 的擴展較為繁瑣 > Gradle 則改善了 Maven 的缺點 ::: ### Gralde - 5 個特點 * Gradle 是眾多建構專案工具中的其中一個,我們可以先了解幾個它的特點,這可以幫助我們學習;Gradle 特點如下 1. **Gradle 是通用的建構工具** 我初次認識到 Gradle 是由於 Android 專案,但 Gradle 其實並沒有限制於建構某項專案;Gradle 幾乎可以建構所有專案 > Gradle 透過添加協議、插件來建構(也可以讓我們拓展,建構自己的插件) 2. **Gradle 核心基於任務** Gralde 將每個建構(Build)的基本單位稱為任務,**這些任務是有向任務(DAG)**;當任務運行時,Gradle 就會依照有向依賴來執行不同任務(如下圖) >  任務的組成包括如下(**以下皆是可選選項,並非固定要實現**) * `Action`:任務的確切行為,抱能包括複製、編譯、檢查... 等等 * `Input`:該任務可操作的文件、目錄... 等等 * `Output`:該任務最終的輸出,可能是生成文件、目錄... 等等 3. **Gradle 建構階段**: 這可以認為是任務的生命週期,其階段是 ^1.^ 初始化、^1.^ 配置、^1.^ 執行任務 :::success * Gradle 建構腳本是由聲明式(抽象)配置,而不是命令式(具體)邏輯 聲明式配置指的是在 Gradle 腳本中聲明你希望使用的庫、插件、依賴、任務...等等,而不是像命令式邏輯一樣寫明每個步驟的具體實現方式 > 聲明式配置的好處是可以讓你的腳本更加簡潔和易讀,並且容易進行重複使用和重構 命令式的概念就如同 Bash Shell 要寫出具體的每個命令步驟 > 項目變大時,其邏輯就會複雜,難以維護 ::: 4. **可拓展的建構** 除了 Gradle 為我們提供的基本任務之外,還可以透過自訂任務的方式拓展,其包括了: * 自定義任務類型 * 自定義任務操作 * Project & Task 的拓展屬性 * 客製化 conventions (契約、約定) > 可以簡化建構的步驟 * 客製化 module(模型) 5. **腳本操作 API** 可簡單地將 Gralde 看作為建構一個可執行的文件(將任務之間做依賴、串接,而不必關注任務的具體細節) 我們在建構的腳本會相應對照到 Gralde API :::info * 由於 Gralde 運行在 JVM 環境之下,所以可以使用大部分的 Java API > Kotlin 建構的腳本,則可以使用 Kotlin 的 API ::: ## Gradle 環境配置 以下描述的環境配置在 Windows、Ubuntu 兩個環境底下,配置好這環境才可以使用 Gradle,**以下主要描述 Ubuntu 配置的環境變量,配置 ==Java==、==Gradle== 環境** ### Gradle - Window 環境配置 * Window 只需安裝過後將安裝目錄加入到系統環境變量 > Window 環境變數,`控制台` -> `系統及安全性` -> `系統` -> `進階系統設定` -> `進階` -> `環境變數`;加入 Gradle、Java 的子目錄 bin > >  ### Gradle - Ubuntu 環境 * 由於 Groovy 也是運行在 JVM 虛擬機之上,所以必須安裝 `JDK` or `JRE` 兩個環境,在 Ubuntu 下是使用 `open jdk` 1. Ubuntu 下載 open jdk、[**gradle**](https://gradle.org/releases/) * Gradle 下載 >  解壓縮 Gradle,會得到以下這些文件 | 資料夾名稱 | 功能、介紹 | | -------- | -------- | | bin | bat 腳本 | | docs | API、DSL、指南文件等等 | | init.d | gradle 的初始化腳本目錄 | | lib | 相關的庫 | | src | source code 原文件 | >  * Open jdk 下載 ```shell= # open jdk sudo apt install -y openjdk-11-jdk ``` :::success * [**SDK Main**](https://sdkman.io/) SDK Main 是一個集成很多工具的管理器,可以透過它來下載 OpenJDK、Gradle ::: 3. 配置 bin 目錄到 [**環境變量**](https://hackmd.io/MtOap5UaR7KcJpdTisc75g#%E7%92%B0%E5%A2%83%E8%AE%8A%E9%87%8F) 中 > 以下是 Gradle 的範例 ```shell= # 編譯使用者的 .bashrc 文件 vim ~/.bashrc # 使用者的 Gradle 文件目錄 GRADLE_HOME=/home/alien/gradle # PATH 串接,上方 gradle 目錄/bin PATH=${PATH}:${GRADLE_HOME}/bin # 使其生效 Export GRADLE_HOME PATH ``` 4. 確認是否設置環境變數成功 ```shell= # check java java -version # check gradle gradle -v ``` ## Gradle wrapper 顧名思義 Wrapper 就是多了一層對 Gradle 的包裝,它的好處有 * **便於團隊開發統一 Gradle 建構的版本**,也就是說開發同個專案的人會使用相同版本的 Gradle 建構 * 為不同環境(Linux, Mac, Windows)又或是不同 IDE 的使用者提供對應版本的 Gradle,而且 **版本的修改完後,Wrapper 會自動幫你下載** > 概念圖如下 > >  ### Gradle wrapper 配置 1. 使用指令 `gradle wrapper`,**在項目的根目錄下使用** :::warning * 如果你尚未初始化 gradle 請先執行 `gradle init` 的命令,初始化過後的資料夾內容應該如下 > 這裡我建立了 Java Application 項目 > >  ::: ```groovy= // 指令 gradle wrapper ``` 2. 先介紹 Gralde init & Gradle wrapper 生成的資料 & 檔案;以下介紹幾個較重要的檔案 | 檔案 | 功能 | | -------- | -------- | | `gradlew` | Linux 可執行腳本 | | `gradlew.bat` | Window 可執行腳本 | | `gradlew-wrapper.jar` | **Gradle wrapper 發行版,實現的 jar 包** | | `gradlew-wrapper.properties` | **配置文件,用來==配置使用哪個版本的 gradle==** | >  ### Wrapper 指令設定 Gradle 版本 * 我們可以透過 CLI Gradle wrapper 指令去指定不同版本的 Gradle,指定完後它就會幫我們下載指定版本的 Gradle > 這些指令最終都會影響到 `gradle-wrapper.properties` 內容 | Options | 說明 | | -------- | -------- | | `--gradle-version` | 指定 Gradle 版本(可用指定標籤) | | `--gradle-distribution-url` | **指定下載 Gradle 發行版本的 url 地址** | | `--gradle-distribution-sha256-sum` | 下載有驗證的 gradle 版本 | | `--distribution-type` | Gradle 版本類型,可選 `bin` or `all`,預設為 `bin` | | `--network-timeout` | 指令等待時長,預設為 10000 | 1. 範例:透過 gradle wrapper `--gradle-version` 將專案中的 Gradle 設定為 2.4 版 ```shell= # 設定 2.4 版本 gradle wrapper --gradle-version 2.4 ``` >  2. 範例:透過 gradle wrapper `--gradle-version` 將專案中的 Gradle 設定為最新版(透過 `latest` 標籤) > 目前標籤有 `latest`、`release-candidate`、`nightly`、`release-nighty` ```shell= # 設定最新版本 gradle wrapper --gradle-version latest --distribution-type all ``` >  3. 範例:gradle wrapper `--gradle-distribution-url` 將專案中的 Gradle 設定為 5.6.4 版 ```shell= # 設定下載 URL,切換成 5.6.4 gradle wrapper --gradle-distribution-url https://services.gradle.org/distributions/gradle-5.6.4-all.zip ``` >  :::success * Gradle 其他版本 [**網址來源**](https://services.gradle.org/distributions/) :::warning * 若是指令等待時間過久 or 卡住,可能要檢查是否可以對此 url 進行訪問 > 有可能 URL 不合法、或者該版本根本被下架了 ::: ::: ### gradle-wrapper.properties 內容 * 看到上面使用 gradle wrapper 指令下載後,`gradle-wrapper.properties` 參數會跟著修改,接下來看看 properties 內部參數說明 >  | 參數名 | 說明功能 | | -------- | -------- | | `distributionUrl` | **指定 gradle 版本的下載地址 and 其依賴的版本** | | `distributionBase` | gradle 解壓縮後的目錄 | | `distributionPath` | gradle 解壓縮後的 Gradle 壓縮包 | | `zipStoreBase` | 同 distributionBase,不過是 zip 壓縮包 | | `zipStorePath` | 同 distributionPath,不過是 zip 壓縮包 | :::success * 如果需要 gradle 所有的源碼就必須下載 all 版本的 > https\://services.gradle.org/distributions/gradle-5.6.4-all.zip * 如果只需要 bin 工具類就設定為 bin 即可 > https\://services.gradle.org/distributions/gradle-5.6.4-bin.zip ::: ### 升級 Gradle wrapper 自身 * 我們知道 Gradle wrapper 自身的運行是基於 `gradle-wrapper.jar` 檔案,如果想升級該檔案(也就是升級 Gradle wrapper 自身);命令如下 :::danger gralde wrapper **指令是 `gradlew`**,並非 `gradle` ::: | options | 說明 | | -------- | -------- | | `--gradle-version` [指定版本] | 指定 Gradle 版本(可用指定標籤) | | `--version` | 查看 Gradle wrapper 版本 | * 範例:下載 `7.5.1` 版本的 Gradle wrapper ```shell= ./gradlew wrapper --gradle-version 7.5.1 ./gradlew --version ``` >  ### 自定義 Wrapper Task * `gradle-wrapper.properties` 是由一個名為 `Wrapper` 的 Task 生成,當然我們也可以自定義 Wrapper 參數,在之前創建的 hello 任務中新增一個 wrapper 任務 ```groovy= // 自定義一個 wrapper 任務 (Type 用來指定任務類型) tasks.named('wrapper') { distributionType = Wrapper.DistributionType.ALL } ``` > 其他參數設置請參考 [**Wrapper**](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.wrapper.Wrapper.html) ### 驗證 Gradle Wrapper * 我們知道運行是基於 `gradle-wrapper.jar` 檔案,如果想驗證該是否被惡意修改,可以使用 Gradle 官方提供的 [**`sha256` 驗證**](https://gradle.org/release-checksums/) Linux 中驗證方式如下 1. 下載 `gradle-wrapper.jar.sha256` 並驗證本地已有的 `gradle-wrapper.jar` ```shell= cd gradle/wrapper curl --location --output gradle-wrapper.jar.sha256 \ https://services.gradle.org/distributions/gradle-8.1.1-wrapper.jar.sha256 ls -laF echo " gradle-wrapper.jar" >> gradle-wrapper.jar.sha256 # 驗證本地是否與下載的 sha256 相同 sha256sum --check gradle-wrapper.jar.sha256 ``` >  :::info * 其他 Mac、Window 驗證請參考 [**官方文檔**](https://docs.gradle.org/current/userguide/gradle_wrapper.html#sec:adding_wrapper) ::: 2. 手動輸出 `sha256`,並到 [**官方網站**](https://gradle.org/release-checksums/) 比對驗證 ```shell= sha256sum gradle-wrapper.jar ``` >  ## 第一個 Gradle 專案 ### Gradle 建構 Java 應用 * 透過 gradle 建立一個基礎專案,透過這個基礎專案來學習 Gradle 建構的配置知識 以下使用 CLI 的方式建構一個 Gradle 管理的 Java 應用 1. 使用 gradle 命令運行 gradle 內置 `init` 任務,該任務會觸發一個互動式的命令,需要你去選擇 項目類型、開發語言、DSL 類型、測試框架... 等等 ```shell= gradle init ``` > 以下是 `init` 的對話內容 > >  2. 使用 `tree` 命令查看 gradle 建構的目錄 ```shell= tree . ``` 其資料夾、文件結構如下 | 資料夾、文件 | 說明 | | - | - | | gradle/ | gradle wrapper 的資料夾 | | gradle.bat/ gradlew | gradle wrapper 的操作腳本 | | settings.gradle | 定義建構名稱、sub module、設定檔案 | | app/src/main/java | source code 資料夾 | | app/src/test/java | test 資料夾 | >  ### Gradle - 文件項目 1. **`settings.gradle` 文件**:專案名稱、建構模組設定 ```groovy= // settings.gradle // 專案名稱: 未建構項目分配的名稱,它會覆蓋在建構所在的目錄名 rootProject.name = 'java_1' // 建構時所包括的 Module include('app') ``` 2. **`app/build.gradle` 文件**:app 模組下的設定(詳細請看註解) ```groovy= // build.gradle plugins { // 應用 `application` 套件,來新增一個支持 CLI 建構 Java 的應用 id 'application' } repositories { // 使用 Maven Central 解決依賴關係 mavenCentral() } // 該 Module 的依賴 dependencies { // JUnit Jupiter 測試 testImplementation 'org.junit.jupiter:junit-jupiter:5.9.1' // 其他第三方依賴 implementation 'com.google.guava:guava:31.1-jre' } // 應用特定的 Java 工具鏈以簡化在不同環境中的工作 // Java 相關設置 java { // 設置 toolchain toolchain { // JDK 17 languageVersion = JavaLanguageVersion.of(17) } } application { // 定義應用的主類(main.class),這裡省略 suffix (.java) // <Project_Name>.<Main_Class_Name> mainClass = 'java_1.App' } tasks.named('test') { // 使用 JUnit 來進行單元測試 useJUnitPlatform() } ``` 3. **`app/src/main/java/java_1/App.java` 檔案**:對照 application#mainClass 的設定,可以找到對應的主類(也就是該應用的入口) ```java= // App.java public class App { public String getGreeting() { return "Hello World!"; } public static void main(String[] args) { System.out.println(new App().getGreeting()); } } ``` 4. **`app/src/test/java/java_1/AppTest.java` 檔案**:Junit 運行的測試檔案 ```java= // AppTest.java class AppTest { @Test void appHasAGreeting() { App classUnderTest = new App(); assertNotNull(classUnderTest.getGreeting(), "app should have a greeting"); } } ``` ### 建構 - build project * 如果沒有 IDE 那可以透過 CLI 下達指令來建構專案項目,如下 ```shell= ./gradlew build ``` >  在建構(build)專案完成後,會產生**兩個文件 `app.tar`、`app.zip`** 在目錄 `app/build/distributions` 下 >  :::success * Build 的細節 一個 `build` 指令其實它後面還會做許多事情,如果要查看 build 過程的經歷可以使用以下命令 ```shell= ./gradlew build --scan ``` >  它最終會產生一個 URL 連結,點擊這個 URL,並指定信箱,它就會把詳細的建構過程(任務、耗時... 等等資訊) 呈現出來 >  ::: ## Appendix & FAQ ###### tags: `Gradle`
×
Sign in
Email
Password
Forgot password
or
Sign in via Google
Sign in via Facebook
Sign in via X(Twitter)
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
Continue with a different method
New to HackMD?
Sign up
By signing in, you agree to our
terms of service
.