对 Gradle 项目进行结构化以优化构建性能非常重要。多项目构建是 Gradle 中的标准。
多项目构建由一个根项目和一个或多个子项目组成。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 描述项目结构
rootProject.name = "basic-multiproject"
include("app")
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
打印到控制台
plugins {
id("application")
}
application {
mainClass = "com.example.Hello"
}
plugins {
id 'application'
}
application {
mainClass = 'com.example.Hello'
}
package com.example;
public class Hello {
public static void main(String[] args) {
System.out.println("Hello, world!");
}
}
你可以通过在项目根目录中执行 应用程序插件 中的 run
任务来运行该应用程序
$ ./gradlew -q run Hello, world!
添加子项目
在设置文件中,你可以使用 include
方法向根项目添加另一个子项目
include("project1", "project2:child1", "project3:child1")
include 'project1', 'project2:child1', 'project3:child1'
include
方法将 项目路径 作为参数。项目路径被假定为等于相对物理文件系统路径。例如,路径 services:api
默认映射到文件夹 ./services/api
(相对于项目根目录 .
)。
有关如何使用项目路径的更多示例,请参阅 Settings.include(java.lang.String[]) 的 DSL 文档。
让我们向之前创建的项目添加另一个名为 lib
的子项目。
我们需要做的就是向根设置文件中添加另一个 include
语句
rootProject.name = "basic-multiproject"
include("app")
include("lib")
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 描述项目架构,设置文件提供了项目描述符。
你可以在设置文件中随时修改这些描述符。
要访问描述符,你可以
include("project-a")
println(rootProject.name)
println(project(":project-a").name)
include('project-a')
println rootProject.name
println project(':project-a').name
使用此描述符,你可以更改项目的名称、项目目录和构建文件
rootProject.name = "main"
include("project-a")
project(":project-a").projectDir = file("custom/my-project-a")
project(":project-a").buildFileName = "project-a.gradle.kts"
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
的子项目。
因此,虽然物理项目布局相同,但逻辑结果不同。
命名建议
随着项目的增长,命名和一致性变得越来越重要。为了保持构建的可维护性,我们建议执行以下操作
-
为子项目保留默认项目名称:可以在设置文件中配置自定义项目名称。但是,对于开发人员来说,跟踪哪些项目属于哪些文件夹是一项不必要的额外工作。
-
对所有项目名称使用小写连字符:所有字母均为小写,单词之间用破折号 (
-
) 字符分隔。 -
在设置文件中定义根项目名称:
rootProject.name
有效地为构建分配一个名称,用于构建扫描等报告中。如果未设置根项目名称,则名称将为容器目录名称,该名称可能不稳定(即,您可以在任何目录中签出您的项目)。如果未设置根项目名称并签出到文件系统的根目录(例如,/
或C:\
),则名称将随机生成。