多專案建置中的子專案通常會分享一些常見的相依性。

structuring builds 3

Gradle 提供了一個特殊目錄來儲存共用建置邏輯,可以自動套用至子專案,無需在每個子專案建置指令碼中複製並貼上相同的 Java 版本和函式庫。

buildSrc 中分享邏輯

buildSrc 是 Gradle 識別且受保護的目錄,具有一些好處

  1. 可重複使用的建置邏輯:

    buildSrc 讓您可以以結構化的方式組織和集中自訂建置邏輯、工作和外掛。在 buildSrc 中撰寫的程式碼可以在專案中重複使用,讓維護和分享常見建置功能更為容易。

  2. 與主建置隔離:

    放置在 buildSrc 中的程式碼會與專案的其他建置指令碼隔離。這有助於讓主建置指令碼更簡潔,並更專注於專案特定的設定。

  3. 自動編譯和類別路徑:

    buildSrc 目錄的內容會自動編譯,並包含在主建置的類別路徑中。這表示在 buildSrc 中定義的類別和外掛可以直接在專案的建置指令碼中使用,無需任何其他設定。

  4. 測試容易:

    由於 buildSrc 是獨立的建置,因此可以輕鬆測試自訂建置邏輯。您可以為建置程式碼撰寫測試,確保其行為符合預期。

  5. Gradle 外掛開發:

    如果您正在為專案開發自訂 Gradle 外掛,buildSrc 是存放外掛程式碼的便利位置。這使得外掛在專案中容易存取。

buildSrc 目錄被視為 包含的建置

對於多專案建置,只能有一個 buildSrc 目錄,而且必須位於根目錄專案中。

使用 buildSrc 的缺點是,對其進行任何變更都會使專案中的每個工作無效,並需要重新執行。

buildSrc 使用適用於 Java、Groovy 和 Kotlin 專案的相同 原始碼慣例。它還提供對 Gradle API 的直接存取。

包含 buildSrc 的典型專案具有下列配置

.
├── buildSrc
│   ├── src
│   │   └──main
│   │      └──kotlin
│   │         └──MyCustomTask.kt    (1)
│   ├── shared.gradle.kts   (2)
│   └── build.gradle.kts
├── api
│   ├── src
│   │   └──...
│   └── build.gradle.kts    (3)
├── services
│   └── person-service
│       ├── src
│       │   └──...
│       └── build.gradle.kts    (3)
├── shared
│   ├── src
│   │   └──...
│   └── build.gradle.kts
└── settings.gradle.kts
1 建立 MyCustomTask 工作。
2 共用建置指令碼。
3 使用 MyCustomTask 工作和共用建置指令碼。
.
├── buildSrc
│   ├── src
│   │   └──main
│   │      └──kotlin
│   │         └──MyCustomTask.groovy    (1)
│   ├── shared.gradle   (2)
│   └── build.gradle
├── api
│   ├── src
│   │   └──...
│   └── build.gradle    (3)
├── services
│   └── person-service
│       ├── src
│       │   └──...
│       └── build.gradle    (3)
├── shared
│   ├── src
│   │   └──...
│   └── build.gradle
└── settings.gradle
1 建立 MyCustomTask 工作。
2 共用建置指令碼。
3 使用 MyCustomTask 工作和共用建置指令碼。

buildSrc 中,建立建置指令碼 shared.gradle(.kts)。它包含相依關係和其他對多個子專案而言共用的建置資訊

shared.gradle.kts
repositories {
    mavenCentral()
}

dependencies {
    implementation("org.slf4j:slf4j-api:1.7.32")
}
shared.gradle
repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.slf4j:slf4j-api:1.7.32'
}

buildSrc 中,也建立 MyCustomTask。它是用作多個子專案建置邏輯一部分的輔助工作。

MyCustomTask.kt
import org.gradle.api.DefaultTask
import org.gradle.api.tasks.TaskAction

open class MyCustomTask : DefaultTask() {
    @TaskAction
    fun calculateSum() {
        // Custom logic to calculate the sum of two numbers
        val num1 = 5
        val num2 = 7
        val sum = num1 + num2

        // Print the result
        println("Sum: $sum")
    }
}
MyCustomTask.groovy
import org.gradle.api.DefaultTask
import org.gradle.api.tasks.TaskAction

class MyCustomTask extends DefaultTask {
    @TaskAction
    void calculateSum() {
        // Custom logic to calculate the sum of two numbers
        int num1 = 5
        int num2 = 7
        int sum = num1 + num2

        // Print the result
        println "Sum: $sum"
    }
}

MyCustomTask 工作用於 apishared 專案的建置指令碼中。工作會自動提供,因為它是 buildSrc 的一部分。

也會套用 shared.build(.kts) 檔案

