这一章着眼于编写构建脚本的一些细节。
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.
构建脚本可以包含任何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.
在教程《第七章,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.
在构建脚本中,你所调用的任何方法,如果在构建脚本中没有定义,那么它将被委托给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属性 - 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 |
|
group |
Object |
未指定 unspecified |
version |
Object |
未指定 unspecified |
ant |
AntBuilder |
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域模型中,所有增强的对象都可以保存用户定义的额外属性。这包括但不限于项目(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
设置为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 可以在你试图设置(预定义的或额外的) 属性,但拼写错误或不存在时快速失败。额外属性在任何能够访问它们所属对象的地方都可以被访问,这使它们有着比局部变量更广的作用域。父项目上的额外属性,在子项目中也可以访问。
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
.
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对标准的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.
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 和 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') }