【构建工具】Gradle中文教程

news2024/9/24 21:26:07

文章目录

  • Gradle 简介
  • Gradle 概述
    • 基于声明的构建和基于约定的构建
    • 为以依赖为基础的编程方式提供语言支持
    • 构建结构化
    • 深度 API
    • Gradle 的扩展
    • 多项目构建
    • 多种方式管理依赖
    • Gradle 是第一个构建集成工具
    • 易于移植
      • Groovy
      • The Gradle wrapper
      • 自由和开源
    • 为什么使用 Groovy?
  • Gradle 安装
    • 先决条件
    • 下载
    • 解压
    • 配置环境变量
    • 测试安装
    • JVM 参数配置
  • Gradle 构建基础
    • Projects 和 tasks
    • Hello world
    • 第一个构建脚本
    • 快速定义任务
    • 快速定义任务
    • 代码即脚本
    • 在 gradle 任务中采用 groovy
    • 任务依赖
    • 在两个任务之间指明依赖关系
    • 延迟依赖
    • 动态任务
    • 创建动态任务
    • 任务操纵
      • 通过 API 进行任务之间的通信 - 增加依赖
      • 通过 API 进行任务之间的通信 - 增加任务行为
    • 短标记法
    • 以属性的方式访问任务
    • 增加自定义属性
    • 调用 Ant 任务
      • 利用 AntBuilder 执行 ant.loadfile
    • 方法抽取
    • 定义默认任务
    • Configure by DAG
    • 依赖任务的不同输出
  • Gradle Java 构建入门
    • Java 插件
    • 一个基本 Java 项目
    • 采用 Java 插件
    • 构建项目
    • 构建 Java 项目
      • clean
      • assemble
      • check
    • 外部依赖
    • 添加 Maven 仓库
    • 添加依赖
    • 自定义项目
    • 自定义 MANIFEST.MF
    • 发布 jar 包
    • 创建 Eclipse 文件
    • Eclipse plugin
    • 示例汇总
    • 多项目构建
    • 多项目构建-项目结构
    • 多项目构建定义
    • 多项目构建中的 settings.gradle
    • 公共配置
    • 多项目构建-公共配置
    • 工程依赖
    • 多项目构建-工程依赖
    • 打包发布
  • Gradle 依赖管理基础
    • 什么是依赖管理?
    • 依赖声明
    • 依赖配置
      • compile
      • runtime
      • testCompile
      • testRuntime
    • 外部依赖
      • 定义外部依赖
      • 快速定义外部依赖
      • 仓库
      • 使用 Maven 中央仓库
      • 采用 Ivy 远程仓库
    • 打包发布
  • Gradle Groovy 快速入门
    • 一个基本的 Groovy 项目
    • 总结
  • Gradle Web 工程构建
    • 打 War 包
    • War plugin
    • Web 工程启动
    • 采用 Jetty plugin 启动 web 工程
  • Gradle 命令行的基本使用
    • 多任务调用
      • 任务依赖
      • 多任务调用
    • 排除任务
    • 失败后继续执行
    • 简化任务名
    • 简化驼峰任务名
    • 选择构建位置
      • 选择文件构建
      • 选择构建目录
      • 获取构建信息
      • 项目列表
      • 收集项目信息
      • 为项目添加描述信息.
      • 任务列表
      • 获取任务信息
      • 更改任务报告内容
      • 获取任务帮助信息
      • 获取任务帮助
      • 获取依赖列表
      • 获取依赖信息
      • 过滤依赖信息
      • 查看特定依赖
      • 获取特定依赖
      • 获取项目属性列表
      • 构建日志
  • 使用 Gradle 图形用户界面
    • 任务树
    • 收藏夹
    • 命令行
    • 设置
  • Gradle 编写构建脚本
    • Gradle 构建语言
    • Project API
    • 标准 project 属性
    • Script API
    • 声明变量
    • 局部变量局部
    • 额外属性
    • 一些 Groovy 的基础知识
      • Groovy JDK
      • Groovy JDK 的方法
      • 属性访问器
      • 括号可选的方法调用
      • 不带括号的方法调用
      • List 和 Map
      • List and map
      • 作为方法最后一个参数的闭包
      • 闭包委托(delegate)
  • Gradle 任务详述
    • 定义任务
    • 定位任务
    • 配置任务
    • 对任务添加依赖
    • 任务排序
    • 向任务添加描述
    • 替换任务
    • 跳过任务
    • 使用 StopExecutionException 跳过任务
    • 启用和禁用任务
    • 跳过处于最新状态的任务
    • 声明一个任务的输入和输出
    • 它是怎么实现的?
    • 任务规则
    • 基于规则的任务依赖
    • 析构器任务
    • 总结

Gradle 简介

介绍
很高兴能向大家介绍 Gradle,读音/ɡreɪdl/这是一个基于 JVM 的富有突破性构建工具。

它为您提供了:

一个像 ant 一样,通用的灵活的构建工具
一种可切换的,像 maven 一样的基于约定约定优于配置的构建框架
强大的多工程构建支持
强大的依赖管理(基于 ApacheIvy)
对已有的 maven 和 ivy 仓库的全面支持
支持传递性依赖管理,而不需要远程仓库或者 pom.xml 或者 ivy 配置文件
ant 式的任务和构建是 gradle 的第一公民
基于 groovy,其 build 脚本使用 groovy dsl 编写
具有广泛的领域模型支持你的构建
在第二章节概述中,你会看到关于 Gradle 的详细介绍和指导

Gradle 概述

基于声明的构建和基于约定的构建

Gradle 的核心在于基于 Groovy 的丰富而可扩展的域描述语言(DSL)。 Groovy 通过声明性的语言元素将基于声明的构建推向下层,你可以按你想要的方式进行组合。 这些元素同样也为支持 Java, Groovy,OSGi,Web 和 Scala 项目提供了基于约定的构建。 并且,这种声明性的语言是可以扩展的。你可以添加新的或增强现有的语言元素。 因此,它提供了简明、可维护和易理解的构建。

为以依赖为基础的编程方式提供语言支持

声明性语言优点在于通用任务图,你可以将其充分利用在构建中. 它提供了最大限度的灵活性,以让 Gradle 适应你的特殊需求。

构建结构化

Gradle 的灵活和丰富性最终能够支持在你的构建中应用通用的设计模式。 例如,它可以很容易地将你的构建拆分为多个可重用的模块,最后再进行组装,但不要强制地进行模块的拆分。 不要把原本在一起的东西强行分开(比如在你的项目结构里),从而避免让你的构建变成一场噩梦。 最后,你可以创建一个结构良好,易于维护,易于理解的构建。

深度 API

Gradle 允许你在构建执行的整个生命周期,对它的核心配置及执行行为进行监视并自定义。

Gradle 的扩展

Gradle 有非常良好的扩展性。 从简单的单项目构建,到庞大的多项目构建,它都能显著地提升你的效率。 这才是真正的结构化构建。通过最先进的增量构建功能,它可以解决许多大型企业所面临的性能瓶颈问题。

多项目构建

Gradle 对多项目构建的支持非常出色。项目依赖是首先需要考虑的问题。 我们允许你在多项目构建当中对项目依赖关系进行建模,因为它们才是你真正的问题域。 Gradle 遵守你的布局。

Gradle 提供了局部构建的功能。 如果你在构建一个单独的子项目,Gradle 也会帮你构建它所依赖的所有子项目。 你也可以选择重新构建依赖于特定子项目的子项目。 这种增量构建将使得在大型构建任务中省下大量时间。

多种方式管理依赖

不同的团队喜欢用不同的方式来管理他们的外部依赖。 从 Maven 和 Ivy 的远程仓库的传递依赖管理,到本地文件系统的 jar 包或目录,Gradle 对所有的管理策略都提供了方便的支持。

Gradle 是第一个构建集成工具

Ant tasks 是最重要的。而更有趣的是,Ant projects 也是最重要的。 Gradle 对任意的 Ant 项目提供了深度导入,并在运行时将 Ant 目标(target)转换为原生的 Gradle 任务(task)。 你可以从 Gradle 上依赖它们(Ant targets),增强它们,甚至在你的 build.xml 上定义对 Gradle tasks 的依赖。Gradle 为属性、路径等等提供了同样的整合。

Gradle 完全支持用于发布或检索依赖的 Maven 或 Ivy 仓库。 Gradle 同样提供了一个转换器,用于将一个 Maven pom.xml 文件转换为一个 Gradle 脚本。Maven 项目的运行时导入的功能将很快会有。

易于移植

Gradle 能适应你已有的任何结构。因此,你总可以在你构建项目的同一个分支当中开发你的 Gradle 构建脚本,并且它们能够并行进行。 我们通常建议编写测试,以保证生成的文件是一样的。 这种移植方式会尽可能的可靠和减少破坏性。这也是重构的最佳做法。

Groovy

Gradle 的构建脚本是采用 Groovy 写的,而不是用 XML。 但与其他方法不同,它并不只是展示了由一种动态语言编写的原始脚本的强大。 那样将导致维护构建变得很困难。 Gradle 的整体设计是面向被作为一门语言,而不是一个僵化的框架。 并且 Groovy 是我们允许你通过抽象的 Gradle 描述你个人的 story 的黏合剂。 Gradle 提供了一些标准通用的 story。这是我们相比其他声明性构建系统的主要特点。 我们的 Groovy 支持也不是简单的糖衣层,整个 Gradle 的 API 都是完全 groovy 化的。只有通过 Groovy才能去运用它并对它提高效率。

The Gradle wrapper

Gradle Wrapper 允许你在没有安装 Gradle 的机器上执行 Gradle 构建。 这一点是非常有用的。比如,对一些持续集成服务来说。 它对一个开源项目保持低门槛构建也是非常有用的。 Wrapper 对企业来说也很有用,它使得对客户端计算机零配置。 它强制使用指定的版本,以减少兼容支持问题。

自由和开源

Gradle 是一个开源项目,并遵循 ASL 许可。

为什么使用 Groovy?

我们认为内部 DSL(基于一种动态语言)相比 XML 在构建脚本方面优势非常大。它们是一对动态语言。 为什么使用 Groovy?答案在于 Gradle 内部的运行环境。 虽然 Gradle 核心目的是作为通用构建工具,但它还是主要面向 Java 项目。 这些项目的团队成员显然熟悉 Java。我们认为一个构建工具应该尽可能地对所有团队成员透明。

你可能会想说,为什么不能使用 Java 来作为构建脚本的语言。 我认为这是一个很有意义的问题。对你们的团队来讲,它确实会有最高的透明度和最低的学习曲线。 但由于 Java 本身的局限性,这种构建语言可能就不会那样友善、 富有表现力和强大。 [1] 这也是为什么像 Python,Groovy 或者 Ruby 这样的语言在这方面表现得更好的原因。 我们选择了 Groovy,因为它向 Java 人员提供了目前为止最大的透明度。 其基本的语法,类型,包结构和其他方面都与 Java 一样,Groovy 在这之上又增加了许多东西。但是和 Java 也有着共同点。

对于那些分享和乐于去学习 Python 知识的 Java 团队而言,上述论点并不适用。 Gradle 的设计非常适合在 JRuby 或 Jython 中创建另一个构建脚本引擎。 那时候,对我们而言,它只是不再是最高优先级的了。我们很高兴去支持任何社区努力创建其他的构建脚本引擎。

