这一章着眼于一些编写构建脚本的详细信息。
This chapter looks at some of the details of writing a build script.
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.
在
第 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:
不要忘记您的构建脚本是简单的 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.
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
|
|
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
|
|
group
|
Object
|
unspecified
|
version
|
Object
|
unspecified
|
ant
|
AntBuilder
|
An
AntBuilder
instance
|
当 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.
有两类可以在生成脚本中声明的变量: 局部变量和额外属性。
There are two kinds of variables that can be declared in a build script: local variables and extra properties.
局部变量是用
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 }
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
设置为
null
(
null
是一个允许的值),一个名为
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
.
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.
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.
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')
调用方法时括号是可选的。
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')
Groovy 提供了一些定义
List
和
Map
实例的简洁写法。
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)
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" })
每个闭包都有一个
委托
对象,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.