Build Lifecycle

Contents

  • Build phases
  • Settings file
  • Initialization
  • Configuration and execution of a single project build
  • Responding to the lifecycle in the build script

我们早期就说过Gradle 是依赖于编程的语言 … 在Gradle 的术语中这意味着你能够定义任务以及任务之间产生依赖 …
Gradle 保证这些任务根据它们的依赖顺序进行执行 …
并且每一个任务只会仅仅执行一次 .. 这些任务会呈现一个 任务图 [Directed Acycle Graph]
构建工具将构建这样的依赖图(当执行它们的任务时) … Gradle 构建在任何任务执行之前将会完善依赖图 …
这是 Gradle 的核心,它使许多原本不可能的事情成为可能 …
你的构建脚本会配置这个依赖图 .. 因此它们被严格的称为 构建配置脚本

Build Phase

Gradle 构建分为三个明确的阶段

  • Initialization

Gradle 支持单个以及多个项目构建 ..
在初始化阶段中, Gradle 决定那些项目作为构建的一部分 … 并且为每一个项目创建Project 实例 …

  • Configuration

在这个阶段中对象将会被配置 … 构建中 所有项目的构建脚本都将会被执行 …

  • execution

Gradle 决定任务的子集,在配置阶段创建并配置 … 然后将被执行 …
这个子集是通过当前目录以及传递给gradle 命令 任务名称参数进行决定…
Gradle 将会执行每一个被选择的任务 …

Settings file

除了构建脚本文件之外,Gradle 也会定义一个 settings 文件… 这个settings文件将会被Gradle 进行命名约定进行判断 …
默认名称是 settings.gradle … 后面讲述了Gradle 如何查找一个settings 文件 …
这个配置文件将会在初始化阶段执行 … 一个多项目构建必须有一个settings.gradle 文件在多项目结构体系中的根项目中 …
它是必要的,因为配置文件定义了那些项目将作为多项目构建的一部分 …打造多项目构建 ..
对于单个项目构建,settings文件是可选的,除了定义包括的项目之外,你可能需要它增加库到你的构建脚本类路径上(查看 构建Gradle 项目 ) ..
先从单个项目构建进行省查:

单项目构建

  1. rootProject.name = 'basic'
  2. println 'This is executed during the initialization phase.'
  1. println 'This is executed during the configuration phase.'
  2. tasks.register('configured') {
  3. println 'This is also executed during the configuration phase, because :configured is used in the build.'
  4. }
  5. tasks.register('test') {
  6. doLast {
  7. println 'This is executed during the execution phase.'
  8. }
  9. }
  10. tasks.register('testBoth') {
  11. doFirst {
  12. println 'This is executed first during the execution phase.'
  13. }
  14. doLast {
  15. println 'This is executed last during the execution phase.'
  16. }
  17. println 'This is executed during the configuration phase as well, because :testBoth is used in the build.'
  18. }

对于构建脚本,属性访问 以及方法调用都会代理到project 对象上…. 在settings文件中的相似的属性访问 / 方法调用 将会代理到settings 对象上 …
查看 Settings 了解更多 …

初始化

怎样让Gradle 知道这是一个单项目构建还是多项目构建?
如果你从一个具有settings.file 的目录中触发一个多项目构建,Gradle 将使用它进行构建配置 …
Gradle 同样允许你去执行构建中的任意一个部分 ..(项目) … - Gradle 支持部分多项目构建(查看 Executing Multi-Project Builds) ..
如果在项目中执行Gradle 但是没有settings.gradle 文件 ..
Gradle 将会根据以下方式进行settings.gradle 进行查找 …

  • 在父级目录中查找 settings.gradle
  • 如果没有发现,构建作为一个单项目构建 ..
  • 如果settings.gradle 发现,Gradle 检查当前的项目是否作为多项目结构体系中的一部分(它应该在settings.gradle 中有所定义) … 如果不是,则作为单项目构建 … 否则作为一个多项目构建执行 ..

这个行为的目的是什么? Gradle 需要判断一个项目是否位于多项目构建中的一个子项目或者不是 …
当然,如果它是一个子项目,仅仅这个子项目以及它所依赖的项目将会被构建 ..
但是Gradle 需要创建构建配置(为多项目构建,查看配置和执行),如果当前的项目包含了一个settings.gradle,构建总是根据以下方式执行:

  • 单项目构建,如果settings.gradle 文件没有定义在多项目体系中 ..
  • 多项目构建,settings.gradle 文件定义在多项目体系中 ..

对于settings.gradle 文件的查找仅仅工作在多项目构建中(它具有默认的项目结构 并且项目路径匹配磁盘上的目录子项目结构) ..Gradle对多项目构建 支持任意的物理结构 ,但是这样的任意结构你需要从settings.gradle 文件位于的目录进行构建执行 …
对于如何从根项目中运行部分构建,查看 Executing Multi-Project Builds

总而言之,非多项目体系下的settings.gradle 的项目将作为一个独立的项目 …
否则就是多项目构建体系下的一个子项目而已 …

读完这句话,其实还有一个疑惑,我仅仅执行多项目构建的一个子项目构建,同时子项目中包含了自己的settings文件 … 根据执行的约定,我猜测,如果从根目录中执行这个子目录构建,它应该是作为一部分进行构建,并且只构建这一部分,反之,可能会单个项目进行构建,或许还会编译失败 ..
等待测试 ..

