Chapter 6. Build Script Basics 构建脚本的基本

在 Gradle 中两个顶级概念:project(项目)和 task 任务)

所有 Gradle 都有一个或多个 project 构成。project 的展现取决于 Gradle 所做的工作。举例。 project 可以是一个 JAR 库 或者是 web 应用。它可以是由项目生产 JAR 组成发布的 ZIP。一个 project 不一定
代表一个东西要构建。它可能是一件要做的事,如将应用程序部署到工作台
或生产环境。如果这看起来有点模糊,现在不要担心。Gradle 基于约定的构建支持增加一个 更具体的定义的 project。

每个项目都是由一个或多个 task。一个 task 代表了一个构建生成的原子的作品。这可能是编写一些类,创建一个 JAR ,生成 Javadoc,或发布一些库。

现在,我们将看看在构建一个 project 时定义一些简单的 task 。后面的章节将介绍多个 project 和更多的 task 。

6.2. Hello world

运行 Gradle 是使用 gradle 命令行。命令行会寻找项目的根目录下 build.gradle 的文件(有关命令行,详见 Appendix D. Gradle Command Line 命令行),这个就是构建的脚本,或者严格说是构建的配置脚本。他定义了project(项目)和 task 任务)。

尝试输出,创建一个 build.gradle 命名的文件:

Example 6.1. Your first build script

build.gradle

  1. task hello {
  2. doLast {
  3. println 'Hello world!'
  4. }
  5. }

命令行切换到包含 build.gradle 文件的目录,执行 gradle -q hello

gradle601.jpg

Example 6.2. Execution of a build script

输出为:

  1. > gradle -q hello
  2. Hello world!

gradle602.jpg

这个脚本定义了一个 名字是hello 的 task,并且添加了动作。当运行 gradle hello ,Gradle 执行这个 hello task,接着执行里面的动作。这里的动作只是简单的包含了一些可以执行的 Groovy 代码。

看上去很像 Ant ,不错,Gradle task 是相当于 Ant 的 target,但是你将看到,他们更强大。我们使用了跟 Ant 不同的术语,因为 task 比 target 更富表现力。

不幸的是,这一术语与 Ant 有冲突,Ant 调用它的命令行,如 javac 或copy 称之为 task 。所以当我们谈论的 task ,默认说的是 Gradle task ,这是相当 Ant 的 target。如果我们谈论的 Ant 的 task (Ant 命令),我们明确地说的 Ant task。

命令行加中 -q 的作用

q 是 quiet 的简写,意思是要安静、干净的输出。如果不加 -q 则会输出日志。详见Chapter 18. Logging 日志.下面是对比

gradle604.jpg

6.3. A shortcut task definition 快捷 task 定义

定义 task 可以使用快捷方式,这样更简明。

Example 6.3. A task definition shortcut

build.gradle

  1. task hello << {
  2. println 'Hello world!'
  3. }

再次执行,得到相同的输出。在下面的文章中,我们都会采用这种定义方式。

gradle605.jpg

6.4. Build scripts are code 构建的脚本都是代码

工具的构建脚本给你完整的 Groovy 的功能。作为开胃菜,看看这个:

Example 6.4. Using Groovy in Gradle’s tasks

build.gradle

  1. task upper << {
  2. String someString = 'mY_nAmE'
  3. println "Original: " + someString
  4. println "Upper case: " + someString.toUpperCase()
  5. }

执行 gradle -q upper 输出

  1. > gradle -q upper
  2. Original: mY_nAmE
  3. Upper case: MY_NAME

gradle606.jpg

或者

Example 6.5. Using Groovy in Gradle’s tasks

build.gradle

  1. task count << {
  2. 4.times { print "$it " }
  3. }

执行 gradle -q count 输出

  1. > gradle -q count
  2. 0 1 2 3

gradle607.jpg

6.5. Task dependencies 依赖

可以声明 task 与 其他 task 的依赖

Example 6.6. Declaration of task that depends on other
task

build.gradle

  1. task hello << {
  2. println 'Hello world!'
  3. }
  4. task intro(dependsOn: hello) << {
  5. println "I'm Gradle"
  6. }

执行 gradle -q intro 输出

  1. > gradle -q intro
  2. Hello world!
  3. I'm Gradle

gradle608.jpg

添加一个依赖,相应的 task 不需要存在