Gradle 安装

先决条件

Gradle 需要 1.5 或更高版本的 JDK.Gradle 自带了 Groovy 库,所以不需要安装 Groovy。Gradle 会忽略已经安装的 Groovy。Gradle 会使用 ptah (这里的"path"应该是指 PATH 环境变量。[Rover12421]译注) 中的 JDK(可以使用 java -version 检查)。当然,你可以配置 JAVA_HOME 环境变量来指向 JDK 的安装目录。

下载

从 Gralde 官方网站下载 Gradle 的最新发行包。

解压

Gradle 发行包是一个 ZIP 文件。完整的发行包包括以下内容(官方发行包有 full 完整版,也有不带源码和文档的版本,可根据需求下载。[Rover12421]译注):

Gradle 可执行文件
用户手册 (有 PDF 和 HTML 两种版本)
DSL 参考指南
API 手册(Javadoc 和 Groovydoc)
样例,包括用户手册中的例子,一些完整的构建样例和更加复杂的构建脚本
源代码。仅供参考使用,如果你想要自己来编译 Gradle 你需要从源代码仓库中检出发行版本源码,具体请查看 Gradle 官方主页。

配置环境变量

运行 gradle 必须将 GRADLE_HOME/bin 加入到你的 PATH 环境变量中。

测试安装

运行如下命令来检查是否安装成功.该命令会显示当前的 JVM 版本和 Gradle 版本。

gradle -v 

JVM 参数配置

Gradle 运行时的 JVM 参数可以通过 GRADLE_OPTS 或 JAVA_OPTS 来设置.这些参数将会同时生效。 JAVA_OPTS 设置的参数将会同其它 JAVA 应用共享,一个典型的例子是可以在 JAVA_OPTS 中设置代理和 GRADLE_OPTS 设置内存参数。同时这些参数也可以在 gradle 或者 gradlew 脚本文件的开头进行设置。

Gradle 构建基础

Projects 和 tasks

projects 和 tasks是 Gradle 中最重要的两个概念。

任何一个 Gradle 构建都是由一个或多个 projects 组成。每个 project 包括许多可构建组成部分。 这完全取决于你要构建些什么。举个例子,每个 project 或许是一个 jar 包或者一个 web 应用,它也可以是一个由许多其他项目中产生的 jar 构成的 zip 压缩包。一个 project 不必描述它只能进行构建操作。它也可以部署你的应用或搭建你的环境。不要担心它像听上去的那样庞大。 Gradle 的 build-by-convention 可以让您来具体定义一个 project 到底该做什么。

每个 project 都由多个 tasks 组成。每个 task 都代表了构建执行过程中的一个原子性操作。如编译,打包,生成 javadoc,发布到某个仓库等操作。

到目前为止,可以发现我们可以在一个 project 中定义一些简单任务,后续章节将会阐述多项目构建和多项目多任务的内容。

Hello world

你可以通过在命令行运行 gradle 命令来执行构建,gradle 命令会从当前目录下寻找 build.gradle 文件来执行构建。我们称 build.gradle 文件为构建脚本。严格来说这其实是一个构建配置脚本,后面你会了解到这个构建脚本定义了一个 project 和一些默认的 task。

你可以创建如下脚本到 build.gradle 中 To try this out,create the following build script named build.gradle。

第一个构建脚本

build.gradle

task hello {
    doLast {
        println 'Hello world!'
    }
}

然后在该文件所在目录执行 gradle -q hello

-q 参数的作用是什么?
该文档的示例中很多地方在调用 gradle 命令时都加了 -q 参数。该参数用来控制 gradle 的日志级别,可以保证只输出我们需要的内容。具体可参阅本文档第十八章日志来了解更多参数和信息。

执行脚本

Output of gradle -q hello
> gradle -q hello
Hello world!

上面的脚本定义了一个叫做 hello 的 task,并且给它添加了一个动作。当执行 gradle hello 的时候, Gralde 便会去调用 hello 这个任务来执行给定操作。这些操作其实就是一个用 groovy 书写的闭包。

如果你觉得它看上去跟 Ant 中的 targets 很像,那么是对的。Gradle 的 tasks 就相当于 Ant 中的 targets。不过你会发现他功能更加强大。我们只是换了一个比 target 更形象的另外一个术语。不幸的是这恰巧与 Ant 中的术语有些冲突。ant 命令中有诸如 javac、copy、tasks。所以当该文档中提及 tasks 时,除非特别指明 ant task。否则指的均是指 Gradle 中的 tasks。

快速定义任务

用一种更简洁的方式来定义上面的 hello 任务。

快速定义任务

build.gradle

task hello << {
println ‘Hello world!’
}
上面的脚本又一次采用闭包的方式来定义了一个叫做 hello 的任务,本文档后续章节中我们将会更多的采用这种风格来定义任务。

代码即脚本

Gradle 脚本采用 Groovy 书写,作为开胃菜,看下下面这个例子。

在 gradle 任务中采用 groovy

build.gradle

task upper << {
    String someString = 'mY_nAmE'
    println "Original: " + someString
    println "Upper case: " + someString.toUpperCase()
}
Output of gradle -q upper
> gradle -q upper
Original: mY_nAmE
Upper case: MY_NAME

或者

在 gradle 任务中采用 groovy

build.gradle

task count << {
    4.times { print "$it " }
}
Output of gradle -q count
> gradle -q count
0 1 2 3

任务依赖

你可以按如下方式创建任务间的依赖关系

在两个任务之间指明依赖关系

build.gradle

task hello << {
    println 'Hello world!'
}
task intro(dependsOn: hello) << {
    println "I'm Gradle"
}

gradle -q intro 的输出结果

Output of gradle -q intro
\> gradle -q intro
Hello world!
I'm Gradle

添加依赖 task 也可以不必首先声明被依赖的 task。

延迟依赖

build.gradle

task taskX(dependsOn: 'taskY') << {
    println 'taskX'
}
task taskY << {
    println 'taskY'
}

Output of gradle -q taskX

 \> gradle -q taskX
taskY
taskX

可以看到,taskX 是 在 taskY 之前定义的,这在多项目构建中非常有用。

注意:当引用的任务尚未定义的时候不可使用短标记法来运行任务。

动态任务

借助 Groovy 的强大不仅可以定义简单任务还能做更多的事。例如,可以动态定义任务。

创建动态任务

build.gradle

4.times { counter ->
    task "task$counter" << {
        println "I'm task number $counter"
    }
}

gradle -q task1 的输出结果。

Output of gradle -q task1
\> gradle -q task1
I'm task number 1

任务操纵

一旦任务被创建后,任务之间可以通过 API 进行相互访问。这也是与 Ant 的不同之处。比如可以增加一些依赖。

通过 API 进行任务之间的通信 - 增加依赖

build.gradle

4.times { counter ->
    task "task$counter" << {
        println "I'm task number $counter"
    }
}
task0.dependsOn task2, task3

gradle -q task0的输出结果。

Output of gradle -q task0
\> gradle -q task0
I'm task number 2
I'm task number 3
I'm task number 0

为已存在的任务增加行为。

通过 API 进行任务之间的通信 - 增加任务行为

build.gradle

task hello << {
    println 'Hello Earth'
}
hello.doFirst {
    println 'Hello Venus'
}
hello.doLast {
    println 'Hello Mars'
}
hello << {
    println 'Hello Jupiter'
}
Output of gradle -q hello
> gradle -q hello
Hello Venus
Hello Earth
Hello Mars
Hello Jupiter

doFirst 和 doLast 可以进行多次调用。他们分别被添加在任务的开头和结尾。当任务开始执行时这些动作会按照既定顺序进行。其中 << 操作符 是 doLast 的简写方式。

短标记法

你早就注意到了吧,没错,每个任务都是一个脚本的属性,你可以访问它:

以属性的方式访问任务

build.gradle

task hello << {
    println 'Hello world!'
}
hello.doLast {
    println "Greetings from the $hello.name task."
}

gradle -q hello 的输出结果

Output of gradle -q hello
\> gradle -q hello
Hello world!
Greetings from the hello task.

对于插件提供的内置任务。这尤其方便(例如:complie)

增加自定义属性

你可以为一个任务添加额外的属性。例如,新增一个叫做 myProperty 的属性,用 ext.myProperty 的方式给他一个初始值。这样便增加了一个自定义属性。

为任务增加自定义属性

build.gradle

task myTask {
    ext.myProperty = "myValue"
}

task printTaskProperties << {
    println myTask.myProperty
}

gradle -q printTaskProperties 的输出结果

Output of gradle -q printTaskProperties
\> gradle -q printTaskProperties
myValue

自定义属性不仅仅局限于任务上,还可以做更多事情。

调用 Ant 任务

Ant 任务是 Gradle 中的一等公民。Gradle 借助 Groovy 对 Ant 任务进行了优秀的整合。Gradle 自带了一个 AntBuilder,在 Gradle 中调用 Ant 任务比在 build.xml 中调用更加的方便和强大。 通过下面的例子你可以学到如何调用一个 Ant 任务以及如何与 Ant 中的属性进行通信。

利用 AntBuilder 执行 ant.loadfile

build.gradle

task loadfile << {
    def files = file('../antLoadfileResources').listFiles().sort()
    files.each { File file ->
        if (file.isFile()) {
            ant.loadfile(srcFile: file, property: file.name)
            println " *** $file.name ***"
            println "${ant.properties[file.name]}"
        }
    }
}

gradle -q loadfile 的输出结果

Output of gradle -q loadfile
\> gradle -q loadfile
*** agile.manifesto.txt ***
Individuals and interactions over processes and tools
Working software over comprehensive documentation
Customer collaboration  over contract negotiation
Responding to change over following a plan
 *** gradle.manifesto.txt ***
Make the impossible possible, make the possible easy and make the easy elegant.
(inspired by Moshe Feldenkrais)

在你脚本里还可以利用 Ant 做更多的事情。想了解更多请参阅在 Gradle 中调用 Ant。

方法抽取

Gradle 的强大要看你如何编写脚本逻辑。针对上面的例子,首先要做的就是要抽取方法。

利用方法组织脚本逻辑

build.gradle

task checksum << {
    fileList('../antLoadfileResources').each {File file ->
        ant.checksum(file: file, property: "cs_$file.name")
        println "$file.name Checksum: ${ant.properties["cs_$file.name"]}"
    }
}
task loadfile << {
    fileList('../antLoadfileResources').each {File file ->
        ant.loadfile(srcFile: file, property: file.name)
        println "I'm fond of $file.name"
    }
}
File[] fileList(String dir) {
    file(dir).listFiles({file -> file.isFile() } as FileFilter).sort()
}

gradle -q loadfile 的输出结果

Output of gradle -q loadfile
\> gradle -q loadfile
I'm fond of agile.manifesto.txt
I'm fond of gradle.manifesto.txt

在后面的章节你会看到类似出去出来的方法可以在多项目构建中的子项目中调用。无论构建逻辑多复杂,Gradle 都可以提供给你一种简便的方式来组织它们。

定义默认任务

Gradle 允许在脚本中定义多个默认任务。

build.gradle

defaultTasks 'clean', 'run'
task clean << {
    println 'Default Cleaning!'
}
task run << {
    println 'Default Running!'
}
task other << {
    println "I'm not a default task!"
}

gradle -q 的输出结果。

