理想情況下,Groovy 建置指令碼看起來大多像設定:設定專案的一些屬性、設定相依性、宣告工作等等。該設定基於 Groovy 語言結構。本入門旨在說明這些結構是什麼,以及 — 最重要的是 — 它們與 Gradle 的 API 文件有何關聯。

Project 物件

由於 Groovy 是一種基於 Java 的物件導向語言,因此其屬性和方法適用於物件。在某些情況下,物件是隱含的 — 特別是在建置指令碼的最上層,即未嵌套在 {} 區塊內。

考慮這段建置指令碼片段,其中包含未限定的屬性和區塊

version = '1.0.0.GA'

configurations {
    ...
}

versionconfigurations {} 都是 org.gradle.api.Project 的一部分。

此範例反映了每個 Groovy 建置指令碼如何由 Project 的隱含實例作為後盾。如果您看到未限定的元素,但不知道它在哪裡定義,請務必查看 Project API 文件,看看它是否來自那裡。

避免在建置指令碼中使用 Groovy MetaClass 程式設計技巧。Gradle 提供了自己的 API 來新增 動態執行時期屬性

使用 Groovy 特有的元程式設計可能會導致建置在建置之間保留大量記憶體,最終導致 Gradle 守護程式記憶體不足。

屬性

<obj>.<name>                // Get a property value
<obj>.<name> = <value>      // Set a property to a new value
"$<name>"                   // Embed a property value in a string
"${<obj>.<name>}"           // Same as previous (embedded value)
範例
version = '1.0.1'
myCopyTask.description = 'Copies some files'

file("$projectDir/src")
println "Destination: ${myCopyTask.destinationDir}"

屬性代表物件的某種狀態。存在 = 符號明確表示您正在檢視屬性。否則,限定名稱(以 <obj>. 開頭)不帶任何其他裝飾也是屬性。

如果名稱未限定,則可能是下列其中一項

  • 具有該名稱的任務執行個體。

  • 專案上的屬性。

  • 專案中其他地方定義的額外屬性

  • 區塊中隱含物件的屬性。

  • 先前在建置指令碼中定義的局部變數

請注意,外掛程式可以將其自己的屬性新增到 專案物件。 API 文件會列出核心外掛程式新增的所有屬性。如果您難以找出屬性來自何處,請查看建置使用的外掛程式的文件。

在建置指令碼中參照非核心外掛程式新增的專案屬性時,請考慮加上 專案. 前綴,這樣就能清楚地表示該屬性屬於專案物件。

API 文件中的屬性

Groovy DSL 參考會顯示屬性在建置指令碼中的使用方式,但 Javadocs 只會顯示方法。這是因為屬性會在幕後實作為方法

  • 如果存在方法名稱為 get<PropertyName>,且不帶任何引數,並傳回與屬性相同的類型,則可以讀取屬性。

  • 如果存在方法名稱為 set<PropertyName>,且帶有一個引數(類型與屬性相同)和 void 回傳類型,則可以修改屬性。

請注意,屬性名稱通常以小寫字母開頭,但該字母在方法名稱中會大寫。因此,getter 方法 getProjectVersion() 對應到屬性 projectVersion。當名稱以至少兩個大寫字母開頭時,此慣例不適用,這種情況下大小寫不會改變。例如,getRAM() 對應到屬性 RAM

範例
project.getVersion()
project.version

project.setVersion('1.0.1')
project.version = '1.0.1'

方法

<obj>.<name>()              // Method call with no arguments
<obj>.<name>(<arg>, <arg>)  // Method call with multiple arguments
<obj>.<name> <arg>, <arg>   // Method call with multiple args (no parentheses)
範例
myCopyTask.include '**/*.xml', '**/*.properties'

ext.resourceSpec = copySpec()   // `copySpec()` comes from `Project`

file('src/main/java')
println 'Hello, World!'

方法代表物件的某種行為,儘管 Gradle 通常也使用方法來設定物件的狀態。方法可透過其引數或空括弧來識別。請注意,有時需要括弧,例如當方法沒有引數時,因此您可能會發現始終使用括弧最簡單。

Gradle 有個慣例,如果方法與基於集合的屬性同名,則該方法會將其值附加到該集合。

區塊

區塊也是方法,只不過最後一個引數有特定的類型。

<obj>.<name> {
     ...
}

<obj>.<name>(<arg>, <arg>) {
     ...
}
範例
plugins {
    id 'java-library'
}

configurations {
    assets
}

sourceSets {
    main {
        java {
            srcDirs = ['src']
        }
    }
}

dependencies {
    implementation project(':util')
}

區塊是一種機制,用於一次設定組建元素的多個面向。它們也提供一種巢狀設定的方式,形成一種結構化資料。

區塊有兩個重要的面向,你應該了解

  1. 它們實作為具有特定簽章的方法。

  2. 它們可以變更未限定方法和屬性的目標(「委派」)。

這兩個面向都基於 Groovy 語言功能,我們會在以下各節說明。

區塊方法簽章

你可以透過簽章,更具體地說,透過其引數類型,輕鬆地識別方法作為區塊背後的實作。如果一個方法對應到一個區塊

例如,Project.copy(Action) 符合這些需求,因此你可以使用下列語法

copy {
    into layout.buildDirectory.dir("tmp")
    from 'custom-resources'
}

這會引發一個問題:into()from() 如何運作?它們顯然是方法,但你可以在 API 文件中找到它們嗎?答案來自於了解物件委派

委派

關於屬性的章節列出了未限定屬性可能在哪裡找到。一個常見的地方是在 Project 物件上。但在區塊內,這些未限定屬性和方法還有另一個來源:區塊的委派物件

為了幫助說明這個概念,請考慮前一章節的最後一個範例

copy {
    into layout.buildDirectory.dir("tmp")
    from 'custom-resources'
}

此範例中的所有方法和屬性都是未限定的。你可以在 Project API 文件 中輕鬆找到 copy()layout,但 into()from() 呢?這些會根據 copy {} 區塊的委派解析。該委派的類型是什麼?你需要查看該 API 文件

有兩種方法可以判斷委派類型,這取決於區塊方法的簽章

  • 對於 Action 引數,請查看類型的參數。

    在上面的範例中,方法簽章為 copy(Action<? super CopySpec>),而尖括號內的位元會告訴您委派類型,在此情況下為 CopySpec

  • 對於 Closure 參數,文件會在說明中明確說明正在設定哪種類型或委派類型為何(相同事物的不同術語)。

因此,您可以在 CopySpec 上找到 into()from()。您甚至可能會注意到這兩個方法都有變體,它們將 Action 作為其最後一個參數,這表示您可以使用區塊語法。

所有新的 Gradle API 宣告 Action 參數類型,而不是 Closure,這使得挑選委派類型變得非常容易。即使是較舊的 API 除了舊的 Closure 之外,還有一個 Action 變體。

局部變數

def <name> = <value>        // Untyped variable
<type> <name> = <value>     // Typed variable
範例
def i = 1
String errorMsg = 'Failed, because reasons'

局部變數是 Groovy 結構,不同於 額外屬性,可用於在建置指令碼中分享值。

避免在專案的根目錄中使用局部變數,即作為偽專案屬性。它們無法在建置指令碼外部讀取,而 Gradle 也不認識它們。

在較狹窄的範圍內,例如設定任務時,局部變數偶爾會很有幫助。