建構 Gradle 專案的結構以最佳化建置效能非常重要。多專案建置是 Gradle 的標準。

structuring builds 1

多專案建置包含一個根專案和一個或多個子專案。Gradle 可以一次執行建置根專案和任意數量的子專案。

專案位置

多專案建置包含一個單一根專案,位於 Gradle 視為根路徑的目錄中:.

子專案實體上位於根路徑下:./subproject

子專案有 路徑,表示該子專案在多專案建置中的位置。在大部分情況下,專案路徑與其在檔案系統中的位置一致。

專案結構在 settings.gradle(.kts) 檔案中建立。設定檔必須存在於根目錄中。

一個簡單的多專案建置

讓我們來看一個包含根專案和單一子專案的基本多專案建置範例。

根專案稱為 basic-multiproject,位於電腦上的某個位置。從 Gradle 的角度來看,根目錄是頂層目錄 .

專案包含一個單一子專案,稱為 ./app

.
├── app
│   ...
│   └── build.gradle.kts
└── settings.gradle.kts
.
├── app
│   ...
│   └── build.gradle
└── settings.gradle

這是開始任何 Gradle 專案的建議專案結構。建置初始化外掛程式也會產生遵循此結構的範本專案 - 一個包含單一子專案的根專案

settings.gradle(.kts) 檔案向 Gradle 描述專案結構

settings.gradle.kts
rootProject.name = "basic-multiproject"
include("app")
settings.gradle
rootProject.name = 'basic-multiproject'
include 'app'

在這個案例中,Gradle 會在 ./app 目錄中尋找 app 子專案的建置檔案。

你可以執行 projects 指令來檢視多專案建置的結構

$ ./gradlew -q projects

------------------------------------------------------------
Root project 'basic-multiproject'
------------------------------------------------------------

Root project 'basic-multiproject'
\--- Project ':app'

To see a list of the tasks of a project, run gradle <project-path>:tasks
For example, try running gradle :app:tasks

在此範例中,app 子專案是一個 Java 應用程式,套用 應用程式外掛程式 並設定主類別。應用程式會將 Hello World 印到主控台

app/build.gradle.kts
plugins {
    id("application")
}

application {
    mainClass = "com.example.Hello"
}
app/build.gradle
plugins {
    id 'application'
}

application {
    mainClass = 'com.example.Hello'
}
app/src/main/java/com/example/Hello.java
package com.example;

public class Hello {
    public static void main(String[] args) {
        System.out.println("Hello, world!");
    }
}

您可以透過執行專案根目錄中應用程式外掛程式run工作來執行應用程式

$ ./gradlew -q run
Hello, world!

加入子專案

在設定檔中,您可以使用include方法將另一個子專案加入根專案

settings.gradle.kts
include("project1", "project2:child1", "project3:child1")
settings.gradle
include 'project1', 'project2:child1', 'project3:child1'

include方法採用專案路徑作為引數。專案路徑假設等於相對的實體檔案系統路徑。例如,路徑services:api預設會對應到資料夾./services/api(相對於專案根目錄.)。

更多關於如何使用專案路徑的範例可以在Settings.include(java.lang.String[])的 DSL 文件中找到。

讓我們將另一個名為lib的子專案加入先前建立的專案。

我們只需要在根設定檔中加入另一條include陳述式

settings.gradle.kts
rootProject.name = "basic-multiproject"
include("app")
include("lib")
settings.gradle
rootProject.name = 'basic-multiproject'
include 'app'
include 'lib'

然後,Gradle 會在./lib/目錄中尋找新的lib子專案的建置檔

.
├── app
│   ...
│   └── build.gradle.kts
├── lib
│   ...
│   └── build.gradle.kts
└── settings.gradle.kts
.
├── app
│   ...
│   └── build.gradle
├── lib
│   ...
│   └── build.gradle
└── settings.gradle

專案描述子

為了進一步向 Gradle 描述專案架構,設定檔提供了專案描述子

您可以隨時在設定檔中修改這些描述子。

若要存取描述子,您可以

settings.gradle.kts
include("project-a")
println(rootProject.name)
println(project(":project-a").name)
settings.gradle
include('project-a')
println rootProject.name
println project(':project-a').name

使用這個描述子,您可以變更專案的名稱、專案目錄和建置檔

settings.gradle.kts
rootProject.name = "main"
include("project-a")
project(":project-a").projectDir = file("custom/my-project-a")
project(":project-a").buildFileName = "project-a.gradle.kts"
settings.gradle
rootProject.name = 'main'
include('project-a')
project(':project-a').projectDir = file('custom/my-project-a')
project(':project-a').buildFileName = 'project-a.gradle'

請參閱 API 文件中的ProjectDescriptor類別以取得更多資訊。

修改子專案路徑

讓我們來看看具有下列結構的假設專案

.
├── app
│   ...
│   └── build.gradle.kts
├── subs // Gradle may see this as a subproject
│   └── web // Gradle may see this as a subproject
│       └── my-web-module // Intended subproject
│           ...
│           └── build.gradle.kts
└── settings.gradle.kts
.
├── app
│   ...
│   └── build.gradle
├── subs // Gradle may see this as a subproject
│   └── web // Gradle may see this as a subproject
│       └── my-web-module // Intended subproject
│           ...
│           └── build.gradle
└── settings.gradle

如果您的settings.gradle(.kts)看起來像這樣

include(':subs:web:my-web-module')

Gradle 會看到一個邏輯專案名稱為:subs:web:my-web-module的子專案,以及兩個可能無意的其他子專案,邏輯名稱分別為:subs:subs:web。這可能會導致幻影建置目錄,特別是在使用allprojects{}subproject{}時。

為避免發生此問題,您可以使用

include(':subs:web:my-web-module')
project(':subs:web:my-web-module').projectDir = "subs/web/my-web-module"

這樣您最後只會得到一個名為 :subs:web:my-web-module 的子專案。

或者您可以使用

include(':my-web-module')
project(':my-web-module').projectDir = "subs/web/my-web-module"

這樣您最後只會得到一個名為 :my-web-module 的子專案。

因此,雖然實際專案配置相同,但邏輯結果卻不同。

命名建議

隨著專案的成長,命名和一致性會變得越來越重要。為了讓您的組建保持可維護性,我們建議您執行下列動作

  1. 對子專案保留預設專案名稱:可以在設定檔中設定自訂專案名稱。不過,這會讓開發人員必須額外追蹤哪些專案屬於哪些資料夾,這是沒有必要的。

  2. 對所有專案名稱使用小寫連字號:所有字母都使用小寫,且字詞之間以破折號 (-) 字元分隔。

  3. 在設定檔中定義根專案名稱rootProject.name 會有效地為組建指定一個名稱,並用於組建掃描等報告中。如果未設定根專案名稱,名稱就會是容器目錄名稱,而這可能會不穩定(例如,您可以在任何目錄中檢出您的專案)。如果未設定根專案名稱,且檢出到檔案系統的根目錄(例如,/C:\),名稱就會隨機產生。