Output of gradle -q
\> gradle -q
Default Cleaning!
Default Running!

这与直接调用 gradle clean run 效果是一样的。在多项目构建中,每个子项目都可以指定单独的默认任务。如果子项目未进行指定将会调用父项目指定的的默认任务。

Configure by DAG

稍后会对 Gradle 的配置阶段和运行阶段进行详细说明 配置阶段后,Gradle 会了解所有要执行的任务 Gradle 提供了一个钩子来捕获这些信息。一个例子就是可以检查已经执行的任务中有没有被释放。借由此,你可以为一些变量赋予不同的值。

在下面的例子中,为 distribution 和 release 任务赋予了不同的 version 值。

依赖任务的不同输出

build.gradle

task distribution << {
    println "We build the zip with version=$version"
}
task release(dependsOn: 'distribution') << {
    println 'We release now'
}
gradle.taskGraph.whenReady {taskGraph ->
    if (taskGraph.hasTask(release)) {
        version = '1.0'
    } else {
        version = '1.0-SNAPSHOT'
    }
}

gradle -q distribution 的输出结果

Output of gradle -q distribution
\> gradle -q distribution
We build the zip with version=1.0-SNAPSHOT

gradle -q release 的输出结果

Output of gradle -q release
\> gradle -q release
We build the zip with version=1.0
We release now

whenReady 会在已发布的任务之前影响到已发布任务的执行。即使已发布的任务不是主要任务(也就是说,即使这个任务不是通过命令行直接调用)

Gradle Java 构建入门

Java 插件

如你所见,Gradle 是一个通用工具。它可以通过脚本构建任何你想要实现的东西,真正实现开箱即用。但前提是你需要在脚本中编写好代码才行。

大部分 Java 项目基本流程都是相似的:编译源文件,进行单元测试,创建 jar 包。使用 Gradle 做这些工作不必为每个工程都编写代码。Gradle 已经提供了完美的插件来解决这些问题。插件就是 Gradle 的扩展,简而言之就是为你添加一些非常有用的默认配置。Gradle 自带了很多插件,并且你也可以很容易的编写和分享自己的插件。Java plugin 作为其中之一,为你提供了诸如编译,测试,打包等一些功能。

Java 插件为工程定义了许多默认值,如Java源文件位置。如果你遵循这些默认规则,那么你无需在你的脚本文件中书写太多代码。当然,Gradle 也允许你自定义项目中的一些规则,实际上,由于对 Java 工程的构建是基于插件的,那么你也可以完全不用插件自己编写代码来进行构建。

后面的章节我们通过许多深入的例子介绍了如何使用 Java 插件来进行以来管理和多项目构建等。但在这个章节我们需要先了解 Java 插件的基本用法。

一个基本 Java 项目

来看一下下面这个小例子,想用 Java 插件,只需增加如下代码到你的脚本里。

采用 Java 插件

build.gradle
apply plugin: 'java'

备注:示例代码可以在 Gralde 发行包中的 samples/java/quickstart 下找到。

定义一个 Java 项目只需如此而已。这将会为你添加 Java 插件及其一些内置任务。

添加了哪些任务?
你可以运行 gradle tasks 列出任务列表。这样便可以看到 Java 插件为你添加了哪些任务。

标准目录结构如下:

project  
    +build  
    +src/main/java  
    +src/main/resources  
    +src/test/java  
    +src/test/resources  

Gradle 默认会从 src/main/java 搜寻打包源码,在 src/test/java 下搜寻测试源码。并且 src/main/resources 下的所有文件按都会被打包,所有 src/test/resources 下的文件 都会被添加到类路径用以执行测试。所有文件都输出到 build 下,打包的文件输出到 build/libs 下。

构建项目

