关于我对Gradle的翻译,以Github上的项目及http://gradledoc.qiniudn.com 上的文档为准。如发现翻译有误的地方,将首先在以上两个地方更新。因时间精力问题,博客中发表的译文基本不会同步修改。
另外,目前Gradle1.12版本的文档进入校稿阶段,校稿的方式可以为到该项目https://github.com/msdx/gradledoc 提交issue或是pull request。校稿的结果不只是在此版本更新,也会用于改善Gradle下一版本(2.0)文档的翻译。
我们之前说,Gradle 的核心是一种以依赖为基础进行编程的语言。用 Gradle 的话说,这意味着您可以定义任务和任务之间的依赖。Gradle 保证这些任务按照它们的依赖关系的顺序进行执行,并且每个任务都只执行一次。这些任务形成一个有向无环图。有许多构建工具在它们执行任务时生成了依赖关系图。Gradle 则在所有任务执行之前生成这这个完整的依赖关系图。这是 Gradle 的核心,它使得很多事情由不可能变得可能。
你的构建脚本能配置这个依赖关系图。因此严格来说,它们都是构建配置脚本。
一个 Gradle 构建具有三个明显的阶段。
Gradle 支持单项目及多项目的构建。在初始化阶段,Gradle 确定哪些项目是需要构建的,并为每个需要构建的项目创建一个Project
实例。
在这一阶段,对project对象进行配置。属于构建的所有 projects 的构建脚本都会被执行。Gradle 1.4 引入了一个孵化中的选择性加入的功能,叫configuration on demand。在此模式下,Gradle 只配置相关的项目(见第 56.1.1.1 节,“Configuration on demand")。
Gradle 确定在配置阶段中,创建和配置的要被执行的任务的子集。这个子集是由传递给gradle命令的任务名称参数和当前目录所决定的。然后 Gradle 执行每个选定的任务。
在构建脚本文件旁边,Gradle 定义了一个设置文件。这个设置文件是由 Gradle 通过一种命名约定来决定的。它的默认名称是settings.gradle
。在本章后面,我们会解释 Gradle 是怎么查找一个设置文件的。
这个配置文件会在初始化阶段执行。一个多项目的构建,必须在这个多项目的层次结构中的根目录下有一个settings.gradle
文件。因为在这个设置文件中,会定义哪些项目会加入这个多项目构建(见第56章,多项目构建)。对于单项目的构建,有没有设置文件都可以。你可能需要一个关于将libraries添加到你的构建脚本类路径中的例子(见第59章,组织构建逻辑)。我们先来看一个一个单项目构建:
示例 55.1. 单项目构建
settings.gradle
println 'This is executed during the initialization phase.'
build.gradle
println 'This is executed during the configuration phase.' task configured { println 'This is also executed during the configuration phase.' } task test << { println 'This is executed during the execution phase.' }
gradle test
的输出结果
> gradle test This is executed during the initialization phase. This is executed during the configuration phase. This is also executed during the configuration phase. :test This is executed during the execution phase. BUILD SUCCESSFUL Total time: 1 secs
对于一个构建脚本,属性的访问和方法的调用会委派给一个project对象。同样,在设置文件中的属性访问和方法调用也会委托给一个settings 对象。可以看一下 Settings
。
多项目构建是指在一次Gradle执行中,构建多个项目。你需要在settings文件中,声明要加入多项目构建的项目。关于多项目构建,在这一主题的章节中有更多的介绍(参见 第 56 章, 多项目构建)。
多项目构建总是由一个具有单个根节点的树表示。树中的每个元素表示一个项目。一个项目有一个路径,表示在多项目构建树中的位置。在大多数情况下,项目路径是符合在文件系统中项目的物理位置的。不过,这种行为是可配置的。项目树在settings.gradle
文件中创建。默认情况下,它假设设置文件的位置也是根项目的位置。但你可以在设置文件中重新定义根项目的位置。
在设置文件中你可以使用一套方法来生成项目树。分层和产面的物理布局,得到了特别的支持。
示例 55.2. 分层布局
settings.gradle
include 'project1', 'project2:child', 'project3:child1'
include
方法采用项目路径作为参数。项目路径被假定为等于物理文件系统的相对路径。例如,一个路径 'services:api',默认情况下映射到文件夹'services/api'(相对于项目根目录)。你只需要指定树上的叶子。这意味着,包含路径 'services:hotels:api' 将导致创建3个项目: 'services'、'services:hotels'和'services:hotels:api'。
示例 55.3. 平面布局
settings.gradle
includeFlat 'project3', 'project4'
includeFlat
方法将目录名称作为参数。这些目录必须存在,并且与根项目目录同级。这些目录的位置被认为是多项目树中根项目的子项目。
在设置文件中创建的多项目树组成了所谓的项目描述符。你可以在任何时间修改设置文件中的这些描述符。你可以这样访问一个描述符:
示例 55.4. 修改项目树中的元素
settings.gradle
println rootProject.name
println project(':projectA').name
使用这个描述符,你可以更改一个项目的名称、项目目录和构建文件。
示例 55.5. 修改项目树中的元素
settings.gradle
rootProject.name = 'main' project(':projectA').projectDir = new File(settingsDir, '../my-project-a') project(':projectA').buildFileName = 'projectA.gradle'
更多信息请参阅ProjectDescriptor
。
Gradle 是怎么知道这是单项目还是多项目构建的?如果你在一个有设置文件的目录中触发一个多项目构建,那很简单。但 Gradle 也允许你在属于这个构建的任何子项目中去执行构建。[20]如果你在没有settings.gradle
文件的项目中执行从 Gradle,Gradle 将执行以下操作︰
它将在一个与当前目录有相同嵌套层的叫做master
的目录里面搜索settings.gradle
如果没有找到settings.gradle
,它将在父目录里搜索settings.gradle
文件。
如果仍没有找到settings.gradle
文件,将作为一个单项目构建来执行。
如果找到了settings.gradle
文件,Gradle 会检查当前项目是否为找到的settings.gradle
文件中定义的多项目层次结构的一部分。如果不是,构建将作为一个单项目构建来执行。否则执行多项目构建。
这种行为的目的是什么?由于一些不明的原因, Gradle 不得不找出,你所进入的项目,它是否为一个多项目构建中的一个子项目。当然,如果它是一个子项目,那么就只构建子项目和它依赖的项目。但 Gradle 需要创建整个多项目构建的构建配置(见第56章,多项目生成)。通过-u
命令行选项,你可以让 Gradle 不去父目录找settings.gradle
文件。那么,当前项目将只会作为单项目构建。如果当前项目中包含一个settings.gradle
文件, -u
选项将没有任何意义。这样的构建总是这样执行:
单项目构建,如果settings.gradle
文件没有定义一个多项目的层次结构
多项目构建,如果settings.gradle
文件定义了一个多项目的层次结构。
自动搜索设置文件只适合具有物理上的层次结构或平面的布局的多项目构建。对于平面布局你必须另外服从上面描述的命名约定。Gradle 支持任意的物理布局的多项目构建。但对于这种任意的布局,你需要在设置文件所在的目录执行构建。有关如何从根目录运行部分的构建,见第 56.4章,“使用其绝对路径运行任务”。在我们下一版本中,我们想通过在命令行参数中,指定一个设置文件的路径,来从子项目中启用部分的构建。在构建中,Gradle 为每个参加构建的项目创建 Project 对象。对于单项目构建,只有一个project。对于多项目构建,则有在 Setting 对象中指定的project(加上根项目的project)。每个project 对象都有和其顶层目录名称一样的默认名称。除了根 project,每个project有一个父project,并且可能还有子projects。
对于一个单项目构建,初始化后阶段的工作流都相当简单。构建脚本会针对在初始化阶段期间创建的 project 对象执行。然后 Gradle 会查找与命令行传进来的参数相同的任务名。如果这些任务名称存在,它们以参数的顺序作为一个单独的构建来执行。关于多项目构建的配置与执行,在
在构建通过其生命周期的时候,你的构建脚本会接收到对应的通知。这些通知一般采取两种形式︰你也可以实现一个特定的监听接口,或提供一个用于在收到通知时执行的闭包。下面是使用闭包的例子。有关如何使用监听器接口的详细信息,请参阅 API 文档。
你可以在一个项目评估之前及之后立刻收到通知。这可以用来做一些事情,比如一旦构建脚本里的定义都被应用时执行额外的配置,或者做一些自定义的日志记录或分析。
下面是一个示例,将一个test
任务添加到每个hasTests
属性为 true 的project中。
示例 55.6. 向每个包含某些属性设置的project 添加测试任务
build.gradle
allprojects { afterEvaluate { project -> if (project.hasTests) { println "Adding test task to $project" project.task('test') << { println "Running tests for $project" } } } }
projectA.gradle
hasTests = true
gradle -q test
的输出结果
> gradle -q test Adding test task to project ':projectA' Running tests for project ':projectA'
上面的例子使用了Project.afterEvaluate()
方法添加一个在project 评估之后执行的闭包。
你也可以在每一个project 评估之后收到收到通知。下面的例子是在执行项目评估的自定义日志记录。注意,无论项目评估是成功还是由于异常而失败,都会收到afterProject
通知。
示例 55.7. 通知
build.gradle
gradle.afterProject {project, projectState -> if (projectState.failure) { println "Evaluation of $project FAILED" } else { println "Evaluation of $project succeeded" } }
gradle -q test
的输出结果
> gradle -q test Evaluation of root project 'buildProjectEvaluateEvents' succeeded Evaluation of project ':projectA' succeeded Evaluation of project ':projectB' FAILED
你也可以将一个ProjectEvaluationListener
添加到Gradle
中,来接收这些事件。
你可以在有任务添加到项目后立即收到通知。这可以用来在构建脚本中的任务可用之前,设置一些默认值或添加行为。
下面的示例是在每个任务创建之前设置srcDir
属性。
示例 55.8. 对所有的任务设置一些属性
build.gradle
tasks.whenTaskAdded { task -> task.srcDir = 'src/main/java' } task a println "source dir is $a.srcDir"
gradle -q a
的输出结果
> gradle -q a source dir is src/main/java
你还可以向TaskContainer
添加一个Action
来接收这些事件。
在任务执行图生成之后你可以立刻收到一个通知。在第 6.13 章,“使用DAG配置”我们已经看到进了。
你也可以向TaskExecutionGraph
添加一个TaskExecutionGraphListener
来接收这些事件。
在任一任务执行前你都会马上收到一条通知。
下面的示例展示了在每个任务执行的开始及结束时打印日志。注意,无论任务成功完成还是异常失败,都会收到afterTask
的通知。
示例55.9. 在每个任务执行的开始及结束时打印日志
build.gradle
task ok task broken(dependsOn: ok) << { 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
的输出结果
> gradle -q broken executing task ':ok' ... done executing task ':broken' ... FAILED
你也可以对TaskExecutionGraph
使用一个TaskExecutionListener
来接收这些事件。
[20] Gradle 支持部分多项目构建(参见 第 56 章, 多项目建)。