Gradle 支持多项目构建。

gradle basic 9

虽然一些小型项目和单体应用可能只包含一个构建文件和源代码树,但更常见的情况是项目被拆分成更小、相互依赖的模块。“相互依赖”这个词至关重要,因为您通常希望通过一个构建将许多模块关联起来。

Gradle 通过 多项目 构建来支持这种场景。这有时也被称为多模块项目。Gradle 将模块称为子项目。

多项目构建由一个根项目和一个或多个子项目组成。

多项目结构

以下是包含三个子项目的多项目构建结构示例

multi project structure

目录结构应如下所示

├── .gradle
│   └── ⋮
├── gradle
│   ├── libs.versions.toml
│   └── wrapper
├── gradlew
├── gradlew.bat
├── settings.gradle.kts (1)
├── sub-project-1
│   └── build.gradle.kts    (2)
├── sub-project-2
│   └── build.gradle.kts    (2)
└── sub-project-3
    └── build.gradle.kts    (2)
1 settings.gradle.kts 文件应包含所有子项目。
2 每个子项目都应有自己的 build.gradle.kts 文件。

多项目标准

Gradle 社区有两种多项目构建结构的规范

  1. 使用 buildSrc 的多项目构建 - 其中 buildSrc 是 Gradle 项目根目录下的一个类似子项目的目录,包含所有构建逻辑。

  2. 复合构建 - 包含其他构建的构建,其中 build-logic 是 Gradle 项目根目录下的一个构建目录,包含可重用的构建逻辑。

multi project standards

1. 使用 buildSrc 的多项目构建

多项目构建允许您组织包含许多模块的项目,连接这些模块之间的依赖关系,并轻松地在它们之间共享通用构建逻辑。

例如,一个包含名为 mobile-appweb-appapilibdocumentation 的许多模块的构建可以结构如下

.
├── gradle
├── gradlew
├── settings.gradle.kts
├── buildSrc
│   ├── build.gradle.kts
│   └── src/main/kotlin/shared-build-conventions.gradle.kts
├── mobile-app
│   └── build.gradle.kts
├── web-app
│   └── build.gradle.kts
├── api
│   └── build.gradle.kts
├── lib
│   └── build.gradle.kts
└── documentation
    └── build.gradle.kts

这些模块之间将存在依赖关系,例如 web-appmobile-app 依赖于 lib。这意味着为了让 Gradle 构建 web-appmobile-app,它必须首先构建 lib

在此示例中,根 settings 文件如下所示

settings.gradle.kts
include("mobile-app", "web-app", "api", "lib", "documentation")
settings.gradle
include("mobile-app", "web-app", "api", "lib", "documentation")
子项目(模块)的包含顺序无关紧要。

buildSrc 目录会被 Gradle 自动识别。它是定义和维护共享配置或命令式构建逻辑(例如自定义任务或插件)的好地方。

如果在 buildSrc 下找到 build.gradle(.kts) 文件,buildSrc 会自动作为特殊子项目包含在您的构建中。

如果将 java 插件应用于 buildSrc 项目,则从 buildSrc/src/main/java 编译的代码将放置在根构建脚本的 classpath 中,从而使其对构建中的任何子项目(web-appmobile-applib 等)可用。

请查阅如何声明子项目之间的依赖关系以了解更多信息。

2. 复合构建

复合构建,也称为 *包含构建*,最适合在构建之间(*而非子项目*)共享逻辑或隔离对共享构建逻辑(即约定插件)的访问。

我们来看看前面的例子。buildSrc 中的逻辑已转化为一个包含插件的项目,该项目可以独立于根项目构建进行发布和工作。

该插件被移至其自己的构建,称为 build-logic,包含构建脚本和 settings 文件。

.
├── gradle
├── gradlew
├── settings.gradle.kts
├── build-logic
│   ├── settings.gradle.kts
│   └── conventions
│       ├── build.gradle.kts
│       └── src/main/kotlin/shared-build-conventions.gradle.kts
├── mobile-app
│   └── build.gradle.kts
├── web-app
│   └── build.gradle.kts
├── api
│   └── build.gradle.kts
├── lib
│   └── build.gradle.kts
└── documentation
    └── build.gradle.kts
build-logic 位于根项目的子目录中这一事实并不重要。如果需要,该文件夹可以位于根项目之外。

根 settings 文件包含整个 build-logic 构建

settings.gradle.kts
pluginManagement {
    includeBuild("build-logic")
}
include("mobile-app", "web-app", "api", "lib", "documentation")

请查阅如何使用 includeBuild 创建复合构建以了解更多信息。

多项目路径

项目路径具有以下模式:它以一个可选的冒号开头,表示根项目。

根项目 : 是路径中唯一不以名称指定的项目。

项目路径的其余部分是冒号分隔的项目名称序列,其中下一个项目是前一个项目的子项目

:sub-project-1

运行 gradle projects 时可以看到项目路径。

------------------------------------------------------------
Root project 'project'
------------------------------------------------------------

Root project 'project'
+--- Project ':sub-project-1'
\--- Project ':sub-project-2'

项目路径通常反映文件系统布局,但也有例外。最显著的是针对复合构建的情况。