Java 插件为你添加了众多任务。但是它们只是在你需要构建项目的时候才能发挥作用。最常用的就是 build 任务,这会构建整个项目。当你执行 gradle build 时,Gralde 会编译并执行单元测试,并且将 src/main/* 下面 class 和资源文件打包。

构建 Java 项目

运行 gradle build 的输出结果

Output of gradle build
> gradle build
:compileJava
:processResources
:classes
:jar
:assemble
:compileTestJava
:processTestResources
:testClasses
:test
:check
:build
BUILD SUCCESSFUL
Total time: 1 secs

其余一些较常用的任务有如下几个:

clean

删除 build 目录以及所有构建完成的文件。

assemble

编译并打包 jar 文件,但不会执行单元测试。一些其他插件可能会增强这个任务的功能。例如,如果采用了 War 插件,这个任务便会为你的项目打出 War 包。

check

编译并测试代码。一些其他插件也可能会增强这个任务的功能。例如,如果采用了 Code-quality 插件,这个任务会额外执行 Checkstyle。

外部依赖

通常,一个 Java 项目拥有许多外部依赖。你需要告诉 Gradle 如何找到并引用这些外部文件。在 Gradle 中通常 Jar 包都存在于仓库中。仓库可以用来搜寻依赖或发布项目产物。下面是一个采用 Maven 仓库的例子。

添加 Maven 仓库

build.gradle
repositories {
    mavenCentral()
}

添加依赖。这里声明了编译期所需依赖 commons-collections 和测试期所需依赖 junit。

添加依赖

build.gradle
dependencies {
    compile group: 'commons-collections', name: 'commons-collections', version: '3.2'
    testCompile group: 'junit', name: 'junit', version: '4.+'
}

了解更多可参阅依赖管理基础

自定义项目

Java 插件为你的项目添加了众多默认配置。这些默认值通常对于一个普通项目来说已经足够了。但如果你觉得不适用修改起来也很简单。看下面的例子,我们为 Java 项目指定了版本号以及所用的 JDK 版本,并且添加一些属性到 mainfest 中。

自定义 MANIFEST.MF

build.gradle
sourceCompatibility = 1.5
version = '1.0'
jar {
    manifest {
        attributes 'Implementation-Title': 'Gradle Quickstart', 'Implementation-Version': version
    }
}

都有哪些可用属性?
可以执行 gradle propertie s来得到项目的属性列表。用这条命令可以看到插件添加的属性以及默认值。

Java 插件添加的都是一些普通任务,如同他们写在 Build 文件中一样。这意味着前面章节展示的机制都可以用来修改这些任务的行为。例如,可以设置任务的属性,添加任务行为,更改任务依赖,甚至是重写覆盖整个任务。在下面的例子中,我们将修改 test 任务,这是一个 Test 类型任务。让我们来在它执行时为它添加一些系统属性。

为 test 添加系统属性

build.gradle
test {
    systemProperties 'property': 'value'
}

发布 jar 包

如何发布 jar 包?你需要告诉 Gradle 发布到到哪。在 Gradle 中 jar 包通常被发布到某个仓库中。在下面的例子中,我们会将 jar 包发布到本地目录。当然你也可以发布到远程仓库或多个远程仓库中。

build.gradle
uploadArchives {
    repositories {
       flatDir {
           dirs 'repos'
       }
    }
}

执行 gradle uploadArchives 以发布 jar 包。

创建 Eclipse 文件

若要把项目导入 Eclipse 中,你需要添加另外一个插件到你的脚本文件中。

Eclipse plugin

build.gradle
apply plugin: 'eclipse'

执行 gradle eclipse 来生成 Eclipse 项目文件。

示例汇总

这是示例代码汇总得到的一个完整脚本:

Java 示例 - 一个完整构建脚本

build.gradle
apply plugin: 'java'
apply plugin: 'eclipse'
sourceCompatibility = 1.5
version = '1.0'
jar {
    manifest {
        attributes 'Implementation-Title': 'Gradle Quickstart', 'Implementation-Version': version
    }
}
repositories {
    mavenCentral()
}
dependencies {
    compile group: 'commons-collections', name: 'commons-collections', version: '3.2'
    testCompile group: 'junit', name: 'junit', version: '4.+'
}
test {
    systemProperties 'property': 'value'
}
uploadArchives {
    repositories {
       flatDir {
           dirs 'repos'
       }
    }
}

多项目构建

现在来看一个典型的多项目构建的例子。项目结构如下:

多项目构建-项目结构

Build layout
multiproject/
  api/
  services/webservice/
  shared/

备注: 本示例代码可在 Gradle 发行包的 samples/java/multiproject 位置找到

此处有三个工程。api 工程用来生成给客户端用的 jar 文件,这个 jar 文件可以为 XML webservice 提供 Java 客户端。webservice 是一个 web 应用,生成 XML。shared 工程包含的是前述两个工程共用的代码。

多项目构建定义

定义一个多项目构建工程需要在根目录(本例中与 multiproject 同级)创建一个setting 配置文件来指明构建包含哪些项目。并且这个文件必需叫 settings.gradle 本例的配置文件如下:

多项目构建中的 settings.gradle

settings.gradle
include "shared", "api", "services:webservice", "services:shared"

公共配置

对多项目构建而言,总有一些共同的配置.在本例中,我们会在根项目上采用配置注入的方式定义一些公共配置。根项目就像一个容器,子项目会迭代访问它的配置并注入到自己的配置中。这样我们就可以简单的为所有工程定义主配置单了:

多项目构建-公共配置

build.gradle
subprojects {
    apply plugin: 'java'
    apply plugin: 'eclipse-wtp'
    repositories {
       mavenCentral()
    }
    dependencies {
        testCompile 'junit:junit:4.11'
    }
    version = '1.0'
    jar {
        manifest.attributes provider: 'gradle'
    }
}

值得注意的是我们为每个子项目都应用了 Java 插件。这意味着我们在前面章节学习的内容在子项目中也都是可用的。所以你可以在根项目目录进行编译,测试,打包等所有操作。

工程依赖

同一个构建中可以建立工程依赖,一个工程的 jar 包可以提供给另外一个工程使用。例如我们可以让 api 工程以依赖于 shared 工程的 jar 包。这样 Gradle 在构建 api 之前总是会先构建 shared 工程。

多项目构建-工程依赖

api/build.gradle
dependencies {
    compile project(':shared')
}

打包发布

如何发布,请看下文:

多项目构建-发布

api/build.gradle
task dist(type: Zip) {
    dependsOn spiJar
    from 'src/dist'
    into('libs') {
        from spiJar.archivePath
        from configurations.runtime
    }
}
artifacts {
   archives dist
}

Gradle 依赖管理基础

什么是依赖管理?

通俗来讲,依赖管理由如下两部分组成。首先,Gradle 需要知道项目构建或运行所需要的一些文件,以便于找到这些需要的文件。我们称这些输入的文件为项目的依赖。其次,你可能需要构建完成后自动上传到某个地方。我们称这些输出为发布。下面来仔细介绍一下这两部分:

大部分工程都不太可能完全自给自足,一般你都会用到其他工程的文件。比如我工程需要 Hibernate 就得把它的类库加进来,比如测试的时候可能需要某些额外 jar 包,例如 JDBC 驱动或 Ehcache 之类的 Jar 包。

这些文件就是工程的依赖。Gradle 需要你告诉它工程的依赖是什么,它们在哪,然后帮你加入构建中。依赖可能需要去远程库下载,比如 Maven 或者 Ivy 库。也可以是本地库,甚至可能是另一个工程。我们称这个过程叫依赖解决。

通常,依赖的自身也有依赖。例如,Hibernate 核心类库就依赖于一些其他的类库。所以,当 Gradle 构建你的工程时,会去找到这些依赖。我们称之为依赖传递。

大部分工程构建的主要目的是脱离工程使用。例如,生成 jar 包,包括源代码、文档等,然后发布出去。

这些输出的文件构成了项目的发布内容。Gralde 也会为你分担这些工作。你声明了发布到到哪,Gradle 就会发布到哪。“发布”的意思就是你想做什么。比如,复制到某个目录,上传到 Maven 或 Ivy 仓库。或者在其它项目里使用,这些都可以称之为发行。

依赖声明

来看一下这个脚本里声明依赖的部分:

声明依赖

build.gradle

apply plugin: 'java'
repositories {
    mavenCentral()
}
dependencies {
    compile group: 'org.hibernate', name: 'hibernate-core', version: '3.6.7.Final'
    testCompile group: 'junit', name: 'junit', version: '4.+'
}

这是什么意思呢?这段脚本是这么个意思。首先,Hibernate-core.3.6.7.final.jar 这货是编译期必需的依赖。并且这货相关的依赖也会一并被加载进来,该段脚本同时还声明项目测试阶段需要 4.0 版本以上的 Junit。同时告诉 Gradle 可以去 Maven 中央仓库去找这些依赖。下面的章节会进行更详细的描述。

依赖配置

Gradle 中依赖以组的形式来划分不同的配置。每个配置都只是一组指定的依赖。我们称之为依赖配置 。你也可以借由此声明外部依赖。后面我们会了解到,这也可用用来声明项目的发布。

Java 插件定义了许多标准配置项。这些配置项形成了插件本身的 classpath。比如下面罗列的这一些,并且你可以从 Table 23.5,“Java 插件 - 依赖配置”了解到更多详细内容.。

compile

编译范围依赖在所有的 classpath 中可用,同时它们也会被打包

runtime

runtime 依赖在运行和测试系统的时候需要,但在编译的时候不需要。比如,你可能在编译的时候只需要 JDBC API JAR,而只有在运行的时候才需要 JDBC 驱动实现

testCompile

测试期编译需要的附加依赖

testRuntime

测试运行期需要

不同的插件提供了不同的标准配置,你甚至也可以定义属于自己的配置项。

外部依赖

依赖的类型有很多种,其中有一种类型称之为外部依赖。这种依赖由外部构建或者在不同的仓库中,例如 Maven 中央仓库或 Ivy 仓库中抑或是本地文件系统的某个目录中。

定义外部依赖需要像下面这样进行依赖配置

定义外部依赖

build.gradle

dependencies {
    compile group: 'org.hibernate', name: 'hibernate-core', version: '3.6.7.Final'
}

外部依赖包含 group,name 和 version 几个属性。根据选取仓库的不同,group 和 version 也可能是可选的。

当然,也有一种更加简洁的方式来声明外部依赖。采用:将三个属性拼接在一起即可。“group:name:version”

快速定义外部依赖

build.gradle

dependencies {
    compile 'org.hibernate:hibernate-core:3.6.7.Final'
}

仓库

Gradle 是在一个被称之为仓库的地方找寻所需的外部依赖。仓库即是一个按 group,name 和 version 规则进行存储的一些文件。Gradle 可以支持不同的仓库存储格式,如 Maven 和 Ivy,并且还提供多种与仓库进行通信的方式,如通过本地文件系统或 Http。

默认情况下,Gradle 没有定义任何仓库,你需要在使用外部依赖之前至少定义一个仓库,例如 Maven 中央仓库。

使用 Maven 中央仓库

build.gradle

repositories {
    mavenCentral()
}

或者其它远程 Maven 仓库:

使用 Maven 远程仓库

build.gradle

repositories {
    maven {
        url "http://repo.mycompany.com/maven2"
    }
}

或者采用 Ivy 远程仓库

采用 Ivy 远程仓库

build.gradle

repositories {
    ivy {
        url "http://repo.mycompany.com/repo"
    }
}

或者在指定本地文件系统构建的库。

采用本地 Ivy 目录

build.gradle

repositories {
    ivy {
        // URL can refer to a local directory
        url "../local-repo"
    }
}

一个项目可以采用多个库。Gradle 会按照顺序从各个库里寻找所需的依赖文件,并且一旦找到第一个便停止搜索。

了解更多库相关的信息请参阅章节 50.6,“仓库”。

打包发布

依赖配置也被用于发布文件[3]我们称之为打包发布或发布。

插件对于打包提供了完美的支持,所以通常而言无需特别告诉 Gradle 需要做什么。但是你需要告诉 Gradle 发布到哪里。这就需要在 uploadArchives 任务中添加一个仓库。下面的例子是如何发布到远程 Ivy 仓库的:

发布到 Ivy 仓库

build.gradle

uploadArchives {
    repositories {
        ivy {
            credentials {
                username "username"
                password "pw"
            }
            url "http://repo.mycompany.com"
        }
    }
}

执行 gradle uploadArchives,Gradle 便会构建并上传你的 jar 包,同时会生成一个 ivy.xml 一起上传到目标仓库。

当然你也可以发布到 Maven 仓库中。语法只需稍微一换就可以了。[4]

p.s:发布到 Maven 仓库你需要 Maven 插件的支持,当然,Gradle 也会同时产生 pom.xml 一起上传到目标仓库。

发布到 Maven 仓库
build.gradle

apply plugin: 'maven'
uploadArchives {
    repositories {
        mavenDeployer {
            repository(url: "file://localhost/tmp/myRepo/")
        }
    }
}

Gradle Groovy 快速入门

要构建一个 Groovy 项目,你需要使用 Groovy 插件。该插件扩展了 Java 插件,对你的项目增加了 Groovy 的编译功能. 你的项目可以包含 Groovy 源码,Java 源码,或者两者都包含。

一个基本的 Groovy 项目

让我们来看一个例子。要使用 Groovy 插件,你需要在构建脚本文件当中添加以下内容:

例子 Groovy plugin

build.gradle

apply plugin: 'groovy'   

注意: 此例子的代码可以在 Gradle 的二进制文件或源码中的 samples/groovy/quickstart 里看到。

这段代码同时会将 Java 插件应用到 project 中,如果 Java 插件还没被应用的话。Groovy 插件继承了 compile 任务 ,在 src/main/groovy 目录中查找源文件;且继承了 compileTest 任务,在 src/test/groovy 目录中查找测试的源文件。这些编译任务对这些目录使用了联合编译,这意味着它们可以同时包含 java 和 groovy 源文件。

要使用 groovy 编译任务,还必须声明要使用的 Groovy 版本以及从哪里获取 Groovy 库。你可以通过在 groovy 配置中添加依赖来完成。compile 配置继承了这个依赖,从而在编译 Groovy和 Java 源代码时,groovy 库也会被包含在类路径中。下面例子中,我们会使用 Maven 中央仓库中的 Groovy 2.2.0 版本。

例子 Dependency on Groovy 2.2.0

build.gradle

repositories {
    mavenCentral()
}
dependencies {
    compile 'org.codehaus.groovy:groovy-all:2.2.0'
}  

这里是我们写好的构建文件:

例子 Groovy example - complete build file

build.gradle

apply plugin: 'Eclipse'
apply plugin: 'groovy'
repositories {
    mavenCentral()
}
dependencies {
    compile 'org.codehaus.groovy:groovy-all:2.2.0'
    testCompile 'junit:junit:4.11'
}  

运行 gradle build 将会对你的项目进行编译,测试和打成 jar 包。

总结

这一章描述了一个很简单的 Groovy 项目。通常情况下,一个真实的项目所需要的不止于此。因为一个 Groovy 项目也 是一个 Java 项目, 由于 Groovy 工程也是一个 Java 工程,因此你能用 Java 做的事情 Groovy 也能做。

Gradle Web 工程构建

Gradle 为 Web 开发提供了两个主要插件,War plugin 和 Jetty plugin。 其中 War plugin 继承自 Java plugin,可以用来打 war 包。jetty plugin 继承自 War plugin 作为工程部署的容器。

打 War 包

需要打包 War 文件,需要在脚本中使用 War plugin:

War plugin

build.gradle

apply plugin: 'war'

备注:本示例代码可以在 Gradle 发行包中的 samples/webApplication/quickstart 路径下找到。

由于继承自 Java 插件,当你执行 gradle build 时,将会编译、测试、打包你的工程。Gradle 会在 src/main/webapp 下寻找 Web 工程文件。编译后的 classes 文件以及运行时依赖也都会被包含在 War 包中。

Groovy web构建
在一个工程中你可以采用多个插件。比如你可以在 web 工程中同时使用 War plugin 和 Groovy plugin。插件会将
Gradle 依赖添加到你的 War 包中。

Web 工程启动

要启动 Web 工程,只需使用 Jetty plugin 即可:

采用 Jetty plugin 启动 web 工程

build.gradle

apply plugin: 'jetty'

由于 Jetty plugin 继承自 War plugin。调用 gradle jettyRun 将会把你的工程启动部署到 jetty 容器中。调用 gradle jettyRunWar 会打包并启动部署到 jetty 容器中。

待添加:使用哪个 URL,配置端口,使用源文件的地方,可编辑你的文件,以及重新加载的内容。

Gradle 命令行的基本使用

多任务调用

你可以以列表的形式在命令行中一次调用多个任务。例如 gradle compile test 命令会依次调用,并且每个任务仅会被调用一次。compile 和 test 任务以及它们的依赖任务。无论它们是否被包含在脚本中:即无论是以命令行的形式定义的任务还是依赖于其它任务都会被调用执行。来看下面的例子。

下面定义了四个任务。dist 和 test 都依赖于 compile,只用当 compile 被调用之后才会调用 gradle dist test 任务。

任务依赖

Gradle Gradle 命令行的基本使用

多任务调用

build.gradle

task compile << {
    println 'compiling source'
}
task compileTest(dependsOn: compile) << {
    println 'compiling unit tests'
}
task test(dependsOn: [compile, compileTest]) << {
    println 'running unit tests'
}
task dist(dependsOn: [compile, test]) << {
    println 'building the distribution'
}

gradle dist test 的输出结果。

\> gradle dist test
:compile
compiling source
:compileTest
compiling unit tests
:test
running unit tests
:dist
building the distribution
BUILD SUCCESSFUL
Total time: 1 secs

由于每个任务仅会被调用一次,所以调用 gradle test test 与调用 gradle test 效果是相同的。

排除任务

你可以用命令行选项 -x 来排除某些任务,让我们用上面的例子来示范一下。

gradle dist -x test 的输出结果。

\> gradle dist -x test
:compile
compiling source
:dist
building the distribution
BUILD SUCCESSFUL
Total time: 1 secs

可以看到,test 任务并没有被调用,即使他是 dist 任务的依赖。同时 test 任务的依赖任务 compileTest 也没有被调用,而像 compile 被 test 和其它任务同时依赖的任务仍然会被调用。

失败后继续执行

默认情况下只要有任务调用失败 Gradle 就是中断执行。这可能会使调用过程更快,但那些后面隐藏的错误不会被发现。所以你可以使用–continue 在一次调用中尽可能多的发现所有问题。

采用了–continue 选项,Gralde会调用每一个任务以及它们依赖的任务。而不是一旦出现错误就会中断执行。所有错误信息都会在最后被列出来。

一旦某个任务执行失败,那么所有依赖于该任务的子任务都不会被调用。例如由于 test 任务依赖于 complie 任务,所以如果 compile 调用出错,test 便不会被直接或间接调用。

简化任务名

当你试图调用某个任务的时候,无需输入任务的全名。只需提供足够的可以唯一区分出该任务的字符即可。例如,上面的例子你也可以这么写。用 gradle di 来直接调用 dist 任务。

gradle di 的输出结果

\> gradle di
:compile
compiling source
:compileTest
compiling unit tests
:test
running unit tests
:dist
building the distribution
BUILD SUCCESSFUL
Total time: 1 secs

你也可以用驼峰命名的任务中每个单词的首字母进行调用。例如,可以执行 gradle compTest or even gradle cT 来调用 compileTest 任务。

简化驼峰任务名

gradle cT 的输出结果。

\> gradle cT
:compile
compiling source
:compileTest
compiling unit tests
BUILD SUCCESSFUL
Total time: 1 secs

简化后你仍然可以使用 -x 参数。

选择构建位置

调用 gradle 时,默认情况下总是会构建当前目录下的文件,可以使用-b 参数选择构建的文件,并且当你使用此参数时settings。gradle 将不会生效,看下面的例子:

选择文件构建

subdir/myproject.gradle

task hello << {
    println "using build file '$buildFile.name' in '$buildFile.parentFile.name'."
}

gradle -q -b subdir/myproject.gradle hello 的输出结果

Output of gradle -q -b subdir/myproject.gradle hello
\> gradle -q -b subdir/myproject.gradle hello
using build file 'myproject.gradle' in 'subdir'.

另外,你可以使用 -p 参数来指定构建的目录,例如在多项目构建中你可以用 -p 来替代 -b 参数。

选择构建目录

gradle -q -p subdir hello 的输出结果

\> gradle -q -p subdir hello
using build file 'build.gradle' in 'subdir'.

获取构建信息

Gradle 提供了许多内置任务来收集构建信息。这些内置任务对于了解依赖结构以及解决问题都是很有帮助的。

了解更多,可以参阅项目报告插件以为你的项目添加构建报告。

项目列表

执行 gradle projects 会为你列出子项目名称列表。如下例。

收集项目信息

gradle -q projects 的输出结果

\> gradle -q projects
\------------------------------------------------------------
Root project
\------------------------------------------------------------
Root project 'projectReports'
+--- Project ':api' - The shared API for the application
\--- Project ':webapp' - The Web application implementation
To see a list of the tasks of a project, run gradle <project-path>:tasks
For example, try running gradle :api:tasks

这份报告展示了每个项目的描述信息。当然你可以在项目中用 description 属性来指定这些描述信息。

为项目添加描述信息.

build.gradle

description = 'The shared API for the application'

任务列表

执行 gradle tasks 会列出项目中所有任务。这会显示项目中所有的默认任务以及每个任务的描述。如下例

获取任务信息

gradle -q tasks的输出结果:

\> gradle -q tasks
\------------------------------------------------------------
All tasks runnable from root project
\------------------------------------------------------------
Default tasks: dists
Build tasks
\-----------
clean - Deletes the build directory (build)
dists - Builds the distribution
libs - Builds the JAR
Build Setup tasks
\-----------------
init - Initializes a new Gradle build. [incubating]
wrapper - Generates Gradle wrapper files. [incubating]
Help tasks
\----------
dependencies - Displays all dependencies declared in root project 'projectReports'.
dependencyInsight - Displays the insight into a specific dependency in root project 'projectReports'.
help - Displays a help message
projects - Displays the sub-projects of root project 'projectReports'.
properties - Displays the properties of root project 'projectReports'.
tasks - Displays the tasks runnable from root project 'projectReports' (some of the displayed tasks may belong to subprojects).
To see all tasks and more detail, run with --all.

默认情况下,这只会显示那些被分组的任务。你可以通过为任务设置 group 属性和 description 来把 这些信息展示到结果中。

更改任务报告内容

build.gradle

dists {
    description = 'Builds the distribution'
    group = 'build'
}

当然你也可以用–all 参数来收集更多任务信息。这会列出项目中所有任务以及任务之间的依赖关系。

Obtaining more information about tasks
gradle -q tasks --all的输出结果:

\> gradle -q tasks --all
\------------------------------------------------------------
All tasks runnable from root project
\------------------------------------------------------------
Default tasks: dists
Build tasks
\-----------
clean - Deletes the build directory (build)
api:clean - Deletes the build directory (build)
webapp:clean - Deletes the build directory (build)
dists - Builds the distribution [api:libs, webapp:libs]
    docs - Builds the documentation
api:libs - Builds the JAR
    api:compile - Compiles the source files
webapp:libs - Builds the JAR [api:libs]
    webapp:compile - Compiles the source files
Build Setup tasks
\-----------------
init - Initializes a new Gradle build. [incubating]
wrapper - Generates Gradle wrapper files. [incubating]
Help tasks
\----------
dependencies - Displays all dependencies declared in root project 'projectReports'.
dependencyInsight - Displays the insight into a specific dependency in root project 'projectReports'.
help - Displays a help message
projects - Displays the sub-projects of root project 'projectReports'.
properties - Displays the properties of root project 'projectReports'.
tasks - Displays the tasks runnable from root project 'projectReports' (some of the displayed tasks may belong to subprojects).

获取任务帮助信息

执行 gradle help --task someTask 可以显示指定任务的详细信息。或者多项目构建中相同任务名称的所有任务的信息。如下例。

获取任务帮助

gradle -q help --task libs 的输出结果

\> gradle -q help --task libs
Detailed task information for libs
Paths
     :api:libs
     :webapp:libs
Type
     Task (org.gradle.api.Task)
Description
     Builds the JAR

这些结果包含了任务的路径、类型以及描述信息等。

获取依赖列表

执行 gradle dependencies 会列出项目的依赖列表,所有依赖会根据任务区分,以树型结构展示出来。如下例。

获取依赖信息

gradle -q dependencies api:dependencies webapp:dependencies 的输出结果

\> gradle -q dependencies api:dependencies webapp:dependencies
\------------------------------------------------------------
Root project
\------------------------------------------------------------
No configurations
\------------------------------------------------------------
Project :api - The shared API for the application
\------------------------------------------------------------
compile
\--- org.codehaus.groovy:groovy-all:2.2.0
testCompile
\--- junit:junit:4.11
     \--- org.hamcrest:hamcrest-core:1.3
\------------------------------------------------------------
Project :webapp - The Web application implementation
\------------------------------------------------------------
compile
+--- project :api
|    \--- org.codehaus.groovy:groovy-all:2.2.0
\--- commons-io:commons-io:1.2
testCompile
No dependencies

虽然输出结果很多,但这对于了解构建任务十分有用,当然你可以通过–configuration 参数来查看 指定构建任务的依赖情况。

过滤依赖信息

gradle -q api:dependencies --configuration testCompile 的输出结果

\> gradle -q api:dependencies --configuration testCompile
\------------------------------------------------------------
Project :api - The shared API for the application
\------------------------------------------------------------
testCompile
\--- junit:junit:4.11
     \--- org.hamcrest:hamcrest-core:1.3

查看特定依赖

执行 Running gradle dependencyInsight 可以查看指定的依赖情况。如下例。

获取特定依赖

gradle -q webapp:dependencyInsight --dependency groovy --configuration compile 的输出结果

> gradle -q webapp:dependencyInsight --dependency groovy --configuration compile
org.codehaus.groovy:groovy-all:2.2.0
— project :api
— compile
这对于了解依赖关系、了解为何选择此版本作为依赖十分有用。了解更多请参阅依赖检查报告。

dependencyInsight 任务是’Help’任务组中的一个。这项任务需要进行配置才可以。如果用了 Java 相关的插件,那么 dependencyInsight 任务已经预先被配置到’Compile’下了。你只需要通过’–dependency’参数来制定所需查看的依赖即可。如果你不想用默认配置的参数项你可以通过 ‘–configuration’ 参数来进行指定。了解更多请参阅依赖检查报告。

获取项目属性列表

执行 gradle properties 可以获取项目所有属性列表。如下例。

属性信息
gradle -q api:properties 的输出结果

\> gradle -q api:properties
\------------------------------------------------------------
Project :api - The shared API for the application
\------------------------------------------------------------
allprojects: [project ':api']
ant: org.gradle.api.internal.project.DefaultAntBuilder@12345
antBuilderFactory: org.gradle.api.internal.project.DefaultAntBuilderFactory@12345
artifacts: org.gradle.api.internal.artifacts.dsl.DefaultArtifactHandler@12345
asDynamicObject: org.gradle.api.internal.ExtensibleDynamicObject@12345
buildDir: /home/user/gradle/samples/userguide/tutorial/projectReports/api/build
buildFile: /home/user/gradle/samples/userguide/tutorial/projectReports/api/build.gradle

构建日志

–profile 参数可以收集一些构建期间的信息并保存到 build/reports/profile 目录下并且以构建时间命名这些文件。

下面这份日志记录了总体花费时间以及各过程花费的时间,并以时间大小倒序排列,并且记录了任务的执行情况。

如果采用了 buildSrc,那么在 buildSrc/build 下同时也会生成一份日志记录记录。

Gradle Gradle 命令行的基本使用

Dry Run
有时可能你只想知道某个任务在一个任务集中按顺序执行的结果,但并不想实际执行这些任务。那么你可以用-m 参数。例如 gradle -m clean compile 会调用 clean 和 compile,这与 tasks 可以形成互补,让你知道哪些任务可以用于执行。

使用 Gradle 图形用户界面

除了支持传统的命令行界面,Gradle 也提供了一个图形用户界面(GUI)。这是一个独立的用户界面,可以通过加上 --gui 参数来启动。

Launching the GUI

gradle --gui  

注意:此命令行窗口被将锁定,直到 Gradle GUI 被关闭。如果是在 Linux/unix 系统下,则可以通过(gradle --gui&)让它作为后台任务运行。

如果你在你的 Gradle 项目目录下运行 Gradle GUI,你应该会看到一个任务树。

GUI Task Tree

Gradle 使用 Gradle 图形用户界面

最好是从 Gradle 项目目录运行此命令,这样对 UI 的设置就可以存储在你的项目目录中。当然,你也可以先运行它,然后通过在 UI 中的设置(Setup)选项卡,改变工作目录。

在 Gradle 的用户界面(UI)中,上面是 4 个选项卡,下面则是输出窗口。

任务树

任务树显示了所有项目和它们的任务的层次结构。双击一个任务可以执行它。

这里还提供了一个过滤器,可以把比较少用的任务隐藏。你可以通过过滤器(Filter)按钮切换是否进行过滤。通过编辑过滤器,你可以对哪些任务和项目要显示进行配置。隐藏的任务显示为红色。注意:新创建的任务默认情况下是显示状态(而不是隐藏状态)

任务树的上下文菜单会提供以下选项:

执行忽略依赖关系。这使得重新构建时不去依赖项目(与 -a 选项一样)
将任务添加到收藏夹(见收藏夹(Favourites)选项卡)
隐藏选择的任务。这将会把它们添加到过滤器中。
编辑 build.gradle 文件。注意:该操作需要 Java 1.6 或更高的版本,并且要求在你的操作系统中关联 gradle 文件。

收藏夹

收藏夹选项卡用来储存经常执行的命令。这些命令可以是复杂的命令(只要它们符合 Gradle 的语法),你可以给它们设置一个显示名称。它用于创建一个自定义的命令,来显示地跳过测试,文档,例子。你可以称之为“快速构建”。

你可以根据自己的喜好,对收藏夹进行排序,甚至可以把它们导出到磁盘,并在其他地方导入。如果你在编辑它们的时候,选上“始终显示实时输出”,它只有在你选上“当发生错误时才显示输出”时有效。它会始终强制显示输出。

命令行

命令行选项卡是直接执行单个的 Gradle 命令的地方。你只需要输入命令行中你经常在“Gradle”后面输入的命令即可。它也对要添加到收藏夹的命令提供了先去尝试的地方。

设置

设置(Setup)选项卡允许你配置一些常规的设置

GUI Setup
在这里插入图片描述

Gradle 使用 Gradle 图形用户界面

当前目录 定义了你的 Gradle 项目(通常是 build.gradle 所在的位置)的根目录。
堆栈跟踪输出 这决定了当出现错误时,有多少信息定到堆栈跟踪。注意:如果你在命令行或收藏夹选项卡上指定了堆栈跟踪级别,将会覆盖这里的设置。
只在出现错误时显示输出 启用此选项将在任务执行时隐藏任何输出,除非构建失败。
使用自定义的 Gradle 执行器 - 高级功能 这为你提供了启动 Gradle 命令行的替代方法。这是很有用的。如果你的项目需要在另一个批处理文件或 shell 脚本中做一些额外的配置(比如指定一个初始化脚本)。

Gradle 编写构建脚本

Gradle 构建语言

Gradle 提供一种领域特定语言或者说是 DSL,来描述构建。这种构建语言基于 Groovy 中,并进行了一些补充,使其易于描述构建。

Project API

在Java 构建入门的教程中,我们使用了 apply ()方法。这方法从何而来?我们之前说在 Gradle 中构建脚本定义了一个项目(project)。在构建的每一个项目中,Gradle 创建了一个 Project 类型的实例,并在构建脚本中关联此 Project 对象。当构建脚本执行时,它会配置此 Project 对象:

在构建脚本中,你所调用的任何一个方法,如果在构建脚本中未定义,它将被委托给 Project 对象。
在构建脚本中,你所访问的任何一个属性,如果在构建脚本里未定义,它也会被委托给 Project 对象。
下面我们来试试这个,试试访问 Project 对象的 name 属性。

访问 Project 对象的属性

build.gradle

println name
println project.name 

gradle -q check 的输出结果

> gradle -q check
projectApi
projectApi  

这两个 println 语句打印出相同的属性。在生成脚本中未定义的属性,第一次使用时自动委托到 Project 对象。其他语句使用了在任何构建脚本中可以访问的 project 属性,则返回关联的 Project 对象。只有当您定义的属性或方法 Project 对象的一个成员相同名字时,你才需要使用 project 属性。

标准 project 属性

Project对象提供了一些在构建脚本中可用的标准的属性。下表列出了常用的几个属性。

表 13.1. Project 属性

名称类型默认值
projectProjectProject实例
nameString项目目录的名称。
pathString项目的绝对路径。
descriptionString项目的描述。
projectDirFile包含生成脚本的目录。
buildDirFileprojectDir/build
groupObject未指定
versionObject未指定
antAntBuilderAntBuilder实例

Script API

当 Gradle 执行一个脚本时,它将脚本编译为一个实现了 Script 接口的类。这意味着所有由该Script 接口声明的属性和方法在您的脚本中是可用的。

声明变量

有两类可以在生成脚本中声明的变量: 局部变量和额外属性。

局部变量局部

局部变量是用 def 关键字声明的。它们只在定义它们的范围内可以被访问。局部变量是 Groovy 语言底层的一个特征。

示例 13.2. 使用局部变量

build.gradle

def dest = "dest"
task copy(type: Copy) {
    from "source"
    into dest
}  

额外属性

Gradle 的域模型中,所有增强的对象都可以容纳用户定义的额外的属性。这包括但并不限于项目(project)、任务(task)和源码集(source set)。额外的属性可以通过所属对象的 ext 属性进行添加,读取和设置。或者,可以使用 ext 块同时添加多个属性。

13.3 例子. 使用额外属性

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

> gradle -q printProperties
3.1.0.RELEASE
build@master.org
main
plugin  

在此示例中, 一个 ext 代码块将两个额外属性添加到 project 对象中。此外,通过将ext.purpose 设置为 null(null是一个允许的值),一个名为 purpose 的属性被添加到每个源码集(source set)。一旦属性被添加,他们就可以像预定的属性一样被读取和设置。

通过添加属性所要求特殊的语法,Gradle 可以在你试图设置 (预定义的或额外的) 的属性,但该属性拼写错误或不存在时 fail fast。[5]额外属性在任何能够访问它们所属的对象的地方都可以被访问,这使它们有着比局部变量更广泛的作用域。父项目上的额外属性,在子项目中也可以访问。

有关额外属性和它们的 API 的详细信息,请参阅 ExtraPropertiesExtension。

一些 Groovy 的基础知识

Groovy 提供了用于创建 DSL 的大量特点,并且 Gradle 构建语言利用了这些特点。了解构建语言是如何工作的,将有助于你编写构建脚本,特别是当你开始写自定义插件和任务的时候。

Groovy JDK

Groovy 对 JVM 的类增加了很多有用的方法。例如, iterable 新增的 each 方法,会对iterable 的元素进行遍历:

Groovy JDK 的方法

build.gradle

// Iterable gets an each() method
configurations.runtime.each { File f -> println f }  

可以看看Http://groovy.codehaus.org/groovy-jdk/,了解更多详细信息。

属性访问器

Groovy 会自动地把一个属性的引用转换为对适当的 getter 或 setter 方法的调用。

属性访问器

build.gradle

// Using a getter method
println project.buildDir
println getProject().getBuildDir()
// Using a setter method
project.buildDir = 'target'
getProject().setBuildDir('target')   

括号可选的方法调用

调用方法时括号是可选的。

不带括号的方法调用

build.gradle

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

List 和 Map

Groovy 提供了一些定义 List 和 Map 实例的快捷写法。

List and map

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 在很多地方使用闭包。你可以在这里查看更多有关闭包的资料。当方法的最后一个参数是一个闭包时,你可以把闭包放在方法调用之后:

作为方法参数的闭包

build.gradle

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

闭包委托(delegate)

每个闭包都有一个委托对象,Groovy 使用它来查找变量和方法的引用,而不是作为闭包的局部变量或参数。Gradle 在配置闭包中使用到它,把委托对象设置为被配置的对象。

闭包委托

build.gradle

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

Gradle 任务详述

在入门教程构建基础中,你已经学习了如何创建简单的任务。之后您还学习了如何将其他行为添加到这些任务中。并且你已经学会了如何创建任务之间的依赖。这都是简单的任务。但 Gradle 让任务的概念更深远。Gradle 支持增强的任务,也就是,有自己的属性和方法的任务。这是真正的与你所使用的 Ant 目标(target)的不同之处。这种增强的任务可以由你提供,或由 Gradle 提供。

定义任务

在构建基础中我们已经看到如何通过关键字这种风格来定义任务。在某些情况中,你可能需要使用这种关键字风格的几种不同的变式。例如,在表达式中不能用这种关键字风格。

定义任务

build.gradle

task(hello) << {
    println "hello"
}
task(copy, type: Copy) {
    from(file('srcDir'))
    into(buildDir)
}  

您还可以使用字符串作为任务名称:

定义任务 — — 使用字符串作为任务名称

build.gradle

task('hello') <<
{
    println "hello"
}
task('copy', type: Copy) {
    from(file('srcDir'))
    into(buildDir)
}  

对于定义任务,有一种替代的语法你可能更愿意使用:

使用替代语法定义任务

build.gradle

tasks.create(name: 'hello') << {
    println "hello"
}
tasks.create(name: 'copy', type: Copy) {
    from(file('srcDir'))
    into(buildDir)
}  

在这里我们将任务添加到 tasks 集合。关于 create() 方法的更多变化可以看看 TaskContainer。

定位任务

你经常需要在构建文件中查找你所定义的任务,例如,为了去配置或是依赖它们。对这样的情况,有很多种方法。首先,每个任务都可作为项目的一个属性,并且使用任务名称作为这个属性名称:

以属性方式访问任务

build.gradle

task hello
println hello.name
println project.hello.name  
任务也可以通过 tasks 集合来访问。

通过 tasks 集合访问任务

build.gradle

task hello
println tasks.hello.name
println tasks['hello'].name  

您可以从任何项目中,使用 tasks.getByPath() 方法获取任务路径并且通过这个路径来访问任务。你可以用任务名称,相对路径或者是绝对路径作为参数调用 getByPath() 方法。

通过路径访问任务

build.gradle

project(':projectA') {
    task hello
}
task hello
println tasks.getByPath('hello').path
println tasks.getByPath(':hello').path
println tasks.getByPath('projectA:hello').path
println tasks.getByPath(':projectA:hello').path  

gradle -q hello的输出结果

> gradle -q hello
:hello
:hello
:projectA:hello
:projectA:hello  

有关查找任务的更多选项,可以看一下 TaskContainer。

配置任务

作为一个例子,让我们看看由 Gradle 提供的 Copy 任务。若要创建 Copy 任务,您可以在构建脚本中声明:

创建一个复制任务

build.gradle

task myCopy(type: Copy)  

上面的代码创建了一个什么都没做的复制任务。可以使用它的 API 来配置这个任务(见 Copy)。下面的示例演示了几种不同的方式来实现相同的配置。

配置任务的几种方式

build.gradle

Copy myCopy = task(myCopy, type: Copy)
myCopy.from 'resources'
myCopy.into 'target'
myCopy.include('**/*.txt', '**/*.xml', '**/*.properties')  

