Gradle 构建基础笔记

Gradle_logo

本文参考自 Gradle User Guide 中文版 感谢其翻译贡献者

安装Gradle

GRADLE_HOME

GRADLE_PATH

Gradle 基本概念

Projects

每一个构建由一个或者多个 Project 构成,每个 Project 代表一个资源,具体做什么取决与如何定义该 Project,它可能代表一个发布的 zip 文件,也可能代表某项动作,比如部署应用等。

Tasks

每个 Project 由一个或者多个 task 组成,每个 task 可以是更细致化的构建操作,比如编译 class文件,创建 jar 文件,生成 javadoc 文档等。

Gradle Hello World

我们可以通过 gradle 命令来完成一次构建,**gradle 命令会在当前目录查找一个叫 build.gradle 的描述文件来完成构建**。严格来说它是一个构建脚本,类似于 Maven 的 POM.xml 文件;用来描述整个构建过程;以下是一个 Hello World 示例。

1
2
3
4
5
6
task helloWorld {
doLast {
println 'Hello World!'
}
}

将以上脚本保存为 build.gradle;并在当前目录执行 gradle -q helloWorld 尝试运行该 task。
注意:-q 代表 quite 模式,不显示 Gradle 的日志信息。

快捷定义任务

以上代码,doLast 作为 helloWorld 的一个 Action,此时可以通过如下方式快捷定义:

1
2
3
4
task helloWorld << {
println 'Hello World!'
}

在 Gradle 中使用 Groovy

Gradle 是基于 Groovy 的,在 build.gradle 中可以使用 Groovy 书写(虽然我不会…);样例如下(测试命令行中文乱码):

1
2
3
4
5
6
task upper << {
String testStr = "AbcD"
println "原始字符:" + testStr
println "转换大写:" + testStr.toUpperCase()
}

任务依赖性

Gradle 支持任务间的依赖关系,如下所示:

1
2
3
4
5
6
7
8
9
task testDepends << {
println "taskDepends"
}

// 设置依赖关系
task test1(dependsOn: testDepends) << {
println "test1"
}

执行 gradle -q test1 命令运行 task1 任务,此时由于 task1 任务依赖于 testDepends 任务,所以会先执行 testDepends 任务,在执行 test1 任务。

注意:build.gradle 脚本中,任务依赖时没有顺序区分,也就是说可以先声明一个任务依赖于另一个任务,而后定义另一个任务,总结来说就是 task 书写顺序不会影响 任务间依赖关系。

任务的动态性

由于 Gradle 使用 Groovy 作为构建语言,Groovy 函数式的特性可以让我们动态的创建任务,而无需预先设定好(个人认为实际构建可能没这么变态的需要);样例如下:

1
2
3
4
5
6
7
// 此时相当于定义了 4个 task 分别是 taskDynamic0、...1、...2、...3
4.times { counter ->
task "taskDynamic$counter" << {
println "taskDynamic$counter"
}
}

通过API使用已存在的任务

Gradle 中可以先定义 task,然后再指定这些 task 之间的关系,就像下面这样:

1
2
3
4
5
6
7
8
9
10
// 先利用动态性 创建4个任务
4.times { counter ->
task "task$counter" << {
println "task----> $counter"
}
}

// 再指定依赖关系
task0.dependsOn task1,task3

同时 Gradle 还允许在任务定以后动态的改变或添加其行为,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 首先定义任务
task test1 << {
println "default"
}

// 然后动态添加 任务开始动作
test1.doFirst {
println "First"
}

// 动态添加 任务结束动作
test1.doLast {
println "Last"
}

// 在添加一个结束动作
test1 << {
println "the end"
}

注意:上面一直使用的 << 实际是 doLast 的简写形式。

任务引用(短标记语法)

Gradle 可以通过 $ 符号引用另一个任务,可以是另一个任务的执行结果;也可以是其属性,比如 task.name 将返回某个任务的名字,样例如下:

1
2
3
4
5
6
7
8
9
// 首先定义一个任务
task test1 << {
prinln "test1111111"
}
// 然后在另一个任务里引用
task test2 << {
println "test2--->$test1.name"
}

自定义任务属性

在 Gradle 中我们可以自定义任务的属性,然后再恰当的时候加以引用,在设置某个任务属性时 采用 ext 关键字定义该属性;个人理解类似 java 的 this 关键字;样例如下:

1
2
3
4
5
6
7
8
9
10
11
// 定义 testTask1 的两个属性
task testTask1 {
ext.property1 = "1"
ext.property2 = "2"
}
// 可直接打印引用,字符串中可采用短标记 $ 引用
task testTask2 << {
println testTask1.property1
println "$testTask1.property2"
}

注意:定义任务属性时必须在任务体中,而不能在 doLast 或 doFirst Action 中定义!

调用 Ant 任务

以下直接 copy 的,本人没玩过 Ant……

Ant 任务是 Gradle 的一等公民. Gradle 通过 Groovy 出色的集成了 Ant 任务. Groovy 自带了一个 AntBuilder. 相比于从一个 build.xml 文件中使用 Ant 任务, 在 Gradle 里使用 Ant 任务更为方便和强大. 从下面的例子中, 你可以学习如何执行 Ant 任务以及如何访问 ant 属性:

1
2
3
4
5
6
7
8
9
10
11
task loadfile << {
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]}"
}
}
}

使用方法

又用 Ant…… My God ….. 接着 copy

Gradle 能很好地衡量你编写脚本的逻辑能力. 首先要做的是如何提取一个方法.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
task checksum << {
fileList('../antLoadfileResources').each {File file ->
ant.checksum(file: file, property: "cs_$file.name")
println "$file.name Checksum: ${ant.properties["cs_$file.name"]}"
}
}
task loadfile << {
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()
}

默认任务

终于没有 Ant 了……

Gradle 允许在一个构建脚本(build.gradle) 中定义默认需要执行的任务,只需要使用 defaultTasks 关键字即可,此时可直接在构建脚本目录执行 gradle [-q] 来执行构建过程,样例如下

1
2
3
4
5
6
7
8
9
10
11
12
// 定义 默认的任务
defaultTasks "testTask1","testTask2"
task testTask1 << {
println "testTask1"
}
task testTask2 << {
println "testTask2"
}
task testTask3 << {
println "testTask3"
}

DAG 配置

原文:正如我们之后的详细描述, Gradle 有一个配置阶段和执行阶
段. 在配置阶段后, Gradle 将会知道应执行的所有任务. Gradle 为你提供一个”钩子”, 以便利用这些信息. 举个例子, 判断发布的任务是否在要被执行的任务当中. 根据这一点, 你可以给一些变量指定不同的值.

样例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
task distribution << {
println "We build the zip with version=$version"
}
task release(dependsOn: 'distribution') << {
println 'We release now'
}
gradle.taskGraph.whenReady {taskGraph ->
if (taskGraph.hasTask(release)) {
version = '1.0'
} else {
version = '1.0-SNAPSHOT'
}
}

个人理解:关于 DAG 配置的描述,大致意思应该是说,Gradle 构建时会执行两个阶段:配置阶段、执行阶段;在配置阶段;Gradle 主要完成所有的配置工作,应该会读取整个构建脚本,分析里面的语义;这也可能是 Gradle 定义 task 等不分先后顺序的原因;在这个阶段,我们可以通过内置变量(DAG)和内置方法来动态的判断并调整后续的构建流程;当所有构建流程确定后,再进行第二个构建阶段。


Gradle 构建基础笔记
https://mritd.com/2016/02/18/gradle-note/
作者
Kovacs
发布于
2016年2月18日
许可协议