第十三章 编写构建脚本

Chapter 13. Writing Build Scripts

这一章着眼于一些编写构建脚本的详细信息。
This chapter looks at some of the details of writing a build script.

13.1. Gradle 构建语言

13.1. The Gradle build language

Gradle 提供一种 领域特定语言 或者说是 DSL,来描述构建。这种构建语言基于 Groovy 中,并进行了一些补充,使其易于描述构建。
Gradle provides a domain specific language , or DSL, for describing builds. This build language is based on Groovy, with some additions to make it easier to describe a build.

13.2. Project API

13.2. The Project API

第 7 章, Java 快速入门 的教程中,我们使用了 apply () 方法。这方法从何而来?我们之前说在 Gradle 中构建脚本定义了一个项目(project)。在构建的每一个项目中,Gradle 创建了一个 Project 类型的实例,并在构建脚本中关联此 Project 对象。当构建脚本执行时,它会配置此 Project 对象:
In the tutorial in Chapter 7, Java Quickstart we used, for example, the apply() method. Where does this method come from? We said earlier that the build script defines a project in Gradle. For each project in the build, Gradle creates an instance of type Project and associates this Project object with the build script. As the build script executes, it configures this Project object:

获取有关编写构建脚本帮助

Getting help writing build scripts

不要忘记您的构建脚本是简单的 Groovy 代码,并驱动着 Gradle API。并且 Project 接口是您在 Gradle API 中访问一切 的入点。所以,如果你想知道什么 '标签(tag)' 在构建脚本中可用,您可以去看 项目 接口的文档。
Don't forget that your build script is simply Groovy code that drives the Gradle API. And the Project interface is your starting point for accessing everything in the Gradle API. So, if you're wondering what 'tags' are available in your build script, you can start with the documentation for the Project interface.

  • 在构建脚本中,你所调用的任何一个方法,如果在构建脚本中 未定义 ,它将被委托给 Project 对象。
    Any method you call in your build script, which is not defined in the build script, is delegated to the Project object.

  • 在构建脚本中,你所访问的任何一个属性,如果在构建脚本里 未定义 ,它也会被委托给 Project 对象。
    Any property you access in your build script, which is not defined in the build script, is delegated to the Project object.

下面我们来试试这个,试试访问 Project 对象的 name 属性。
Let's try this out and try to access the name property of the Project object.

示例 13.1. 访问 Project 对象的属性
Example 13.1. Accessing property of the Project object

build.gradle

println name
println project.name

gradle -q check 的输出结果
Output of gradle -q check

> gradle -q check
projectApi
projectApi

这两个 println 语句打印出相同的属性。在生成脚本中未定义的属性,第一次使用时自动委托到 Project 对象。其他语句使用了在任何构建脚本中可以访问的 project 属性,则返回关联的 Project 对象。只有当您定义的属性或方法 Project 对象的一个成员相同名字时,你才需要使用 project 属性。
Both println statements print out the same property. The first uses auto-delegation to the Project object, for properties not defined in the build script. The other statement uses the project property available to any build script, which returns the associated Project object. Only if you define a property or a method which has the same name as a member of the Project object, you need to use the project property.

13.2.1. 标准project属性

13.2.1. Standard project properties

Project 对象提供了一些在构建脚本中可用的标准的属性。下表列出了常用的几个属性。
The Project object provides some standard properties, which are available in your build script. The following table lists a few of the commonly used ones.

表 13.1. Project属性

名称 类型 默认值
project Project Project 实例
name String 项目目录的名称。
path String 项目的绝对路径。
description String 项目的描述。
projectDir File 包含生成脚本的目录。
buildDir File projectDir /build
group Object 未指定
version Object 未指定
ant AntBuilder AntBuilder 实例

Table 13.1. Project Properties

Name Type Default Value
project Project The Project instance
name String The name of the project directory.
path String The absolute path of the project.
description String A description for the project.
projectDir File The directory containing the build script.
buildDir File projectDir /build
group Object unspecified
version Object unspecified
ant AntBuilder An AntBuilder instance

13.3. Script API