这类似于我们通常在 Java 中配置对象的方式。您必须在每一次的配置语句重复上下文 (myCopy)。这显得很冗余并且很不好读。

还有另一种配置任务的方式。它也保留了上下文,且可以说是可读性最强的。它是我们通常最喜欢的方式。

配置任务-使用闭包

build.gradle

task myCopy(type: Copy)
myCopy {
   from 'resources'
   into 'target'
   include('**/*.txt', '**/*.xml', '**/*.properties')
}  

这种方式适用于任何任务。该例子的第 3 行只是 tasks.getByName() 方法的简洁写法。特别要注意的是,如果您向 getByName() 方法传入一个闭包,这个闭包的应用是在配置这个任务的时候,而不是任务执行的时候。

您也可以在定义一个任务的时候使用一个配置闭包。

使用闭包定义任务

build.gradle

task copy(type: Copy) {
   from 'resources'
   into 'target'
   include('**/*.txt', '**/*.xml', '**/*.properties')
}  

对任务添加依赖

定义任务的依赖关系有几种方法。在任务依赖中,已经向你介绍了使用任务名称来定义依赖。任务的名称可以指向同一个项目中的任务,或者其他项目中的任务。要引用另一个项目中的任务,你需要把它所属的项目的路径作为前缀加到它的名字中。下面是一个示例,添加了从 projectA:taskX 到 projectB:taskY 的依赖关系:

