编写构建脚本

这一章我们将要深入的学习如何编写构建脚本.

Gradle 构建语言

Gradle 是以 Groovy 语言为基础, 基于DSL (领域特定语言) 语法的自动化构建工具,但是它增加了一些额外的特性,这使得Gradle更加的容易去阐释构建.

一个构建脚本能够包含任何Groovy语言的元素 ( Any language element except for statement labels ), 每个构建脚本都使用UTF-8编码.

项目 API

在第七章 Java构建入门那部分我们使用了 apply() 方法,这个方法是从哪里来的呢? 我们之前说过Gradle在构建脚本中定义了一个项目. 对于构建脚本中每个项目,Gradle 都创建了一个 Project 类型的对象用来关联此项目. 当构建脚本执行时,它会去配置所关联的工程对象.

  • 构建脚本中每个被调用的方法(这些方法并未在构建脚本中定义)都被委托给当前工程对象(使用工程对象引用方法)。

  • 构建脚本中每个被操作的属性(这些属性并未在构建脚本中定义)都被委托给当前工程对象(使用工程对象引用属性).

让我们尝试下如何操作工程对象的属性.

例子:13.1 操作工程对象的属性

build.gradle

println name
println project.name

使用 gradle -q check 命令输出结果:

> gradle -q check
projectApi
projectApi

如您所见,两个 println 语句都输出了相同的属性,第一个输出使用的是自动委托 ( auto-delegation ), 因为当前属性并没有在构建脚本中定义. 另一个语句使用了项目一个属性,这个属性在任何构建脚本中都可用,它的返回值是被关联的工程对象. 只有当您定义了一个属性或者一个方法, 它的名字和工程对象的某个成员的名字相同时, 你应该使用项目属性.

标准项目属性

Project 对象提供了一些标准的属性,您可以在构建脚本中很方便的使用他们. 下面列出了常用的属性:

Name Type Default Value
project Project Project 实例对象
name String 项目目录的名称
path String 项目的绝对路径
description String 项目描述
projectDir File 包含构建脚本的目录
build File projectDir/build
group Object 未具体说明
version Object 未具体说明
ant AntBuilder Ant实例对象

建议:

不要忘记我们的构建脚本只是个很简单的 Groovy 代码 ,不过它会再调用 Gradle APIProject 接口通过调用 Gradle API 让我们可以操作任何事情,因此如果你想知道哪个标签('tags') 可以在构建脚本种使用,您可以翻阅 Project 接口的的说明文档.

脚本 API

当 Gradle 执行一个脚本时,它会将这个脚本编译为实现了 Script 的类. 也就是说所有的属性和方法都是在 Script 接口中声明的,由于你的脚本实现了 Script 接口,所以你可以在自己的脚本中使用它们.

声明变量

在 Gradle 构建脚本中有两种类型的变量可以声明:局部变量 ( local ) 和 扩展属性 ( extra ) .

局部变量

局部变量使用关键字 def 来声明,其只在声明它的地方可见 . 局部变量是 Groovy 语言的一个基本特性.

例子 13.2 . 使用局部变量

    def dest = "dest"

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

    }

扩展属性

在 Gradle 领域模型中所有被增强的对象能够拥有自己定义的属性. 这包括,但不仅限于 projects , tasks , 还有 source sets . Project 对象可以添加,读取,更改扩展的属性. 另外,使用 ext 扩展块可以一次添加多个属性.

例子 13.3. 使用扩展属性

build.gradle

apply plugin: "java"

ext {
    springVersion = "3.1.0.RELEASE"
    emailNotification = "[email protected]"
}

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输出结果

> gradle -q printProperties
3.1.0.RELEASE
[email protected]
main
plugin

在上面的例子中,一个 ext 扩展块向 Project 对象添加了两个扩展属性. 名为 perpose 的属性被添加到每个 source set,然后设置 ext.purpose 等于 null ( null值是被允许的 ). 当这些扩展属性被添加后,它们就像预定义的属性一样可以被读取,更改值.

例子中我们通过一个特殊的语句添加扩展属性,当您试图设置一个预定义属性或者扩展属性,但是属性名拼写错误或者并不存在时,操作就会失败. Project 对象可以在任何地方使用其扩展属性 ,它们比局部变量有更大的作用域. 一个项目的扩展属性对其子项目也可见.

关于扩展属性更多的细节还有它的API,请看 ExtraPropertiesExtension 类的 API 文档说明.

Groovy 基础

Groovy 提供了大量的特性用来创建 DSL. Gradle 构建语言知道 Groovy 语言的工作原理,并利用这些特性帮助您编写构建脚本,特别是您在编写 plugin 或者 task 的时候,你会觉得很方便.

Groovy JDK

Groovy 在 Java 基础上添加了很多有用的方法. 例如,Iterable 有一个 each 方法, 通过使用 each 方法,我们可以迭代出 Iterable 中的每一个元素:

例子: 13.4.Groovy JDK 方法

build.gradle

configuration.runtime.each { File f -> println f }

更多内容请阅读 http://groovy.codehaus.org/groovy-jdk/

属性存取器

Groovy 自动将一个属性的引用转换为相应的 getter 或 setter 方法.

例子: 13.5. 属性存取器

// 使用 getter 方法
println project.buildDir
println getProject().getBuildDir()

// 使用 setter 方法
project.buildDir = 'target'
getProject().setBuildDir('target')

可有可无的圆括号

在调用方法时,圆括号可有可无,是个可选的.

例子: 13.6.不使用圆括号调用方法

build.gradle

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

List 和 Map 集合

Groovy 为预定义的 List 和 Map 集合提供了一些操作捷径,这两个字面值都比较简单易懂,但是 Map 会有一些不同.

例如,当您使用 "apply" 方法使用插件时,apply 会自动加上 Map 的一个参数,当您这样写 " apply plugin: 'java' "时,实际上使用的是 name 参数(name-value),只不过在 Groovy 中 使用 Map 没有 < > ,当方法被调用的时候,name 参数就会被转换成 Map 键值对,只不过在 Groovy 中看起来不像一个 Map.

**例子 13.7.List 和 Map 集合

build.gradle

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

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

// Map 集合
Map<String,String> map = [key1:'value1', key2:'valu2']

// Groovy 会强制将Map的键值对转换为只有value的映射
apply plugin: 'java'

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

Gradle DSL 在很多地方使用闭包,这里我们将讨论更多关于闭包的使用. 当一个方法的最后一个参数是一个闭包时,您可以在方法调用后放置一个闭包.

例子: 13.8.闭包作为方法的参数

build.gradle

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

闭包委托对象

每个闭包都有一个委托对象,当闭包既不是局部变量也不是作为方法参数时,Groovy 使用委托对象查找变量和方法引用. 当委托对象被用来管理时,Gradle 使用它来管理闭包.

例子 13.9.闭包引用

build.gradle

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