理想情況下,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 Daemon 記憶體不足。

屬性

<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>. 開頭 — 且沒有任何其他裝飾也是屬性。

如果名稱未限定,則它可能是以下之一

請注意,外掛程式可以將自己的屬性新增至 Project 物件。API 文件列出了核心外掛程式新增的所有屬性。如果您在尋找屬性來源時遇到困難,請檢查建置使用的外掛程式的文件。

當在您的建置腳本中參考非核心外掛程式新增的專案屬性時,請考慮以 project. 作為前綴 — 這樣可以清楚地表明該屬性屬於專案物件。

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 對它們一無所知。

在較窄的上下文中 — 例如配置任務 — 區域變數有時可能很有用。