从另一个项目的任务上添加依赖

build.gradle

project('projectA') {
    task taskX(dependsOn: ':projectB:taskY') << {
        println 'taskX'
    }
}
project('projectB') {
    task taskY << {
        println 'taskY'
    }
}  

gradle -q taskX 的输出结果

> gradle -q taskX
taskY
taskX  

您可以使用一个 Task 对象而不是任务名称来定义依赖,如下:

使用 task 对象添加依赖

build.gradle

task taskX << {
    println 'taskX'
}
task taskY << {
    println 'taskY'
}
taskX.dependsOn taskY  

gradle -q taskX的输出结果

> gradle -q taskX
taskY
taskX  

对于更高级的用法,您可以使用闭包来定义任务依赖。在计算依赖时,闭包会被传入正在计算依赖的任务。这个闭包应该返回一个 Task 对象或是 Task 对象的集合,返回值会被作为这个任务的依赖项。下面的示例是从 taskX 加入了项目中所有名称以 lib 开头的任务的依赖:

使用闭包添加依赖

build.gradle

task taskX << {
    println 'taskX'
}
taskX.dependsOn {
    tasks.findAll { task -> task.name.startsWith('lib') }
}
task lib1 << {
    println 'lib1'
}
task lib2 << {
    println 'lib2'
}
task notALib << {
    println 'notALib'
}  

