第十三章. 编写构建脚本

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.

构建脚本可以包含任何Groovy语言元素。 [5] Gradle假定每个构建脚本都是使用UTF-8编码。
A build script can contain any Groovy language element. [5] Gradle assumes that each build script is encoded using UTF-8.

13.2. Project API

13.2. The Project API

在教程《第七章,Java快速入门》中,我们使用了apply()方法。这种方法从何而来?我们以前说过,构建脚本在Gradle中定义了一个项目。对于构建中的每个项目,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 object of type Project and associates this Project object with the build script. As the build script executes, it configures this Project object:

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

不要忘了,你的构建脚本只是简单的可以调用Gradle API的Groovy代码。而且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.

Getting help writing build scripts
  • 在构建脚本中,你所调用的任何方法,如果在构建脚本中没有定义,那么它将被委托给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属性 - Table 13.1. Project Properties

名称
Name
类型
Type
默认值
Default Value
project 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 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域模型中,所有增强的对象都可以保存用户定义的额外属性。这包括但不限于项目(projects),任务(tasks)和源码集(source sets)。额外属性可以通过所属对象的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 可以在你试图设置(预定义的或额外的) 属性,但拼写错误或不存在时快速失败。额外属性在任何能够访问它们所属对象的地方都可以被访问,这使它们有着比局部变量更广的作用域。父项目上的额外属性,在子项目中也可以访问。
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. 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 project are visible from its 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对标准的Java类添加了许多有用的方法。例如,Iterable新增了each方法,可以对Iterable的元素进行遍历:
Groovy adds lots of useful methods to the standard Java 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 和 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] 除语句标签之外的任何语言元素。
[5] Any language element except for statement labels.