--- title: 'Gradle 基礎 - 腳本' disqus: kyleAlien --- Gradle 基礎 - 腳本 === ## Overview of Content 請先簡單建立一個 Gradle 項目,接下來的腳本都會操作該項目 [TOC] ## 項目、插件、任務 - 概述 1. **項目**:Gradle 可以管理 1 ~ 多個項目 > 其中項目可能是 Jar Library, Android Application, Web Application... 等等 :::warning 項目不一定會有產品產出(建造東西),它可能代表某項需要完成的事項(eg. 部署) ::: 2. **任務**:Gradle 中一個項目可由 1 ~ 多個任務定義,**任務在 Gradle 中就是最小單位(原子)** > 其中任務可能有清理、產生 Jar、發布任務 ... 等等 :::success * **插件是任務的一種表示方式**,同常一個插件內會有 1 ~ 多個不等的任務 ::: ### 第一個腳本 - hello * Gradle 的腳本允許使用 `kotlin`、`groovy` 這兩種程式語言開發;接下來我們寫兩個簡單腳本(編寫專案中的 `build.gradle` 文件) 1. 最簡單的腳本 ```groovy= tasks.register("hello") { doLast { println "Hello script" } } ``` 使用 gradle CLI 執行 `hello` 任務 ```shell= gradle -q "hello" ``` >  2. 腳本中使用程式語言特性 ```groovy= tasks.register("program") { doLast { println "Hello script".toUpperCase() } } ``` 使用 gradle CLI 執行 `program` 任務 ```shell= gradle -q "program" ``` >  ## 任務特性 ### 依賴性 & 無順序性 * **任務之間可以有依賴性,而 Gradle 也會依照其依賴性,對任務順序作出調整**;範例如下 `Banana` 任務,依賴於 `Apple` 任務 ```groovy= tasks.register("Apple") { doLast { println "Apple task" } } tasks.register("Banana") { dependsOn("Apple") doLast { println "Banana task." } } ``` 使用 gradle CLI 執行 `Banana` 任務 ```shell= gradle -q "Banana" ``` >  :::info * **無順序性**: 雖然是腳本,但任務也符合高級語言特性,不會有順序性(C 語言就有順序性);修改如下也可以 ```groovy= // 修改腳本順序 tasks.register("Banana") { dependsOn("Apple") doLast { println "Banana task." } } tasks.register("Apple") { doLast { println "Apple task" } } ``` ::: ### 動態註冊 * 任務可以使用動態註冊(依照邏輯在運行時產生相對應任務) ```groovy= 3.times { time -> tasks.register("hello_$time") { println "task $time finish." } } ``` Gradle CLI 運行多個任務 ```shell= gradle hello_0 hello_1 hello_2 ``` >  ### 透過 Name 獲得任務 * 在腳本中有一個 `tasks` 參數,它存有全部的任務,透過 `tasks#named` 函數就可以取得之前已註冊的任務 ```groovy= 3.times { time -> tasks.register("hello_$time") { println "task $time finish." } } tasks.named("hello_1") { dependsOn("hello_0", "hello_2") } ``` 使用 gradle CLI 執行 `hello_1` 任務 ```shell= gradle -q "hello_1" ``` >  ## 腳本特性 ### 方法 * 撰寫 Gradle 腳本如同寫一般程式,腳本中可以有自己的方法,提供腳本內覆用 > 你使用什麼語言就可以直接使用那種語言的方法格式 > > 以下使用 Groovy 語言,所以方法格式就是 Groovy 方法的格式 ```groovy= tasks.register("script_function") { doLast { println getMsg() } } private String getMsg() { return "Hello function." } ``` 使用 gradle CLI 執行 `script_function` 任務 ```shell= gradle -q "script_function" ``` >  ### 默認任務 - defaultTasks * 腳本本身有提供一個 `defaultTasks` 方法,該方法是 **提供所有任務的基礎依賴**(所有任務執行前都必須執行這些依賴) ```groovy= defaultTasks("clean", "init_") tasks.register("clean") { doLast { println "clean project." } } tasks.register("init_") { doLast { println "init project." } } ``` 運行空任務命令 ```shell= gradle -q ``` >  ### 外部依賴 - Plugin * 我們目前撰寫的腳本內所提供的方法,都是 gradle 提供給我們的,如果 **腳本需要使用到自己、第三方所提供的方法,那就必須寫外部依賴** > 這個外部依賴是針對腳本,讓腳本有其他 API 可用,**專案源碼 (Source code) 是無法使用外部依賴所提供的 API** :::info * 這種外部依賴通常就是 **以 `Plugin` 的方式出現**,提供給腳本使用 ::: ```groovy= import org.apache.commons.codec.binary.Base64 buildscript { repositories { mavenCentral() } // 依賴外部程式,拓展腳本可使用的 API dependencies { classpath group: 'commons-codec', name: 'commons-codec', version: '1.2' } } tasks.register('encode') { doLast { // 原本 gradle 並沒有提供 Base64 類 def byte[] encodedString = new Base64().encode('hello world\n'.getBytes()) println new String(encodedString) } } ``` Gradle CLI 執行 `encode` 任務 ```shell= gradle -q encode ``` >  ## API & Gradle 前面我們有提到(也有使用到),在腳本內可以使用某一些 API,而這些 API 又是 Gradle 提供;這裡我們就要來說明這些 API 與腳本的關係 ### 項目的 API * 我們知道一個專案中可以有多個項目,而 **一個項目又會配對一個腳本**,然後 **Gradle 在建構流程中就會幫我們創建一個 ++`Project` 對象++** **這個 ++`Project` 對象++ 就會提供給所有腳本使用**! :::success * [**Project API**](https://docs.gradle.org/8.1.1/dsl/org.gradle.api.Project.html) 全集請查看官方提供的 API * 所有腳本持有的 `Project` 對象 都相同 ::: * 以下是幾個腳本中常見的 `Project` API | 參數名 | 類型 | 說明 | | - | - | - | | project | Project | Project 實例 | | name | String | 項目名稱 | | path | String | 項目絕對路徑 | | description | String | 項目描述 | | projectDir | File | 包含建構腳本的目錄 | | buildDir | File | 項目的 `build` 資料夾 | | group | Object | 未指定 | | version | Object | 未指定 | ```groovy= println "Project instance: " + project println "Project name: " + name println "Path: " + path println "Description: " + description println "ProjectDir: " + projectDir println "BuildDir: " + buildDir println "Group: " + group println "Version: " + version ``` >  ### 腳本的 API * 當 Gradle 在運行時,會將腳本(所有 `.gradle`、`.gradle.kts` 結尾的文件)進行分析,並 **創建出一個 ++`Script` 對象++** 這個 **++`Script` 對象++ 就可以提供各自腳本使用** :::info * 不同語言腳本產生的對象也不同 * `.gradle` 腳本產生 [**`Script` 對象**](https://docs.gradle.org/8.1.1/dsl/org.gradle.api.Script.html) * `.gradle.kts` 腳本產生 [**`KotlinBuildScript` 對象**](https://gradle.github.io/kotlin-dsl-docs/api/org.gradle.kotlin.dsl/-kotlin-build-script/index.html) ::: // TODO: Some sample ## 腳本變量 我們可以在腳本中建立所需的變量,在 Gradle 中變量分為幾類:^1.^ **局部變量**、^2.^ **額外屬性**、^3.^ **腳本變量** ### 局部變量 * 局部變量會根據你腳本使用的語言而有不同的宣告方式 * `Groovy` 使用 `def` 關鍵字:**只在聲明範圍內可見(也就是超出範圍後,就不在可見)** > `Kotlin` 則使用 `val` 關鍵字,同樣在聲明範圍內可見 範例如下: ```groovy= def localExValue = "Local ex-value" tasks.register("Hello") { def innerValue = 10 doLast { println "Local ex-value: " + localExValue println "Inner value: " + innerValue } } tasks.register("World") { doLast { println "Local ex-value: " + localExValue // Error !! println "Inner value: " + innerValue } } ``` >  ### 額外屬性 - ext * 額外屬性 (`ext`) 是 Project 屬性中的一個成員(類型為 [**ExtraPropertiesExtension**](https://docs.gradle.org/current/dsl/org.gradle.api.plugins.ExtraPropertiesExtension.html)),透過它就可以拓展而外的屬性 :::info * Gradle 中的 `Project` 對像在單項目構建中是唯一的,而在多項目構建中,根項目和每個子項目都有一個對應的 `Project` 對象 > 也就是說每個項目都可以有自己的 `ext` 拓展 ::: 範例如下 ```groovy= plugins { id 'java-library' } // 額外屬性設置 ext { springVersion = "3.1.0.RELEASE" emailNotification = "build@master.org" } sourceSets.configureEach { ext.purpose = null } sourceSets { main { purpose = "production" } test { purpose = "test" } plugin { purpose = "production" } } tasks.register('printProperties') { def springVersion = springVersion def emailNotification = emailNotification def productionSourceSets = provider { sourceSets.matching { it.purpose == "production" }.collect { it.name } } doLast { println springVersion println emailNotification productionSourceSets.get().each { println it } } } ``` >  ### 配置任意對象 - configure * `configure` 也是 Project 提供的方法,透過它可以有結構性的獲得你需要的對象 ```groovy= // configure_object.gradle class UserInfo { String name String email } tasks.register('configure') { def user = configure(new UserInfo()) { name = "Isaac Newton" email = "isaac@newton.me" } doLast { println user.name println user.email } } ``` >  ### 腳本變量 - 作用域 * Groovy 有三種腳本變量,它聲明方式也不同 * **使用 `def` 聲明的變量**:只能在該 **腳本範圍內被使用** ```groovy= def localScope2 = 'localScope2' ``` * **使用指定類型聲明**:同上,只能在腳本內使用 ```groovy= String localScope1 = 'localScope1' ``` * **無任何聲明**:無任何聲明的變量,可以在 **任何腳本內獲取** :::warning * 最新版本 8.1.1 好像已經取消無聲明變數了 ::: ## Appendix & FAQ :::info ::: ###### tags: `Gradle`
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up