gradle -q taskX 的输出结果

> gradle -q taskX
lib1
lib2
taskX  

有关任务依赖的详细信息,请参阅 Task 的 API。

任务排序

任务排序还是一个孵化中的功能。请注意此功能在以后的 Gradle 版本中可能会改变。

在某些情况下,控制两个任务的执行的顺序,而不引入这些任务之间的显式依赖,是很有用的。任务排序和任务依赖之间的主要区别是,排序规则不会影响那些任务的执行,而仅将执行的顺序。

任务排序在许多情况下可能很有用:

强制任务顺序执行: 如,‘build’ 永远不会在 ‘clean’ 前面执行。
在构建中尽早进行构建验证:如,验证在开始发布的工作前有一个正确的证书。
通过在长久验证前运行快速验证以得到更快的反馈:如,单元测试应在集成测试之前运行。
一个任务聚合了某一特定类型的所有任务的结果:如,测试报告任务结合了所有执行的测试任务的输出。
有两种排序规则是可用的:“必须在之后运行"和"应该在之后运行”。

通过使用 “ 必须在之后运行”的排序规则,您可以指定 taskB 必须总是运行在 taskA 之后,无论 taskA 和 taskB 这两个任务在什么时候被调度执行。这被表示为 taskB.mustRunAfter(taskA) 。“应该在之后运行”的排序规则与其类似,但没有那么严格,因为它在两种情况下会被忽略。首先是如果使用这一规则引入了一个排序循环。其次,当使用并行执行,并且一个任务的所有依赖项除了任务应该在之后运行之外所有条件已满足,那么这个任务将会运行,不管它的“应该在之后运行”的依赖项是否已经运行了。当倾向于更快的反馈时,会使用“应该在之后运行”的规则,因为这种排序很有帮助但要求不严格。

目前使用这些规则仍有可能出现 taskA 执行而 taskB 没有执行,或者 taskB 执行而 taskA 没有执行。

添加 '必须在之后运行 ’ 的任务排序

build.gradle

task taskX << {
    println 'taskX'
}
task taskY << {
    println 'taskY'
}
taskY.mustRunAfter taskX  

gradle -q taskY taskX 的输出结果

> gradle -q taskY taskX
taskX
taskY  

添加 '应该在之后运行 ’ 的任务排序

build.gradle

task taskX << {
    println 'taskX'
}
task taskY << {
    println 'taskY'
}
taskY.shouldRunAfter taskX  

gradle -q taskY taskX 的输出结果

> gradle -q taskY taskX
taskX
taskY  

在上面的例子中,它仍有可能执行 taskY 而不会导致 taskX 也运行:

任务排序并不意味着任务执行

gradle -q taskY 的输出结果

> gradle -q taskY
taskY  

如果想指定两个任务之间的“必须在之后运行”和“应该在之后运行”排序,可以使用 Task.mustRunAfter() 和 Task.shouldRunAfter() 方法。这些方法接受一个任务实例、 任务名称或 Task.dependsOn()所接受的任何其他输入作为参数。

请注意"B.mustRunAfter(A)"或"B.shouldRunAfter(A)"并不意味着这些任务之间的任何执行上的依赖关系:

它是可以独立地执行任务 A 和 B 的。排序规则仅在这两项任务计划执行时起作用。
当–continue 参数运行时,可能会是 A 执行失败后B执行了。
如之前所述,如果“应该在之后运行”的排序规则引入了排序循环,那么它将会被忽略。

当引入循环时,“应该在其之后运行”的任务排序会被忽略

build.gradle

task taskX << {
    println 'taskX'
}
task taskY << {
    println 'taskY'
}
task taskZ << {
    println 'taskZ'
}
taskX.dependsOn taskY
taskY.dependsOn taskZ
taskZ.shouldRunAfter taskX  

gradle -q taskX 的输出结果

> gradle -q taskX
taskZ
taskY
taskX  

向任务添加描述

你可以向你的任务添加描述。例如,当执行 gradle tasks 时显示这个描述。

向任务添加描述

build.gradle

task copy(type: Copy) {
   description 'Copies the resource directory to the target directory.'
   from 'resources'
   into 'target'
   include('**/*.txt', '**/*.xml', '**/*.properties')
}  

替换任务

有时您想要替换一个任务。例如,您想要把通过 Java 插件添加的一个任务与不同类型的一个自定义任务进行交换。你可以这样实现:

重写任务

build.gradle

task copy(type: Copy)
task copy(overwrite: true) << {
    println('I am the new one.')
}  

gradle -q copy 的输出结果

> gradle -q copy
I am the new one.  

在这里我们用一个简单的任务替换 Copy 类型的任务。当创建这个简单的任务时,您必须将 overwrite 属性设置为 true。否则 Gradle 将抛出异常,说这种名称的任务已经存在。

跳过任务

Gradle 提供多种方式来跳过任务的执行。

使用断言
你可以使用 onlyIf()方法将断言附加到一项任务中。如果断言结果为 true,才会执行任务的操作。你可以用一个闭包来实现断言。闭包会作为一个参数传给任务,并且任务应该执行时返回 true,或任务应该跳过时返回 false。断言只在任务要执行前才计算。

使用断言跳过一个任务

build.gradle

task hello << {

    println 'hello world'
}
hello.onlyIf { !project.hasProperty('skipHello') }  

gradle hello -PskipHello 的输出结果

> gradle hello -PskipHello
:hello SKIPPED
BUILD SUCCESSFUL
Total time: 1 secs  

使用 StopExecutionException
如果跳过任务的规则不能与断言同时表达,您可以使用 StopExecutionException。如果一个操作(action)抛出了此异常,那么这个操作(action)接下来的行为和这个任务的其他 操作(action)都会被跳过。构建会继续执行下一个任务。

使用 StopExecutionException 跳过任务

build.gradle

task compile << {
    println 'We are doing the compile.'
}
compile.doFirst {
    // Here you would put arbitrary conditions in real life. But we use this as an integration test, so we want defined behavior.
    if (true) { throw new StopExecutionException() }
}
task myTask(dependsOn: 'compile') << {
   println 'I am not affected'
}  

gradle -q myTask 的输出结果

> gradle -q myTask
I am not affected  

如果您使用由 Gradle 提供的任务,那么此功能将非常有用。它允许您向一个任务的内置操作中添加执行条件。

启用和禁用任务
每一项任务有一个默认值为 true 的 enabled 标记。将它设置为 false,可以不让这个任务的任何操作执行。

启用和禁用任务

build.gradle

task disableMe << {
    println 'This should not be printed if the task is disabled.'
}
disableMe.enabled = false  

Gradle disableMe 的输出结果

> gradle disableMe
:disableMe SKIPPED
BUILD SUCCESSFUL
Total time: 1 secs  

跳过处于最新状态的任务

如果您使用 Gradle 自带的任务,如 Java 插件所添加的任务的话,你可能已经注意到 Gradle 将跳过处于最新状态的任务。这种行在您自己定义的任务上也有效,而不仅仅是内置任务。

声明一个任务的输入和输出

让我们来看一个例子。在这里我们的任务从一个 XML 源文件生成多个输出文件。让我们运行它几次。

一个生成任务

build.gradle

task transform {
    ext.srcFile = file('mountains.xml')
    ext.destDir = new File(buildDir, 'generated')
    doLast {
        println "Transforming source file."
        destDir.mkdirs()
        def mountains = new XmlParser().parse(srcFile)
        mountains.mountain.each { mountain ->
            def name = mountain.name[0].text()
            def height = mountain.height[0].text()
            def destFile = new File(destDir, "${name}.txt")
            destFile.text = "$name -> ${height}\n"
        }
    }
}  

gradle transform 的输出结果

> gradle transform
:transform
Transforming source file.  

gradle transform的输出结果

> gradle transform
:transform
Transforming source file.  

请注意 Gradle 第二次执行执行这项任务时,即使什么都未作改变,也没有跳过该任务。我们的示例任务被用一个操作(action)闭包来定义。Gradle 不知道这个闭包做了什么,也无法自动判断这个任务是否为最新状态。若要使用 Gradle 的最新状态(up-to-date)检查,您需要声明这个任务的输入和输出。

每个任务都有一个 inputs 和 outputs 的属性,用来声明任务的输入和输出。下面,我们修改了我们的示例,声明它将 XML 源文件作为输入,并产生输出到一个目标目录。让我们运行它几次。

声明一个任务的输入和输出

build.gradle

task transform {
    ext.srcFile = file('mountains.xml')
    ext.destDir = new File(buildDir, 'generated')
    inputs.file srcFile
    outputs.dir destDir
    doLast {
        println "Transforming source file."
        destDir.mkdirs()
        def mountains = new XmlParser().parse(srcFile)
        mountains.mountain.each { mountain ->
            def name = mountain.name[0].text()
            def height = mountain.height[0].text()
            def destFile = new File(destDir, "${name}.txt")
            destFile.text = "$name -> ${height}\n"
        }
    }
}  

gradle transform 的输出结果

> gradle transform
:transform
Transforming source file.  

gradle transform 的输出结果

> gradle transform
:transform UP-TO-DATE  

现在,Gradle 知道哪些文件要检查以确定任务是否为最新状态。

任务的 inputs 属性是 TaskInputs 类型。任务的 outputs 属性是 TaskOutputs 类型。

一个没有定义输出的任务将永远不会被当作是最新的。对于任务的输出并不是文件的场景,或者是更复杂的场景, TaskOutputs.upToDateWhen() 方法允许您以编程方式计算任务的输出是否应该被判断为最新状态。

一个只定义了输出的任务,如果自上一次构建以来它的输出没有改变,那么它会被判定为最新状态。

它是怎么实现的?

在第一次执行任务之前,Gradle 对输入进行一次快照。这个快照包含了输入文件集和每个文件的内容的哈希值。然后 Gradle 执行该任务。如果任务成功完成,Gradle 将对输出进行一次快照。该快照包含输出文件集和每个文件的内容的哈希值。Gradle 会保存这两个快照,直到任务的下一次执行。

