gradle构建脚本和生命周期介绍
参考文档:
https://www.jianshu.com/p/a132f6a77e7a
一、构建脚本的学习
1.1、概念
Gradle构建脚本中最重要的两个概念是: project 和 task.任何一个Gradle构建都是有一个或者多个project组成,每个project包含许多的构建部分,可以是一个jar包,也可以是一个war应用,也可以是多个jar的整合等等。
每个project 由一个或者多个task 组成,每个task表示在构建过程中的一个原子操作。例如:编译、打包、生成javadoc、发布到仓库等操作。
project 和 project 以及 project 和 task 或者 task 和 task 之间的关系如下:
项目1 依赖于 项目2,所以需要先构建项目2,项目2中有三个任务 Task(DEF),由于依赖关系,需要先执行task D,然后是task E,接着是task F, 项目1 中的task也是先执行被依赖的任务。
1.2、Project对象
一个project 代表着一个正在构建的组件,当构建开始时, Gradle会 基于build.gradle 实例化一个 org.gradle.api.Project 对象,并通过 project变量 来隐式调用其成员,Project的成员属性如下:
名字 | 类型 | 描述 |
---|---|---|
project | Project | project实例本身 |
group | Object | 项目分组 |
name | String | 项目名称 |
version | Object | 项目版本 |
path | String | 项目的绝对路径 |
description | String | 项目描述 |
projectDir | File | 包含生成脚本目录 |
buildDir | File | projectDir/build |
ant | AntBuilder | AntBuilder 实例 |
… | … | … |
1.2.1、Project其他常用配置
- 1、plugins、apply、plugin 用来引入插件使用(不同版本的引入方式会有些差异)
- 2、dependencies 依赖配置
- 3、repositories 仓库配置
- 4、task 任务书写
- 5、ext、gradle.properties 用于Project中属性的其他配置方式
结论:所有的配置都会被封装到 Project 对象中。
1.3、Task对象
每个任务在构建执行过程中会 被封装成 org.gradle.api.Task 对象,主要包括任务的动作和任务的依赖,任务动作定义了一个原子操作,可以定义依赖其它任务、动作顺序和执行条件。
Task 主要操作动作
- dependsOn :依赖相关操作
- doFirst :执行任务之前执行的方法
- doLast,<< :执行任务之后执行的方法,gradle 5.0后去掉了 “<<” 的后置方法
1.3.1、自定义task介绍
gradle 命令会在当前目录中查找一个叫 build.gradle 的文件. 我们称这个 build.gradle 文件为一个构建脚本 (build script), 但是严格来说它是一个构建配置脚本 (build configuration script). 这个脚本定义了一个 project 和它的 tasks.
所以可以创建build.gradle来编写我们的测试脚本,由于我使用的gradle-6.2.2版本,如果只创建build.gradle会报错,具体报错信息如下:
FAILURE: Build failed with an exception.
* What went wrong:
A problem occurred configuring root project ''.
> The project name must not be empty. Set the 'rootProject.name' or adjust the 'include' statement (see https://docs.gradle.org/6.2.2/dsl/org.gradle.api.initialization.Settings.html#org.gradle.api.initialization.Settings:include(java.lang.String[]) for more details).
* 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 2s
所以需要在build.gradle 的同级目录创建一个settings.gradle文件,随便指定一个 rootProject.name,则可以解决报错问题,例如:
rootProject.name = 'hello-gradle'
1.3.2、自定义task
这里先介绍一下结论,否则再看这个执行结果的时候会让人看得莫名其妙:
- 所有自定义在build.gradle 中的task,在配置阶段都会执行,其它时候不执行,就算(被)依赖也不执行
- 只有在 doFirst 和 doLast 中配置的内容再调用任务或者依赖时才会被调用
结论:所以以后自定义任务执行代码需要填写在doFirst 和 doLast 中,除非想要在构建 Project 时就执行
1、先创建一个简单的task任务
// 定义一个helloTask的任务
task helloTask{
println("定义的一个task 任务")
}
执行helloTask任务
$ gradle helloTask
> Configure project :
定义的一个task 任务
BUILD SUCCESSFUL in 2s
2、创建一个依赖任务
// 定义一个helloTask的任务
task helloTask {
doFirst {
println("helloTask......doFirst")
}
doLast {
println("helloTask......doLast")
}
}
// 此处task的内容再初始化阶段就会被执行
task helloTask1 {
println("此处的内容初始化阶段就会被执行")
}
// 定义一个依赖任务,想要执行taskDependsOn 任务,必须先执行helloTask
// 多个依赖的定义方式:task taskDependsOn(dependsOn: ['helloTask'.....]){}
task taskDependsOn(dependsOn: 'helloTask'){
doFirst{
println("taskDependsOn......doFirst")
}
}
执行taskDependsOn任务
$ gradle taskDependsOn
> Configure project :
此处的内容初始化阶段就会被执行
> Task :helloTask
helloTask......doFirst
helloTask......doLast
> Task :taskDependsOn
taskDependsOn......doFirst
BUILD SUCCESSFUL in 1s
2 actionable tasks: 2 executed
3、多个task的依赖
task A{
doLast{
println "A....."
}
}
task B(dependsOn: A){
doLast{
println "B....."
}
}
task C{
doLast{
println "C....."
}
}
// 多以依赖的写法
task D(dependsOn: [C, B]){
doLast{
println "D....."
}
}
执行 D 任务
$ gradle D
> Task :A
A.....
> Task :B
B.....
> Task :C
C.....
> Task :D
D.....
BUILD SUCCESSFUL in 2s
4 actionable tasks: 4 executed
1.3.3、动态任务
借助 Groovy 的强大不仅可以定义简单任务还能做更多的事。例如,可以动态定义任务。
6.times {tempVar ->
task "task$tempVar" {
doLast{
println "I'm task number $tempVar"
}
}
}
这里动态的创建了task0-task5 ,共6个task,随便执行其中的一个task
$ gradle task2
> Task :task2
I'm task number 2
BUILD SUCCESSFUL in 2s
1 actionable task: 1 executed
这里的 6.times 调用的类是 org.codehaus.groovy.runtime.DefaultGroovyMethods ,具体代码如下:
package org.codehaus.groovy.runtime;
public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
.......
public static void times(Number self, @ClosureParams(value = SimpleType.class,options = {"int"}) Closure closure) {
int i = 0;
for(int size = self.intValue(); i < size; ++i) {
closure.call(i);
if (closure.getDirective() == 1) {
break;
}
}
}
.......
}
1.3.4、给任务自定义属性
你可以给任务加入自定义的属性. 列如加入一个叫做 myProperty 属性, 设置一个初始值给 ext.myProperty. 然后该属性就可以像一个预定义的任务属性那样被读取和设置了.
task myTask {
println "hello myTask"
ext.myProperty = "myValue"
}
task printTaskProperties {
doLast {
println myTask.myProperty
}
}
执行task
$ gradle printTaskProperties
> Configure project :
hello myTask
> Task :printTaskProperties
myValue
BUILD SUCCESSFUL in 2s
1 actionable task: 1 executed
1.3.5、Gradle默认任务
Gradle 允许在脚本中定义一个或多个默认任务.
defaultTasks 'clean', 'run'
task clean {
doLast{
println 'Default Cleaning!'
}
}
task run {
doLast{
println 'Default Running!'
}
}
task other {
doLast{
println "I'm not a default task!"
}
}
gradle -q 命令的输出
$ gradle -q
Default Cleaning!
Default Running!
等价于 gradle -q clean run. 在一个多项目构建中, 每一个子项目都可以有它特别的默认任务. 如果一个子项目没有特别的默认任务, 父项目的默认任务将会被执行.
二、gradle项目构建生命周期
Gradle 的生命周期分为三个阶段:
- 初始化阶段
- 配置阶段
- 执行阶段
2.1、初始化阶段
通过 settings.gradle 判断哪些项目需要初始化,加载所有需要初始化项目的 build.gradle 文件并为每个项目创建 project 对象
2.2、配置阶段
执行各项目的 build.gradle 脚本,完成project的配置,并且构造 Task 任务依赖关系图以便在执行阶段按照关系执行Task 中的配置代码.
配置代码代码如下:
task configCode{
println 'config Code'
}
2.3、执行阶段
通过配置阶段的 Task 图,按顺序执行需要执行task中的动作代码,就是执行任务中写的doFirst 和 doLast 中的代码。
动作代码:任务调用才会被执行的代码;例如:
task exeCode{
doLast{
println "exeCode....."
}
}
以上就是 Gradle 构建项目时的生命周期,由于Gradle 的强大和易配性,如果想在构建的过程中去做一些额外的操作的话可以使用Gradle 自带的钩子方法.以下咱们通过一个图来描述 Gradle 的生命周期和钩子方法.
2.4、Gradle 生命周期和钩子方法
上图表明了Gradle 构建的整个流程,在生命周期的三个阶段中有很多的钩子方法供用户自行覆盖使用.
注意:
-
1 初始化阶段的钩子方法和 gradle.beforeProject() 只能定义在 setting.gradle 或 init.gradle 脚本中.
执行 build.gradle 时已经有了 project 对象.且执行前就调用了beforeProject钩子方法. -
2 gradle.buildStarted() 钩子方法无法执行到.通过源码得知,在初始化前就已经调用了 buildStarted 方法,所以在初始化阶段无法回调的到.
例子
setting.gradle
rootProject.name = 'hello-gradle'
// 初始化阶段
gradle.settingsEvaluated {
println '初始化阶段settingsEvaluated'
}
gradle.projectsLoaded {
println '初始化阶段 projectsLoaded'
}
// 配置阶段
gradle.beforeProject {
println '配置阶段 beforeProject'
}
build.gradle
// 自定义任务
task t1 {
println 't1 configuration'
doLast {
println 't1 execute doLast'
}
doFirst {
println 't1 execute doFirst'
}
}
// 钩子方法
gradle.afterProject {
println '配置阶段 afterProject'
}
project.beforeEvaluate {
println '配置阶段 beforeEvaluate'
}
gradle.projectsEvaluated {
println '配置阶段 projectsEvaluated'
}
project.afterEvaluate {
println '配置阶段 afterEvaluate'
}
gradle.taskGraph.whenReady {
println '配置任务 whenReady'
}
// 执行阶段
gradle.taskGraph.beforeTask {
println "执行阶段 before task"
}
gradle.taskGraph.afterTask {
println "执行阶段 afterTask "
}
gradle.buildFinished {
println '构建结束 buildFinished'
}
执行 gradle t1 结果如下:
$ gradle t1
Starting a Gradle Daemon (subsequent builds will be faster)
初始化阶段settingsEvaluated
初始化阶段 projectsLoaded
> Configure project :
配置阶段 beforeProject
t1 configuration
配置阶段 afterProject
配置阶段 afterEvaluate
配置阶段 projectsEvaluated
配置任务 whenReady
> Task :t1
执行阶段 before task
t1 execute doFirst
t1 execute doLast
执行阶段 afterTask
构建结束 buildFinished
BUILD SUCCESSFUL in 13s
1 actionable task: 1 executed
以上就是Gradle 项目构建的生命周期和钩子方法的介绍,这些钩子方法一般使用得少,特殊需求情况下可以使用.