多專案建置中的子專案通常共用一些常見的依賴關係。

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
│   │      └──groovy
│   │         └──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.gradle(.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.kts"))

// 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.kts  (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
│   │      └──groovy
│   │         └──myproject.java-conventions.gradle  (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"
}

為了編譯慣例外掛程式,需要在 buildSrc 目錄的建置檔案中應用基本配置

buildSrc/build.gradle.kts
plugins {
    `kotlin-dsl`
}

repositories {
    mavenCentral()
}
buildSrc/build.gradle
plugins {
    id 'groovy-gradle-plugin'
}

慣例外掛程式應用於 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. 從特定類型的子專案中提取資訊。
    此使用案例可以使用傳出配置變體建模。