之后每一次,在执行任务之前,Gradle 会对输入和输出进行一次新的快照。如果新的快照和前一次的快照一样,Gradle 会假定这些输出是最新状态的并跳过该任务。如果它们不一则, Gradle 则会执行该任务。Gradle 会保存这两个快照,直到任务的下一次执行。

请注意,如果一个任务有一个指定的输出目录,在它上一次执行之后添加到该目录的所有文件都将被忽略,并且不会使这个任务成为过时状态。这是不相关的任务可以在不互相干扰的情况下共用一个输出目录。如果你因为一些理由而不想这样,请考虑使用 TaskOutputs.upToDateWhen()

任务规则

有时你想要有这样一项任务,它的行为依赖于参数数值范围的一个大数或是无限的数字。任务规则是提供此类任务的一个很好的表达方式:

任务规则

build.gradle

tasks.addRule("Pattern: ping<ID>") { String taskName ->
    if (taskName.startsWith("ping")) {
        task(taskName) << {
            println "Pinging: " + (taskName - 'ping')
        }
    }
}  

Gradle q pingServer1 的输出结果

> gradle -q pingServer1
Pinging: Server1  

这个字符串参数被用作这条规则的描述。当对这个例子运行 gradle tasks 的时候,这个描述会被显示。

规则不只是从命令行调用任务才起作用。你也可以对基于规则的任务创建依赖关系:

基于规则的任务依赖

build.gradle

tasks.addRule("Pattern: ping<ID>") { String taskName ->
    if (taskName.startsWith("ping")) {
        task(taskName) << {
            println "Pinging: " + (taskName - 'ping')
        }
    }
}
task groupPing {
    dependsOn pingServer1, pingServer2
}  

Gradle q groupPing 的输出结果

> gradle -q groupPing
Pinging: Server1
Pinging: Server2  

析构器任务

析构器任务是一个孵化中的功能 。当最终的任务准备运行时,析构器任务会自动地添加到任务图中。

添加一个析构器任务

build.gradle

task taskX << {
    println 'taskX'
}
task taskY << {
    println 'taskY'
}
taskX.finalizedBy taskY  

gradle -q taskX 的输出结果

> gradle -q taskX
taskX
taskY  

即使最终的任务执行失败,析构器任务也会被执行。

执行失败的任务的任务析构器

build.gradle

task taskX << {
    println 'taskX'
    throw new RuntimeException()
}
task taskY << {
    println 'taskY'
}
taskX.finalizedBy taskY  

gradle -q taskX 的输出结果

> gradle -q taskX
taskX
taskY  

另一方面,如果最终的任务什么都不做的话,比如由于失败的任务依赖项或如果它被认为是最新的状态,析构任务不会执行。

在不管构建成功或是失败,都必须清理创建的资源的情况下,析构认为是很有用的。这样的资源的一个例子是,一个 web 容器会在集成测试任务前开始,并且在之后关闭,即使有些测试失败。

你可以使用 Task.finalizedBy()方法指定一个析构器任务。这个方法接受一个任务实例、任务名称或Task.dependsOn()所接受的任何其他输入作为参数。

总结

如果你是从 Ant 转过来的,像 Copy 这种增强的 Gradle 任务,看起来就像是一个 Ant 目标(target)和一个 Ant 任务(task)之间的混合物。实际上确实是这样子。Gradle 没有像 Ant 那样对任务和目标进行分离。简单的 Gradle 任务就像 Ant 的目标,而增强的 Gradle 任务还包括 Ant 任务方面的内容。Gradle 的所有任务共享一个公共 API,您可以创建它们之间的依赖性。这样的一个任务可能会比一个 Ant 任务更好配置。它充分利用了类型系统,更具有表现力而且易于维护。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/355014.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Pycharm搭建一个Django项目

File->new project 点击create&#xff0c; 等待一下即可 查看安装 Django 版本&#xff1a; 在 Pycharm 底部选择 Terminal 然后在里面输入&#xff1a;python -m django --version 启动项目&#xff1a; 在 Terminal 里面输入: python manage.py runserver 查看文件目…

一文精通MVCC机制

MVCC(Multi-Version Concurrency Control)多版本并发控制机制使用串行化隔离级别时&#xff0c;mysql会将所有的操作加锁互斥&#xff0c;来保证并发安全。这种方式必然降低并发性能。mysql在读已提交和可重复读隔离级别下&#xff0c;对一行数据的读和写两个操作默认是不会通过…

【Unity3d】Unity与iOS之间通信

在unity开发或者sdk开发经常遇到unity与移动端原生层之间进行通信&#xff0c;这里把它们之间通信做一个整理。 关于Unity与Android之间通信&#xff0c;参考【Unity3d】Unity与Android之间通信 Unity调用Objective-C 主要分三个步骤&#xff1a; (一)、在xcode中定义要被u…

php学习笔记

之前看过php的基础教学视频&#xff0c;了解了一下&#xff0c;自己没有上手实践&#xff0c;现在为了项目需要&#xff0c;需要扎实学习一下&#xff0c;所以做一下笔记吧。 php学习笔记1.基础2.动态网站的开发学习2.1会员管理系统1.基础 之前看过一个4小时的基础视频&#x…

Go 数组和切片反思

切片的底层数据结构是数组&#xff0c;所以&#xff0c;切片是基于数组的上层封装&#xff0c;使用数组的场景&#xff0c;也完全可以使用切片。 类型比较 我看到 go 1.17 有对切片和数组转换的优化&#xff0c;禁不住纳闷&#xff0c;有什么场景是必须数组来完成的呢&#x…

vue项目第七天

项目中模块操做业务使用ajax&#xff08;需要使用接口认证&#xff09;修改封装的findData发送ajax请求管理员列表内部搜索业务复用之前的findData 方法即可实现整个查询业务。实现退出业务在下拉菜单上添加事件以及属性。用户退出登录&#xff0c;二次登录系统菜单可能不存在的…

linux环境搭建私有gitlab仓库

搭建之前&#xff0c;需要安装相应的依赖包&#xff0c;并且要启动sshd服务(1).安装policycoreutils-python openssh-server openssh-clients [rootVM-0-2-centos ~]# sudo yum install -y curl policycoreutils-python openssh-server openssh-clients [rootVM-0-2-centos ~]…

(API)接口测试的关键技术

接口测试也就是API测试&#xff0c;从名字上可以知道是面向接口的测试活动。所以在讲API测试之前&#xff0c;我们应该说清楚接口是什么&#xff0c;那么接口就是有特定输入和特定输出的一套逻辑处理单元&#xff0c;而对于接口调用方来说&#xff0c;不用知道自身的内部实现逻…

Spring 中经典的 9 种设计模式

1.简单工厂(非23种设计模式中的一种) 2.工厂方法 3.单例模式 4.适配器模式 5.装饰器模式 6.代理模式 7.观察者模式 8.策略模式 9.模版方法模式 Spring中涉及的设计模式总结 1.简单工厂(非23种设计模式中的一种) 实现方式&#xff1a; BeanFactory。Spring中的BeanFa…

Android 初代 K-V 存储框架 SharedPreferences,旧时代的余晖?

本文已收录到 AndroidFamily&#xff0c;技术和职场问题&#xff0c;请关注公众号 [彭旭锐] 提问。 前言 大家好&#xff0c;我是小彭。 SharedPreferences 是 Android 平台上轻量级的 K-V 存储框架&#xff0c;亦是初代 K-V 存储框架&#xff0c;至今被很多应用沿用。 有的…

【C语言】大小端字节序问题

一、大小端字节序问题 大小端是由CPU决定的&#xff0c;大小端可以理解为字节顺序&#xff0c;所以大小端全称叫大端字节序、小端字节序。其实大端、小端这两个词是从《格列佛游记》里出来的。《格列佛游记》有一段讲的是吃鸡蛋是从大的那头敲开还是小的那头敲开的问题&#xf…

拯救了大批爬虫程序员,因为一个简单的神器

相信大家应该都写过爬虫&#xff0c;简单的爬虫只需要使用 requests 即可。遇到复杂的爬虫&#xff0c;就需要在程序里面加上请求头和参数信息。类似这种&#xff1a;我们一般的步骤是&#xff0c;先到浏览器的网络请求中找到我们需要的请求&#xff0c;然后将请求头和参数信息…

CI/CD --- 什么才是真正的自动化平台

近2年在软件开发中比较火的两个术语&#xff0c;一个是敏捷开发&#xff0c;另外一个就是CI/CD了&#xff1b;敏捷开发顾名思义就是“以用户的需求进化为核心&#xff0c;采用迭代、循序渐进的方法进行软件开发”。那CI/CD&#xff08;Continuous Integration、 Continuous Del…

自抗扰控制ADRC之微分器TD

目录 前言 1 全程快速微分器 1.1仿真分析 1.2仿真模型 1.3仿真结果 1.4结论 2 Levant微分器 2.1仿真分析 2.2仿真模型 2.3仿真结果 3.总结 前言 工程上信号的微分是难以得到的&#xff0c;所以本文采用微分器实现带有噪声的信号及其微分信号提取&#xff0c;从而实现…

0216-0218复习:继承

目录 继承 一、基本介绍 二、示意图 三、基本语法 四、入门案例 父类 子类1 子类2 main方法 五、继承细节 第一条 第二条 第三条 第四条 ​编辑 第五条 第六条 第七条 第八条 第九条 第十条 六、继承本质 七、练习题 第三题 继承 一、基本介绍 继承可以…

RAY - 小记

文章目录关于 RAYRAY 结构关于 RAY Ray is a unified framework for scaling AI and Python applications. Ray consists of a core distributed runtime and a toolkit of libraries (Ray AIR) for accelerating ML workloads. RAY 是一个简单、通用的分布式计算框架。 RAY 解…

TikTok话题量超30亿,这款承载美好记忆的剪贴簿引发讨论

回忆风剪贴簿在TikTok引起关注小超在浏览超店有数后台时发现&#xff0c;有一款平平无奇的剪贴簿的种草视频爆火&#xff0c;在24h内收获了9.9K点赞&#xff0c;播放量更是突破了100W&#xff0c;直接冲到了【种草视频飙升榜】第六名的位置&#xff0c;并且这个数字目前仍在继续…

利用5G工业网关实现工业数字化的工业互联网解决方案

5G工业网关是一种用于将工业生产环境中的数据连接到工业互联网的解决方案。它可以利用高带宽、高速率、低时延的5G网络连接工业现场的PLC、传感器、工业设备和云端数据中心&#xff0c;从而实现工业数字化。 物通博联工业互联网解决方案 物通博联5G工业网关的使用步骤&#x…

XXL-JOB分布式任务调度框架(二)-策略详解

文章目录1.引言2.任务详解2.1.执行器2.2.基础配置3.路由策略(第一个)-案例4.路由策略(最后一个)-案例5.轮询策略-案例6.随机选取7.轮询选取8.一致性hash9.最不经常使用 (LFU)10.最近最久未使用&#xff08;LRU&#xff09;11.故障转移12.忙碌转移7.分片广播任务1.引言 本篇文章…

中外互免签证协定一览表(普通护照与公务普通护照)

普通护照&#xff1a;由公安部出入境管理机构或者公安部委托的县级以上地方人民政府公安机关出入境管理机构以及中华人民共和国驻外使馆、领馆和外交部委托的其他驻外机构签发&#xff0c;主要颁发给出国定居、探亲、访友、继承财产、留学、就业、旅游等因私事出国的中国公民。…