13.3. The Script API

当 Gradle 执行一个脚本时,它将脚本编译为一个实现了 Script 接口的类。这意味着所有由该 Script 接口声明的属性和方法在您的脚本中是可用的。
When Gradle executes a script, it compiles the script into a class which implements Script . This means that all of the properties and methods declared by the Script interface are available in your script.

13.4. 声明变量

13.4. Declaring variables

有两类可以在生成脚本中声明的变量: 局部变量和额外属性。
There are two kinds of variables that can be declared in a build script: local variables and extra properties.

13.4.1. 局部变量局部

13.4.1. Local variables

局部变量是用 def 关键字声明的。它们只在定义它们的范围内可以被访问。局部变量是Groovy 语言底层的一个特征。
Local variables are declared with the def keyword. They are only visible in the scope where they have been declared. Local variables are a feature of the underlying Groovy language.

示例 13.2. 使用局部变量
Example 13.2. Using local variables

build.gradle

def dest = "dest"

task copy(type: Copy) {
    from "source"
    into dest
}

13.4.2. 额外属性

13.4.2. Extra properties

Gradle 的域模型中,所有增强的对象都可以容纳用户定义的额外的属性。这包括但并不限于项目(project)、任务(task)和源码集(source set)。额外的属性可以通过所属对象的 ext 属性进行添加,读取和设置。或者,可以使用 ext 块同时添加多个属性。
All enhanced objects in Gradle's domain model can hold extra user-defined properties. This includes, but is not limited to, projects, tasks, and source sets. Extra properties can be added, read and set via the owning object's ext property. Alternatively, an ext block can be used to add multiple properties at once.

示例 13.3. 使用额外属性
Example 13.3. Using extra properties

build.gradle

apply plugin: "java"

ext {
    springVersion = "3.1.0.RELEASE"
    emailNotification = "build@master.org"
}

sourceSets.all { ext.purpose = null }

sourceSets {
    main {
        purpose = "production"
    }
    test {
        purpose = "test"
    }
    plugin {
        purpose = "production"
    }
}

task printProperties << {
    println springVersion
    println emailNotification
    sourceSets.matching { it.purpose == "production" }.each { println it.name }
}

gradle -q printProperties 的输出结果
Output of gradle -q printProperties

> gradle -q printProperties
3.1.0.RELEASE
build@master.org
main
plugin

在此示例中, 一个 ext 代码块将两个额外属性添加到 project 对象中。此外,通过将 ext.purpose 设置为 nullnull 是一个允许的值),一个名为 purpose 的属性被添加到每个源码集(source set)。一旦属性被添加,他们就可以像预定的属性一样被读取和设置。
In this example, an ext block adds two extra properties to the project object. Additionally, a property named purpose is added to each source set by setting ext.purpose to null ( null is a permissible value). Once the properties have been added, they can be read and set like predefined properties.

通过添加属性所要求特殊的语法,Gradle 可以在你试图设置 (预定义的或额外的) 的属性,但该属性拼写错误或不存在时 fail fast。 [ 5 ] 额外属性在任何能够访问它们所属的对象的地方都可以被访问,这使它们有着比局部变量更广泛的作用域。父项目上的额外属性,在子项目中也可以访问。
By requiring special syntax for adding a property, Gradle can fail fast when an attempt is made to set a (predefined or extra) property but the property is misspelled or does not exist. [ 5 ] Extra properties can be accessed from anywhere their owning object can be accessed, giving them a wider scope than local variables. Extra properties on a parent project are visible from subprojects.

有关额外属性和它们的 API 的详细信息,请参阅 ExtraPropertiesExtension
For further details on extra properties and their API, see ExtraPropertiesExtension .

13.5.一些 Groovy 的基础知识

13.5. Some Groovy basics

Groovy 提供了用于创建 DSL 的大量特点,并且 Gradle 构建语言利用了这些特点。了解构建语言是如何工作的,将有助于你编写构建脚本,特别是当你开始写自定义插件和任务的时候。
Groovy provides plenty of features for creating DSLs, and the Gradle build language takes advantage of these. Understanding how the build language works will help you when you write your build script, and in particular, when you start to write custom plugins and tasks.

