构建块

每个 Gradle 构建都包含三个基本构建块:projects、tasks 和 properties。 每个构建至少包含一个 project,而该 project 又包含一个或多个 task。 project 和 task 公开可用于控制构建的 property。 图 4.1 说明了 Gradle 核心组件之间的依赖关系。

Gradle 应用领域驱动设计 (DDD) 的原理来建模自己的领域构建软件。 因此,项目和任务在 Gradle 的 API 中具有直接的类表示。 让我们仔细看看每个组件及其 API 对应部分。

Projects

在 Gradle 的术语中,项目代表您尝试构建的组件(例如 JAR 文件),或者您尝试实现的目标,例如部署应用程序。 如果您来自 Maven,这个概念听起来应该很熟悉。 Gradle 相当于 Maven 的 pom.xml 是 build.gradle 文件。 每个 Gradle 构建脚本至少定义一个项目。 当开始构建过程时,Gradle 根据您在 build.gradle 中的配置实例化 org.gradle.api.Project 类,并通过项目变量使其隐式可用。 图 4.2 显示了 API 接口及其最重要的方法。

image 2024 03 08 16 58 30 083
Figure 1. 图 4.1 Gradle 构建的两个基本概念是项目和任务。 在多项目构建的上下文中,一个项目可以依赖于其他项目。 类似地,任务可以形成保证其执行顺序的依赖图。
image 2024 03 08 16 59 49 848
Figure 2. 图 4.2 Gradle 构建的主要入口点——Project 接口

项目可以创建新任务、添加依赖项和配置以及应用插件和其他构建脚本。 它的许多属性,如 name 和 description,都可以通过 getter 和 setter 方法访问。

那么为什么我们一开始就谈论 Gradle 的 API 呢? 您会发现,在了解 Gradle 的基础知识后,您会想要进一步了解这些概念,并将这些概念应用到您的实际项目中。 API 是充分利用 Gradle 的关键。

Project 实例使您能够以编程方式访问构建中的所有 Gradle 功能,例如任务创建和依赖项管理。 在本书中,您将通过调用相应的 API 方法来使用其中的许多功能。 请记住,访问项目(Project 实例)的属性和方法时不需要使用 project 变量。 以下代码片段说明了 Project 实例上的有效方法调用:

setDescription("myProject") // 在不显式使用项目变量的情况下设置项目的描述
// 使用 Groovy 语法在使用和不使用 project 变量的情况下访问 name 和 description 属性
println "Description of project $name: " + project.description

在前面的章节中,您只需处理单个项目的构建。 Gradle 还提供对多项目构建的支持。 软件开发最重要的原则之一是关注点分离。 软件系统变得越复杂,您就越想将其分解为模块化功能,其中模块可以相互依赖。 每个分解的部分都将表示为具有自己的 build.gradle 脚本的 Gradle 项目。 为了简单起见,我们在这里不再赘述。 如果您渴望了解更多信息,请随时跳转到第 6 章,该章完全致力于在 Gradle 中创建多项目构建。 接下来,我们将了解任务的特征,这是 Gradle 的另一个核心构建块。

Tasks

您已经在第 2 章中创建了一些简单的任务。尽管我介绍的用例很琐碎,但您必须了解任务的一些重要功能:任务操作任务依赖性。 操作定义了任务运行时执行的原子工作单元。 这可以像打印 “Hello world!” 这样的文本一样简单。 或者像编译 Java 源代码一样复杂,如第 2 章所示。很多时候,一个任务需要先运行另一个任务。 如果任务依赖于另一个任务产生的输出作为输入来完成自己的操作,则尤其如此。 例如,您已经看到需要先编译 Java 源代码,然后才能将其打包到 JAR 文件中。 我们来看一下 Gradle 的任务 API 表示,接口 org.gradle.api.Task,如图 4.3 所示。

Task 接口提供的方法比图中显示的还要多。 当您将它们应用于整本书的具体示例时,您将一一使用它们。 现在我们已经讨论了项目和任务,让我们看看不同类型的属性。

image 2024 03 08 17 14 39 792
Figure 3. 图 4.3 Gradle API 中的任务接口。 任务可以定义对其他任务的依赖关系、一系列操作和条件执行。

Properties

Project 和 Task 的每个实例都提供可通过 getter 和 setter 方法访问的属性。 属性可以是任务的描述或项目的版本。 在本章后面,您将在实际示例中阅读和修改这些值。 通常,您需要定义自己的属性。 例如,您可能想要声明一个变量来引用在同一构建脚本中多次使用的文件。 Gradle 允许通过额外的属性定义用户定义的变量。

额外的属性

Gradle 的许多域模型类都提供对临时属性的支持。 在内部,这些属性作为键值对存储在 map 中。 要添加属性,您需要使用 ext 命名空间。 让我们看一个具体的例子。 以下代码片段演示了可以通过多种不同方式添加、读取和修改属性:

// 只有额外属性的初始声明需要使用 ext 命名空间
project.ext.myProp = 'myValue'

ext {
    someOtherProp = 123
}

// 使用 ext 命名空间来访问额外的属性是可选的
assert myProp == 'myValue'
println project.someOtherProp
ext.someOtherProp = 567

同样,可以通过属性文件提供其他属性。

GRADLE 属性

通过在 <USER_HOME>/.gradle 目录或项目根目录下名为 gradle.properties 的属性文件中声明属性,可以将属性直接注入到您的项目中。 可以通过项目实例访问它们。 请记住,即使您正在处理多个项目,每个用户在 <USER_HOME>/.gradle 下只能有一个 Gradle 属性文件。 这是目前 Gradle 的一个限制。 属性文件中声明的任何属性都可用于您的所有项目。 假设您的 gradle.properties 文件中声明了以下属性:

exampleProp = myValue
someOtherProp = 455

您可以按如下方式访问项目中的这两个变量:

assert project.exampleProp == 'myValue'

task printGradleProperty << {
    println "Second property: $someOtherProp"
}

声明属性的其他方式

额外属性和 Gradle 属性是您可能最常使用的机制来声明自定义变量及其值。 Gradle 提供了许多其他方法来为您的构建提供属性,例如:

  • 通过 -P 命令行选项的项目属性

  • 通过 -D 命令行选项的系统属性

  • 遵循如下模式的环境属性 ORG_GRADLE_PROJECT_propertyName=someValue

我不会向您展示这些声明属性的替代方法的具体示例,但您可以根据需要使用它们。 如果您想进一步了解,在线 Gradle 用户指南提供了出色的使用示例。 在本章的其余部分中,您将广泛使用任务和 Gradle 的构建生命周期。