Contents

  • Projects,plugins and tasks
  • Hello world
  • Build script are code
  • Task dependencies
  • Flexible task registration
  • Manipulating existing tasks
  • Using Ant Tasks
  • Using methods
  • Default tasks
  • External dependencies for the build script
  • Futher Reading

如何编写Gradle 构建脚本,这里使用了基本的例子解释Gradle 基本的功能 …
它能够帮助你理解基础概念 ..
特别是如果你从其他构建工具迁移到Gradle(例如Ant) 并且你想理解它们的不同以及优势 …
然而,为了从一个标准的项目构建开始,你不需要完全详细的理解这些概念 … 相反你能够进行快速的认识,可以从它开始 step-by-step samples .

Project,plugins and tasks

每一个Gradle 由一个或者多个项目组成 … 什么样的项目取决于你想要Gradle 用来干什么 … 举个例子,一个项目可能是库 或者 web 应用 ..
它可能呈现了为zip 发行文件(根据由其他项目生产的Jar 进行打包而成) … .
一个项目并没有必要一定要构建东西 .. 它可能代表一件可以完成的事情,例如部署你的应用到 阶段或者 生产环境 …
如果你现在看的有一点小小的模糊,混乱,请不要担心 ..
Gradle 的通过约定的构建支持 增加了更多具体的定义 - 决定一个项目到底是什么 …

Gradle 的工作能够在项目上是通过定义一个或者多个任务完成的 ..
一个任务呈现了构建执行的某一个工作的原子块 …
它可能编译某些类 或者创建一个JAR ,生成Javadoc 或者发布某些归档文件到Maven 仓库 ..
通常来说,任务通过应用插件的形式进行提供, 因此你不需要自己去定义它们 .. 仍然,为了给你一个思考,任务到底是什么? 我们将尝试在构建中的一个项目中定义某些简单的任务 ..

Hello world

你能够使用gradle 命令运行Gradle 构建 … gradle 命令在当前的目录中寻找一个叫做build.gradle 的文件 …
我们叫它为构建脚本,尽管严格的说法它是叫做 构建配置脚本,这个构建脚本将会定义一个项目以及它的任务 ..
首先创建一个build.gradle,并创建一个任务 …

  1. tasks.register('hello') {
  2. doLast {
  3. println 'Hello world!'
  4. }
  5. }

在命令行环境下,迁移到包含这个脚本的目录并执行构建脚本 去执行任务 …

  1. > gradle -q hello

-q 到底在做什么 ? 在我们的大多数示例中我们都使用了-q 命令行选项 .. 这压制了Gradle 的日志消息 .. 因此仅仅任务的消息将会展示 .. 让我们的输出更加的清晰 .. 你可以不使用此选项 … 查看 Logging 了解更多详情, 关于如何通过命令行选项影响Gradle 的输出 ..

  1. Output of gradle -q hello
  2. > gradle -q hello
  3. Hello world!

这里我们定义了一个任务并执行它 .. 通过gradle hello 命令,那么Gradle 将会执行hello 任务, 这将会执行我们提供的action … 这个action 是一个简单的闭包(其中包含了需要执行的代码) …
如果你任务这看起来类似于 Ant的 targets,你的感觉是正确的 … Gradle 的任务等价于 Ant 的 targets .. 但是正如你所见,它们是更好的 … 我们使用不同的技术 并认为 task 一词比target 表达的含义更加清楚 … 因此我们引入了和Ant的一些技术冲击 .. 因为Ant 调用任务将他们视为命令,例如 javac / copy ,任务 …
因此当我们在谈论任务时,我们总是描述的是Gradle 的任务,它们等价于Ant的Targets .
如果我们谈论Ant 任务(Ant commands),我们会显式的说明这是Ant 任务 …

Build scripts are code

Gradle 的构建脚本让你能够完全得到Groovy 和Kotlin 的支持 … 作为一个开胃菜,我们可以先看一看:

使用Groovy define Gradle 的 任务

  1. tasks.register('upper') {
  2. doLast {
  3. String someString = 'mY_nAmE'
  4. println "Original: $someString"
  5. println "Upper case: ${someString.toUpperCase()}"
  6. }
  7. }
  1. Output of gradle -q upper
  2. > gradle -q upper
  3. Original: mY_nAmE
  4. Upper case: MY_NAME