13.5.1. Groovy JDK

13.5.1. Groovy JDK

Groovy 对 JVM 的类增加了很多有用的方法。例如, iterable 新增的 each 方法,会对 iterable 的元素进行遍历:
Groovy adds lots of useful methods to JVM classes. For example, Iterable gets an each method, which iterates over the elements of the Iterable :

示例 13.4. Groovy JDK 的方法
Example 13.4. Groovy JDK methods

build.gradle

// Iterable gets an each() method
configurations.runtime.each { File f -> println f }

可以看看 http://groovy.codehaus.org/groovy-jdk/ ,了解更多详细信息。
Have a look at http://groovy.codehaus.org/groovy-jdk/ for more details.

13.5.2. 属性访问器

13.5.2. Property accessors

Groovy 会自动地把一个属性的引用转换为对适当的 getter 或 setter 方法的调用。
Groovy automatically converts a property reference into a call to the appropriate getter or setter method.

例子 13.5. 属性访问器
Example 13.5. Property accessors

build.gradle

// Using a getter method
println project.buildDir
println getProject().getBuildDir()

// Using a setter method
project.buildDir = 'target'
getProject().setBuildDir('target')

13.5.3 括号可选的方法调用

13.5.3. Optional parentheses on method calls

调用方法时括号是可选的。
Parentheses are optional for method calls.

示例 13.6. 不带括号的方法调用
Example 13.6. Method call without parentheses

build.gradle

test.systemProperty 'some.prop', 'value'
test.systemProperty('some.prop', 'value')

13.5.4. List 和 Map

13.5.4. List and map literals

Groovy 提供了一些定义 ListMap 实例的简洁写法。
Groovy provides some shortcuts for defining List and Map instances.

示例 13.7. List and map
Example 13.7. List and map literals

build.gradle

// List literal
test.includes = ['org/gradle/api/**', 'org/gradle/internal/**']

List<String> list = new ArrayList<String>()
list.add('org/gradle/api/**')
list.add('org/gradle/internal/**')
test.includes = list

// Map literal
apply plugin: 'java'

Map<String, String> map = new HashMap<String, String>()
map.put('plugin', 'java')
apply(map)

13.5.5.作为方法最后一个参数的闭包

13.5.5. Closures as the last parameter in a method

Gradle DSL 在很多地方使用闭包。你可以 在这里 查看更多有关闭包的资料。当方法的最后一个参数是一个闭包时,你可以把闭包放在方法调用之后:
The Gradle DSL uses closures in many places. You can find out more about closures here . When the last parameter of a method is a closure, you can place the closure after the method call:

示例 13.8. 作为方法参数的闭包
Example 13.8. Closure as method parameter

build.gradle

repositories {
    println "in a closure"
}
repositories() { println "in a closure" }
repositories({ println "in a closure" })

13.5.6. 闭包委托(delegate)

13.5.6. Closure delegate

每个闭包都有一个 委托 对象,Groovy 使用它来查找变量和方法的引用,而不是作为闭包的局部变量或参数。Gradle 在 配置闭包 中使用到它,把 委托 对象设置为被配置的对象。
Each closure has a delegate object, which Groovy uses to look up variable and method references which are not local variables or parameters of the closure. Gradle uses this for configuration closures , where the delegate object is set to the object to be configured.

示例 13.9. 闭包委托
Example 13.9. Closure delegates

build.gradle

dependencies {
    assert delegate == project.dependencies
    compile('junit:junit:4.11')
    delegate.compile('junit:junit:4.11')
}



[ 5 ] 截至 Gradle 1.0-milestone-9版本,我们鼓励但不强制要求使用 ext 来添加额外属性因此,当未知的属性被设置时,Gradle不会构建失败。然而,它将打印一个警告。
[ 5 ] As of Gradle 1.0-milestone-9, using ext to add extra properties is strongly encouraged but not yet enforced. Therefore, Gradle will not fail when an unknown property is set. However, it will print a warning.