组织 Gradle 项目
每个软件项目的源代码和构建逻辑都应该以有意义的方式组织。本页列出了可实现可读、可维护项目的最佳实践。以下各节还介绍了常见问题以及如何避免这些问题。
单独的特定于语言的源文件
Gradle 的语言插件建立了发现和编译源代码的约定。例如,应用Java插件的项目会自动编译目录中的代码src/main/java
。其他语言插件遵循相同的模式。目录路径的最后部分通常指示源文件的预期语言。
有些编译器能够在同一源目录中交叉编译多种语言。 Groovy 编译器可以处理混合位于src/main/groovy
. Gradle 建议您根据源语言将源代码放置在目录中,因为构建的性能更高,并且用户和构建都可以做出更强有力的假设。
以下源代码树包含 Java 和 Kotlin 源文件。 Java 源文件位于src/main/java
,而 Kotlin 源文件位于src/main/kotlin
.
.
├── build.gradle.kts
└── src
└── main
├── java
│ └── HelloWorld.java
└── kotlin
└── Utils.kt
.
├── build.gradle
└── src
└── main
├── java
│ └── HelloWorld.java
└── kotlin
└── Utils.kt
每个测试类型单独的源文件
项目定义和执行不同类型的测试是很常见的,例如单元测试、集成测试、功能测试或冒烟测试。最佳情况下,每种测试类型的测试源代码应存储在专用的源目录中。分离的测试源代码对可维护性和关注点分离具有积极影响,因为您可以彼此独立地运行测试类型。
查看演示 如何将单独的集成测试配置添加到基于 Java 的项目的示例。
始终定义一个设置文件
每次调用构建时,Gradle 都会尝试定位settings.gradle
(Groovy DSL) 或(Kotlin DSL) 文件。settings.gradle.kts
为此,运行时将目录树的层次结构向上移动到根目录。一旦找到设置文件,算法就会停止搜索。
始终将 a 添加settings.gradle
到构建的根目录以避免初始性能影响。该文件可以为空,也可以定义所需的项目名称。
多项目构建必须settings.gradle(.kts)
在多项目层次结构的根项目中有一个文件。这是必需的,因为设置文件定义了哪些项目正在参与多项目构建。除了定义包含的项目之外,您可能还需要它将库添加到构建脚本类路径中。
以下示例显示了标准 Gradle 项目布局:
.
├── settings.gradle.kts
├── subproject-one
│ └── build.gradle.kts
└── subproject-two
└── build.gradle.kts
.
├── settings.gradle
├── subproject-one
│ └── build.gradle
└── subproject-two
└── build.gradle
用于buildSrc
抽象命令式逻辑
复杂的构建逻辑通常适合封装为自定义任务或二进制插件。自定义任务和插件实现不应存在于构建脚本中。buildSrc
只要代码不需要在多个独立项目之间共享,就可以非常方便地用于此目的。
该目录buildSrc
被视为包含的构建。发现该目录后,Gradle 会自动编译和测试此代码,并将其放入构建脚本的类路径中。对于多项目构建,只能有一个buildSrc
目录,该目录必须位于根项目目录中。
buildSrc
应该优先于脚本插件,因为它更容易维护、重构和测试代码。
buildSrc
使用适用于 Java 和 Groovy 项目的相同源代码约定。它还提供对 Gradle API 的直接访问。额外的依赖项可以在专用build.gradle
的buildSrc
.
repositories {
mavenCentral()
}
dependencies {
testImplementation("junit:junit:4.13")
}
repositories {
mavenCentral()
}
dependencies {
testImplementation 'junit:junit:4.13'
}
一个典型的项目包括buildSrc
以下布局。下面的任何代码buildSrc
都应该使用与应用程序代码类似的包。或者,如果需要额外的配置(例如应用插件或声明依赖项),该buildSrc
目录可以托管构建脚本。
.
├── buildSrc
│ ├── build.gradle.kts
│ └── src
│ ├── main
│ │ └── java
│ │ └── com
│ │ └── enterprise
│ │ ├── Deploy.java
│ │ └── DeploymentPlugin.java
│ └── test
│ └── java
│ └── com
│ └── enterprise
│ └── DeploymentPluginTest.java
├── settings.gradle.kts
├── subproject-one
│ └── build.gradle.kts
└── subproject-two
└── build.gradle.kts
.
├── buildSrc
│ ├── build.gradle
│ └── src
│ ├── main
│ │ └── java
│ │ └── com
│ │ └── enterprise
│ │ ├── Deploy.java
│ │ └── DeploymentPlugin.java
│ └── test
│ └── java
│ └── com
│ └── enterprise
│ └── DeploymentPluginTest.java
├── settings.gradle
├── subproject-one
│ └── build.gradle
└── subproject-two
└── build.gradle
更改 因此,当进行小的增量更改时, |
gradle.properties
在文件中声明属性
gradle.properties
在 Gradle 中,属性可以在构建脚本、文件中或命令行上的参数中定义。
对于临时场景,在命令行上声明属性是很常见的。例如,您可能希望传入一个特定的属性值来控制构建的这一次调用的运行时行为。构建脚本中的属性很容易成为维护难题,并使构建脚本逻辑变得复杂。这gradle.properties
有助于将属性与构建脚本分开,应该将其作为可行的选项进行探索。这是放置控制构建环境的属性的好位置。
典型的项目设置将该gradle.properties
文件放置在构建的根目录中。或者,GRADLE_USER_HOME
如果您希望该文件应用于计算机上的所有版本,则该文件也可以位于该目录中。
.
├── gradle.properties
└── settings.gradle.kts
├── subproject-a
│ └── build.gradle.kts
└── subproject-b
└── build.gradle.kts
.
├── gradle.properties
└── settings.gradle
├── subproject-a
│ └── build.gradle
└── subproject-b
└── build.gradle
避免任务输出重叠
任务应该定义输入和输出以获得增量构建功能的性能优势。声明任务的输出时,请确保用于写入输出的目录在项目中的所有任务中是唯一的。
混合或覆盖不同任务生成的输出文件会损害最新检查,导致构建速度变慢。反过来,这些文件系统更改可能会阻止 Gradle 的构建缓存正确识别和缓存本来可缓存的任务。
使用自定义 Gradle 发行版标准化构建
企业通常希望通过定义通用约定或规则来标准化组织中所有项目的构建平台。您可以借助初始化脚本来实现这一点。 初始化脚本使得在单台机器上的所有项目中应用构建逻辑变得非常容易。例如,声明内部存储库及其凭据。
该方法有一些缺点。首先,您必须与公司的所有开发人员沟通设置过程。此外,统一更新初始化脚本逻辑可能具有挑战性。
自定义 Gradle 发行版是解决这个问题的实用方法。自定义 Gradle 发行版由标准 Gradle 发行版以及一个或多个自定义初始化脚本组成。初始化脚本与发行版捆绑在一起,并在每次运行构建时应用。开发人员只需将签入的Wrapper文件指向自定义 Gradle 发行版的 URL。
自定义 Gradle 发行版还可能gradle.properties
在发行版的根目录中包含一个文件,该文件提供了一组组织范围内的属性来控制构建环境。
以下步骤是创建自定义 Gradle 发行版的典型步骤:
-
实现下载和重新打包 Gradle 发行版的逻辑。
-
使用所需逻辑定义一个或多个初始化脚本。
-
将初始化脚本与 Gradle 发行版捆绑在一起。
-
将 Gradle 分发存档上传到 HTTP 服务器。
-
更改所有项目的 Wrapper 文件以指向自定义 Gradle 发行版的 URL。
plugins {
id 'base'
}
// This is defined in buildSrc
import org.gradle.distribution.DownloadGradle
version = '0.1'
tasks.register('downloadGradle', DownloadGradle) {
description = 'Downloads the Gradle distribution with a given version.'
gradleVersion = '4.6'
}
tasks.register('createCustomGradleDistribution', Zip) {
description = 'Builds custom Gradle distribution and bundles initialization scripts.'
dependsOn downloadGradle
def projectVersion = project.version
archiveFileName = downloadGradle.gradleVersion.map { gradleVersion ->
"mycompany-gradle-${gradleVersion}-${projectVersion}-bin.zip"
}
from zipTree(downloadGradle.destinationFile)
from('src/init.d') {
into "${downloadGradle.distributionNameBase.get()}/init.d"
}
}