多项目构建中的子项目通常共享一些公共依赖项。

Gradle 提供了一个特殊的目录,用于存储可以在子项目中自动应用的共享构建逻辑,而无需在每个子项目构建脚本中复制和粘贴相同的 Java 版本和库。
在 buildSrc
中共享逻辑
buildSrc
是 Gradle 识别和保护的目录,它带来了一些好处
-
可重用的构建逻辑:
buildSrc
允许您以结构化的方式组织和集中自定义构建逻辑、任务和插件。在 buildSrc 中编写的代码可以在您的项目中重用,从而更容易维护和共享通用的构建功能。 -
与主构建隔离:
放置在
buildSrc
中的代码与项目的其他构建脚本隔离。这有助于保持主构建脚本的清洁,并更专注于特定于项目的配置。 -
自动编译和类路径:
buildSrc
目录的内容会自动编译并包含在主构建的类路径中。这意味着在 buildSrc 中定义的类和插件可以直接在项目的构建脚本中使用,而无需任何额外的配置。 -
易于测试:
由于
buildSrc
是一个单独的构建,因此可以轻松测试您的自定义构建逻辑。您可以为构建代码编写测试,确保其行为符合预期。 -
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)
。它包含多个子项目共有的依赖项和其他构建信息
repositories {
mavenCentral()
}
dependencies {
implementation("org.slf4j:slf4j-api:1.7.32")
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.slf4j:slf4j-api:1.7.32'
}
在 buildSrc
中,还创建了 MyCustomTask
。它是一个辅助任务,用作多个子项目的构建逻辑的一部分
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")
}
}
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
任务在 api
和 shared
项目的构建脚本中使用。该任务是自动可用的,因为它属于 buildSrc
。
shared.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")
// 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 约定插件。 |
此构建包含三个子项目
rootProject.name = "dependencies-java"
include("api", "shared", "services:person-service")
rootProject.name = 'dependencies-java'
include 'api', 'shared', 'services:person-service'
在 buildSrc
目录中创建的约定插件的源代码如下
plugins {
id("java")
}
group = "com.example"
version = "1.0"
repositories {
mavenCentral()
}
dependencies {
testImplementation("junit:junit:4.13")
}
plugins {
id 'java'
}
group = 'com.example'
version = '1.0'
repositories {
mavenCentral()
}
dependencies {
testImplementation "junit:junit:4.13"
}
为了编译约定插件,需要在 buildSrc
目录的构建文件中应用基本配置
plugins {
`kotlin-dsl`
}
repositories {
mavenCentral()
}
plugins {
id 'groovy-gradle-plugin'
}
约定插件应用于 api
、shared
和 person-service
子项目
plugins {
id("myproject.java-conventions")
}
dependencies {
implementation(project(":shared"))
}
plugins {
id("myproject.java-conventions")
}
plugins {
id("myproject.java-conventions")
}
dependencies {
implementation(project(":shared"))
implementation(project(":api"))
}
plugins {
id 'myproject.java-conventions'
}
dependencies {
implementation project(':shared')
}
plugins {
id 'myproject.java-conventions'
}
plugins {
id 'myproject.java-conventions'
}
dependencies {
implementation project(':shared')
implementation project(':api')
}
不要使用跨项目配置
在子项目之间共享构建逻辑的不正确方法是通过 subprojects {}
和 allprojects {}
DSL 构造的跨项目配置。
避免使用 subprojects {} 和 allprojects {} 。 |
通过跨项目配置,可以将构建逻辑注入到子项目中,这在其构建脚本中并不明显。
从长远来看,跨项目配置通常会变得复杂,并成为负担。跨项目配置还可能在项目之间引入配置时耦合,这可能会阻止按需配置等优化正常工作。
约定插件与跨项目配置对比
跨项目配置的两个最常见用途可以使用约定插件更好地建模
-
将插件或其他配置应用于特定类型的子项目。
通常,跨项目配置逻辑是如果子项目是 X 类型,则配置 Y
。这等效于将X-conventions
插件直接应用于子项目。 -
从特定类型的子项目中提取信息。
此用例可以使用传出配置变体建模。