或者

  1. tasks.register('count') {
  2. doLast {
  3. 4.times { print "$it " }
  4. }
  5. }
  1. Output of gradle -q count
  2. > gradle -q count
  3. 0 1 2 3

任务依赖

你可能会想到,你能够定义任务依赖于那些任务

  1. tasks.register('hello') {
  2. doLast {
  3. println 'Hello world!'
  4. }
  5. }
  6. tasks.register('intro') {
  7. dependsOn tasks.hello
  8. doLast {
  9. println "I'm Gradle"
  10. }
  11. }
  1. Output of gradle -q intro
  2. > gradle -q intro
  3. Hello world!
  4. I'm Gradle

增加一个依赖,不需要相关的任务一定存在 …

懒依赖,其他任务可以不存在

  1. tasks.register('taskX') {
  2. dependsOn 'taskY'
  3. doLast {
  4. println 'taskX'
  5. }
  6. }
  7. tasks.register('taskY') {
  8. doLast {
  9. println 'taskY'
  10. }
  11. }
  1. Output of gradle -q taskX
  2. > gradle -q taskX
  3. taskY
  4. taskX

例如taskX的依赖是taskY,它们声明是随意的,任务依赖详情在这里 .

Flexible task registration

Groovy / kotlin 的能力不仅仅被用来定义任务做什么 .. 例如,你能够使用它去注册相同类型的多个任务(在循环中) …

  1. 4.times { counter ->
  2. tasks.register("task$counter") {
  3. doLast {
  4. println "I'm task number $counter"
  5. }
  6. }
  7. }

或者 kotlin

  1. repeat(4) { counter ->
  2. tasks.register("task$counter") {
  3. doLast {
  4. println("I'm task number $counter")
  5. }
  6. }
  7. }

管理 / 操作存在的任务

  1. 4.times { counter ->
  2. tasks.register("task$counter") {
  3. doLast {
  4. println "I'm task number $counter"
  5. }
  6. }
  7. }
  8. tasks.named('task0') { dependsOn('task2', 'task3') }

一旦任务注册,它们能够通过API 进行访问,例如 你能够动态的增加依赖给任务 ..
在运行时,Ant 不允许类似这样的事情 …

  1. Output of gradle -q task0
  2. > gradle -q task0
  3. I'm task number 2
  4. I'm task number 3
  5. I'm task number 0

或者你能够为存在的任务增加行为

  1. tasks.register('hello') {
  2. doLast {
  3. println 'Hello Earth'
  4. }
  5. }
  6. tasks.named('hello') {
  7. doFirst {
  8. println 'Hello Venus'
  9. }
  10. }
  11. tasks.named('hello') {
  12. doLast {
  13. println 'Hello Mars'
  14. }
  15. }
  16. tasks.named('hello') {
  17. doLast {
  18. println 'Hello Jupiter'
  19. }
  20. }
  1. > gradle -q hello
  2. Hello Venus
  3. Hello Earth
  4. Hello Mars
  5. Hello Jupiter

那么这个任务具有4个动作 …
doFirst / doLast 能够执行多次 … 它们增加动作到任务动作列表的前面或者结束 …
动作列表中的所有动作依次执行 …

使用Ant 任务

Ant 任务是Gradle中的一等公民 .. Gradle 提供了对Ant 任务优秀的集成(通过简单的依赖于Groovy) …
Groovy 附带了出色的 AntBuilder .
从Gradle 中使用Ant 任务是非常便利的并且比使用build.xml中的Ant 任务更强大 ..
Ant 也可以通过kotlin 进行使用, 例如学习如何执行Ant 任务并如何访问Ant 属性 …

Using AntBuilder to execute ant.loadfile target

  1. tasks.register('loadfile') {
  2. doLast {
  3. def files = file('./antLoadfileResources').listFiles().sort()
  4. files.each { File file ->
  5. if (file.isFile()) {
  6. ant.loadfile(srcFile: file, property: file.name)
  7. println " *** $file.name ***"
  8. println "${ant.properties[file.name]}"
  9. }
  10. }
  11. }
  12. }
  1. > gradle -q loadfile
  2. *** agile.manifesto.txt ***
  3. Individuals and interactions over processes and tools
  4. Working software over comprehensive documentation
  5. Customer collaboration over contract negotiation
  6. Responding to change over following a plan
  7. *** gradle.manifesto.txt ***
  8. Make the impossible possible, make the possible easy and make the easy elegant.
  9. (inspired by Moshe Feldenkrais)