build.gradle.kts
// Apply any other configurations specific to your project

// Use the build script defined in buildSrc
apply(from = rootProject.file("buildSrc/shared.gradle"))

// Use the custom task defined in buildSrc
tasks.register<MyCustomTask>("myCustomTask")
build.gradle
// Apply any other configurations specific to your project

// Use the build script defined in buildSrc
apply from: rootProject.file('buildSrc/shared.gradle')

// Use the custom task defined in buildSrc
tasks.register('myCustomTask', MyCustomTask)

使用慣例外掛共用邏輯

Gradle 建議的建置邏輯組織方式是使用其外掛系統。

我們可以撰寫一個外掛,將專案中多個子專案共用的建置邏輯封裝起來。這種外掛稱為慣例外掛

儘管撰寫外掛不在本節的範疇內,但建議建立 Gradle 專案的方式是將共用建置邏輯放入位於 buildSrc 中的慣例外掛。

我們來看看一個範例專案

.
├── buildSrc
│   ├── src
│   │   └──main
│   │      └──kotlin
│   │         └──myproject.java-conventions.gradle  (1)
│   └── build.gradle.kts
├── api
│   ├── src
│   │   └──...
│   └── build.gradle.kts    (2)
├── services
│   └── person-service
│       ├── src
│       │   └──...
│       └── build.gradle.kts    (2)
├── shared
│   ├── src
│   │   └──...
│   └── build.gradle.kts    (2)
└── settings.gradle.kts
1 建立 myproject.java-conventions 慣例外掛。
2 套用 myproject.java-conventions 慣例外掛。
.
├── buildSrc
│   ├── src
│   │   └──main
│   │      └──kotlin
│   │         └──myproject.java-conventions.gradle.kts  (1)
│   └── build.gradle
├── api
│   ├── src
│   │   └──...
│   └── build.gradle    (2)
├── services
│   └── person-service
│       ├── src
│       │   └──...
│       └── build.gradle    (2)
├── shared
│   ├── src
│   │   └──...
│   └── build.gradle    (2)
└── settings.gradle
1 建立 myproject.java-conventions 慣例外掛。
2 套用 myproject.java-conventions 慣例外掛。

此建置包含三個子專案

settings.gradle.kts
rootProject.name = "dependencies-java"
include("api", "shared", "services:person-service")
settings.gradle
rootProject.name = 'dependencies-java'
include 'api', 'shared', 'services:person-service'

buildSrc 目錄中建立的慣例外掛的原始碼如下

buildSrc/src/main/kotlin/myproject.java-conventions.gradle.kts
plugins {
    id("java")
}

group = "com.example"
version = "1.0"

repositories {
    mavenCentral()
}

dependencies {
    testImplementation("junit:junit:4.13")
}
buildSrc/src/main/groovy/myproject.java-conventions.gradle
plugins {
    id 'java'
}

group = 'com.example'
version = '1.0'

repositories {
    mavenCentral()
}

dependencies {
    testImplementation "junit:junit:4.13"
}

慣例外掛套用至 apisharedperson-service 子專案

api/build.gradle.kts
plugins {
    id("myproject.java-conventions")
}

dependencies {
    implementation(project(":shared"))
}
shared/build.gradle.kts
plugins {
    id("myproject.java-conventions")
}
services/person-service/build.gradle.kts
plugins {
    id("myproject.java-conventions")
}

dependencies {
    implementation(project(":shared"))
    implementation(project(":api"))
}
api/build.gradle
plugins {
    id 'myproject.java-conventions'
}

dependencies {
    implementation project(':shared')
}
shared/build.gradle
plugins {
    id 'myproject.java-conventions'
}
services/person-service/build.gradle
plugins {
    id 'myproject.java-conventions'
}

dependencies {
    implementation project(':shared')
    implementation project(':api')
}

不要使用跨專案設定

在子專案之間共用建置邏輯的不當方式是透過 subprojects {}allprojects {} DSL 建構來進行跨專案設定

避免使用 subprojects {}allprojects {}

使用跨設定時,建置邏輯可以注入到子專案中,而從其建置指令碼中無法明顯看出。

從長遠來看,跨設定通常會變得更複雜,並成為負擔。跨設定也可能在專案之間引入設定時間的關聯,這可能會阻止按需設定等最佳化正常運作。

慣例外掛與跨設定

跨設定最常見的兩種用法可以透過使用慣例外掛來建模

  1. 套用外掛或其他設定至特定類型的子專案。
    通常,跨設定邏輯是 如果子專案是 X 類型,則設定 Y。這等同於直接套用 X-conventions 外掛至子專案。

  2. 從特定類型的子專案中擷取資訊。
    此使用案例可以使用 傳出設定變異 來建模。