Example 6.7. Lazy dependsOn - the other task does not exist (yet)

build.gradle

  1. task taskX(dependsOn: 'taskY') << {
  2. println 'taskX'
  3. }
  4. task taskY << {
  5. println 'taskY'
  6. }

执行 gradle -q taskX输出

  1. > gradle -q taskX
  2. taskY
  3. taskX

gradle609.jpg

taskX 的依赖 taskY 是在 taskY 定义之前 声明的。这个在多 project 构建时很重要。关于 task 的依赖详见 Chapter 15. More about Tasks 更多关于任务

请注意不要使用快捷符号,当引用的 task 还没有定义的情况下。

6.6. Dynamic tasks 动态 task

Groovy 的能力不仅仅是定义一个 task。例如,你也可以用它来动态创建的 task。

Example 6.8. Dynamic creation of a task

build.gradle

  1. 4.times { counter ->
  2. task "task$counter" << {
  3. println "I'm task number $counter"
  4. }
  5. }

执行 gradle -q task1 输出

  1. > gradle -q task1
  2. I'm task number 1

gradle610.jpg

6.7. Manipulating existing tasks 利用现有的任务

一旦 task 创建,他们可以通过一个 API 访问。例如,在运行时您可以使用此动态添加依赖到 task 。Ant 不允许这样的事情。

Example 6.9. Accessing a task via API - adding a dependency

build.gradle

  1. 4.times { counter ->
  2. task "task$counter" << {
  3. println "I'm task number $counter"
  4. }
  5. }
  6. task0.dependsOn task2, task3

执行 gradle -q task0 输出

  1. > gradle -q task0
  2. I'm task number 2
  3. I'm task number 3
  4. I'm task number 0

或者 可以添加行为到一个已经存在 task 中

Example 6.10. Accessing a task via API - adding behaviour

build.gradle

  1. task hello << {
  2. println 'Hello Earth'
  3. }
  4. hello.doFirst {
  5. println 'Hello Venus'
  6. }
  7. hello.doLast {
  8. println 'Hello Mars'
  9. }
  10. hello << {
  11. println 'Hello Jupiter'
  12. }

执行 gradle -q hello 输出

  1. > gradle -q hello
  2. Hello Venus
  3. Hello Earth
  4. Hello Mars
  5. Hello Jupiter

gradle611.jpg

doFirst 和 doLast 可以多次执行调用。他们在开始或结束的 task 动作清单中添加动作。task 执行时,按动作列表的顺序执行的动作。操作符 << 仅仅是 doLast 的别名。

6.8. Shortcut notations 快捷符号

在前面的示例中已经注意到,有一个方便的符号访问现有的
task 。每个 task 可以作为构建脚本的一个属性:

Example 6.11. Accessing task as a property of the build script

build.gradle

  1. task hello << {
  2. println 'Hello world!'
  3. }
  4. hello.doLast {
  5. println "Greetings from the $hello.name task."
  6. }

执行 gradle -q hello输出

  1. > gradle -q hello
  2. Hello world!
  3. Greetings from the hello task.

gradle612.jpg

这使得代码可读性增强,尤其是当使用的插件提供的 task ,如 compile task

6.9. Extra task properties 额外 task 属性

可以添加自己属性到 task ,添加 myProperty属性,设置 、ext.myProperty 初始值,从这一点上,该属性可以读取和设置就像一个预定义的任务属性。

Example 6.12. Adding extra properties to a task

build.gradle

  1. task myTask {
  2. ext.myProperty = "myValue"
  3. }
  4. task printTaskProperties << {
  5. println myTask.myProperty
  6. }

执行 gradle -q printTaskProperties输出

  1. > gradle -q printTaskProperties
  2. myValue

gradle613.jpg

task 不对额外属性做限制,更多详见Chapter 13. Writing Build Scripts 编写构建脚本 中 13.4.2 节 “Extra properties”.

6.10. Using Ant Tasks 使用 Ant task

Ant task 是 Gradle 一等公民。 Gradle 给 Ant task 提供了不错的整合通过简单依靠于 Gradle 。Groovy 被奇异的 AntBuilder 装载。从 Gradle 使用 Ant task 比使用 build.xml 文件更方便和更强大。从下面的例子中,你可以学习如何执行 Ant task 和如何访问 Ant 属性:

Example 6.13. Using AntBuilder to execute ant.loadfile target

