文章摘要(AI生成)
构建生命周期前面我们说过,Gradle 的核心是一种基于依赖的编程语言。在 Gradle 术语中,这意味着您可以定义任务和任务之间的依赖关系。Gradle 保证这些任务按照它们的依赖顺序执行,并且每个任务只执行一次。这些任务形成了一个有向无环图。有一些构建工具可以在执行任务时构建这样的依赖关系图。G
构建生命周期
前面我们说过,Gradle 的核心是一种基于依赖的编程语言。在 Gradle 术语中,这意味着您可以定义任务和任务之间的依赖关系。Gradle 保证这些任务按照它们的依赖顺序执行,并且每个任务只执行一次。这些任务形成了一个有向无环图。有一些构建工具可以在执行任务时构建这样的依赖关系图。Gradle 在执行任何任务*之前构建完整的依赖关系图。*这是 Gradle 的核心,它使许多原本不可能的事情成为可能。
您的构建脚本配置此依赖关系图。因此,它们严格来说是构建配置脚本。
构建阶段
Gradle 构建具有三个不同的阶段。
-
初始化
Gradle 支持单项目和多项目构建。在初始化阶段,Gradle 确定哪些项目将参与构建,并为每个项目创建一个Project实例。
-
配置
在此阶段配置项目对象。执行作为构建一部分的所有项目的构建脚本。
-
执行
Gradle 确定在配置阶段创建和配置的要执行的任务子集。该子集由传递给
gradle
命令和当前目录的任务名称参数确定。Gradle 然后执行每个选定的任务。
设置文件
除了构建脚本文件,Gradle 还定义了一个设置文件。设置文件由 Gradle 通过命名约定确定。此文件的默认名称是settings.gradle
. 本章稍后我们将解释 Gradle 如何查找设置文件。
设置文件在初始化阶段执行。多项目构建必须settings.gradle
在多项目层次结构的根项目中有一个文件。这是必需的,因为设置文件定义了哪些项目参与了多项目构建(请参阅创作多项目构建)。对于单项目构建,设置文件是可选的。除了定义包含的项目外,您可能还需要将库添加到构建脚本类路径(请参阅组织 Gradle 项目)。让我们首先对单个项目构建进行一些自省:
示例 1. 单个项目构建
settings.gradle
rootProject.name = 'basic'
println 'This is executed during the initialization phase.'
build.gradle
println 'This is executed during the configuration phase.'
tasks.register('configured') {
println 'This is also executed during the configuration phase, because :configured is used in the build.'
}
tasks.register('test') {
doLast {
println 'This is executed during the execution phase.'
}
}
tasks.register('testBoth') {
doFirst {
println 'This is executed first during the execution phase.'
}
doLast {
println 'This is executed last during the execution phase.'
}
println 'This is executed during the configuration phase as well, because :testBoth is used in the build.'
}
**gradle test testBoth
**的输出
Groovy``Kotlin
> gradle test testBoth
This is executed during the initialization phase.
> Configure project :
This is executed during the configuration phase.
This is executed during the configuration phase as well, because :testBoth is used in the build.
> Task :test
This is executed during the execution phase.
> Task :testBoth
This is executed first during the execution phase.
This is executed last during the execution phase.
BUILD SUCCESSFUL in 0s
2 actionable tasks: 2 executed
对于构建脚本,属性访问和方法调用被委托给项目对象。类似地,设置文件中的属性访问和方法调用被委托给设置对象。查看 API 文档中的Settings类以获取更多信息。
初始化
Gradle 如何知道是进行单项目构建还是多项目构建?如果您从包含文件的目录触发多项目构建settings.gradle
,Gradle 将使用它来配置构建。Gradle 还允许您从参与构建的任何子项目中执行构建。[ 1 ]settings.gradle
如果您在没有文件 的项目中执行 Gradle ,Gradlesettings.gradle
会通过以下方式查找文件:
settings.gradle
它在父目录中查找。- 如果未找到,则构建将作为单个项目构建执行。
- 如果找到文件,Gradle 会检查当前项目是否是在找到的文件
settings.gradle
中定义的多项目层次结构的一部分。settings.gradle
如果不是,则构建将作为单个项目构建执行。否则将执行多项目构建。
这种行为的目的是什么?Gradle 需要判断你所在的项目是否是多项目构建的子项目。当然,如果是子项目,则只构建子项目及其依赖的项目,但 Gradle 需要为整个多项目构建创建构建配置(参见配置和执行)。如果当前项目包含settings.gradle
文件,则构建始终执行为:
- 单个项目构建,如果
settings.gradle
文件未定义多项目层次结构 - 如果
settings.gradle
文件确实定义了多项目层次结构,则为多项目构建。
文件的自动搜索settings.gradle
仅适用于具有默认项目布局的多项目构建,其中项目路径与磁盘上的物理子项目布局相匹配。Gradle 支持多项目构建的任意物理布局,但对于这样的任意布局,您需要从设置文件所在的目录执行构建。有关如何从根目录运行部分构建的信息,请参阅按完全限定名称执行任务。
Gradle 为参与构建的每个项目创建一个项目对象。对于多项目构建,这些是在设置对象中指定的项目(加上根项目)。默认情况下,每个项目对象的名称与其顶级目录的名称相同,并且除根项目之外的每个项目都有一个父项目。任何项目都可能有子项目。
单个项目构建的配置和执行
对于单个项目构建,初始化后阶段的工作流程非常简单。构建脚本针对在初始化阶段创建的项目对象执行。然后 Gradle 查找名称与作为命令行参数传递的名称相同的任务。如果这些任务名称存在,它们将按照您传递它们的顺序作为单独的构建执行。多项目构建的配置和执行在配置和执行中讨论。
响应构建脚本中的生命周期
随着构建在其生命周期中的进展,您的构建脚本可以接收通知。这些通知通常采用两种形式:您可以实现特定的侦听器接口,也可以提供一个闭包以在触发通知时执行。下面的示例使用闭包。监听接口的使用方法请参考 API 文档。
项目评估
您可以在项目评估前后立即收到通知。一旦应用了构建脚本中的所有定义,或者用于某些自定义日志记录或分析,这可用于执行其他配置等操作。
下面是一个示例,它test
为每个项目添加一个hasTests
属性值为 true 的任务。
示例 2. 将测试任务添加到具有特定属性集的每个项目
build.gradle
allprojects {
afterEvaluate { project ->
if (project.hasTests) {
println "Adding test task to $project"
project.task('test') {
doLast {
println "Running tests for $project"
}
}
}
}
}
project-a.gradle
hasTests = true
gradle -q test
的输出
> gradle -q test
Adding test task to project ':project-a'
Running tests for project ':project-a'
此示例使用方法Project.afterEvaluate()
添加在评估项目后执行的闭包。
也可以在评估任何项目时收到通知。此示例执行项目评估的一些自定义日志记录。请注意,afterProject
无论项目评估成功还是因异常而失败,都会收到通知。
示例 3. 通知
build.gradle
gradle.afterProject { project ->
if (project.state.failure) {
println "Evaluation of $project FAILED"
} else {
println "Evaluation of $project succeeded"
}
}
**gradle -q test
**的输出
Groovy``Kotlin
> gradle -q test
Evaluation of root project 'build-project-evaluate-events' succeeded
Evaluation of project ':project-a' succeeded
Evaluation of project ':project-b' FAILED
FAILURE: Build failed with an exception.
* Where:
Build file '/home/user/gradle/samples/project-b.gradle' line: 1
* What went wrong:
A problem occurred evaluating project ':project-b'.
> broken
* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.
* Get more help at https://help.gradle.org
BUILD FAILED in 0s
您还可以将ProjectEvaluationListener添加到Gradle以接收这些事件。
任务创建
将任务添加到项目后,您可以立即收到通知。这可用于在构建文件中提供任务之前设置一些默认值或添加行为。
以下示例在srcDir
创建每个任务时设置它的属性。
示例 4. 为所有任务设置某些属性
build.gradle
tasks.whenTaskAdded { task ->
task.ext.srcDir = 'src/main/java'
}
tasks.register('a')
println "source dir is $a.srcDir"
**gradle -q a
**的输出
> gradle -q a
source dir is src/main/java
您还可以将Action添加到TaskContainer以接收这些事件。
任务执行图准备就绪
您可以在填充任务执行图后立即收到通知。
您还可以将TaskExecutionGraphListener添加到TaskExecutionGraph以接收这些事件。
任务执行
您可以在执行任何任务之前和之后立即收到通知。
以下示例记录每个任务执行的开始和结束。请注意,afterTask
无论任务是成功完成还是因异常而失败,都会收到通知。
示例 5. 记录每个任务执行的开始和结束
build.gradle
tasks.register('ok')
tasks.register('broken') {
dependsOn ok
doLast {
throw new RuntimeException('broken')
}
}
gradle.taskGraph.beforeTask { Task task ->
println "executing $task ..."
}
gradle.taskGraph.afterTask { Task task, TaskState state ->
if (state.failure) {
println "FAILED"
}
else {
println "done"
}
}
**gradle -q broken
**的输出
Groovy``Kotlin
> gradle -q broken
executing task ':ok' ...
done
executing task ':broken' ...
FAILED
FAILURE: Build failed with an exception.
* Where:
Build file '/home/user/gradle/samples/build.gradle' line: 6
* What went wrong:
Execution failed for task ':broken'.
> broken
* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.
* Get more help at https://help.gradle.org
BUILD FAILED in 0s
您还可以使用TaskExecutionListener到TaskExecutionGraph来接收这些事件。
Gradle 使用的目录和文件
Gradle 使用两个主要目录来执行和管理其工作:Gradle 用户主目录和项目根目录。以下两节描述了它们各自存储的内容以及如何清理临时文件和目录。
Gradle 用户主目录
Gradle 用户主目录($USER_HOME/.gradle
默认)用于存储全局配置属性和初始化脚本以及缓存和日志文件。大致结构如下:
├── caches
│ ├── 4.8
│ ├── 4.9
│ ├── ⋮
│ ├── jars-3
│ └── modules-2
├── daemon
│ ├── ⋮
│ ├── 4.8
│ └── 4.9
├── init.d
│ └── my-setup.gradle
├── jdks
│ ├── ⋮
│ └── jdk-14.0.2+12
├── wrapper
│ └── dists
│ ├── ⋮
│ ├── gradle-4.8-bin
│ ├── gradle-4.9-all
│ └── gradle-4.9-bin
└── gradle.properties
- 全局缓存目录(适用于非项目特定的所有内容)
- 特定于版本的缓存(例如,支持增量构建)
- 共享缓存(例如,用于依赖项的工件)
- Gradle 守护进程的注册表和日志
- 全局初始化脚本
- 工具链支持下载的JDK
- Gradle Wrapper下载的发行版
- 全局Gradle 配置属性
清理缓存和分布
从 4.10 版本开始,Gradle 会自动清理其用户主目录。当 Gradle 守护程序停止或关闭时,清理会在后台运行。如果使用--no-daemon
,它会在构建会话之后使用可视进度指示器在前台运行。
定期(最多每 24 小时)应用以下清理策略:
- 检查特定版本的缓存
caches/<gradle-version>/
是否仍在使用中。如果没有,则在 30 天不活动后删除发布版本的目录,在 7 天不活动后删除快照版本。 caches/
检查(例如)中的共享缓存jars-*
是否仍在使用中。如果没有仍在使用它们的 Gradle 版本,它们将被删除。- 当前 Gradle 版本在
caches/
(例如jars-3
或modules-2
)中使用的共享缓存中的文件在上次访问时检查。取决于文件是否可以在本地重新创建或必须再次从远程存储库下载,它将在之后被删除分别为 7 天或 30 天不被访问。 - 检查其中的Gradle 发行版
wrapper/dists/
是否仍在使用中,即是否存在相应的特定于版本的缓存目录。未使用的发行版将被删除。
项目根目录
项目根目录包含作为项目一部分的所有源文件。此外,它还包含由 Gradle 生成的文件和目录,例如.gradle
和build
. 虽然前者通常被签入到源代码控制中,但后者是 Gradle 用来支持增量构建等功能的临时文件。总的来说,一个典型的项目根目录的结构大致如下:
├── .gradle
│ ├── 4.8
│ ├── 4.9
│ └── ⋮
├── build
├── gradle
│ └── wrapper
├── gradle.properties
├── gradlew
├── gradlew.bat
├── settings.gradle or settings.gradle.kts
├── subproject-one
| └── build.gradle or build.gradle.kts
├── subproject-two
| └── build.gradle or build.gradle.kts
└── ⋮
- Gradle 生成的项目特定缓存目录
- 特定于版本的缓存(例如,支持增量构建)
- 此项目的构建目录,Gradle 在其中生成所有构建工件。
- 包含Gradle Wrapper的 JAR 文件和配置
- 项目特定的Gradle 配置属性
- 使用Gradle Wrapper执行构建的脚本
- 定义子项目列表的项目
- 通常一个项目被组织成一个或多个子项目
- 每个子项目都有自己的 Gradle 构建脚本
项目缓存清理
从 4.10 版本开始,Gradle 会自动清理项目特定的缓存目录。构建项目后,会定期(最多每 24 小时)检查特定版本的缓存目录.gradle/<gradle-version>/
是否仍在使用中。如果 7 天未使用它们,它们将被删除。
构建脚本基础
内容
本章向您介绍编写 Gradle 构建脚本的基础知识。它使用玩具示例来解释 Gradle 的基本功能,有助于理解基本概念。特别是如果您从 Ant 等其他构建工具迁移到 Gradle 并希望了解差异和优势。
但是,要开始使用标准项目设置,您甚至不需要详细了解这些概念。相反,您可以通过我们的分步示例快速上手介绍。
项目、插件和任务
每个 Gradle 构建都由一个或多个项目组成。一个项目代表什么取决于你用 Gradle 做什么。例如,一个项目可能代表一个库 JAR 或一个 Web 应用程序。它可能代表由其他项目生成的 JAR 组装而成的分发 ZIP。一个项目不一定代表要建造的东西。它可能代表要完成的事情,例如将应用程序部署到登台或生产环境。如果这看起来有点模糊,请不要担心。Gradle 的 build-by-convention 支持为项目是什么添加了更具体的定义。
Gradle 可以在项目上完成的工作由一个或多个任务定义。任务代表构建执行的一些原子工作。这可能是编译一些类、创建 JAR、生成 Javadoc 或将一些档案发布到存储库。
通常,任务是通过应用插件来提供的,因此您不必自己定义它们。尽管如此,为了让您了解什么是任务,我们将在本章中使用一个项目在构建中定义一些简单的任务。
你好世界
gradle`您使用该命令运行 Gradle 构建。该命令查找在当前目录中`gradle`调用的文件。[ [1](https://docs.gradle.org/current/userguide/tutorial_using_tasks.html#_footnotedef_1) ]我们称这个文件为*构建脚本*,虽然严格来说它是一个构建配置脚本,我们将在后面看到。构建脚本定义了一个项目及其任务。`build.gradle``build.gradle
要尝试这一点,请创建以下名为build.gradle
.
示例 1. 您的第一个构建脚本
build.gradle
tasks.register('hello') {
doLast {
println 'Hello world!'
}
}
在命令行 shell 中,移动到包含目录并使用以下命令执行构建脚本gradle -q hello
:
做什么
-q
?本用户指南中的大多数示例都使用-q
命令行选项运行。这会抑制 Gradle 的日志消息,因此只显示任务的输出。这使本用户指南中的示例输出更加清晰。如果您不想,则不需要使用此选项。有关影响 Gradle 输出的命令行选项的更多详细信息,请参阅日志记录。
示例 2. 执行构建脚本
的输出**gradle -q hello
**
> gradle -q hello
Hello world!
这里发生了什么?此构建脚本定义了一个名为 的任务,hello
并向其添加了一个操作。当您运行时gradle hello
,Gradle 会执行hello
任务,而任务又会执行您提供的操作。该动作只是一个包含一些要执行的代码的块。
如果您认为这看起来与 Ant 的目标相似,那么您是对的。Gradle 任务相当于 Ant 目标,但正如您将看到的,它们更强大。我们使用了与 Ant 不同的术语,因为我们认为任务这个词比**目标这个词更具表现力。不幸的是,这引入了与 Ant 的术语冲突,因为 Ant 将其命令称为任务,例如javac
or copy
。所以当我们谈论任务时,我们总是指 Gradle 任务,它相当于 Ant 的目标。如果我们谈论 Ant 任务(Ant 命令),我们明确地说Ant task。
构建脚本是代码
Gradle 的构建脚本为您提供了 Groovy 和 Kotlin 的全部功能。作为开胃菜,看看这个:
示例 3. 在 Gradle 的任务中使用 Groovy 或 Kotlin
build.gradle
tasks.register('upper') {
doLast {
String someString = 'mY_nAmE'
println "Original: $someString"
println "Upper case: ${someString.toUpperCase()}"
}
}
的输出**gradle -q upper
**
> gradle -q upper
Original: mY_nAmE
Upper case: MY_NAME
或者
示例 4. 在 Gradle 的任务中使用 Groovy 或 Kotlin
Groovy``Kotlin
构建.gradle
tasks.register('count') {
doLast {
4.times { print "$it " }
}
}
**gradle -q count
**的输出
> gradle -q count
0 1 2 3
任务依赖
您可能已经猜到了,您可以声明依赖于其他任务的任务。
示例 5. 依赖于其他任务的任务声明
build.gradle
tasks.register('hello') {
doLast {
println 'Hello world!'
}
}
tasks.register('intro') {
dependsOn tasks.hello
doLast {
println "I'm Gradle"
}
}
的输出**gradle -q intro
**
> gradle -q intro
Hello world!
I'm Gradle
要添加依赖项,相应的任务不需要存在。
示例 6. Lazy dependsOn - 其他任务不存在
build.gradle
tasks.register('taskX') {
dependsOn 'taskY'
doLast {
println 'taskX'
}
}
tasks.register('taskY') {
doLast {
println 'taskY'
}
}
的输出**gradle -q taskX
**
> gradle -q taskX
taskY
taskX
taskX
to的依赖关系taskY
可以在taskY
定义之前声明。将依赖项添加到任务中更详细地讨论了任务依赖项。
灵活的任务注册
Groovy 或 Kotlin 的强大功能不仅仅可以用于定义任务的用途。例如,您可以使用它在一个循环中注册多个相同类型的任务。
示例 7. 任务的灵活注册
build.gradle
4.times { counter ->
tasks.register("task$counter") {
doLast {
println "I'm task number $counter"
}
}
}
**gradle -q task1
**的输出
> gradle -q task1
I'm task number 1
操作现有任务
一旦注册了任务,就可以通过API访问它们。例如,您可以使用它在运行时动态地将依赖项添加到任务。Ant 不允许这样的事情。
示例 8. 通过 API 访问任务 - 添加依赖项
build.gradle
4.times { counter ->
tasks.register("task$counter") {
doLast {
println "I'm task number $counter"
}
}
}
tasks.named('task0') { dependsOn('task2', 'task3') }
**gradle -q task0
**的输出
> gradle -q task0
I'm task number 2
I'm task number 3
I'm task number 0
或者,您可以将行为添加到现有任务。
示例 9. 通过 API 访问任务 - 添加行为
build.gradle
tasks.register('hello') {
doLast {
println 'Hello Earth'
}
}
tasks.named('hello') {
doFirst {
println 'Hello Venus'
}
}
tasks.named('hello') {
doLast {
println 'Hello Mars'
}
}
tasks.named('hello') {
doLast {
println 'Hello Jupiter'
}
}
的输出**gradle -q hello
**
> gradle -q hello
Hello Venus
Hello Earth
Hello Mars
Hello Jupiter
调用doFirst
和doLast
可以多次执行。他们将一个动作添加到任务的动作列表的开头或结尾。任务执行时,动作列表中的动作按顺序执行。
使用 Ant 任务
Ant 任务是 Gradle 中的一等公民。Gradle 仅依靠 Groovy 就为 Ant 任务提供了出色的集成。Groovy 附带了很棒的AntBuilder
. 使用 Gradle 中的 Ant 任务与使用build.xml
文件中的 Ant 任务一样方便且功能更强大。它也可以从 Kotlin 中使用。从下面的示例中,您可以了解如何执行 Ant 任务以及如何访问 Ant 属性:
示例 10. 使用 AntBuilder 执行 ant.loadfile 目标
build.gradle
tasks.register('loadfile') {
doLast {
def files = file('./antLoadfileResources').listFiles().sort()
files.each { File file ->
if (file.isFile()) {
ant.loadfile(srcFile: file, property: file.name)
println " *** $file.name ***"
println "${ant.properties[file.name]}"
}
}
}
}
**gradle -q loadfile
**的输出
> gradle -q loadfile
*** agile.manifesto.txt ***
Individuals and interactions over processes and tools
Working software over comprehensive documentation
Customer collaboration over contract negotiation
Responding to change over following a plan
*** gradle.manifesto.txt ***
Make the impossible possible, make the possible easy and make the easy elegant.
(inspired by Moshe Feldenkrais)
您可以在构建脚本中使用 Ant 执行更多操作。您可以在Ant中找到更多信息。
使用方法
Gradle 可扩展您如何组织构建逻辑。对于上面的示例,组织构建逻辑的第一级是提取方法。
示例 11. 使用方法来组织构建逻辑
build.gradle
tasks.register('checksum') {
doLast {
fileList('./antLoadfileResources').each { File file ->
ant.checksum(file: file, property: "cs_$file.name")
println "$file.name Checksum: ${ant.properties["cs_$file.name"]}"
}
}
}
tasks.register('loadfile') {
doLast {
fileList('./antLoadfileResources').each { File file ->
ant.loadfile(srcFile: file, property: file.name)
println "I'm fond of $file.name"
}
}
}
File[] fileList(String dir) {
file(dir).listFiles({file -> file.isFile() } as FileFilter).sort()
}
**gradle -q loadfile
**的输出
> gradle -q loadfile
I'm fond of agile.manifesto.txt
I'm fond of gradle.manifesto.txt
稍后您将看到这些方法可以在多项目构建中的子项目之间共享。如果您的构建逻辑变得更加复杂,Gradle 为您提供了其他非常方便的方式来组织它。我们为此专门写了一章。请参阅组织 Gradle 项目。
默认任务
Gradle 允许您定义一个或多个在未指定其他任务时执行的默认任务。
示例 12. 定义默认任务
build.gradle
defaultTasks 'clean', 'run'
tasks.register('clean') {
doLast {
println 'Default Cleaning!'
}
}
tasks.register('run') {
doLast {
println 'Default Running!'
}
}
tasks.register('other') {
doLast {
println "I'm not a default task!"
}
}
的输出**gradle -q
**
> gradle -q
Default Cleaning!
Default Running!
这相当于运行gradle clean run
. 在多项目构建中,每个子项目都可以有自己特定的默认任务。如果子项目未指定默认任务,则使用父项目的默认任务(如果已定义)。
构建脚本的外部依赖项
建议使用自带类路径的插件,而不是直接操作脚本类路径。对于自定义构建逻辑,建议使用自定义插件。
如果您的构建脚本需要使用外部库,您可以将它们添加到构建脚本本身的脚本类路径中。您可以使用该buildscript()
方法执行此操作,传入一个声明构建脚本类路径的块。
示例 13. 为构建脚本声明外部依赖项
build.gradle
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath group: 'commons-codec', name: 'commons-codec', version: '1.2'
}
}
传递给该buildscript()
方法的块配置一个ScriptHandler实例。您可以通过将依赖项添加到classpath
配置来声明构建脚本类路径。这与声明 Java 编译类路径的方式相同。您可以使用除项目依赖项之外的任何依赖项类型。
声明了构建脚本类路径后,您可以像使用类路径上的任何其他类一样使用构建脚本中的类。以下示例添加到前面的示例,并使用构建脚本类路径中的类。
示例 14. 具有外部依赖项的构建脚本
build.gradle
import org.apache.commons.codec.binary.Base64
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath group: 'commons-codec', name: 'commons-codec', version: '1.2'
}
}
tasks.register('encode') {
doLast {
def byte[] encodedString = new Base64().encode('hello world\n'.getBytes())
println new String(encodedString)
}
}
**gradle -q encode
**的输出
> gradle -q encode
aGVsbG8gd29ybGQK
对于多项目构建,使用项目buildscript()
方法声明的依赖项可用于其所有子项目的构建脚本。
构建脚本依赖可能是 Gradle 插件。有关 Gradle 插件的更多信息,请参阅使用 Gradle插件。
每个项目都会自动有一个BuildEnvironmentReportTaskbuildEnvironment
类型的任务,可以调用它来报告构建脚本依赖项的解决情况。
编写构建脚本
本章着眼于编写构建脚本的一些细节。
Gradle 构建语言
Gradle 提供了一种领域特定的语言或 DSL,用于描述构建。这种构建语言在 Groovy 和 Kotlin 中可用。
Groovy 构建脚本可以包含任何 Groovy 语言元素。[ 1 ] Kotlin 构建脚本可以包含任何 Kotlin 语言元素。Gradle 假设每个构建脚本都使用 UTF-8 编码。
项目 API
构建脚本通过配置项目来描述您的构建。项目是一个抽象概念,但您通常将 Gradle 项目映射到需要构建的软件组件,例如库或应用程序。您拥有的每个构建脚本都与一个Project类型的对象相关联,并且在构建脚本执行时,它会配置这个Project
.
事实上,构建脚本中的几乎所有顶级属性和块都是Project
API 的一部分。为了演示,看一下这个打印项目名称的示例构建脚本,可以通过Project.name属性访问:
示例 1. 访问 Project 对象的属性
build.gradle
println name
println project.name
**gradle -q check
**的输出
> gradle -q check
project-api
project-api
两个println
语句都打印出相同的属性。name
第一个使用对对象属性的顶级引用Project
。另一条语句使用project
可用于任何构建脚本的属性,该属性返回关联的Project
对象。仅当您定义与对象成员同名的属性或方法时Project
,您才需要使用该project
属性。
标准项目属性
该Project
对象提供了一些标准属性,这些属性在您的构建脚本中可用。下表列出了一些常用的。
名称 | 类型 | 默认值 |
---|---|---|
project |
项目 | Project 实例_ |
name |
String |
项目目录的名称。 |
path |
String |
项目的绝对路径。 |
description |
String |
项目的描述。 |
projectDir |
File |
包含构建脚本的目录。 |
buildDir |
File |
*projectDir*/build |
group |
Object |
unspecified |
version |
Object |
unspecified |
ant |
AntBuilder | 一个AntBuilder 实例 |
带有其他目标的脚本此处描述的构建脚本
Project
以对象为目标。还有分别针对Settings和Gradle对象的设置脚本和初始化脚本。
脚本 API
当 Gradle 执行 Groovy 构建脚本 ( .gradle
) 时,它会将脚本编译为实现Script的类。这意味着Script
接口声明的所有属性和方法在您的脚本中都可用。
当 Gradle 执行 Kotlin 构建脚本 ( .gradle.kts
) 时,它会将脚本编译为KotlinBuildScript的子类。这意味着该KotlinBuildScript
类型声明的所有可见属性和函数在您的脚本中都可用。另请参见分别用于设置脚本和初始化脚本的KotlinSettingsScript和KotlinInitScript类型。
声明变量
在构建脚本中可以声明两种变量:局部变量和额外属性。
局部变量
局部变量用def
关键字声明。它们仅在声明它们的范围内可见。局部变量是底层 Groovy 语言的一个特性。
示例 2. 使用局部变量
build.gradle
def dest = 'dest'
tasks.register('copy', Copy) {
from 'source'
into dest
}
额外属性
Gradle 域模型中的所有增强对象都可以包含额外的用户定义属性。这包括但不限于项目、任务和源集。
可以通过拥有对象的属性添加、读取和设置额外的ext
属性。或者,一个ext
块可用于一次添加多个属性。
示例 3. 使用额外的属性
build.gradle
plugins {
id 'java-library'
}
ext {
springVersion = "3.1.0.RELEASE"
emailNotification = "build@master.org"
}
sourceSets.all { ext.purpose = null }
sourceSets {
main {
purpose = "production"
}
test {
purpose = "test"
}
plugin {
purpose = "production"
}
}
tasks.register('printProperties') {
doLast {
println springVersion
println emailNotification
sourceSets.matching { it.purpose == "production" }.each { println it.name }
}
}
**gradle -q printProperties
**的输出
> gradle -q printProperties
3.1.0.RELEASE
build@master.org
main
plugin
在此示例中,ext
块向对象添加了两个额外的属性project
。purpose
此外,通过设置ext.purpose
为null
(null
是允许的值)将一个名为的属性添加到每个源集。一旦添加了属性,就可以像预定义的属性一样读取和设置它们。
通过要求添加属性的特殊语法,当尝试设置(预定义或额外)属性但该属性拼写错误或不存在时,Gradle 可能会快速失败。可以从可以访问其拥有对象的任何地方访问额外的属性,从而为它们提供比局部变量更广泛的范围。项目的额外属性可以从其子项目中看到。
有关额外属性及其 API 的更多详细信息,请参阅 API 文档中的ExtraPropertiesExtension类。
配置任意对象
您可以通过以下非常易读的方式配置任意对象。
示例 4. 配置任意对象
build.gradle
import java.text.FieldPosition
tasks.register('configure') {
doLast {
def pos = configure(new FieldPosition(10)) {
beginIndex = 1
endIndex = 5
}
println pos.beginIndex
println pos.endIndex
}
}
的输出**gradle -q configure
**
> gradle -q configure
1
5
使用外部脚本配置任意对象
您还可以使用外部脚本配置任意对象。
仅受 Groovy 脚本支持Kotlin DSL 尚不支持使用外部脚本配置任意对象。有关更多信息,请参阅gradle/kotlin-dsl#659。
示例 5. 使用脚本配置任意对象
build.gradle
tasks.register('configure') {
doLast {
def pos = new java.text.FieldPosition(10)
// Apply the script
apply from: 'other.gradle', to: pos
println pos.beginIndex
println pos.endIndex
}
}
other.gradle
// Set properties.
beginIndex = 1
endIndex = 5
gradle -q configure
的输出
> gradle -q configure
1
5
一些 Groovy 基础知识
寻找一些 Kotlin 基础知识,Kotlin 参考文档和Kotlin Koans应该对您有用。
Groovy 语言提供了大量用于创建 DSL的特性,而 Gradle 构建语言利用了这些特性。了解构建语言的工作原理将在您编写构建脚本时帮助您,尤其是在您开始编写自定义插件和任务时。
Groovy JDK
Groovy 为标准 Java 类添加了许多有用的方法。例如,Iterable
获取一个each
方法,该方法迭代以下元素Iterable
:
示例 6. Groovy JDK 方法
build.gradle
// Iterable gets an each() method
configurations.runtimeClasspath.each { File f -> println f }
查看https://groovy-lang.org/gdk.html了解更多详情。
属性访问器
Groovy 自动将属性引用转换为对适当 getter 或 setter 方法的调用。
示例 7. 属性访问器
build.gradle
// Using a getter method
println project.buildDir
println getProject().getBuildDir()
// Using a setter method
project.buildDir = 'target'
getProject().setBuildDir('target')
方法调用上的可选括号
括号对于方法调用是可选的。
示例 8. 不带括号的方法调用
build.gradle
test.systemProperty 'some.prop', 'value'
test.systemProperty('some.prop', 'value')
列表和映射文字
Groovy 提供了一些用于定义List
和Map
实例的快捷方式。这两种文字都很简单,但地图文字有一些有趣的转折。
例如,“ apply
”方法(通常应用插件的地方)实际上采用了一个 map 参数。然而,当你有一个像“ apply plugin:'java'
”这样的行时,你实际上并没有使用映射文字,你实际上使用的是“命名参数”,它的语法几乎与映射文字完全相同(没有包装括号)。调用该方法时,该命名参数列表会转换为映射,但它不会以映射开始。
示例 9. 列表和映射文字
build.gradle
// List literal
test.includes = ['org/gradle/api/**', 'org/gradle/internal/**']
List<String> list = new ArrayList<String>()
list.add('org/gradle/api/**')
list.add('org/gradle/internal/**')
test.includes = list
// Map literal.
Map<String, String> map = [key1:'value1', key2: 'value2']
// Groovy will coerce named arguments
// into a single map argument
apply plugin: 'java'
闭包作为方法中的最后一个参数
Gradle DSL 在很多地方都使用了闭包。你可以在这里找到更多关于闭包的信息。当方法的最后一个参数是闭包时,可以将闭包放在方法调用之后:
示例 10. 闭包作为方法参数
build.gradle
repositories {
println "in a closure"
}
repositories() { println "in a closure" }
repositories({ println "in a closure" })
闭包委托
每个闭包都有一个delegate
对象,Groovy 使用该对象查找不是闭包的局部变量或参数的变量和方法引用。Gradle 将此用于配置闭包,其中delegate
对象设置为要配置的对象。
示例 11. 闭包委托
build.gradle
dependencies {
assert delegate == project.dependencies
testImplementation('junit:junit:4.13')
delegate.testImplementation('junit:junit:4.13')
}
默认导入
为了使构建脚本更加简洁,Gradle 会自动将一组导入语句添加到 Gradle 脚本中。这意味着throw new org.gradle.api.tasks.StopExecutionException()
您可以直接键入throw new StopExecutionException()
而不是使用。
下面列出了添加到每个脚本的导入:
Gradle 默认导入
import org.gradle.*
import org.gradle.api.*
import org.gradle.api.artifacts.*
import org.gradle.api.artifacts.component.*
import org.gradle.api.artifacts.dsl.*
import org.gradle.api.artifacts.ivy.*
import org.gradle.api.artifacts.maven.*
import org.gradle.api.artifacts.query.*
import org.gradle.api.artifacts.repositories.*
import org.gradle.api.artifacts.result.*
import org.gradle.api.artifacts.transform.*
import org.gradle.api.artifacts.type.*
import org.gradle.api.artifacts.verification.*
import org.gradle.api.attributes.*
import org.gradle.api.attributes.java.*
import org.gradle.api.attributes.plugin.*
import org.gradle.api.capabilities.*
import org.gradle.api.component.*
import org.gradle.api.credentials.*
import org.gradle.api.distribution.*
import org.gradle.api.distribution.plugins.*
import org.gradle.api.execution.*
import org.gradle.api.file.*
import org.gradle.api.initialization.*
import org.gradle.api.initialization.definition.*
import org.gradle.api.initialization.dsl.*
import org.gradle.api.initialization.resolve.*
import org.gradle.api.invocation.*
import org.gradle.api.java.archives.*
import org.gradle.api.jvm.*
import org.gradle.api.launcher.cli.*
import org.gradle.api.logging.*
import org.gradle.api.logging.configuration.*
import org.gradle.api.model.*
import org.gradle.api.plugins.*
import org.gradle.api.plugins.antlr.*
import org.gradle.api.plugins.catalog.*
import org.gradle.api.plugins.jvm.*
import org.gradle.api.plugins.quality.*
import org.gradle.api.plugins.scala.*
import org.gradle.api.provider.*
import org.gradle.api.publish.*
import org.gradle.api.publish.ivy.*
import org.gradle.api.publish.ivy.plugins.*
import org.gradle.api.publish.ivy.tasks.*
import org.gradle.api.publish.maven.*
import org.gradle.api.publish.maven.plugins.*
import org.gradle.api.publish.maven.tasks.*
import org.gradle.api.publish.plugins.*
import org.gradle.api.publish.tasks.*
import org.gradle.api.reflect.*
import org.gradle.api.reporting.*
import org.gradle.api.reporting.components.*
import org.gradle.api.reporting.dependencies.*
import org.gradle.api.reporting.dependents.*
import org.gradle.api.reporting.model.*
import org.gradle.api.reporting.plugins.*
import org.gradle.api.resources.*
import org.gradle.api.services.*
import org.gradle.api.specs.*
import org.gradle.api.tasks.*
import org.gradle.api.tasks.ant.*
import org.gradle.api.tasks.application.*
import org.gradle.api.tasks.bundling.*
import org.gradle.api.tasks.compile.*
import org.gradle.api.tasks.diagnostics.*
import org.gradle.api.tasks.diagnostics.configurations.*
import org.gradle.api.tasks.incremental.*
import org.gradle.api.tasks.javadoc.*
import org.gradle.api.tasks.options.*
import org.gradle.api.tasks.scala.*
import org.gradle.api.tasks.testing.*
import org.gradle.api.tasks.testing.junit.*
import org.gradle.api.tasks.testing.junitplatform.*
import org.gradle.api.tasks.testing.testng.*
import org.gradle.api.tasks.util.*
import org.gradle.api.tasks.wrapper.*
import org.gradle.authentication.*
import org.gradle.authentication.aws.*
import org.gradle.authentication.http.*
import org.gradle.build.event.*
import org.gradle.buildinit.*
import org.gradle.buildinit.plugins.*
import org.gradle.buildinit.tasks.*
import org.gradle.caching.*
import org.gradle.caching.configuration.*
import org.gradle.caching.http.*
import org.gradle.caching.local.*
import org.gradle.concurrent.*
import org.gradle.external.javadoc.*
import org.gradle.ide.visualstudio.*
import org.gradle.ide.visualstudio.plugins.*
import org.gradle.ide.visualstudio.tasks.*
import org.gradle.ide.xcode.*
import org.gradle.ide.xcode.plugins.*
import org.gradle.ide.xcode.tasks.*
import org.gradle.ivy.*
import org.gradle.jvm.*
import org.gradle.jvm.application.scripts.*
import org.gradle.jvm.application.tasks.*
import org.gradle.jvm.tasks.*
import org.gradle.jvm.toolchain.*
import org.gradle.language.*
import org.gradle.language.assembler.*
import org.gradle.language.assembler.plugins.*
import org.gradle.language.assembler.tasks.*
import org.gradle.language.base.*
import org.gradle.language.base.artifact.*
import org.gradle.language.base.compile.*
import org.gradle.language.base.plugins.*
import org.gradle.language.base.sources.*
import org.gradle.language.c.*
import org.gradle.language.c.plugins.*
import org.gradle.language.c.tasks.*
import org.gradle.language.cpp.*
import org.gradle.language.cpp.plugins.*
import org.gradle.language.cpp.tasks.*
import org.gradle.language.java.artifact.*
import org.gradle.language.jvm.tasks.*
import org.gradle.language.nativeplatform.*
import org.gradle.language.nativeplatform.tasks.*
import org.gradle.language.objectivec.*
import org.gradle.language.objectivec.plugins.*
import org.gradle.language.objectivec.tasks.*
import org.gradle.language.objectivecpp.*
import org.gradle.language.objectivecpp.plugins.*
import org.gradle.language.objectivecpp.tasks.*
import org.gradle.language.plugins.*
import org.gradle.language.rc.*
import org.gradle.language.rc.plugins.*
import org.gradle.language.rc.tasks.*
import org.gradle.language.scala.tasks.*
import org.gradle.language.swift.*
import org.gradle.language.swift.plugins.*
import org.gradle.language.swift.tasks.*
import org.gradle.maven.*
import org.gradle.model.*
import org.gradle.nativeplatform.*
import org.gradle.nativeplatform.platform.*
import org.gradle.nativeplatform.plugins.*
import org.gradle.nativeplatform.tasks.*
import org.gradle.nativeplatform.test.*
import org.gradle.nativeplatform.test.cpp.*
import org.gradle.nativeplatform.test.cpp.plugins.*
import org.gradle.nativeplatform.test.cunit.*
import org.gradle.nativeplatform.test.cunit.plugins.*
import org.gradle.nativeplatform.test.cunit.tasks.*
import org.gradle.nativeplatform.test.googletest.*
import org.gradle.nativeplatform.test.googletest.plugins.*
import org.gradle.nativeplatform.test.plugins.*
import org.gradle.nativeplatform.test.tasks.*
import org.gradle.nativeplatform.test.xctest.*
import org.gradle.nativeplatform.test.xctest.plugins.*
import org.gradle.nativeplatform.test.xctest.tasks.*
import org.gradle.nativeplatform.toolchain.*
import org.gradle.nativeplatform.toolchain.plugins.*
import org.gradle.normalization.*
import org.gradle.platform.base.*
import org.gradle.platform.base.binary.*
import org.gradle.platform.base.component.*
import org.gradle.platform.base.plugins.*
import org.gradle.plugin.devel.*
import org.gradle.plugin.devel.plugins.*
import org.gradle.plugin.devel.tasks.*
import org.gradle.plugin.management.*
import org.gradle.plugin.use.*
import org.gradle.plugins.ear.*
import org.gradle.plugins.ear.descriptor.*
import org.gradle.plugins.ide.*
import org.gradle.plugins.ide.api.*
import org.gradle.plugins.ide.eclipse.*
import org.gradle.plugins.ide.idea.*
import org.gradle.plugins.signing.*
import org.gradle.plugins.signing.signatory.*
import org.gradle.plugins.signing.signatory.pgp.*
import org.gradle.plugins.signing.type.*
import org.gradle.plugins.signing.type.pgp.*
import org.gradle.process.*
import org.gradle.swiftpm.*
import org.gradle.swiftpm.plugins.*
import org.gradle.swiftpm.tasks.*
import org.gradle.testing.base.*
import org.gradle.testing.base.plugins.*
import org.gradle.testing.jacoco.plugins.*
import org.gradle.testing.jacoco.tasks.*
import org.gradle.testing.jacoco.tasks.rules.*
import org.gradle.testkit.runner.*
import org.gradle.util.*
import org.gradle.vcs.*
import org.gradle.vcs.git.*
import org.gradle.work.*
import org.gradle.workers.*
使用 Gradle 插件
Gradle 的核心是有意为现实世界的自动化提供的很少。所有有用的特性,比如编译 Java 代码的能力,都是由插件添加的。插件添加新任务(例如JavaCompile)、域对象(例如SourceSet)、约定(例如 Java 源代码位于src/main/java
)以及从其他插件扩展核心对象和对象。
在本章中,我们将讨论如何使用插件以及围绕插件的术语和概念。
插件有什么作用
将插件应用于项目允许插件扩展项目的功能。它可以执行以下操作:
- 扩展 Gradle 模型(例如添加可配置的新 DSL 元素)
- 根据约定配置项目(例如添加新任务或配置合理的默认值)
- 应用特定配置(例如添加组织存储库或执行标准)
通过应用插件,而不是向项目构建脚本添加逻辑,我们可以获得许多好处。应用插件:
- 促进重用并减少跨多个项目维护类似逻辑的开销
- 允许更高程度的模块化,增强可理解性和组织性
- 封装命令式逻辑并允许构建脚本尽可能声明性
插件类型
Gradle 中有两种通用的插件类型,二进制插件和脚本插件。二进制插件可以通过实现Plugin接口以编程方式编写,也可以使用 Gradle 的一种 DSL 语言以声明方式编写。二进制插件可以驻留在构建脚本中、项目层次结构中或外部插件 jar 中。脚本插件是额外的构建脚本,可以进一步配置构建,并且通常实现一种声明性的方法来操作构建。它们通常在构建中使用,尽管它们可以外部化并从远程位置访问。
插件通常以脚本插件开始(因为它们易于编写),然后随着代码变得更有价值,它被迁移到可以在多个项目或组织之间轻松测试和共享的二进制插件。
使用插件
要使用插件中封装的构建逻辑,Gradle 需要执行两个步骤。首先,它需要解析插件,然后需要将插件应用到目标,通常是一个Project。
解析插件意味着找到包含给定插件的正确版本的 jar 并将其添加到脚本类路径中。一旦一个插件被解析,它的 API 就可以在构建脚本中使用。脚本插件是自解析的,因为它们是从应用它们时提供的特定文件路径或 URL 解析的。作为 Gradle 发行版的一部分提供的核心二进制插件会自动解析。
应用插件意味着在要使用插件增强的项目上实际执行插件的Plugin.apply(T) 。应用插件是幂等的。也就是说,您可以安全地多次应用任何插件而不会产生副作用。
使用插件最常见的用例是解析插件并将其应用于当前项目。由于这是一个常见的用例,建议构建作者使用插件 DSL来一步解决和应用插件。
二进制插件
您可以通过插件 id应用插件,这是插件的全局唯一标识符或名称。核心 Gradle 插件的特殊之处在于它们提供了短名称,例如'java'
核心JavaPlugin。所有其他二进制插件必须使用插件 id 的完全限定形式(例如com.github.foo.bar
),尽管一些遗留插件可能仍使用短的、不合格的形式。放置插件 id 的位置取决于您使用的是插件 DSL还是buildscript 块。
二进制插件的位置
插件就是任何实现插件接口的类。Gradle 提供核心插件(例如JavaPlugin
)作为其发行版的一部分,这意味着它们会自动解析。但是,非核心二进制插件需要先解析才能应用。这可以通过多种方式实现:
- 包括来自插件门户的插件或使用插件 DSL 的自定义存储库(请参阅使用插件 DSL 应用插件)。
- 包括定义为 buildscript 依赖项的外部 jar 中的插件(请参阅使用 buildscript 块应用插件)。
- 将插件定义为项目中 buildSrc 目录下的源文件(请参阅使用 buildSrc 提取功能逻辑)。
- 将插件定义为构建脚本中的内联类声明。
有关定义您自己的插件的更多信息,请参阅自定义插件。
使用插件 DSL 应用插件
插件 DSL 提供了一种简洁方便的方式来声明插件依赖项。它与Gradle 插件门户一起使用,可轻松访问核心插件和社区插件。plugins DSL 块配置PluginDependenciesSpec的一个实例。
要应用核心插件,可以使用短名称:
示例 1. 应用核心插件
build.gradle
plugins {
id 'java'
}
要从门户应用社区插件,必须使用完全限定的插件 ID:
示例 2. 应用社区插件
build.gradle
plugins {
id 'com.jfrog.bintray' version '1.8.5'
}
有关使用插件 DSL 的更多信息,请参阅PluginDependenciesSpec。
插件 DSL 的限制
这种将插件添加到项目中的方式不仅仅是一种更方便的语法。插件 DSL 的处理方式允许 Gradle 非常早且非常快速地确定正在使用的插件。这允许 Gradle 做一些聪明的事情,例如:
- 优化插件类的加载和重用。
- 允许不同的插件使用不同版本的依赖。
- 为编辑提供有关构建脚本中潜在属性和值的详细信息,以帮助编辑。
这要求在执行构建脚本的其余部分之前,以 Gradle 可以轻松快速地提取的方式指定插件。它还要求要使用的插件的定义有些静态。
plugins {}
块机制和“传统”apply()
方法机制之间存在一些关键差异。还有一些限制,其中一些是机制仍在开发中的临时限制,一些是新方法所固有的。
受约束的语法
该plugins {}
块不支持任意代码。它是受约束的,以便幂等(每次产生相同的结果)和无副作用(Gradle 可以随时执行)。
参数如下:
build.gradle
plugins {
id «plugin id»
id «plugin id» version «plugin version» [apply «false»]
}
对于核心 Gradle 插件或构建脚本已经可用的插件
对于需要解析的二进制 Gradle 插件
where «plugin id»
and«plugin version»
必须是常量、文字、字符串和apply
带有 a 的语句boolean
可用于禁用立即应用插件的默认行为(例如,您只想在 中应用它subprojects
)。不允许有其他陈述;它们的存在会导致编译错误。
如果您想使用变量来定义插件版本,请参阅插件版本管理。
该plugins {}
块还必须是构建脚本中的顶级语句。它不能嵌套在另一个构造中(例如 if 语句或 for 循环)。
只能在构建脚本和设置文件中使用
该plugins {}
块目前只能在项目的构建脚本和 settings.gradle 文件中使用。它不能用于脚本插件或初始化脚本。
Gradle 的未来版本将删除此限制。
如果plugins {}
块的限制是禁止的,推荐的方法是使用buildscript {} 块应用插件。
将具有相同版本的外部插件应用于子项目
如果您有一个多项目构建,您可能希望将插件应用于构建中的部分或全部子项目,而不是root
项目。该plugins {}
块的默认行为是立即resolve
和 apply
插件。但是,您可以使用apply false
语法告诉 Gradle 不要将插件应用于当前项目,然后plugins {}
在子项目的构建脚本中使用没有版本的块:
示例 3. 仅在某些子项目上应用插件
settings.gradle
include 'hello-a'
include 'hello-b'
include 'goodbye-c'
build.gradle
plugins {
id 'com.example.hello' version '1.0.0' apply false
id 'com.example.goodbye' version '1.0.0' apply false
}
hello-a/build.gradle
plugins {
id 'com.example.hello'
}
hello-b/build.gradle
plugins {
id 'com.example.hello'
}
goodbye-c/build.gradle
plugins {
id 'com.example.goodbye'
}
更好的是 - 您可以通过使用自己的约定插件组合构建逻辑来封装外部插件的版本。
从buildSrc目录应用插件
您可以应用驻留在项目的buildSrc目录中的插件,只要它们具有定义的 ID。以下示例显示了如何将buildSrcmy.MyPlugin
中定义的插件实现类绑定到 ID“my-plugin”:
示例 4. 使用 ID 定义 buildSrc 插件
buildSrc/build.gradle
plugins {
id 'java-gradle-plugin'
}
gradlePlugin {
plugins {
myPlugins {
id = 'my-plugin'
implementationClass = 'my.MyPlugin'
}
}
}
然后可以照常按 ID 应用插件:
示例 5. 从 buildSrc 应用插件
build.gradle
plugins {
id 'my-plugin'
}
插件管理
该pluginManagement {}
块只能出现在settings.gradle
文件中,它必须是文件中的第一个块,或者出现在初始化脚本中。
示例 6. 为每个项目和全局配置 pluginManagement
setting.gradle
pluginManagement {
plugins {
}
resolutionStrategy {
}
repositories {
}
}
rootProject.name = 'plugin-management'
init.gradle
settingsEvaluated { settings ->
settings.pluginManagement {
plugins {
}
resolutionStrategy {
}
repositories {
}
}
}
自定义插件存储库
默认情况下,plugins {}
DSL 从公共Gradle 插件门户解析插件。许多构建作者还希望从私有 Maven 或 Ivy 存储库中解析插件,因为这些插件包含专有的实现细节,或者只是为了更好地控制哪些插件可用于他们的构建。
要指定自定义插件存储库,请使用repositories {}
里面的块pluginManagement {}
:
示例 7. 示例:使用自定义插件存储库中的插件。
setting.gradle
pluginManagement {
repositories {
maven {
url './maven-repo'
}
gradlePluginPortal()
ivy {
url './ivy-repo'
}
}
}
这告诉 Gradle 在../maven-repo
解析插件时首先查看 Maven 存储库,然后如果在 Maven 存储库中找不到插件,则检查 Gradle 插件门户。如果您不希望搜索 Gradle 插件门户,请省略该gradlePluginPortal()
行。最后,../ivy-repo
将检查 Ivy 存储库。
插件版本管理
内部的plugins {}
块pluginManagement {}
允许在单个位置定义构建的所有插件版本。然后插件可以通过 id 通过plugins {}
块应用到任何构建脚本。
以这种方式设置插件版本的一个好处是它pluginManagement.plugins {}
没有与构建脚本块相同的约束语法。plugins {}
这允许插件版本取自gradle.properties
,或通过另一种机制加载。
示例 8. 示例:通过pluginManagement
.
setting.gradle
pluginManagement {
plugins {
id 'com.example.hello' version "${helloPluginVersion}"
}
}
build.gradle
plugins {
id 'com.example.hello'
}
gradle.properties
helloPluginVersion=1.0.0
插件版本从gradle.properties
设置脚本加载并配置,允许将插件添加到任何项目而无需指定版本。
插件解析规则
插件解析规则允许您修改plugins {}
块中的插件请求,例如更改请求的版本或明确指定实现工件坐标。
要添加解析规则,请使用块resolutionStrategy {}
内部pluginManagement {}
:
示例 9. 插件解析策略。
setting.gradle
pluginManagement {
resolutionStrategy {
eachPlugin {
if (requested.id.namespace == 'com.example') {
useModule('com.example:sample-plugins:1.0.0')
}
}
}
repositories {
maven {
url './maven-repo'
}
gradlePluginPortal()
ivy {
url './ivy-repo'
}
}
}
这告诉 Gradle 使用指定的插件实现工件,而不是使用其从插件 ID 到 Maven/Ivy 坐标的内置默认映射。
除了实际实现插件的工件之外,自定义 Maven 和 Ivy 插件存储库还必须包含插件标记工件。有关将插件发布到自定义存储库的更多信息,请阅读Gradle 插件开发插件。
有关使用该块的完整文档,请参阅PluginManagementSpec 。pluginManagement {}
插件标记工件
由于plugins {}
DSL 块仅允许通过其全局唯一的插件id
和version
属性来声明插件,Gradle 需要一种方法来查找插件实现工件的坐标。为此,Gradle 将查找具有坐标的插件标记工件plugin.id:plugin.id.gradle.plugin:plugin.version
。这个标记需要依赖于实际的插件实现。发布这些标记由java-gradle-plugin自动完成。
例如,该sample-plugins
项目的以下完整示例显示了如何使用java-gradle-plugin、maven-publish插件和ivy-publishcom.example.hello
插件的组合将插件和com.example.goodbye
插件发布到 Ivy 和 Maven 存储库。
示例 10. 完整的插件发布示例
build.gradle
plugins {
id 'java-gradle-plugin'
id 'maven-publish'
id 'ivy-publish'
}
group 'com.example'
version '1.0.0'
gradlePlugin {
plugins {
hello {
id = 'com.example.hello'
implementationClass = 'com.example.hello.HelloPlugin'
}
goodbye {
id = 'com.example.goodbye'
implementationClass = 'com.example.goodbye.GoodbyePlugin'
}
}
}
publishing {
repositories {
maven {
url '../../consuming/maven-repo'
}
ivy {
url '../../consuming/ivy-repo'
}
}
}
在示例目录中运行gradle publish
会创建以下 Maven 存储库布局(Ivy 布局类似):
旧版插件应用程序
随着插件 DSL的引入,用户应该没有理由使用传统的插件应用方法。如果构建作者由于当前工作方式的限制而无法使用插件 DSL,则会在此处记录。
应用二进制插件
示例 11. 应用二进制插件
build.gradle
apply plugin: 'java'
可以使用插件 id应用插件。在上述情况下,我们使用短名称 ’ java
’ 来应用JavaPlugin。
除了使用插件 id,还可以通过简单地指定插件的类来应用插件:
示例 12. 按类型应用二进制插件
build.gradle
apply plugin: JavaPlugin
上述示例中的JavaPlugin
符号指的是JavaPlugin。此类不需要严格导入,因为org.gradle.api.plugins
包会在所有构建脚本中自动导入(请参阅默认导入)。
此外,不必.class
像在 Java 中那样在 Groovy 中附加来标识类文字。
使用 buildscript 块应用插件
通过将插件添加到构建脚本类路径然后应用插件,可以将已作为外部 jar 文件发布的二进制插件添加到项目中。可以使用构建脚本的外部依赖buildscript {}
项中所述的块将外部 jar 添加到构建脚本类路径中。
示例 13. 使用 buildscript 块应用插件
build.gradle
buildscript {
repositories {
gradlePluginPortal()
}
dependencies {
classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.5'
}
}
apply plugin: 'com.jfrog.bintray'
脚本插件
示例 14. 应用脚本插件
build.gradle
apply from: 'other.gradle'
脚本插件是自动解析的,可以从本地文件系统或远程位置的脚本中应用。文件系统位置是相对于项目目录的,而远程脚本位置是使用 HTTP URL 指定的。可以将多个脚本插件(任何一种形式)应用于给定目标。
评论区