Gradle-构建生命周期
两个重要的概念
项目
实际上,一个项目是什么取决于你要用 gradle 做什么?项目通常代表的是构建内容。 例如在 android 中,一个 module 就是一个项目;
- 项目是注册在 setting.gradle 中的
- 通常一个项目有一个 build.gradle
gradle 构建就是由一个或多个项目组成的。
任务
任务 顾名思义就是一个在构建阶段被执行的操作。它是 gradle 构建的原子工作单位。例如 编译 java 源代码;
任务是定义在项目的构建脚本中,并且可以彼此依赖。
一个项目就是由一个个任务组成的。
每一个 gradle 构建都会按照相同的顺序经历三个不同的阶段:
初始化
gradle 支持单项目构建和多项目构建。
在这个阶段 gradle 会确认哪些项目将会参与构建。gradle 会通过 setting.gradle 确定是多项目还是单项目构建。
gradle 会为每个项目创建 project 实例。
配置
在这个阶段执行在初始化阶段中确定的每一个项目的配置脚本,但是并不会执行其中的任务,只会评估任务的依赖性,根据其依赖性创建任务的有向无环图。
gradle引入了一个称为随需求变配置的特性,该特性使它能够在构建过程中只配置相关和必要的项目。这在大型多项目构建中非常有用,因为它可以大大减少构建时间。
执行
在这个阶段,gradle 会识别在配置阶段创建的任务的有向无环图。并按照他们的依赖顺序开始执行。
所有的构建工作都是在这个阶段执行的。如编译源码,生成 .class 文件,复制文件等。
setting.gradle
这个文件是由 gradle 约定命名的,默认名为 setting.gradle ,在初始化阶段被执行。
对于多项目构建,必须在这里声明要参与构建的所有项目。对于单项目构建就是可选的了,可有可无。
gradle 是如何寻找 setting.gradle 的?
- 在当前目录寻找
- 没有找到的话就去父目录寻找
- 仍然没有找到就是是单项目构建了
- 如果找到了就是确定其中的项目,如果当前执行的项目在 setting.gradle 有定义就执行多项目构建,否则就执行单项目构建。
一个脚本的属性访问和方法调用是委托给 project 类的实例的,类似的 setting.gradle 的属性访问和方法调用是委托给 settings 类的实例对象的。
单项目构建
对于单项目构建,在初始化后的工作流程很简单,构建脚本针对初始化阶段创建的项目对象执行。查找在命令行传入的任务名称相同的任务。
如果任务存在则作为一个单独的构建按照命令行传递的顺序执行。
多项目构建
多项目构建是在 gradle 的单个执行过程中构建多个项目的构建。必须把参与构建的项目声明在 setting.gradle 里
项目位置
可以把多项目构建看作一个单根的树。每一个项目都是树上的一个节点。一个项目有一个路径表示在树中的位置。
通常情况下项目的路径和在文件系统中的位置是一致的,当然了这个路径也是可以配置的。
项目树是 setting.gradle 生成的,默认情况下 setting.gradle 的位置就是根项目的位置。但是你可以在 setting.gradle 文件中更改。
构建项目树
在 setting.gradle 设置文件中你可以使用一些列的方法配置构建项目树。分层和平面物理布局都支持。
分层布局
groovy
include 'project1', 'project2:child', 'project3:child1'
kotlin
include("project1", "project2:child", "project3:child1")
include 方法使用项目路径作为参数,假定项目路径与相对物理文件系统路径相等。
例如 "project2:child" 默认对应的是相对于根目录的 "project2/child"。
这也意味着包含路径 “services:hotels:api” 将创建3个项目:
- “services”
- “services:hotels”
- “services:hotels:api”
更详细的说明可以 dsl文档
平面布局
groovy
includeflat 'project3', 'project4'
kotlin
includeflat("project3", "project4")
includeflat 也是目录名字作为参数。这些目录要和根项目目录同级。
这些目录的位置在项目树中是根项目的子项目。
更改项目树的元素
在设置文件中创建的多项目树由所谓的项目描述符组成。这些项目符号可以随时更改。
可以通过下面这种方式访问描述符
查找项目树的元素
groovy
println rootproject.name println project(':projecta').name
kotlin
println(rootproject.name) println(project(":projecta").name)
使用这个描述符你可以一个项目的名字,项目目录和构建文件
更改项目树元素
groovy
rootproject.name = 'main' project(':projecta').projectdir = new file(settingsdir, '../my-project-a') project(':projecta').buildfilename = 'projecta.gradle'
kotlin
rootproject.name = "main" project(":projecta").projectdir = file(settingsdir, "../my-project-a") project(":projecta").buildfilename = "projecta.gradle"
更详细的信息可以查看 projectdescriptor 类的 api 文档。
接收生命周期事件
构建脚本可以接收生命周期构建进度的通知。
接收这些通知一般是两种形式
- 实现详细的监听接口
- 在发送通知时提供一个闭包来执行
项目评估事件
可以在项目评估后马上接到事件通知 使用的是 project.afterevaluate 方法,传入一个闭包,gradle会将评估的项目和状态传递进闭包里。
kotlin
afterevaluate { println("${project.getname()} 评估结果:${state.getexecuted()}") }
groovy
afterevaluate{ project,state-> println "$project 评估成功否:${state.failure==null}" }
如果是在多项目构建里,可以在 allprojects 的闭包里使用,这样每个项目的评估事件就都接受到了
groovy
allprojects{ afterevaluate{ project,state-> println "$project 评估成功否:${state.failure==null}" } }
评估前的事件通知使用 project.beforeevaluate 照样是传入一个闭包,gradle会将要评估的项目传递进闭包里
groovy
allprojects{ afterevaluate{ project,state-> println "$project 评估成功否:${state.failure==null}" } beforeevaluate { project -> println "开始评估 $project" } }
这里列出了使用的 api文档。
任务
任务被添加到项目
groovy
tasks.whentaskadded { task -> println "$task 被添加到项目了。" }
kotlin
tasks.whentaskadded { extra["srcdir"] = "src/main/java" } val a by tasks.registering println("source dir is ${a.get().extra["srcdir"]}")
有向无环图填充完毕
使用的是 taskexecutiongraph.whenready 方法
groovy
gradle.taskgraph.whenready{ graph-> println "任务图准备好了:\n" graph.alltasks.each { print "$it , " } }
任务执行
groovy
task ok task 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 api 的查询地址
文档参考