关于在gradle 构建脚本中大量使用 Ant,可以从这里查看 ..

Using methods

Gradle 可扩展您如何组织构建逻辑。
例如为刚才的例子抓取一个方法:

  1. tasks.register('checksum') {
  2. doLast {
  3. fileList('./antLoadfileResources').each { File file ->
  4. ant.checksum(file: file, property: "cs_$file.name")
  5. println "$file.name Checksum: ${ant.properties["cs_$file.name"]}"
  6. }
  7. }
  8. }
  9. tasks.register('loadfile') {
  10. doLast {
  11. fileList('./antLoadfileResources').each { File file ->
  12. ant.loadfile(srcFile: file, property: file.name)
  13. println "I'm fond of $file.name"
  14. }
  15. }
  16. }
  17. File[] fileList(String dir) {
  18. file(dir).listFiles({file -> file.isFile() } as FileFilter).sort()
  19. }

输出同上 …
然后将会看到 这些方法几乎在当前多项目构建中的所有子项目中共享 …
如果你的构建逻辑变得很复杂,Gradle 提供了你其他非常方便的方式去组织它 …
查看另一篇文章了解 … Organizing Gradle Projects

默认任务

Gradle 允许你 定义一个或者多个默认任务(如果在构建时没有指定运行任务) …

  1. defaultTasks 'clean' , 'run'
  2. tasks.register('celan') {
  3. doLast {
  4. println Cleaning!'
  5. }
  6. }
  7. tasks.register('run') {
  8. doLast {
  9. println 'Default Running!'
  10. }
  11. }
  12. tasks.register("other") {
  13. doLast {
  14. println "I'm not a default task!"
  15. }
  16. }

如果执行 gradle -q
那么将输出:

> gradle -q
Default Cleaning!
Default Running!

这完全等价于 gradle clean run , 在多项目构建中子项目能够有自己的指定的默认任务 ..
如果子项目没有指定默认任务, 那么将会使用父项目的默认任务(如果定义) ….

构建脚本的外部依赖

处理直接操作脚本类路径之外,建议应用自带类路径的插件 .. 对于自定义构建逻辑, 推荐使用自定义插件 …

如果你的构建脚本需要使用外部库,你能够将他们增加到脚本的类路径上 (在构建脚本中) …
通过buildscript() 方法配置构建脚本,传递一个闭包用来声明构建脚本类路径 …

声明构建脚本的类路径

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath group: 'commons-codec', name: 'commons-codec', version: '1.2'
    }
}

这个方法将会配置一个代理对象,类型为 ScriptHandler, 你能够通过 classpath 配置增加类路径依赖 ..
这等价于在Java 编译类路径上声明的依赖相同方式 …
你能够使用任何依赖类型,除了项目依赖 …(因为类路径不一致) ..
当你在构建类路径上声明了依赖,你能够使用这些类到构建脚本中 …
例如:

import org.apache.commons.codec.binary.Base64

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath group: 'commons-codec', name: 'commons-codec', version: '1.2'
    }
}

tasks.register('encode') {
    doLast {
        def byte[] encodedString = new Base64().encode('hello world\n'.getBytes())
        println new String(encodedString)
    }
}

导入apache的计算库进行使用 …

Output of gradle -q encode
> gradle -q encode
aGVsbG8gd29ybGQK

对于多项目构建,单个项目的buildscript项目上声明的依赖 .. 同样对它的所有子项目的buildscript 脚本有效 …
构建脚本依赖也可以抽象为一个Gradle plugins …
请考虑 Using Gradle Plugins[TODO] 了解gradle 的插件信息 …
每一个项目会自动包含一个 BuildEnvironmentReportTask 类型的 buildEnvironment 任务 … 它能够执行去报告构建脚本依赖解析情况 ..

继续阅读