build.gradle

  1. task loadfile << {
  2. def files = file('antLoadfileResources').listFiles().sort()
  3. files.each { File file ->
  4. if (file.isFile()) {
  5. ant.loadfile(srcFile: file, property: file.name)
  6. println " *** $file.name ***"
  7. println "${ant.properties[file.name]}"
  8. }
  9. }
  10. }

执行 gradle -q loadfile输出

  1. > gradle -q loadfile
  2. *** agile.manifesto.txt ***
  3. Individuals and interactions over processes and tools
  4. Working software over comprehensive documentation
  5. Customer collaboration over contract negotiation
  6. Responding to change over following a plan
  7. *** gradle.manifesto.txt ***
  8. Make the impossible possible, make the possible easy and make the easy elegant.
  9. (inspired by Moshe Feldenkrais)

gradle614.jpg

更多关于 构建脚本中使用 Ant ,详见 Chapter 17. Using Ant from Gradle 从 Gradle 使用 Ant

6.11. Using methods 使用方法

Gradle 延伸取决你如何组织的建造逻辑。上面的例子中的第一级别的组织你的构建逻辑,是提取方法。

Example 6.14. Using methods to organize your build logic

build.gradle

  1. task checksum << {
  2. fileList('../antLoadfileResources').each {File file ->
  3. ant.checksum(file: file, property: "cs_$file.name")
  4. println "$file.name Checksum: ${ant.properties["cs_$file.name"]}"
  5. }
  6. }
  7. task loadfile << {
  8. fileList('../antLoadfileResources').each {File file ->
  9. ant.loadfile(srcFile: file, property: file.name)
  10. println "I'm fond of $file.name"
  11. }
  12. }
  13. File[] fileList(String dir) {
  14. file(dir).listFiles({file -> file.isFile() } as FileFilter).sort()
  15. }

执行 gradle -q loadfile输出

  1. > gradle -q loadfile
  2. I'm fond of agile.manifesto.txt
  3. I'm fond of gradle.manifesto.txt

gradle615.jpg

以后你会发现这样的方法可以在多 project 构建的子 project 之间共享。如果你建立逻辑变得越来越复杂,Gradle 为您提供其他工具很方便的方式来组织它。我们有专门一章Chapter 60. Organizing Build Logic

6.12. Default tasks 默认 task

Gradle 允许你定义一个或多个默认 task 给你的构建

Example 6.15. Defining a default tasks

build.gradle

  1. defaultTasks 'clean', 'run'
  2. task clean << {
  3. println 'Default Cleaning!'
  4. }
  5. task run << {
  6. println 'Default Running!'
  7. }
  8. task other << {
  9. println "I'm not a default task!"
  10. }

执行 gradle -q 输出

  1. > gradle -q
  2. Default Cleaning!
  3. Default Running!

gradle617.jpg

这个等于执行了 gradle clean run ,在多 project 中构建所有的子 project 都可以有自己具体的默认 task 。如果 子 project 没有明确的默认 task,则执行父 project 的默认 task(如果定义的话)

6.13. Configure by DAG 通过 DAG 配置

以后会详细描述(见Chapter 56. The Build Lifecycle 构建生命周期 ),Gradle 有配置阶段和执行阶段。配置阶段后,Gradle 知道所有的 task 应该执行。Gradle 提供给你一个钩子来利用这些信息。这个用例将检查发布 的 task 是否是要执行的 task。基于此,你可以赋予不同的值到一些变量。

在下面的例子中,在不同 version 变量中的 distribution 和 release task 执行结果不同。

Example 6.16. Different outcomes of build depending on chosen tasks

build.gradle

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

执行 gradle -q distribution 输出

  1. > gradle -q distribution
  2. We build the zip with version=1.0-SNAPSHOT

执行 gradle -q release 输出

  1. > gradle -q release
  2. We build the zip with version=1.0
  3. We release now

gradle618.jpg

whenReady影响了 release task 在 release task 被执行之前。同样适用于 release task 不是 主 task 的情况(比如,task 被 gradle 命令通过了)

6.14. Where to next? 下一步工作

本章,我们大概浏览了下 task ,但这不是 task 的全部,可以详见Chapter 15. More about Tasks 更多关于任务

另外,继续教程 Chapter 7. Java Quickstart 快速开始 JavaChapter 8. Dependency Management Basics 依赖管理的基础知识.md