识别项目结构

您可以使用 gradle projects 命令来识别项目结构。

例如,我们使用具有以下结构的多项目构建

$ gradle -q projects

Projects:

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

Root project 'multiproject'
+--- Project ':api'
+--- Project ':services'
|    +--- Project ':services:shared'
|    \--- Project ':services:webservice'
\--- Project ':shared'

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

多项目构建是可以运行的任务集合。不同之处在于,您可能想要控制执行 哪个 项目的任务。

以下部分将介绍在多项目构建中执行任务的两种选项。

按名称执行任务

命令 gradle test 将在相对于当前工作目录的任何包含 test 任务的子项目中执行该任务。

如果您从根项目目录运行此命令,它将在 apisharedservices:sharedservices:webservice 中运行 test

如果您从 services 项目目录运行此命令,它将仅在 services:sharedservices:webservice 中执行该任务。

Gradle 行为背后的基本规则是:向下遍历层次结构并执行所有名为 名称的任务。并且,如果遍历的任何子项目中 没有找到 此类 任务,则报错

一些任务选择器,例如 helpdependencies,将仅在它们被调用的项目上运行任务,而不是在所有子项目上运行,以减少屏幕上打印的信息量。

按完全限定名称执行任务

您可以使用任务的完全限定名称在特定子项目中执行特定任务。例如:gradle :services:webservice:build 将运行 webservice 子项目的 build 任务。

任务的完全限定名称是其项目路径加上任务名称。

此方法适用于任何任务,因此如果您想了解特定子项目中有哪些任务,请使用 tasks 任务,例如 gradle :services:webservice:tasks

多项目构建和测试

build 任务通常用于编译、测试和检查单个项目。

在多项目构建中,您可能经常希望在多个项目上执行所有这些任务。buildNeededbuildDependents 任务可以帮助您实现这一点。

此示例中,:services:person-service 项目依赖于 :api:shared 项目。:api 项目也依赖于 :shared 项目。

假设您正在处理单个项目,即 :api 项目,您一直在进行修改,但自执行 clean 后尚未构建整个项目。您希望构建所有必要的支持 JAR,但只对您更改的项目部分执行代码质量和单元测试。

build 任务执行此操作。

$ gradle :api:build
> Task :shared:compileJava
> Task :shared:processResources
> Task :shared:classes
> Task :shared:jar
> Task :api:compileJava
> Task :api:processResources
> Task :api:classes
> Task :api:jar
> Task :api:assemble
> Task :api:compileTestJava
> Task :api:processTestResources
> Task :api:testClasses
> Task :api:test
> Task :api:check
> Task :api:build

BUILD SUCCESSFUL in 0s

如果您刚刚从版本控制系统获取了最新的源代码版本,其中包括 :api 依赖的其他项目中的更改,您可能希望构建所有您依赖的项目并同时测试它们。

buildNeeded 任务会构建并测试 testRuntime 配置的项目依赖项中的所有项目。

$ gradle :api:buildNeeded
> Task :shared:compileJava
> Task :shared:processResources
> Task :shared:classes
> Task :shared:jar
> Task :api:compileJava
> Task :api:processResources
> Task :api:classes
> Task :api:jar
> Task :api:assemble
> Task :api:compileTestJava
> Task :api:processTestResources
> Task :api:testClasses
> Task :api:test
> Task :api:check
> Task :api:build
> Task :shared:assemble
> Task :shared:compileTestJava
> Task :shared:processTestResources
> Task :shared:testClasses
> Task :shared:test
> Task :shared:check
> Task :shared:build
> Task :shared:buildNeeded
> Task :api:buildNeeded

BUILD SUCCESSFUL in 0s

您可能希望重构 :api 项目中被其他项目使用的某些部分。如果您进行这些更改,仅测试 :api 项目是不够的。您必须测试所有依赖于 :api 项目的项目。

buildDependents 任务会测试所有(在 testRuntime 配置中)对指定项目有项目依赖关系的项目。

$ gradle :api:buildDependents
> Task :shared:compileJava
> Task :shared:processResources
> Task :shared:classes
> Task :shared:jar
> Task :api:compileJava
> Task :api:processResources
> Task :api:classes
> Task :api:jar
> Task :api:assemble
> Task :api:compileTestJava
> Task :api:processTestResources
> Task :api:testClasses
> Task :api:test
> Task :api:check
> Task :api:build
> Task :services:person-service:compileJava
> Task :services:person-service:processResources
> Task :services:person-service:classes
> Task :services:person-service:jar
> Task :services:person-service:assemble
> Task :services:person-service:compileTestJava
> Task :services:person-service:processTestResources
> Task :services:person-service:testClasses
> Task :services:person-service:test
> Task :services:person-service:check
> Task :services:person-service:build
> Task :services:person-service:buildDependents
> Task :api:buildDependents

BUILD SUCCESSFUL in 0s

最后,您可以在所有项目中构建和测试所有内容。您在根项目文件夹中运行的任何任务都将导致在所有子项目上运行同名任务。

您可以运行 gradle build 来构建和测试所有项目。

请查阅组织构建结构章节以了解更多信息。