Gradle 会为每一个项目创建一个Project 对象,对于一个多项目构建,位于构建的所有项目都会在Settings 对象中进行指定(包括根项目) …
每一个项目对象都有默认的名称(等于它们独自项目目录的名称), 并且每一个项目 / 除了根项目都会有一个 父项目,任何项目都有子项目 …

Configuration and execution of a single project build

对于单个项目构建,初始化阶段之后的工作十分简单, 构建构建将会针对每一个项目进行执行(在初始化阶段结束时创建的project 对象) …
然后Gradle 将会查询等价于命令行参数 任务参数名相匹配的任务 …
如果任务存在,那么任务将按照你传递它们的顺序作为独立的构建 …
多项目构建的配置和执行 着重在 Configuration and Execution 进行讨论 …

构建脚本相关的生命周期

你的构建脚本在构建过程中通过它们的生命周期接收通知 … 这些通知通常有两种形式:

  • 要么实现特定的监听器接口
  • 要么提供闭包进行执行(针对给定的通知触发时)

接下来的例子使用闭包进行解释,对于接口也是类似 ..

项目评估

在项目评估前后你都能够接收一些通知 … 它能够做一些额外的配置(一旦在构建脚本中的一切都已经应用,执行额外的配置,或者进行自定义的日志或者刨析) …
例如增加一个test任务(如果项目中包含一个hasTests 属性值为true) …

Adding of test task to each project which has certain property set

  1. allprojects {
  2. afterEvaluate { project ->
  3. if (project.hasTests) {
  4. println "Adding test task to $project"
  5. project.task('test') {
  6. doLast {
  7. println "Running tests for $project"
  8. }
  9. }
  10. }
  11. }
  12. }
  1. hasTests = true
  1. > gradle -q test
  2. Adding test task to project ':project-a'
  3. Running tests for project ':project-a'

在执行之后添加了一个闭包,当项目评估之后进行执行 …
它是可以接收来自任何项目评估完成的通知,执行某些自定义的项目评估日志记录 …
主要afterProject 通知接收而不会管项目是否评估成功还是失败 …

Notification

  1. gradle.afterProject { project ->
  2. if (project.state.failure) {
  3. println "Evaluation of $project FAILED"
  4. } else {
  5. println "Evaluation of $project succeeded"
  6. }
  7. }
  1. > gradle -q test
  2. Evaluation of root project 'build-project-evaluate-events' succeeded
  3. Evaluation of project ':project-a' succeeded
  4. Evaluation of project ':project-b' FAILED
  5. FAILURE: Build failed with an exception.
  6. * Where:
  7. Build file '/home/user/gradle/samples/project-b.gradle' line: 1
  8. * What went wrong:
  9. A problem occurred evaluating project ':project-b'.
  10. > broken
  11. * Try:
  12. > Run with --stacktrace option to get the stack trace.
  13. > Run with --info or --debug option to get more log output.
  14. > Run with --scan to get full insights.
  15. * Get more help at https://help.gradle.org
  16. BUILD FAILED in 0s

这里的构建脚本的名称不一样,在配置文件中我们可以指定每一个子项目的构建脚本的名称….
详情可以查看对象的API 文档 …
当然我们还可以增加一些 ProjectEvaluationListener 到Gradle 对象上接收这些事件 …

Tasks creation

在一个任务增加到项目中立即接收某个通知 … 这是能够被用来设置某些默认值 或者在任务变得可用之前在构建文件中增加某些行为 …
以下的例子 为每一个任务设置 srcDir(当任务创建时) …

为所有的任务配置某些属性

这同样是一些通知 …

  1. tasks.whenTaskAdded { task ->
  2. task.ext.srcDir = 'src/main/java'
  3. }
  4. tasks.register('a')
  5. println "source dir is $a.srcDir"
  1. > gradle -q a
  2. source dir is src/main/java

你同样能够为TaskContainer 增加 Action 来接收这些事件 …

准备好任务执行图

你能够在任务执行图已经完全收集之后 立即接收 通知 …
你能够增加 TaskExecutionGraphListener to the TaskExecutionGraph to receive these events.

Task execution

在任务执行前后接收通知 …
例如为每个任务执行记录开始和结束 …
例如 afterTask 通知也可以接收这样的通知,并且不会管任务到底是否执行成功 …

Logging of start and end of each task execution

  1. tasks.register('ok')
  2. tasks.register('broken') {
  3. dependsOn ok
  4. doLast {
  5. throw new RuntimeException('broken')
  6. }
  7. }
  8. gradle.taskGraph.beforeTask { Task task ->
  9. println "executing $task ..."
  10. }
  11. gradle.taskGraph.afterTask { Task task, TaskState state ->
  12. if (state.failure) {
  13. println "FAILED"
  14. }
  15. else {
  16. println "done"
  17. }
  18. }
  1. > gradle -q broken
  2. executing task ':ok' ...
  3. done
  4. executing task ':broken' ...
  5. FAILED
  6. FAILURE: Build failed with an exception.
  7. * Where:
  8. Build file '/home/user/gradle/samples/build.gradle' line: 6
  9. * What went wrong:
  10. Execution failed for task ':broken'.
  11. > broken
  12. * Try:
  13. > Run with --stacktrace option to get the stack trace.
  14. > Run with --info or --debug option to get more log output.
  15. > Run with --scan to get full insights.
  16. * Get more help at https://help.gradle.org
  17. BUILD FAILED in 0s

同样你能够使用TaskExecutionListenerTaskExecutionGraph 去接收这些事件 …