Gradle User Guide 中文版
User Manual:
Open the PDF directly: View PDF .
Page Count: 275 [warning: Documents this large are best viewed by clicking the View PDF Link!]
1.1
1.2
1.2.1
1.3
1.3.1
1.3.2
1.4
1.5
1.5.1
1.5.2
1.5.3
1.6
1.7
1.7.1
1.7.2
1.7.3
1.7.4
1.7.5
1.7.6
1.7.7
1.7.8
1.7.9
1.7.10
1.7.11
1.7.12
1.7.13
1.8
1.8.1
1.8.2
1.8.2.1
目錄
关于本书
介绍
关于这本指南
概述
特点
为什么用Groovy?
教程
安装Gradle
准备阶段
下载与安装
JVM选项
排除故障
构建脚本基础
Projects和tasks
Helloworld
快捷的任务定义
构建脚本代码
任务依赖
动态任务
使用已经存在的任务
短标记法
自定义任务属性
调用Ant任务
使用方法
默认任务
通过DAG配置
Java构建入门
Java插件
一个基础的Java项目
建立项目
2
1.8.2.2
1.8.2.3
1.8.2.4
1.8.2.5
1.8.2.6
1.8.3
1.8.3.1
1.8.3.2
1.8.3.3
1.8.3.4
1.9
1.9.1
1.9.2
1.9.3
1.9.4
1.9.5
1.9.6
1.9.7
1.10
1.10.1
1.10.2
1.11
1.11.1
1.11.2
1.11.3
1.12
1.12.1
1.12.2
1.12.3
1.12.4
1.12.5
1.12.6
1.12.6.1
1.12.6.2
外部的依赖
定制项目
发布JAR文件
创建Eclipse项目
总结
多项目的Java构建
定义一个多项目构建
通用配置
项目之间的依赖
创建一个发行版本
依赖管理的基础知识
什么是依赖管理
声明你的依赖
依赖配置
外部的依赖
仓库
发布artifacts
下一步?
Groovy快速入门
一个基本的Groovy项目
总结
网页应用快速入门
构建一个WAR文件
运行Web应用
总结
使用Gradle命令行
多任务调用
排除任务
失败后继续执行构建
简化任务名
选择文件构建
获取构建信息
项目列表
任务列表
3
1.12.6.3
1.12.6.4
1.12.6.5
1.12.6.6
1.12.6.7
1.13
1.13.1
1.13.2
1.13.3
1.13.4
1.14
1.14.1
1.14.2
1.14.2.1
1.14.3
1.14.4
1.14.4.1
1.14.4.2
1.14.5
1.14.5.1
1.14.5.2
1.14.5.3
1.14.5.4
1.14.5.5
1.14.5.6
1.15
1.15.1
1.15.2
1.15.3
1.15.4
1.15.5
1.15.6
1.15.7
获取任务具体信息
获取依赖列表
查看特定依赖
获取项目属性列表
构建日志
使用Gradle图形界面
任务树
收藏夹
命令行
设置
编写构建脚本
Gradle构建语言
项目API
标准项目属性
脚本API
声明变量
局部变量
扩展属性
Groovy基础
GroovyJDK
属性存取器
可有可无的圆括号
List和Map集合
闭合作为方法的最后一个参数
闭合委托对象
深入了解Tasks
定义tasks
定位tasks
配置tasks
给task加入依赖
给tasks排序
给task加入描述
替换tasks
4
1.15.8
1.15.9
1.15.10
1.15.11
1.15.12
1.15.12.1
1.15.12.2
1.15.12.3
1.15.12.4
1.15.12.5
1.16
1.16.1
1.16.2
1.16.3
1.16.4
1.16.5
1.16.6
1.16.7
1.16.8
1.17
1.17.1
1.17.1.1
1.17.2
1.17.3
1.17.4
1.18
1.18.1
1.18.2
1.18.3
1.18.4
1.19
1.19.1
1.19.2
1.19.2.1
跳过tasks
跳过up-to-date的任务
Task规则
终止tasks
补充
Gradle属性和system属性
使用其他的脚本配置项目
使用其他的脚本配置任意对象
配置任意对象
缓存
文件操作
定位文件
文件集合
文件树
使用一个归档文件的内容作为文件树
指定一组输入文件
复制文件
使用同步任务
创建归档文件
使用Ant插件
使用Ant任务和Ant类型的构建
在构建中使用自定义Ant任务
导入一个Ant构建
Ant的属性与引用
API
Logging
选择日志等级
编写自己的日志信息
外部工具和库的log
改变Gradle记录的内容
Gradle的守护进程
什么是Gradle的守护进程
管理和配置
如何启用的摇篮守护进程
5
1.19.2.2
1.19.2.3
1.19.2.4
1.19.2.5
1.19.2.6
1.19.2.7
1.19.3
1.19.4
1.19.5
1.19.5.1
1.20
1.20.1
1.20.2
1.20.3
1.20.3.1
1.20.3.2
1.20.3.2.1
1.20.4
1.20.5
1.20.5.1
1.20.5.2
1.20.5.3
1.20.5.4
1.20.6
1.20.7
1.21
1.21.1
1.21.2
1.21.3
1.21.4
1.21.5
1.21.6
1.21.7
如何禁用Gradle的守护进程
怎样抑制“pleaseconsiderusingtheGradleDaemon”消息
为什么会在机器上出现不只一个守护进程
守护进程占用多大内存并且能不能给它更大的内存?
如何停止守护进程
守护进程何时会出错
什么时候使用Gradle守护进程
工具和集成开发环境
摇篮守护进程如何使构建速度更快
未来可能的改进
GradlePlugins
插件的作用
插件的类型
应用插件
脚本插件
二进制插件
二进制插件的位置
使用构建脚本块应用插件
使用插件的插件DSL
插件DSL的限制
约束语法
只能在构建脚本中使用
不能与subjects{},allprojects{}等结合使用
查找社区插件
更多关于插件
Gradle插件规范
语言插件
孵化中的语言插件
集成插件
孵化中的集成插件
软件开发插件
孵化中的软件开发插件
基础插件
6
1.21.8
1.22
1.22.1
1.22.2
1.22.3
1.22.4
1.22.5
1.22.6
1.22.7
1.22.7.1
1.22.7.2
1.22.7.3
1.22.8
1.22.9
1.22.10
1.22.11
1.22.12
1.22.13
1.22.13.1
1.22.13.2
1.22.13.3
1.22.13.4
1.22.13.5
1.22.13.6
1.22.13.7
1.22.13.7.1
1.22.13.8
1.22.14
1.22.14.1
1.22.15
1.23
1.23.1
1.23.2
1.23.3
第三方插件
Java插件
使用
资源集
任务
项目布局
依赖管理
公共配置
使用资源集工作
资源集属性
定义新的资源集
资源集例子
Javadoc
清除
资源
编译Java
增量Java编译
测试
测试执行
测试调试
测试过滤
通过系统属性执行单独测试
测试检测
测试分组
测试报告
TestNG的参数化方法和报告
公共值
Jar
Manifest
上传
War插件
使用
任务
项目布局
7
GradleUserGuide中文版
阅读地址
GradleUserGuide中文版目前正在更新到官方4.6版本(branch/#22),将会在翻译完主要
内容后合并的该分支.因工作量大,希望大家耐心等待,也欢迎大家一起加入和完善.
如果发现不通顺或者有歧义的地方,可以在评论里指出来,我们会及时改正的.
Github托管地址
原文地址
查看4.6翻译进度
如何参与&联系我们
邮箱:dongchuanyz@163.com
微信:dongchuan55
QQ群:324020116
任何问题都欢迎直接联系我们
Gitbook提供了非常棒的在线编辑功能,所以想贡献的同学可以直接联系我申请权限!
许可证
本作品采用ApacheLicense2.0国际许可协议进行许可.传播此文档时请注意遵循以上许可
协议.关于本许可证的更多详情可参考http://www.apache.org/licenses/LICENSE-2.0
贡献者列表
成员 联系方式 Github
dongchuan55 dongchuan55@gmail.com Github
UFreedom sunfreedomsun@gmail.com Github
张扬 zhangyang911120@gmail.com Github
d0048 d0048@foxmail.com Github
关于本书
9
关于本书
10
介绍
很高兴能向大家介绍Gradle,这是一个构建系统,我们认为它是java(JVM)世界中构建技术
的一个飞跃.
Gradle提供了:
一个像Ant一样的非常灵活的通用构建工具
一种可切换的,像maven一样的基于合约构建的框架
支持强大的多工程构建
支持强大的依赖管理(基于ApacheIvy)
支持已有的maven和ivy仓库
支持传递性依赖管理,而不需要远程仓库或者pom.xml或者ivy配置文件
优先支持Ant式的任务和构建
基于groovy的构建脚本
有丰富的领域模型来描述你的构建
介绍
11
关于这本指南
这本用户指南还并不完善,就像Gradle一样还在开发当中.
在这本指南中,Gradle的一些功能并没有被完整的展示出来.一些内容的解释也并不是十分的
清楚,或者假设关于Gradle你知道得更多.我们需要你的帮助来完善这本指南.在Gradle网站
上你可以找到更多关于完善这本指南的信息.
通过这本指南,你将会看到一些代表Gradle任务之间依赖关系的图表.类似于UML依赖关系
的表示方法,从一个任务A指向另一个任务B的箭头代表A依赖于B.
关于这本指南
12
概述
特点
为什么用Groovy?
概述
13
特点
这里简述下Gradle的特点.
1.声明式构建和合约构建
Gradle的核心是基于Groovy的领域特定语言(DSL),具有十分优秀的扩展性.Gradle通过提
供可以随意集成的声明式语言元素将声明性构建推到了一个新的高度.这些元素也为Java,
Groovy,OSGi,Web和Scala等项目提供基于合约构建的支持.而且,这种声明式语言是可扩展
的.你可以添加自己的语言元素或加强现有的语言元素,从而提供简洁,易于维护和易于理解的
构建.
2.基于依赖的编程语言
声明式语言位于通用任务图(generalpurposetaskgraph)的顶端,它可以被充分利用在你
的构建中.它具有强大的灵活性,可以满足使用者对Gradle的一些特别的需求.
3.让构建结构化
Gradle的易适应性和丰富性可让你在构建里直接套用通用的设计原则.例如,你可以非常容易
地使用一些可重用的组件来构成你的构建.但是不必要的间接内联内容是不合适的.不要强行
拆分已经结合在一起的部分(例如,在你的项目层次结构中).避免使构建难以维护.总之,你可
以创建一个结构良好,易于维护和易于理解的构建.
4.API深化
你会非常乐意在整个构建执行的生命周期中使用Gradle,因为Gradle允许你管理和定制它的
配置和执行行为.
5.Gradle扩展
Gradle扩展得非常好.不管是简单的独立项目还是大型的多项目构建,它都能显著的提高效率.
这是真正的结构构建.顶尖水平的构建功能,还可以解决许多大公司碰到的构建性能低下的问
题.
6.多项目构建
Gradle对多项目的支持是非常出色的.项目依赖是很重要的部分.它允许你模拟在多项目构建
中项目的关系,这正是你所要关注的地方.Gradle可以适应你的项目的结构,而不是反过来.
Gradle提供了局部构建的功能.如果你构建一个单独的子项目,Gradle会构建这个子项目依赖
的所有子项目.你也可以选择依赖于另一个特别的子项目重新构建这些子项目.这样在一些大
型项目里就可以节省非常多的时间.
7.多种方式来管理你的依赖
特点
14
不同的团队有不同的管理外部依赖的方法.Gradle对于任何管理策略都提供了合适的支持.从
远程Maven和Ivy库的依赖管理到本地文件系统的jars或者dirs.
8.Gradle是第一个构建整合工具
Ant的tasks是Gradle中很重要的部分,更有趣是Ant的projects也是十分重要的部分.
Gradle可以直接引入Ant项目,并在运行时直接将Anttargets转换成Gradletasks.你可以从
Gradle中依赖它们,并增强它们的功能,甚至可以在build.xml文件中声明Gradletasks的依
赖.并且properties,paths等也可以通过同样的方法集成进来.
Gradle完全支持你已有的Maven或者lvy仓库来构造发布或者提取依赖.Gradle也提供了一
个转化器,用来将maven的pom.xml文件转换成Gradle脚本.在运行时引入Maven项目也
会在稍后推出.
9.易于迁移
Gradle可以兼容任何结构.因此你可以直接在你的产品构建的分支上开发你的Gradle构建,
并且二者可以并行.我们通常建议编写一些测试代码来确保它们的功能是相同的.通过这种方
式,在迁移的时候就不会显得那么混乱和不可靠,这是通过婴儿学步的方式来获得最佳的实践.
10.Groovy
Gradle的构建脚本是通过Groovy编写的而不是XML.但是并不像其他方式,这并不是为了简
单的展示用动态语言编写的原始脚本有多么强大.不然的话,只会导致维护构建变得非常困难.
Gradle的整个设计是朝着一种语言的方向开发的,并不是一种死板的框架.Groovy就像胶水一
样,把你想实现的构想和抽象的Gradle粘在一起.Gradle提供了一些标准的构想,但是他们并
不享有任何形式的特权.相比于其他声明式构建系统,对我们来说这是一个比较突出的特点.
10.Gradle包装器
Gradle包装器允许你在没有安装Gradle的机器上运行Gradle构建.在一些持续集成的服务
器上,这个功能将非常有用.它同样也能降低使用一个开源项目的门槛,也就是说构建它将会非
常简单.这个包装器对于公司来说也是很有吸引力的.它并不需要为客户机提供相应的管理防
范.这种方式同样也能强制某一个版本Gradle的使用从而最小化某些支持问题.
11.免费和开源
Gradle是一个开源项目,遵循ASL许可.
特点
15
为什么用Groovy?
我们认为在脚本构建时,一个内部的DSL(基于一个动态语言)相对于XML的优势是巨大的.
有这么多的动态语言,为什么选择Groovy?答案在于Gradle的运行环境.虽然Gradle以一个
通用构建工具为核心,但是它的重点是Java项目.在这样的项目中,显然团队每个成员都对
Java非常熟悉.我们认为构建应尽可能对所有团队成员都是透明的,所以选择了Groovy.
你可能会说,为什么不直接使用Java作为构建脚本的语言.我们认为这是一个很有用的问题.
对于你的团队,它要有最高的透明度和最低的学习曲线,也就是说容易掌握.但由于Java的限
制,这样的构建语言不会那么完美和强大.而像Python,Groovy或Ruby语言用来作为构建语
言会更好.我们选择了Groovy是因为它给Java开发人员提供了迄今为止最大的透明度.其基
本的符号和类型与Java是一样的,其封装结构和许多其他的地方也是如此.Groovy在这基础
上提供了更多的功能,而且与java有共同的基础.
对于那些同时是或者即将是Python或Ruby开发者的Java开发人员来说,上述的讨论并不适
用.Gradle的设计非常适合在JRuby和Jython中创建另一个构建脚本引擎.它对于我们来说
只是目前开发中没有最高优先级.我们十分支持任何人来做贡献,创建额外的构建脚本引擎.
为什么用Groovy?
16
教程
接下来的教程讲先介绍Gradle的基础知识
Chapter4,安装Gradle
描述如何安装Gradle.
Chapter5,脚本构建基础
介绍脚本构建的基础元素:projects和tasks.
Chapter6,Java快速入门
展示如何使用Gradle来构建Java项目.
Chapter7,依赖管理基础
展示如何使用Gradle的依赖管理功能.
Chapter8,Groovy快速入门
展示如何使用Gradle来构建Groovy项目.
Chapter9,网页应用快速入门
展示如何使用Gradle来构建网页应用项目.
Chapter10,使用Gradle命令行
教程
17
安装Gradle
先决条件
下载
解压缩
环境变量
运行并测试您的安装
JVM选项
安装Gradle
18
准备阶段
Gradle需要运行在一个Java环境里
安装一个JavaJDK或者JRE.而且Java版本必须至少是6以上.
Gradle自带Groovy库,所以没必要安装Groovy.任何已经安装的Groovy会被Gradle忽
略.
Gradle使用任何已经存在在你的路径中的JDK(可以通过java-version检查,如果有就说明
系统已经安装了Java环境).或者,你也可以设置JAVA_HOME环境参数来指定希望使用的
JDK的安装目录.
准备阶段
19
#
下载与安装
你可以从Gradle网站下载任意一个已经发布的版本
解压缩
Gradle发布的版本为**ZIP格式.所有文件包含:
Gradle的二进制文件.
用户指南(HTML和PDF).
DSL参考指南.
API文档(Javadoc和Groovydoc).
扩展的例子,包括用户指南中引用的实例,以及一些更复杂的实例来帮助用户构建自己的
build.
二进制源码.此代码仅供参考.
设置环境变量
然后我们需要设置环境变量
1. 添加一个GRADLE_HOME环境变量来指明Gradle的安装路径
2. 添加GRADLE_HOME/bin到您的PATH环境变量中.通常,这样已经足够运行Gradle了.
扩充教程
MaxOX:
假设您下载好的Gradle文件在/Users/UFreedom/gradle目录
1.vim~/.bash_profile
2.添加下面内容:
exportGRADLE_HOME=/Users/UFreedom/gradle
exportexportPATH=$PATH:$GRADLE_HOME/bin
3.source~/.brash_profile
下载与安装
20
实际配置中,您需要将上面的目录换成您的Gradle文件在系统中的目录.
运行并测试您的安装
然后我们需要测试Gradle是否已经正确安装.您可以通过在控制台输入gradle命令来运行
Gradle.通过gradle-v命令来检测Gradle是否已经正确安装.如果正确安装,会输出Gradle版
本信息以及本地的配置环境(groovy和JVM版本等).显示的版本信息应该与您所下载的
gradle版本信息相匹配.
下载与安装
21
JVM选项
JVM选项可以通过设置环境变量来更改.您可以使用GRADLE_OPTS或者JAVA_OPTS.
JAVA_OPTS是一个用于JAVA应用的环境变量.一个典型的用例是在JAVA_OPTS里设
置HTTP代理服务器(proxy),
GRADLE_OPTS是内存选项.这些变量可以在gradle的一开始就设置或者通过gradlew
脚本来设置.
JVM选项
22
排除故障
当使用Gradle时,你肯定会碰到许多问题.
解决遇到的问题
如果你碰到了问题,首先要确定你使用的是最新版本的Gradle.我们会经常发布新版本,解决
一些bug并加入新的功能.所以你遇到的问题可能就在新版本里解决了.
如果你正在使用GradleDaemon,先暂时关闭daemon(你可以使用switch--no-daemon命
令).在第19章我们可以了解到更多关于daemon的信息.
获得帮助
可以浏览http://forums.gradle.org获得相关的帮助.在Gradle论坛里,你可以提交问题,当然
也可以回答其他Gradle开发人员和使用者的问题.
如果你碰到不能解决的问题,请在论坛里报告或者提出这个问题,通常这是解决问题最快的方
法.你也可以提出建议或者一些新的想法.通过论坛,你当然也可以获得Gradle最新的开发信
息.
排除故障
23
构建脚本的基础知识
这一章主要讲解以下内容
Projects和tasks
Helloworld
快捷的任务定义
构建脚本代码
任务依赖
动态任务
使用已经存在的任务
快捷注释
附加的task属性
使用Ant任务
使用方法
默认的任务
通过DAG配置
构建脚本基础
24
Projects和tasks
Gradle里的任何东西都是基于这两个基础概念:
projects(项目)
tasks(任务)
每一个构建都是由一个或多个projects构成的.一个project到底代表什么取决于你想用
Gradle做什么.举个例子,一个project可以代表一个JAR或者一个网页应用.它也可能代表一
个发布的ZIP压缩包,这个ZIP可能是由许多其他项目的JARs构成的.但是一个project不一
定非要代表被构建的某个东西.它可以代表一件**要做的事,比如部署你的应用.
不要担心现在看不懂这些说明.Gradle的合约构建可以让你来具体定义一个project到底该做
什么.
每一个project是由一个或多个tasks构成的.一个task代表一些更加细化的构建.可能是编
译一些classes,创建一个JAR,生成javadoc,或者生成某个目录的压缩文件.
目前,我们先来看看定义构建里的一些简单的task.以后的章节会讲解多项目构建以及如何通
过projects和tasks工作.
Projects和tasks
25
Helloworld
你可以通过gradle命令运行一个Gradle构建.
gradle命令会在当前目录中查找一个叫build.gradle的文件.我们称这个build.gradle文件为
一个构建脚本(buildscript),但是严格来说它是一个构建配置脚本(buildconfigurationscript).
这个脚本定义了一个project和它的tasks.
让我们来先看一个例子,创建一个名为build.gradle的构建脚本.
例子6.1第一个构建脚本
build.gradle
taskhello{
doLast{
println'Helloworld!'
}
}
在命令行里,进入脚本所在的文件夹然后输入gradle-qhello来执行构建脚本:
gradle-qhello的输出
>gradle-qhello
Helloworld!
这里发生了什么?这个构建脚本定义了一个独立的task,叫做hello,并且加入了一个action.
当你运行gradlehello,Gradle执行叫做hello的task,也就是执行了你所提供的action.这个
action是一个包含了一些Groovy代码的闭包(closure这个概念不清楚的同学好好谷歌下).
如果你认为这些看上去和Ant的targets很相像,好吧,你是对的.Gradletasks和Ant的
targets是对等的.但是你将会会看到,Gradletasks更加强大.我们使用一个不同于Ant的术语
task,看上去比target更加能直白.不幸的是这带来了一个术语冲突,因为Ant称它的命令,比
如javac或者copy,叫tasks.所以当我们谈论tasks,是指Gradle的tasks.如果我们讨论Ant
的tasks(Ant命令),我们会直接称呼anttask.
补充一点命令里的-q是干什么的?
这个指南里绝大多说的例子会在命令里加入-q.代表quiet模式.它不会生成Gradle的日志信
息(logmessages),所以用户只能看到tasks的输出.它使得的输出更加清晰.你并不一定需要
加入这个选项.参考第18章,日志的Gradle影响输出的详细信息.
Helloworld
26
Helloworld
27
快捷的任务定义
有一种比我们之前定义的hello任务更简明的方法
例子6.3.快捷的任务定义
build.gradle
taskhello<<{
println'Helloworld!'
}
它定义了一个叫做hello的任务,这个任务是一个可以执行的闭包.我们将使用这种方式来定义
这本指南里所有的任务.
翻译者补充
与前面的例子比较,doLast被替换成了<<.它们有一样的功能,但看上去更加简洁了,会在后
续章节(6.7)详细讲解它们的功能.
快捷的任务定义
28
构建脚本代码
Gradle的构建脚本展示了Groovy的所有能力.作为开胃菜,来看看这个:
例子6.4.在Gradle任务里使用Groovy
build.gradle
taskupper<<{
StringsomeString='mY_nAmE'
println"Original:"+someString
println"Uppercase:"+someString.toUpperCase()
}
gradle-qupper命令的输出
>gradle-qupper
Original:mY_nAmE
Uppercase:MY_NAME
或者
例子6.5.在Gradle任务里使用Groovy
build.gradle
taskcount<<{
4.times{print"$it"}
}
gradle-qcount命令的输出
>gradle-qcount
0123
构建脚本代码
29
任务依赖
就像你所猜想的那样,你可以声明任务之间的依赖关系.
例子6.6.申明任务之间的依赖关系
build.gradle
taskhello<<{
println'Helloworld!'
}
taskintro(dependsOn:hello)<<{
println"I'mGradle"
}
gradle-qintro命令的输出
>gradle-qintro
Helloworld!
I'mGradle
intro依赖于hello,所以执行intro的时候hello命令会被优先执行来作为启动intro任务的条
件.
在加入一个依赖之前,这个依赖的任务不需要提前定义,来看下面的例子.
例子6.7.LazydependsOn-其他的任务还没有存在
build.gradle
tasktaskX(dependsOn:'taskY')<<{
println'taskX'
}
tasktaskY<<{
println'taskY'
}
gradle-qtaskX命令的输出
>gradle-qtaskX
taskY
taskX
任务依赖
30
taskX到taskY的依赖在taskY被定义之前就已经声明了.这一点对于我们之后讲到的多任务
构建是非常重要的.任务依赖将会在14.4具体讨论.
请注意你不能使用快捷注释(参考6.8,“快捷注释”)当所关联的任务还没有被定义.
任务依赖
31
动态任务
Groovy不仅仅被用来定义一个任务可以做什么.举个例子,你可以使用它来动态的创建任务.
例子6.8.动态的创建一个任务
build.gradle
4.times{counter->
task"task$counter"<<{
println"I'mtasknumber$counter"
}
}
这里动态的创建了task0,task1,task2,task3
gradle-qtask1命令的输出
>gradle-qtask1
I'mtasknumber1
动态任务
32
使用已经存在的任务
当任务创建之后,它可以通过API来访问.这个和Ant不一样.举个例子,你可以创建额外的依
赖.
例子6.9.通过API访问一个任务-加入一个依赖
build.gradle
4.times{counter->
task"task$counter"<<{
println"I'mtasknumber$counter"
}
}
task0.dependsOntask2,task3
gradle-qtask0命令的输出
>gradle-qtask0
I'mtasknumber2
I'mtasknumber3
I'mtasknumber0
或者你可以给一个已经存在的任务加入行为.
例子6.10.通过API访问一个任务-加入行为
build.gradle
taskhello<<{
println'HelloEarth'
}
hello.doFirst{
println'HelloVenus'
}
hello.doLast{
println'HelloMars'
}
hello<<{
println'HelloJupiter'
}
gradle-qhello命令的输出
使用已经存在的任务
33
>gradle-qhello
HelloVenus
HelloEarth
HelloMars
HelloJupiter
doFirst和doLast可以被执行许多次.他们分别可以在任务动作列表的开始和结束加入动作.
当任务执行的时候,在动作列表里的动作将被按顺序执行.这里第四个行为中<<操作符是
doLast的简单别称.
使用已经存在的任务
34
短标记法
正如同你已经在之前的示例里看到,有一个短标记$可以访问一个存在的任务.也就是说每个
任务都可以作为构建脚本的属性:
例子6.11.当成构建脚本的属性来访问一个任务
build.gradle
taskhello<<{
println'Helloworld!'
}
hello.doLast{
println"Greetingsfromthe$hello.nametask."
}
gradle-qhello命令的输出
>gradle-qhello
Helloworld!
Greetingsfromthehellotask.
这里的name是任务的默认属性,代表当前任务的名称,这里是hello.
这使得代码易于读取,特别是当使用了由插件(如编译)提供的任务时尤其如此.
短标记法
35
自定义任务属性
你可以给任务加入自定义的属性.列如加入一个叫做myProperty属性,设置一个初始值给
ext.myProperty.然后,该属性就可以像一个预定义的任务属性那样被读取和设置了.
例子6.12.给任务加入自定义属性
build.gradle
taskmyTask{
ext.myProperty="myValue"
}
taskprintTaskProperties<<{
printlnmyTask.myProperty
}
gradle-qprintTaskProperties命令的输出
>gradle-qprintTaskProperties
myValue
给任务加自定义属性是没有限制的.你可以在13.4.2,“自定义属性”里获得更多的信息.
自定义任务属性
36
调用Ant任务
Ant任务是Gradle的一等公民.Gradle通过Groovy出色的集成了Ant任务.Groovy自带了
一个AntBuilder.相比于从一个build.xml文件中使用Ant任务,在Gradle里使用Ant任务更
为方便和强大.从下面的例子中,你可以学习如何执行Ant任务以及如何访问ant属性:
例子6.13.使用AntBuilder来执行ant.loadfile任务
build.gradle
taskloadfile<<{
deffiles=file('../antLoadfileResources').listFiles().sort()
files.each{Filefile->
if(file.isFile()){
ant.loadfile(srcFile:file,property:file.name)
println"***$file.name***"
println"${ant.properties[file.name]}"
}
}
}
gradle-qloadfile命令的输出
>gradle-qloadfile
***agile.manifesto.txt***
Individualsandinteractionsoverprocessesandtools
Workingsoftwareovercomprehensivedocumentation
Customercollaborationovercontractnegotiation
Respondingtochangeoverfollowingaplan
***gradle.manifesto.txt***
调用Ant任务
37
使用方法
Gradle能很好地衡量你编写脚本的逻辑能力.首先要做的是如何提取一个方法.
例子6.14.使用方法组织脚本逻辑
build.gradle
taskchecksum<<{
fileList('../antLoadfileResources').each{Filefile->
ant.checksum(file:file,property:"cs_$file.name")
println"$file.nameChecksum:${ant.properties["cs_$file.name"]}"
}
}
taskloadfile<<{
fileList('../antLoadfileResources').each{Filefile->
ant.loadfile(srcFile:file,property:file.name)
println"I'mfondof$file.name"
}
}
File[]fileList(Stringdir){
file(dir).listFiles({file->file.isFile()}asFileFilter).sort()
}
adle-qloadfile命令的输出
>gradle-qloadfile
I'mfondofagile.manifesto.txt
I'mfondofgradle.manifesto.txt
稍后你看到,这种方法可以在多项目构建的子项目之间共享.如果你的构建逻辑变得更加复杂,
Gradle为你提供了其他非常方便的方法.请参见第59章,组织构建逻辑。
使用方法
38
默认任务
Gradle允许在脚本中定义一个或多个默认任务.
例子6.15.定义默认任务
build.gradle
defaultTasks'clean','run'
taskclean<<{
println'DefaultCleaning!'
}
taskrun<<{
println'DefaultRunning!'
}
taskother<<{
println"I'mnotadefaulttask!"
}
gradle-q命令的输出
>gradle-q
DefaultCleaning!
DefaultRunning!
等价于gradle-qcleanrun.在一个多项目构建中,每一个子项目都可以有它特别的默认任务.
如果一个子项目没有特别的默认任务,父项目的默认任务将会被执行.
默认任务
39
通过DAG配置
正如我们之后的详细描述(参见第55章,构建的生命周期),Gradle有一个配置阶段和执行阶
段.在配置阶段后,Gradle将会知道应执行的所有任务.Gradle为你提供一个"钩子",以便利用
这些信息.举个例子,判断发布的任务是否在要被执行的任务当中.根据这一点,你可以给一些
变量指定不同的值.
在接下来的例子中,distribution任务和release任务将根据变量的版本产生不同的值.
例子6.16.根据选择的任务产生不同的输出
build.gradle
taskdistribution<<{
println"Webuildthezipwithversion=$version"
}
taskrelease(dependsOn:'distribution')<<{
println'Wereleasenow'
}
gradle.taskGraph.whenReady{taskGraph->
if(taskGraph.hasTask(release)){
version='1.0'
}else{
version='1.0-SNAPSHOT'
}
}
gradle-qdistribution命令的输出
>gradle-qdistribution
Webuildthezipwithversion=1.0-SNAPSHOT
Outputofgradle-qrelease
>gradle-qrelease
Webuildthezipwithversion=1.0
Wereleasenow
最重要的是whenReady在release任务执行之前就已经影响了release任务.甚至release任
务不是首要任务(i.e.,首要任务是指通过gradle命令的任务).
通过DAG配置
40
Java构建入门
Java插件
一个基础的Java项目
多项目的Java构建
Java构建入门
41
Java插件
如你所见,Gradle是一种多用途的构建工具.它可以在你的构建脚本里构建任何你想要实现的
东西.但前提是你必须先在构建脚本里加入代码,不然它什么都不会执行.
大多数Java项目是非常相似的:你需要编译你的Java源文件,运行一些单元测试,同时创建
一个包含你类文件的JAR.如果你可以不需要为每一个项目重复执行这些步骤,我想你会非常
乐意的.
幸运的是,你现在不再需要做这些重复劳动了.Gradle通过使用插件解决了这个问题.插件是
Gradle的扩展,它会通过某种方式配置你的项目,典型的有加入一些预配置任务.Gradle自带
了许多插件,你也可以很简单地编写自己的插件并和其他开发者分享它.Java插件就是一个这
样的插件.这个插件在你的项目里加入了许多任务,这些任务会编译和单元测试你的源文件,
并且把它们都集成一个JAR文件里.
Java插件是基于合约的.这意味着插件已经给项目的许多方面定义了默认的参数,比如Java
源文件的位置.如果你在项目里遵从这些合约,你通常不需要在你的构建脚本里加入太多东西.
如果你不想要或者是你不能遵循合约,Gradle也允许你自己定制你的项目.事实上,因为对
Java项目的支持是通过插件实现的,如果你不想要的话,你一点也不需要使用这个插件来构建
你的项目.
在后面的章节,我们有许多机会来让你深入了解Java插件,依赖管理和多项目构建.在本章
中,先来初步认识如何使用Java插件来构建一个Java项目.
Java插件
42
一个基础的Java项目
让我们先来看一个简单的例子.
我们可以加入下面的代码来使用Java插件:
例子7.1.使用Java插件
build.gradle
applyplugin:'java'
(注:此例子的代码可以再所有“-all”结尾的发行版的samples/java/quickstart目录下找到)
它将会把Java插件加入到你的项目中,这意味着许多预定制的任务会被自动加入到你的项目
里.
Gradle默认在src/main/java目录下寻找到你的正式(生产)源码,在src/test/java目录下
寻找到你的测试源码,并在src/main/resources目录下寻找到你准备打包进jar的资源文件。测
试代码会被加入到环境变量中设置的目录里运行。所有的输出文件都会被创建在构建目录里,
生成的JAR文件会被存放在build/libs目录下.
都加了什么可以执行的任务呢?
请在项目目录下使用gradletasks来列出该项目的所有任务。
一个基础的Java项目
43
建立项目
尽管Java插件在你的项目里加入了许多任务,只有几个会在项目构建中经常用到。
最常用的任务是build任务,用于完全构建你的项目.运行gradlebuild命令执行后,Gradle将
会编译和测试你的代码,并生成一个包含所有类与资源的JAR文件:
例子7.2.建立一个Java项目
gradlebuild命令的输出:
>gradlebuild
:compileJava
:processResources
:classes
:jar
:assemble
:compileTestJava
:processTestResources
:testClasses
:test
:check
:build
BUILDSUCCESSFUL
Totaltime:1secs
其余一些有用的任务是:
clean
删除build目录和所有为build生成的文件.
assemble
编译并打包你的代码,但是并不运行单元测试.其他插件会在这个任务里加入更多的步骤.举个
例子,如果你使用War插件,这个任务还将根据你的项目生成一个WAR文件.
check
编译并测试你的代码.其他的插件会加入更多的检查步骤.举个例子,如果你使用checkstyle插
件,这个任务将会运行Checkstyle来检查你的代码.
建立项目
44
建立项目
45
外部的依赖
通常,一个Java项目的依赖许多外部的JAR文件.为了在项目里引用这些JAR文件,你需要告
诉Gradle去哪里找它们.在Gradle中,JAR文件位于一个仓库中,这里的仓库类似于MAVEN
的仓库,可以被用来提取依赖,或者放入依赖。
举个例子,我们将使用开放的Maven仓库:
例子7.3.加入Maven仓库
build.gradle
repositories{
mavenCentral()
}
接下来让我们加入一些依赖.这里,我们假设我们的项目在编译阶段有一些依赖:
例子6.4.加入依赖
build.gradle
dependencies{
compilegroup:'commons-collections',name:'commons-collections',version:'3.2'
testCompilegroup:'junit',name:'junit',version:'4.+'
}
可以看到commons-collections被加入到了编译阶段,junit也被加入到了测试编译阶段.
你可以在第8章里看到更多这方面的内容.
外部的依赖
46
定制项目
Java插件给项目加入了一些属性(propertiy).这些属性已经被赋予了默认的值,已经足够来开始
构建项目了.如果你认为不合适,改变它们的值也是很简单的.让我们看下这个例子.这里我们将
指定Java项目的版本号,以及我们所使用的Java的版本.我们同样也加入了一些属性在jar的
manifest里.
例子7.5.定制MANIFEST.MF文件
build.gradle
sourceCompatibility=1.5
version='1.0'
jar{
manifest{
attributes'Implementation-Title':'GradleQuickstart','Implementation-Versio
n':version
}
}
Java插件加入的任务是常规性的任务,准确地说,就如同它们在构建文件里声明地一样.这意味
着你可以使用任务之前的章节提到的方法来定制这些任务.举个例子,你可以设置一个任务的属
性,在任务里加入行为,改变任务的依赖,或者完全重写一个任务,我们将配置一个测试任务,当测
试执行的时候它会加入一个系统属性:
例子7.6.测试阶段加入一个系统属性
build.gradle
test{
systemProperties'property':'value'
}
哪些属性是可用的?
你可以使用gradleproperties命令来列出项目的所有属性.这样你就可以看到Java插件加入
的属性以及它们的默认值.
定制项目
47
发布JAR文件
通常JAR文件需要在某个地方发布.为了完成这一步,你需要告诉Gradle哪里发布JAR文件.
在Gradle里,生成的文件比如JAR文件将被发布到仓库里.在我们的例子里,我们将发布到一
个本地的目录.你也可以发布到一个或多个远程的地点.
Example7.7.发布JAR文件
build.gradle
uploadArchives{
repositories{
flatDir{
dirs'repos'
}
}
}
运行gradleuploadArchives命令来发布JAR文件.
发布JAR文件
48
创建Eclipse项目
为了把你的项目导入到Eclipse,你需要加入另外一个插件:
Example7.8.Eclipse插件
build.gradle
applyplugin:'eclipse'
现在运行gradleeclipse命令来生成Eclipse的项目文件.Eclipse任务将在第38章,Eclipse
插件里详细讨论.
创建Eclipse项目
49
总结
下面是一个完整的构建文件的样本:
Example7.9.Java例子-完整的构建文件
build.gradle
applyplugin:'java'
applyplugin:'eclipse'
sourceCompatibility=1.5
version='1.0'
jar{
manifest{
attributes'Implementation-Title':'GradleQuickstart','Implementation-Versio
n':version
}
}
repositories{
mavenCentral()
}
dependencies{
compilegroup:'commons-collections',name:'commons-collections',version:'3.2'
testCompilegroup:'junit',name:'junit',version:'4.+'
}
test{
systemProperties'property':'value'
}
uploadArchives{
repositories{
flatDir{
dirs'repos'
}
}
}
总结
50
多项目的Java构建
现在让我们看一个典型的多项目构建.下面是项目的布局:
Example7.10.多项目构建-分层布局
构建布局
multiproject/
api/
services/webservice/
shared/
注意:这个例子的代码可以在samples/java/multiproject里找到.
现在我们能有三个项目.项目的应用程序接口(API)产生一个JAR文件,这个文件将提供给用
户,给用户提供基于XML的网络服务.项目的网络服务是一个网络应用,它返回XML.shared
目录包含被api和webservice共享的代码.
多项目的Java构建
51
定义一个多项目构建
为了定义一个多项目构建,你需要创建一个设置文件(settingsfile).设置文件放在源代码的根
目录,它指定要包含哪个项目.它的名字必须叫做settings.gradle.在这个例子中,我们使用一
个简单的分层布局.下面是对应的设置文件:
Example7.11.多项目构建-settings.gradlefile
settings.gradle
include"shared","api","services:webservice","services:shared"
在第56章.多项目构建,你可以找到更多关于设置文件的信息.
定义一个多项目构建
52
通用配置
对于绝大多数多项目构建,有一些配置对所有项目都是常见的或者说是通用的.在我们的例子
里,我们将在根项目里定义一个这样的通用配置,使用一种叫做配置注入的技术(configuration
injection).这里,根项目就像一个容器,subprojects方法遍历这个容器的所有元素并且注入指
定的配置.通过这种方法,我们可以很容易的定义所有档案和通用依赖的内容清单:
Example7.12.多项目构建-通用配置
build.gradle
subprojects{
applyplugin:'java'
applyplugin:'eclipse-wtp'
repositories{
mavenCentral()
}
dependencies{
testCompile'junit:junit:4.11'
}
version='1.0'
jar{
manifest.attributesprovider:'gradle'
}
}
注意我们例子中,Java插件被应用到了每一个子项目中plugintoeach.这意味着我们前几章看
到的任务和属性都可以在子项目里被调用.所以,你可以通过在根目录里运行gradlebuild命
令编译,测试,和JAR所有的项目.
通用配置
53
项目之间的依赖
你可以在同一个构建里加入项目之间的依赖,举个例子,一个项目的JAR文件被用来编译另外
一个项目.在api构建文件里我们将加入一个由shared项目产生的JAR文件的依赖.由于这
个依赖,Gradle将确保shared项目总是在api之前被构建.
Example7.13.多项目构建-项目之间的依赖
api/build.gradle
dependencies{
compileproject(':shared')
}
项目之间的依赖
54
创建一个发行版本
(该章需加入更多内容。。。原稿写的太简单了)我们同时也加入了一个发行版本,将会送到客
户端:
Example7.14.多项目构建-发行文件
api/build.gradle
taskdist(type:Zip){
dependsOnspiJar
from'src/dist'
into('libs'){
fromspiJar.archivePath
fromconfigurations.runtime
}
}
artifacts{
archivesdist
}
创建一个发行版本
55
依赖管理的基础知识
什么是依赖管理
声明你的依赖
依赖配置
外部的依赖
仓库
发布artifacts
依赖管理的基础知识
56
什么是依赖管理?
粗略的讲,依赖管理由两部分组成.首先,Gradle需要了解你的项目需要构建或运行的东西,以
便找到它们.我们称这些传入的文件为项目的dependencies(依赖项).其次,Gradle需要构建
并上传你的项目产生的东西.我们称这些传出的项目文件为publications(发布项).让我们来看
看这两条的详细信息:
大多数项目都不是完全独立的.它们需要其它项目进行编译或测试等等.举个例子,为了在项目
中使用Hibernate,在编译的时候需要在classpath中添加一些Hibernate的jar路径.要运行
测试的时候,需要在testclasspath中包含一些额外的jar,比如特定的JDBC驱动或者
Ehcachejars.
这些传入的文件构成上述项目的依赖.Gradle允许你告诉它项目的依赖关系,以便找到这些依
赖关系,并在你的构建中维护它们.依赖关系可能需要从远程的Maven或者Ivy仓库中下载,
也可能是在本地文件系统中,或者是通过多项目构建另一个构建.我们称这个过程为
dependencyresolution(依赖解析).
这一特性与Ant相比效率提高了许多.使用Ant,你只有指定jar的绝对路径或相对路径才能读
取jar.使用Gradle,你只需要申明依赖的名称,然后它会通过其它的设置来决定在哪里获取这
些依赖关系,比如从Maven库.你可以为Ant添加ApacheIvy库或得类似的方法,但是
Gradle做的更好.
通常,一个项目本身会具有依赖性.举个例子,运行Hibernate的核心需要其他几个类库在
classpath中.因此,Gradle在为你的项目运行测试的时候,它会找到这些依赖关系,并使其可
用.我们称之为transitivedependencies(依赖传递).
大部分项目的主要目的是要建立一些文件,在项目之外使用.比如,你的项目产生一个Java库,
你需要构建一个jar,可能是一个jar和一些文档,并将它们发布在某处.
这些传出的文件构成了项目的发布.Gradle当然会为你负责这个重要的工作.你声明项目的发
布,Gradle会构建并发布在某处.究竟什么是"发布"取决于你想做什么.可能你希望将文件复制
到本地目录,或者将它们上传到一个远程Maven或者Ivy库.或者你可以使用这些文件在多项
目构建中应用在其它的项目中.我们称这个过程为publication(发布)
什么是依赖管理
57
声明你的依赖
让我们看一下一些依赖的声明.下面是一个基础的构建脚本:
例子8.1.声明依赖
build.gradle
applyplugin:'java'
repositories{
mavenCentral()
}
dependencies{
compilegroup:'org.hibernate',name:'hibernate-core',version:'3.6.7.Final'
testCompilegroup:'junit',name:'junit',version:'4.+'
}
这里发生了什么?这个构建脚本声明Hibernatecore3.6.7.最终被用来编译项目的源代码.言
外之意是,在运行阶段同样也需要Hibernatecore和它的依赖.构建脚本同样声明了需要junit
>=4.0的版本来编译项目测试.它告诉Gradle到Maven中央仓库里找任何需要的依赖.接下
来的部分会具体说明.
声明你的依赖
58
依赖配置
在Gradle里,依赖可以组合成configurations(配置).一个配置简单地说就是一系列的依赖.我
们称它们为(dependencyconfiguration)依赖配置.你可以使用它们声明项目的外部依赖.正如
我们将在后面看到,它们也被用来声明项目的发布.
Java插件定义了许多标准的配置.下面列出了一些,你也可以在Table23.5,“Java插件-依赖
管理”里发现更多具体的信息.
compile
用来编译项目源代码的依赖.
runtime
在运行时被生成的类使用的依赖.默认的,也包含了编译时的依赖.
testCompile
编译测试代码的依赖.默认的,包含生成的类运行所需的依赖和编译源代码的依赖.
testRuntime
运行测试所需要的依赖.默认的,包含上面三个依赖.
各种各样的插件加入许多标准的配置.你也可以定义你自己的配置.参考Section52.3,“配置依
赖”可以找到更加具体的定义和定制一个自己的依赖配置.
依赖配置
59
外部的依赖
你可以声明许多种依赖.其中一种是externaldependency(外部依赖).这是一种在当前构建之
外的一种依赖,它被存放在远程或本地的仓库里,比如Maven的库,或者Ivy库,甚至是一个本
地的目录.
下面的例子讲展示如何加入外部依赖
例子8.2.定义一个外部依赖
build.gradle
dependencies{
compilegroup:'org.hibernate',name:'hibernate-core',version:'3.6.7.Final'
}
引用一个外部依赖需要使用group,name和version属性.根据你想要使用的库,group和
version可能会有所差别.
有一种简写形式,只使用一串字符串"group:name:version".
例子8.3.外部依赖的简写形式
build.gradle
dependencies{
compile'org.hibernate:hibernate-core:3.6.7.Final'
}
要了解跟多关于定义并使用依赖工作的信息,参见Section52.4,"Howtodeclareyou
dependencies".
外部的依赖
60
仓库
Gradle是怎样找到那些外部依赖的文件的呢?Gradle会在一个repository(仓库)里找这些文件.
仓库其实就是文件的集合,通过group, name和version整理分类.Gradle能解析好几种不
同的仓库形式,比如Maven和Ivy,同时可以理解各种进入仓库的方法,比如使用本地文件系统
或者HTTP.
默认地,Gradle不提前定义任何仓库.在使用外部依赖之前,你需要自己至少定义一个库.比如
使用下面例子中的Mavencentral仓库:
例子8.4.Mavencentral仓库
build.gradle
repositories{
mavenCentral()
}
或者使用一个远程的Maven仓库:
例子8.5.使用远程的Maven仓库
build.gradle
repositories{
maven{
url"http://repo.mycompany.com/maven2"
}
}
或者一个远程的Ivy仓库:
例子8.6.使用远程的Ivy仓库
build.gradle
repositories{
ivy{
url"http://repo.mycompany.com/repo"
}
}
你也可以使用本地的文件系统里的库.Maven和Ivy都支持下载的本地.
仓库
61
发布artifacts
依赖配置也可以用来发布文件 .我们称这些文件 publicationartifacts,或者就叫artifacts.
插件可以很好的定义一个项目的artifacts,所以你并不需要做一些特别的工作来让Gradle需要
发布什么.你可以通过在uploadArchives任务里加入仓库来完成.下面是一个发布远程Ivy库
的例子:
例子8.8.发布一个Ivy库
build.gradle
uploadArchives{
repositories{
ivy{
credentials{
username"username"
password"pw"
}
url"http://repo.mycompany.com"
}
}
}
现在,当你运gradleuploadArchives,Gradle将构建和上传你的Jar.Gradle也会生成和上传
ivy.xml.
你也可以发布到Maven库.语法是稍有不同 .请注意你需要加入Maven插件来发布一个
Maven库.在下面的例子里,Gradle将生成和上传pom.xml.
例子8.9.发布Maven库
build.gradle
applyplugin:'maven'
uploadArchives{
repositories{
mavenDeployer{
repository(url:"file://localhost/tmp/myRepo/")
}
}
}
在Chapter53,Publishingartifacts,发布artifacts里有更加具体的介绍.
[3]
[4]
发布artifacts
63
[3]我们认为这令人困惑,我们正在在GradleDSL中逐步的区别这两个概念.
[4]我们正在努力解决从Maven仓库发布,获取的语法一致性.
发布artifacts
64
下一步?
对于依赖关系的所有细节,参见Chapter52,依赖管理,artifact发布细节,参见Chapter53,
Publishingartifacts.
如果你对这里提及的DSL元素感兴趣,看看Project.configurations{}),
[Project.repositories{}]https://docs.gradle.org/current/dsl/org.gradle.api.Project.html#org.grad
le.api.Project:repositories(groovy.lang.Closure)和[Project.dependencies{}]
(https://docs.gradle.org/current/dsl/org.gradle.api.Project.html#org.gradle.api.Project:depend
encies(groovy.lang.Closure)).
否则,继续到其他tutorials.
下一步?
65
一个基本的Groovy项目
让我们看一个例子.为了使用Groovy插件,加入下面的代码:
例子8.1.Groovy插件
build.gradle
applyplugin:'groovy'
注意:这个例子的代码可以在samples/groovy/quickstart在Gradle分布的"-all"中找
到.
它也会同时把Java插件加入到你的项目里.Groovy插件扩展了编译任务,这个任务会在
src/main/groovy目录里寻找源代码文件,并且加入了编译测试任务来寻找src/test/groovy目
录里的测试源代码.编译任务使用联合编译(jointcompilation)来编译这些目录,这里的联合指
的是它们混合有java和groovy的源文件.
使用groovy编译任务,你必须声明Groovy的版本和Groovy库的位置.你可以在配置文件里
加入依赖,编译配置会继承这个依赖,然后groovy库将被包含在classpath里.
例子8.2.Groovy2.2.0
build.gradle
repositories{
mavenCentral()
}
dependencies{
compile'org.codehaus.groovy:groovy-all:2.3.3'
}
下面是完整的构建文件:
例子8.3.完整的构建文件
build.gradle
一个基本的Groovy项目
67
applyplugin:'eclipse'
applyplugin:'groovy'
repositories{
mavenCentral()
}
dependencies{
compile'org.codehaus.groovy:groovy-all:2.3.3'
testCompile'junit:junit:4.11'
}
运行gradlebuild命令将会开始编译,测试和创建JAR文件.
一个基本的Groovy项目
68
网页应用快速入门
本章是一项正在进行中的工作.
Gradle提供了两个插件用来支持网页应用:War插件和Jetty插件.War插件是在Java插件的
基础上扩充的用来构建WAR文件.Jetty插件是在War插件的基础上扩充的,允许用户将网页
应用发布到一个介入的Jetty容器里.
网页应用快速入门
70
构建一个WAR文件
为了构建一个WAR文件,需要在项目中加入War插件:
例子9.1.War插件
build.gradle
applyplugin:'war'
注意:项目代码可以在 samples/webApplication/quickstart在Gradle的发行包 "-all"中
找到.
这个插件也会在你的项目里加入Java插件.运行gradlebuild将会编译,测试和创建项目的
WAR文件.Gradle将会把源文件包含在WAR文件的src/main/webapp目录里.编译后的
classe,和它们运行所需要的依赖也会被包含在WAR文件里.
构建一个WAR文件
71
Runningyourwebapplication
要启动Web工程,在项目中加入Jettyplugin即可:
例9.2.采用Jettyplugin启动web工程
build.gradle
applyplugin:'jetty'
由于Jettyplugin继承自Warplugin.使用gradlejettyRun命令将会把你的工程启动部署到
jetty容器中.调用gradlejettyRunWar命令会打包并启动部署到jetty容器中.
Groovywebapplications你可以结合多个插件在一个项目中,所以你可以一起使用 War
和Groovy插件构建一个Groovy基础web应用.合适的Groovy类库会添加到你的WAR
文件中.
TODO:whichurl,configureport,usessourcefilesinplaceandcanedityourfilesand
reload.
运行Web应用
72
使用Gradle命令行
本章介绍了命令行的一些基本功能.正如在前面的章节里你所见到的调用gradle命令来运行一
个构建.
使用Gradle命令行
74
多任务调用
你可以以列表的形式在命令行中一次调用多个任务.例如gradlecompiletest命令会依次调
用compile和test任务,它们所依赖的任务也会被调用.这些任务只会被调用一次,无论它们是
否被包含在脚本中:即无论是以命令行的形式定义的任务还是依赖于其它任务都会被调用执行.
来看下面的例子.
下面定义了四个任务dist和test都依赖于compile,只用当compile被调用之后才会调用
gradledisttest任务
示例图11.1.任务依赖
例子11.1.多任务调用
build.gradle
taskcompile<<{
println'compilingsource'
}
taskcompileTest(dependsOn:compile)<<{
println'compilingunittests'
}
tasktest(dependsOn:[compile,compileTest])<<{
println'runningunittests'
}
taskdist(dependsOn:[compile,test])<<{
println'buildingthedistribution'
}
gradledisttest命令的输出
多任务调用
75
>gradledisttest
:compile
compilingsource
:compileTest
compilingunittests
:test
runningunittests
:dist
buildingthedistribution
BUILDSUCCESSFUL
Totaltime:1secs
由于每个任务仅会被调用一次,所以调用gradletesttest与调用gradletest效果是相同的.
多任务调用
76
排除任务
你可以用命令行选项-x来排除某些任务,让我们用上面的例子来示范一下.
例子11.2.排除任务
gradledist-xtest命令的输出
>gradledist-xtest
:compile
compilingsource
:dist
buildingthedistribution
BUILDSUCCESSFUL
Totaltime:1secs
可以看到,test任务并没有被调用,即使它是dist任务的依赖.同时test任务的依赖任务
compileTest也没有被调用,而像compile被test和其它任务同时依赖的任务仍然会被调用.
排除任务
77
失败后继续执行构建
默认情况下,只要有任务调用失败,Gradle就会中断执行.这可能会使调用过程更快,但那些后
面隐藏的错误就没有办法发现了.所以你可以使用--continue选项在一次调用中尽可能多的
发现所有问题.
采用--continue选项,Gralde会调用每一个任务以及它们依赖的任务.而不是一旦出现错误
就会中断执行.所有错误信息都会在最后被列出来.
一旦某个任务执行失败,那么所有依赖于该任务的子任务都不会被调用.例如由于test任务依赖
于complie任务,所以如果compile调用出错,test便不会被直接或间接调用.
失败后继续执行构建
78
简化任务名
当你试图调用某个任务的时候,你并不需要输入任务的全名.只需提供足够的可以唯一区分出
该任务的字符即可.例如,上面的例子你也可以这么写.用gradledi来直接调用dist任务:
例11.3.简化任务名
gradledi命令的输出
>gradledi
:compile
compilingsource
:compileTest
compilingunittests
:test
runningunittests
:dist
buildingthedistribution
BUILDSUCCESSFUL
Totaltime:1secs
你也可以用在驼峰命名方式(通俗的说就是每个单词的第一个字母大写,除了第一个单词)的任
务中每个单词的首字母进行调用.例如,可以执行gradlecompTest或者gradlecT来调用
compileTest任务
例11.4.简化驼峰方式的任务名
gradlecT命令的输出
>gradlecT
:compile
compilingsource
:compileTest
compilingunittests
BUILDSUCCESSFUL
Totaltime:1secs
简化后你仍然可以使用-x参数.
简化任务名
79
简化任务名
80
选择执行构建
调用gradle命令时,默认情况下总是会构建当前目录下的文件,可以使用-b参数选择其他目
录的构建文件,并且当你使用此参数时settings.gradle将不会生效,看下面的例子:
例11.5.选择文件构建
subdir/myproject.gradle
taskhello<<{
println"usingbuildfile'$buildFile.name'in'$buildFile.parentFile.name'."
}
gradle-q-bsubdir/myproject.gradlehello的输出
gradle-q-bsubdir/myproject.gradlehello
usingbuildfile'myproject.gradle'in'subdir'.
另外,你可以使用-p参数来指定构建的目录,例如在多项目构建中你可以用-p来替代-b参数
例10.6.选择构建目录
gradle-q-psubdirhello命令的输出
gradle-q-psubdirhello
usingbuildfile'build.gradle'in'subdir'.
-b参数用以指定脚本具体所在位置,格式为dirpwd/build.gradle.
-p参数用以指定脚本目录即可.
选择文件构建
81
项目列表
执行gradleprojects命令会为你列出子项目名称列表.
例11.7.收集项目信息
gradle-qprojects命令的输出结果
>gradle-qprojects
------------------------------------------------------------
Rootproject
------------------------------------------------------------
Rootproject'projectReports'
+---Project':api'-ThesharedAPIfortheapplication
\---Project':webapp'-TheWebapplicationimplementation
Toseealistofthetasksofaproject,rungradle<project-path>:tasks
Forexample,tryrunninggradle:api:tasks
这份报告展示了每个项目的描述信息.当然你可以在项目中用description属性来指定这些描
述信息.
例10.8.为项目添加描述信息
build.gradle
description='ThesharedAPIfortheapplication'
项目列表
83
任务列表
执行gradletasks命令会列出项目中所有任务.这会显示项目中所有的默认任务以及每个任
务的描述.
例11.9获取任务信息
gradle-qtasks命令的输出
>gradle-qtasks
------------------------------------------------------------
Alltasksrunnablefromrootproject
------------------------------------------------------------
Defaulttasks:dists
Buildtasks
-----------
clean-Deletesthebuilddirectory(build)
dists-Buildsthedistribution
libs-BuildstheJAR
BuildSetuptasks
-----------------
init-InitializesanewGradlebuild.[incubating]
wrapper-GeneratesGradlewrapperfiles.[incubating]
Helptasks
----------
dependencies-Displaysalldependenciesdeclaredinrootproject'projectReports'.
dependencyInsight-Displaystheinsightintoaspecificdependencyinrootproject'p
rojectReports'.
help-Displaysahelpmessage
projects-Displaysthesub-projectsofrootproject'projectReports'.
properties-Displaysthepropertiesofrootproject'projectReports'.
tasks-Displaysthetasksrunnablefromrootproject'projectReports'(someofthedi
splayedtasksmaybelongtosubprojects).
Toseealltasksandmoredetail,runwith--all.
默认情况下,这只会显示那些被分组的任务.你可以通过为任务设置group属性和description
来把这些信息展示到结果中.
例11.10.更改任务报告内容
build.gradle
任务列表
84
dists{
description='Buildsthedistribution'
group='build'
}
当然你也可以用--all参数来收集更多任务信息.这会列出项目中所有任务以及任务之间的依赖
关系.
例11.11获得更多的任务信息
gradle-qtasks--all命令的输出
任务列表
85
>gradle-qtasks--all
------------------------------------------------------------
Alltasksrunnablefromrootproject
------------------------------------------------------------
Defaulttasks:dists
Buildtasks
-----------
clean-Deletesthebuilddirectory(build)
api:clean-Deletesthebuilddirectory(build)
webapp:clean-Deletesthebuilddirectory(build)
dists-Buildsthedistribution[api:libs,webapp:libs]
docs-Buildsthedocumentation
api:libs-BuildstheJAR
api:compile-Compilesthesourcefiles
webapp:libs-BuildstheJAR[api:libs]
webapp:compile-Compilesthesourcefiles
BuildSetuptasks
-----------------
init-InitializesanewGradlebuild.[incubating]
wrapper-GeneratesGradlewrapperfiles.[incubating]
Helptasks
----------
dependencies-Displaysalldependenciesdeclaredinrootproject'projectReports'.
api:dependencies-Displaysalldependenciesdeclaredinproject':api'.
webapp:dependencies-Displaysalldependenciesdeclaredinproject':webapp'.
dependencyInsight-Displaystheinsightintoaspecificdependencyinrootproject'p
rojectReports'.
api:dependencyInsight-Displaystheinsightintoaspecificdependencyinproject':a
pi'.
webapp:dependencyInsight-Displaystheinsightintoaspecificdependencyinproject
':webapp'.
help-Displaysahelpmessage
api:help-Displaysahelpmessage
webapp:help-Displaysahelpmessage
projects-Displaysthesub-projectsofrootproject'projectReports'.
api:projects-Displaysthesub-projectsofproject':api'.
webapp:projects-Displaysthesub-projectsofproject':webapp'.
properties-Displaysthepropertiesofrootproject'projectReports'.
api:properties-Displaysthepropertiesofproject':api'.
webapp:properties-Displaysthepropertiesofproject':webapp'.
tasks-Displaysthetasksrunnablefromrootproject'projectReports'(someofthedi
splayedtasksmaybelongtosubprojects).
api:tasks-Displaysthetasksrunnablefromproject':api'.
webapp:tasks-Displaysthetasksrunnablefromproject':webapp'.
任务列表
86
任务列表
87
获取任务具体信息
执行gradlehelp--tasksomeTask可以显示指定任务的详细信息.或者多项目构建中相同任
务名称的所有任务的信息.如下例.
例11.12.获取任务帮助
gradle-qhelp--tasklibs的输出结果
>gradle-qhelp--tasklibs
Detailedtaskinformationforlibs
Paths
:api:libs
:webapp:libs
Type
Task(org.gradle.api.Task)
Description
BuildstheJAR
这些结果包含了任务的路径、类型以及描述信息等.
获取任务具体信息
88
获取依赖列表
执行gradledependencies命令会列出项目的依赖列表,所有依赖会根据任务区分,以树型结构
展示出来.如下例:
例11.13.获取依赖信息
gradle-qdependenciesapi:dependencieswebapp:dependencies的输出结果
>gradle-qdependenciesapi:dependencieswebapp:dependencies
------------------------------------------------------------
Rootproject
------------------------------------------------------------
Noconfigurations
------------------------------------------------------------
Project:api-ThesharedAPIfortheapplication
------------------------------------------------------------
compile
\---org.codehaus.groovy:groovy-all:2.3.3
testCompile
\---junit:junit:4.11
\---org.hamcrest:hamcrest-core:1.3
------------------------------------------------------------
Project:webapp-TheWebapplicationimplementation
------------------------------------------------------------
compile
+---project:api
|\---org.codehaus.groovy:groovy-all:2.3.3
\---commons-io:commons-io:1.2
testCompile
Nodependencies
虽然输出结果很多,但这对于了解构建任务十分有用,当然你可以通过--configuration参数
来查看指定构建任务的依赖情况:
例11.14.过滤依赖信息
gradle-qapi:dependencies--configurationtestCompile的输出结果
获取依赖列表
89
>gradle-qapi:dependencies--configurationtestCompile
------------------------------------------------------------
Project:api-ThesharedAPIfortheapplication
------------------------------------------------------------
testCompile
\---junit:junit:4.11
\---org.hamcrest:hamcrest-core:1.3
获取依赖列表
90
查看特定依赖
执行gradledependencyInsight命令可以查看指定的依赖.如下面的例子.
例11.15.获取特定依赖
gradle-qwebapp:dependencyInsight--dependencygroovy--configurationcompile的输出结
果
>gradle-qwebapp:dependencyInsight--dependencygroovy--configurationcompile
org.codehaus.groovy:groovy-all:2.3.3
\---project:api
\---compile
这个task对于了解依赖关系、了解为何选择此版本作为依赖十分有用.了解更多请参阅
DependencyInsightReportTask.
dependencyInsight任务是'Help'任务组中的一个.这项任务需要进行配置才可以使用.Report
查找那些在指定的配置里特定的依赖.
如果用了Java相关的插件,那么dependencyInsight任务已经预先被配置到'compile'下了.你
只需要通过'--dependency'参数来制定所需查看的依赖即可.如果你不想用默认配置的参数
项你可以通过 '--configuration'参数来进行指定.了解更多请参阅
DependencyInsightReportTask.
查看特定依赖
91
获取项目属性列表
执行gradleproperties可以获取项目所有属性列表.如下例:
例11.16.属性信息
gradle-qapi:properties的输出结果
>gradle-qapi:properties
------------------------------------------------------------
Project:api-ThesharedAPIfortheapplication
------------------------------------------------------------
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
baseClassLoaderScope:org.gradle.api.internal.initialization.DefaultClassLoaderScope@1
2345
buildDir:/home/user/gradle/samples/userguide/tutorial/projectReports/api/build
buildFile:/home/user/gradle/samples/userguide/tutorial/projectReports/api/build.gradl
e
获取项目属性列表
92
构建日志
--profile参数可以收集一些构建期间的信息并保存到build/reports/profile目录下.并且会以构
建时间命名这些文件.
下面是一份日志.这份日志记录了总体花费时间以及各过程花费的时间.并以时间大小倒序排
列.并且记录了任务的执行情况.
如果采用了buildSrc,那么在buildSrc/build下同时也会生成一份日志记录记录.
构建日志
93
使用Gradle图形界面
为了辅助传统的命令行交互,Gradle还提供了一个图形界面.我们可以使用Gradle命令中--gui
选项来启动它.
例子12.1.启动图形界面
gradle--gui
注意:这个命令执行后会使得命令行一直处于封锁状态,直到我们关闭图形界面.不过我们可
以另外加上“&”让它在后台执行:
gradle--gui&
如果我们从自己的Gradle项目目录中启动这个图形界面,我们应该会看到任务树.
图12.1.任务树
我们建议您从当前的Gradle项目目录启动图形界面,因为这种方式可以将有关于界面的一些
设置存储到您目录里面.不过您也可以在启动它后切换工作目录,方式:通过界面
中“Setup”选项卡可以设置.
如您所见,这个界面在顶部有4个选项卡和底部一个输出窗口.
使用Gradle图形界面
94
任务树
任务树使用分层方式显示了所有的项目和它们的任务,双击一个任务,您就可以执行它.
另外我们还可以使用过滤器过滤掉不常用的任务.您可以点击Filter按钮来设置过滤条件.设
定哪些任务和项目可以显示.隐藏的任务会使用红色来标记.
注意:最新被创建的任务会默认被显示出来(相反是被隐藏).
如您所见,在任务树界面我们可以做以下几种事情:
执行任务时忽略依赖性,而且并不需要重新编译独立的项目.
添加自己喜欢的任务,将其收藏(具体请看“Favorities”选项卡).
隐藏自己不想看到的任务,这个操作会将他们添加到过滤器中.
编辑build.gradke文件,注意:这个需要你的jdk版本为1.6以上,而且你的操作系统需
要关联.gradle文件.
任务树
95
收藏夹
"Favorites"选项卡是个好地方.您可以收藏常用的命令.即使是复杂的命令集,只要它符合
Gradle规范,您都可以添加收藏,而且您还可以为它起个通俗易懂的别名.这个方法逼格是不
是很高.一个一眼看上去就让人明白的自定义的命令,我们可以称它为“侩子手”(fastbuild).
您还可以对收藏的任务进行排序,或者您可以导出它们到磁盘,然后将导出的命令分享给别
的小伙伴使用.如果您想编辑它们,如您所见,我们会看到有个"AlwaysShowLive
Output"选项,如果您勾选了,可以强制命令在执行时显示在输出窗口.
收藏夹
96
命令行
在“CommandLine”选项卡,您只需将命令填入到gradle输入框.就可以直接执行单个的
Gradle命令.或者说在您将某个命令添加到收藏夹之前,您想看看是什么效果的话,不妨来这
里试试.
命令行
97
设置
在设置界面,你可以配置一些常用的设置.
图12.2设置界面
“CurrentDirectory”图形界面会默认设置您的Gradle项目的根目录(build.gradle文件所
在的目录)为当前目录.
“StackTraceOutput“这个选项可以指定当错误发生时,有多少信息可以写入到轨迹栈
中,注意:在您设定轨迹栈级别后,如果"CommandLine"(命令行)选项卡中,或者
在"Favorites"(收藏夹)选项卡中的命令发生错误,这个设置就不会起作用了.
”OnlyShowOutputWhenErrorsOccur”设定当编译出问题时输出窗口才显示相关信
息.
"UseCustomGradleExecutor"高级功能,您可以指定一个路径启动Gradle命令代替
默认的设置,例如您的项目需要在别的批处理文件或者shell脚本进行额外的配置(例如
指定一个初始化脚本),这种情况您就可以使用它.
设置
98
编写构建脚本
这一章我们将要深入的学习如何编写构建脚本.
编写构建脚本
99
Gradle构建语言
Gradle是以Groovy语言为基础,基于DSL(领域特定语言)语法的自动化构建工具,但是它增
加了一些额外的特性,这使得Gradle更加的容易去阐释构建.
一个构建脚本能够包含任何Groovy语言的元素(Anylanguageelementexceptforstatement
labels),每个构建脚本都使用UTF-8编码.
Gradle构建语言
100
项目API
在第七章Java构建入门那部分我们使用了apply()方法,这个方法是从哪里来的呢?我们之前
说过Gradle在构建脚本中定义了一个项目.对于构建脚本中每个项目,Gradle都创建了一个
Project类型的对象用来关联此项目.当构建脚本执行时,它会去配置所关联的工程对象.
构建脚本中每个被调用的方法(这些方法并未在构建脚本中定义)都被委托给当前工程
对象(使用工程对象引用方法)。
构建脚本中每个被操作的属性(这些属性并未在构建脚本中定义)都被委托给当前工程
对象(使用工程对象引用属性).
让我们尝试下如何操作工程对象的属性.
例子:13.1操作工程对象的属性
build.gradle
printlnname
printlnproject.name
使用gradle-qcheck命令输出结果:
>gradle-qcheck
projectApi
projectApi
如您所见,两个println语句都输出了相同的属性,第一个输出使用的是自动委托(auto-
delegation),因为当前属性并没有在构建脚本中定义.另一个语句使用了项目一个属性,这个
属性在任何构建脚本中都可用,它的返回值是被关联的工程对象.只有当您定义了一个属性或
者一个方法,它的名字和工程对象的某个成员的名字相同时,你应该使用项目属性.
项目API
101
标准项目属性
Project对象提供了一些标准的属性,您可以在构建脚本中很方便的使用他们.下面列出了常
用的属性:
Name Type DefaultValue
project Project Project实例对象
name String 项目目录的名称
path String 项目的绝对路径
description String 项目描述
projectDir File 包含构建脚本的目录
build File projectDir/build
group Object 未具体说明
version Object 未具体说明
ant AntBuilder Ant实例对象
建议:
不要忘记我们的构建脚本只是个很简单的Groovy代码,不过它会再调用Gradle
API,Project接口通过调用GradleAPI让我们可以操作任何事情,因此如果你想知道哪个标
签('tags')可以在构建脚本种使用,您可以翻阅Project接口的的说明文档.
标准项目属性
102
声明变量
在Gradle构建脚本中有两种类型的变量可以声明:局部变量(local)和扩展属性(extra).
声明变量
104
局部变量
局部变量使用关键字def来声明,其只在声明它的地方可见.局部变量是Groovy语言的一个
基本特性.
例子13.2.使用局部变量
defdest="dest"
taskcopy(type:Copy){
form"source"
intodest
}
局部变量
105
扩展属性
在Gradle领域模型中所有被增强的对象能够拥有自己定义的属性.这包括,但不仅限于
projects,tasks,还有sourcesets.Project对象可以添加,读取,更改扩展的属性.另外,使
用ext扩展块可以一次添加多个属性.
例子13.3.使用扩展属性
build.gradle
applyplugin:"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"
}
}
taskprintProperties<<{
printlnspringVersion
printlnemailNotification
sourceSets.matching{it.purpose=="production"}.each{printlnit.name}
}
使用gradle-qprintProperties输出结果
>gradle-qprintProperties
3.1.0.RELEASE
build@master.org
main
plugin
扩展属性
106
在上面的例子中,一个ext扩展块向Project对象添加了两个扩展属性.名为perpose的属性
被添加到每个sourceset,然后设置ext.purpose等于null(null值是被允许的).当这些扩展
属性被添加后,它们就像预定义的属性一样可以被读取,更改值.
例子中我们通过一个特殊的语句添加扩展属性,当您试图设置一个预定义属性或者扩展属
性,但是属性名拼写错误或者并不存在时,操作就会失败.Project对象可以在任何地方使用
其扩展属性,它们比局部变量有更大的作用域.一个项目的扩展属性对其子项目也可见.
关于扩展属性更多的细节还有它的API,请看ExtraPropertiesExtension类的API文档说明.
扩展属性
107
Groovy基础
Groovy提供了大量的特性用来创建DSL.Gradle构建语言知道Groovy语言的工作原理,并
利用这些特性帮助您编写构建脚本,特别是您在编写plugin或者task的时候,你会觉得很方
便.
Groovy基础
108
属性存取器
Groovy自动将一个属性的引用转换为相应的getter或setter方法.
例子:13.5.属性存取器
//使用getter方法
printlnproject.buildDir
printlngetProject().getBuildDir()
//使用setter方法
project.buildDir='target'
getProject().setBuildDir('target')
属性存取器
110
可有可无的圆括号
在调用方法时,圆括号可有可无,是个可选的.
例子:13.6.不使用圆括号调用方法
build.gradle
test.systemProperty'some.prop','value'
test.systemProperty('some.prop','value')
可有可无的圆括号
111
List和Map集合
Groovy为预定义的List和Map集合提供了一些操作捷径,这两个字面值都比较简单易懂,
但是Map会有一些不同.
例如,当您使用"apply"方法使用插件时,apply会自动加上Map的一个参数,当您这样写"
applyplugin:'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=newArraryList<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的映射
applyplugin:'java'
List和Map集合
112
闭包作为方法的最后一个参数
GradleDSL在很多地方使用闭包,这里我们将讨论更多关于闭包的使用.当一个方法的最后
一个参数是一个闭包时,您可以在方法调用后放置一个闭包.
例子:13.8.闭包作为方法的参数
build.gradle
repositories{
println"inaclosure"
}
repositories(){println"inaclosure"}
repositories({println"inaclosure"})
闭合作为方法的最后一个参数
113
闭包委托对象
每个闭包都有一个委托对象,当闭包既不是局部变量也不是作为方法参数时,Groovy使用委
托对象查找变量和方法引用.当委托对象被用来管理时,Gradle使用它来管理闭包.
例子13.9.闭包引用
build.gradle
dependencies{
assertdelegate==project.dependencies
testCompile('junit:junit:4.11')
delegate.testCompile('junit:junit:4.11')
}
闭合委托对象
114
深入了解Tasks
在这本教程的一开始(第6章,构建脚本基础)你已经学习了如何创建简单的任务.然后你也学
习了如何给这些任务加入额外的行为,以及如何在任务之间建立依赖关系.这些仅仅是用来构
建简单的任务.Gradle可以创建更为强大复杂的任务.这些任务可以有它们自己的属性和方法.
这一点正是和Anttargets不一样的地方.这些强大的任务既可以由你自己创建也可以使用
Gradle内建好的.
深入了解Tasks
115
定义tasks
我们已经在第6章学习了定义任务的形式(keyword形式).当然也会有一些定义形式的变化来
适应某些特殊的情况.比如下面的例子中任务名被括号括起来了.这是因为之前定义简单任务
的形式(keyword形式)在表达式里是不起作用的.
例子15.1.定义tasks
build.gradle
task(hello)<<{
println"hello"
}
task(copy,type:Copy){
from(file('srcDir'))
into(buildDir)
}
你也可以使用strings来定义任务的名字:
例子15.2.例子tasks-使用strings来定义任务的名字
build.gradle
task('hello')<<
{
println"hello"
}
task('copy',type:Copy){
from(file('srcDir'))
into(buildDir)
}
还有另外一种语法形式来定义任务,更加直观:
例子15.3.另外一种语法形式
build.gradle
定义tasks
116
定位tasks
你经常需要在构建文件里找到你定义的tasks,举个例子,为了配置它们或者使用它们作为依
赖.有许多种方式都可以来实现定位.首先,每一个任务都必须是一个project的有效属性,并使
用任务名来作为属性名:
例子15.4.通过属性获取tasks
build.gradle
taskhello
printlnhello.name
printlnproject.hello.name
Tasks也可以通过taskscollection来得到.
例子15.5.通过taskscollection获取tasks
build.gradle
taskhello
printlntasks.hello.name
printlntasks['hello'].name
你也可以使用tasks.getByPath()方法通过任务的路径来使用任何project里的任务.你可以通
过使用任务的名字,任务的相对路径或者绝对路径作为getByPath()方法的输入.
例子15.6.通过路径获取tasks
build.gradle
project(':projectA'){
taskhello
}
taskhello
printlntasks.getByPath('hello').path
printlntasks.getByPath(':hello').path
printlntasks.getByPath('projectA:hello').path
printlntasks.getByPath(':projectA:hello').path
定位tasks
118
配置tasks
举一个例子,让我们看一看Gradle自带的Copytask.为了创建一个Copytask,你需要在你的
构建脚本里先声明它:
例子15.7.创建一个copytask
build.gradle
taskmyCopy(type:Copy)
它创建了一个没有默认行为的copytask.这个task可以通过它的API来配置(参考Copy).接
下来例子展示了不同的实现方法.
补充说明一下,这个task的名字是“myCopy”,但是它是“Copy”类(type).你可以有许多同样
type不同名字的tasks.这个在实现特定类型的所有任务的cross-cuttingconcerns时特别有
用.
例子15.8.配置一个任务-不同的方法
build.gradle
CopymyCopy=task(myCopy,type:Copy)
myCopy.from'resources'
myCopy.into'target'
myCopy.include('**/*.txt','**/*.xml','**/*.properties')
这个我们通过Java配置对象是一样的形式.但是你每次都必须在语句里重复上下文
(myCopy).这种方式可能读起来并不是那么的漂亮.
下面一种方式就解决了这个问题.是公认的最具可读性的方式.
例子15.9.配置一个任务-通过闭包closure
build.gradle
taskmyCopy(type:Copy)
myCopy{
from'resources'
into'target'
include('**/*.txt','**/*.xml','**/*.properties')
}
配置tasks
120
上面例子里的第三行是tasks.getByName()方法的一个简洁的写法.特别要注意的是,如果你
通过闭包的形式来实现getByName()方法,这个闭包会在task配置的时候执行而不是在task
运行的时候执行.
你也可以直接在定义task时使用闭包.
例子15.10.通过定义一个任务
build.gradle
taskcopy(type:Copy){
from'resources'
into'target'
include('**/*.txt','**/*.xml','**/*.properties')
}
请不要忘了构建的各个阶段.
一个任务有配置和动作.当使用<<时,你只是简单的使用捷径定义了动作.定义在配置区域的
代码只会在构建的配置阶段执行,而且不论执行哪个任务.可以参考第55章,TheBuild
Lifecycleformoredetailsaboutthebuildlifecycle.
配置tasks
121
给task加入依赖
有许多种加入依赖的方式.在6.5小节,“任务依赖”里,你已经学习了如何使用任务的名称定义
依赖.任务名称可以指向同一个项目里的任务,或者其他项目里的任务.为了指向其他项目,你
必须在任务的名称前加入项目的路径.下面的例子给projectA:taskX加入依赖projectB:taskY:
例子15.11.从另外一个项目给任务加入依赖
build.gradle
project('projectA'){
tasktaskX(dependsOn:':projectB:taskY')<<{
println'taskX'
}
}
project('projectB'){
tasktaskY<<{
println'taskY'
}
}
gradle-qtaskX的输出
>gradle-qtaskX
taskY
taskX
除了使用任务名称,你也可以定义一个依赖对象y:
例子15.12.通过任务对象加入依赖
build.gradle
tasktaskX<<{
println'taskX'
}
tasktaskY<<{
println'taskY'
}
taskX.dependsOntaskY
给task加入依赖
122
gradle-qtaskX的输出
>gradle-qtaskX
taskY
taskX
更加先进的用法,你可以通过闭包定义一个任务依赖.闭包只能返回一个单独的Task或者
Task对象的collection,这些返回的任务就将被当做依赖.接下来的例子给taskX加入了一个
复杂的依赖,所有以lib开头的任务都将在taskX之前执行:
例子15.13.通过闭包加入依赖
build.gradle
tasktaskX<<{
println'taskX'
}
taskX.dependsOn{
tasks.findAll{task->task.name.startsWith('lib')}
}
tasklib1<<{
println'lib1'
}
tasklib2<<{
println'lib2'
}
tasknotALib<<{
println'notALib'
}
gradle-qtaskX的输出
>gradle-qtaskX
lib1
lib2
taskX
Formoreinformationabouttaskdependencies,seetheTaskAPI.
给task加入依赖
123
给tasks排序
任务的排序功能正在测试和优化.请注意,这项功能在Gradle之后的版本里可能会改变.
在某些情况下,我们希望能控制任务的的执行顺序,这种控制并不是向上一张那样去显示地加
入依赖关系.最主要的区别是我们设定的排序规则不会影响那些要被执行的任务,只是影响执
行的顺序本身.好吧,我知道可能有点抽象.
我们来看看以下几种有用的场景:
执行连续的任务:eg.'build'从来不会在'clean'之前执行.
在build的一开始先运行构建确认(buildvalidations):eg.在正式的发布构建前先确认我的
证书是正确的.
在运行长时间的检测任务前先运行快速的检测任务来获得更快的反馈:eg.单元测试总是
应该在集成测试之前被执行.
一个聚集(aggregates)某种特定类型的所有任务结果的任务:eg.测试报告任务(test
reporttask)包含了所有测试任务的运行结果.
目前,有2种可用的排序规则:"mustrunafter"和"shouldrunafter".
当你使用“mustrunafter”时即意味着taskB必须总是在taskA之后运行,无论taskA和taskB
是否将要运行:
taskB.mustRunAfter(taskA)
"shouldrunafter"规则其实和"mustrunafter"很像,只是没有那么的严格,在2种情况下它会
被忽略:
1. 使用规则来阐述一个执行的循环.
2. 当并行执行并且一个任务的所有依赖除了“shouldrunafter”任务其余都满足了,那么这个
任务无论它的“shouldrunafter”依赖是否执行,它都可以执行.(编者:翻译待商榷,提供具
体例子)
总之,当要求不是那么严格时,“shouldrunafter”是非常有用的.
即使有目前的这些规则,我们仍可以执行taskA而不管taskB,反之亦然.
例子15.14.加入'mustrunafter'
build.gradle
给tasks排序
124
tasktaskX<<{
println'taskX'
}
tasktaskY<<{
println'taskY'
}
taskY.mustRunAftertaskX
gradle-qtaskYtaskX的输出
>gradle-qtaskYtaskX
taskX
taskY
例子15.15.加入'shouldrunafter'
build.gradle
tasktaskX<<{
println'taskX'
}
tasktaskY<<{
println'taskY'
}
taskY.shouldRunAftertaskX
gradle-qtaskYtaskX的输出
>gradle-qtaskYtaskX
taskX
taskY
在上面的例子里,我们仍可以直接执行taskY而不去taskX:
例子15.16.任务排序不影响任务执行
gradle-qtaskY的输出
>gradle-qtaskY
taskY
为了在2个任务间定义“mustrunafter”或者“shouldrunafter”排序,我们需要使用
Task.mustRunAfter()和Task.shouldRunAfter()方法.这些方法接收一个任务的实例, 任务的
名字或者任何Task.dependsOn()可以接收的输入.
给tasks排序
125
注意“B.mustRunAfter(A)”或者“B.shouldRunAfter(A)”并不影响任何任务间的执行依赖:
tasksA和B可以被独立的执行.排序规则只有当2个任务同时执行时才会被应用.
在运行时加上--continue,当A失败时B仍然会执行.
之前提到过,“shouldrunafter”规则在一个执行循环中将被忽略:
例子15.17.'shouldrunafter'任务的忽略
build.gradle
tasktaskX<<{
println'taskX'
}
tasktaskY<<{
println'taskY'
}
tasktaskZ<<{
println'taskZ'
}
taskX.dependsOntaskY
taskY.dependsOntaskZ
taskZ.shouldRunAftertaskX
gradle-qtaskX的输出
>gradle-qtaskX
taskZ
taskY
taskX
给tasks排序
126
给task加入描述
你可以给你的任务加入一段描述性的文字.它将会在任务执行的时候显示出来.
例子15.18.给任务加入描述
build.gradle
taskcopy(type:Copy){
description'Copiestheresourcedirectorytothetargetdirectory.'
from'resources'
into'target'
include('**/*.txt','**/*.xml','**/*.properties')
}
给task加入描述
127
替换tasks
有时候你想要替换一个任务.举个例子,如果你想要互换一个通过java插件定义的任务和一个
自定义的不同类型的任务:
例子14.19.覆写一个任务
build.gradle
taskcopy(type:Copy)
taskcopy(overwrite:true)<<{
println('Iamthenewone.')
}
gradle-qcopy的输出
>gradle-qcopy
Iamthenewone.
这种方式将用你自己定义的任务替换一个Copy类型的任务,因为它使用了同样的名字.但你
定义一个新的任务时,你必须设置overwrite属性为true.否则的话Gradle会抛出一个异常,
taskwiththatnamealreadyexists.
替换tasks
128
跳过tasks
Gradle提供了好几种跳过一个任务的方式.
1.使用判断条件(predicate)
你可以使用onlyIf()方法来为一个任务加入判断条件.就和Java里的if语句一样,任务只有在
条件判断为真时才会执行.你通过一个闭包来实现判断条件.闭包像变量一样传递任务,如果任
务应该被执行则返回真,反之亦然.判断条件在任务执行之前进行判断.
例子15.20.使用判断条件跳过一个任务
build.gradle
taskhello<<{
println'helloworld'
}
hello.onlyIf{!project.hasProperty('skipHello')}
gradlehello-PskipHello的输出
>gradlehello-PskipHello
:helloSKIPPED
BUILDSUCCESSFUL
Totaltime:1secs
2.使用StopExecutionException
如果想要跳过一个任务的逻辑并不能被判断条件通过表达式表达出来,你可以使用
StopExecutionException.如果这个异常是被一个任务要执行的动作抛出的,这个动作之后的
执行以及所有紧跟它的动作都会被跳过.构建将会继续执行下一个任务.
例子15.21.通过StopExecutionException跳过任务
build.gradle
跳过tasks
129
taskcompile<<{
println'Wearedoingthecompile.'
}
compile.doFirst{
//Hereyouwouldputarbitraryconditionsinreallife.
//Butthisisusedinanintegrationtestsowewantdefinedbehavior.
if(true){thrownewStopExecutionException()}
}
taskmyTask(dependsOn:'compile')<<{
println'Iamnotaffected'
}
gradle-qmyTask的输出
>gradle-qmyTask
Iamnotaffected
如果你直接使用Gradle提供的任务,这项功能还是十分有用的.它允许你为内建的任务加入条
件来控制执行.[6]
3.激活和注销tasks
每一个任务都有一个已经激活的标记(enabledflag),这个标记一般默认为真.将它设置为假,那
它的任何动作都不会被执行.
例子15.22.激活和注销tasks
build.gradle
taskdisableMe<<{
println'Thisshouldnotbeprintedifthetaskisdisabled.'
}
disableMe.enabled=false
gradledisableMe的输出
>gradledisableMe
:disableMeSKIPPED
BUILDSUCCESSFUL
Totaltime:1secs
跳过tasks
130
跳过tasks
131
跳过up-to-date的任务
如果你正在使用一些附加的任务,比如通过Java插件加入的任务,你可能会注意到Gradle会
跳过一些任务,这些任务后面会标注up-to-date.代表这个任务已经运行过了或者说是最新的
状态,不再需要产生一次相同的输出.不仅仅是这些内建任务,其实你在运行自己的任务时,也
会碰到这种情况.
1.声明一个任务的输入和输出
让我们先看一个例子.这里我们的任务会根据一个XML文件生成好几个输出文件.让我们运行
这个任务2次.
例子15.23.Ageneratortask
build.gradle
tasktransform{
ext.srcFile=file('mountains.xml')
ext.destDir=newFile(buildDir,'generated')
doLast{
println"Transformingsourcefile."
destDir.mkdirs()
defmountains=newXmlParser().parse(srcFile)
mountains.mountain.each{mountain->
defname=mountain.name[0].text()
defheight=mountain.height[0].text()
defdestFile=newFile(destDir,"${name}.txt")
destFile.text="$name->${height}\n"
}
}
}
gradletransform的输出
>gradletransform
:transform
Transformingsourcefile.
gradletransform的输出
>gradletransform
:transform
Transformingsourcefile.
跳过up-to-date的任务
132
这里Gradle执行了这个任务两次,即使什么都没有改变,它也没有跳过这个任务.这个例子里
的任务,它的行为是通过闭包定义的.Gradle并不知道闭包会做什么,也并不能自动指出是否
这个任务是up-to-date.为了使用Gradle的up-to-date检测,你需要定义任务的输入和输出.
每个任务都有输入和输出属性,你需要使用这些属性来声明任务的输入和输出.下面的例子中,
我们将声明XML文件作为输入,并且把输出放在一个指定的目录.让我们运行这个任务2次.
例子15.24.声明任务的输入和输出
build.gradle
tasktransform{
ext.srcFile=file('mountains.xml')
ext.destDir=newFile(buildDir,'generated')
inputs.filesrcFile
outputs.dirdestDir
doLast{
println"Transformingsourcefile."
destDir.mkdirs()
defmountains=newXmlParser().parse(srcFile)
mountains.mountain.each{mountain->
defname=mountain.name[0].text()
defheight=mountain.height[0].text()
defdestFile=newFile(destDir,"${name}.txt")
destFile.text="$name->${height}\n"
}
}
}
gradletransform的输出
>gradletransform
:transform
Transformingsourcefile.
gradletransform的输出
>gradletransform
:transformUP-TO-DATE
现在,Gradle就能够检测出任务是否是up-to-date状态.
任务的输入属性是TaskInputs类型.任务的输出属性是TaskOutputs类型.
一个任务如果没有定义输出的话,那么它永远都没用办法判断up-to-date.对于某些场景,比如
一个任务的输出不是文件,或者更复杂的场景,TaskOutputs.upToDateWhen())方法会计算任
务的输出是否应被视为最新.
跳过up-to-date的任务
133
总而言之,如果一个任务只定义了输出,如果输出不变的话,它就会被视为up-to-date.
2.它是如何工作的?
当一个任务是首次执行时,Gradle会取一个输入的快照(snapshot).该快照包含组输入文件和
每个文件的内容的散列.然后当Gradle执行任务时,如果任务成功完成,Gradle会获得一个
输出的快照.该快照包含输出文件和每个文件的内容的散列.Gradle会保留这两个快照用来在
该任务的下一次执行时进行判断.
之后,每次在任务执行之前,Gradle都会为输入和输出取一个新的快照,如果这个快照和之前
的快照一样,Gradle就会假定这个任务已经是最新的(up-to-date)并且跳过任务,反之亦然.
需要注意的是,如果一个任务有指定的输出目录,自从该任务上次执行以来被加入到该目录的
任务文件都会被忽略,并且不会引起任务过时(outofdate).这是因为不相关任务也许会共用同
一个输出目录.如果这并不是你所想要的情况,可以考虑使用TaskOutputs.upToDateWhen())
跳过up-to-date的任务
134
Task规则
有时候也想要一个任务的行为是基于已经定义好的取值范围或者特定规则,下面的例子就提供
了一种很直观漂亮的方式:
例子15.25.任务规则
build.gradle
tasks.addRule("Pattern:ping<ID>"){StringtaskName->
if(taskName.startsWith("ping")){
task(taskName)<<{
println"Pinging:"+(taskName-'ping')
}
}
}
gradle-qpingServer1的输出
>gradle-qpingServer1
Pinging:Server1
这里的String参数就是用来定义规则的.
规则并不只是在通过命令行使用任务的时候执行.你也可以基于规则来创建依赖关系:
例子15.26.基于规则的任务依赖
build.gradle
tasks.addRule("Pattern:ping<ID>"){StringtaskName->
if(taskName.startsWith("ping")){
task(taskName)<<{
println"Pinging:"+(taskName-'ping')
}
}
}
taskgroupPing{
dependsOnpingServer1,pingServer2
}
gradle-qgroupPing的输出
Task规则
135
>gradle-qgroupPing
Pinging:Server1
Pinging:Server2
如果你运行“gradle-qtasks”,你并不能找到名叫“pingServer1”或者“pingServer2”的任务,但
是这个脚本仍然会执行这些任务.
Task规则
136
终止tasks
终止任务是一个正在开发的功能.
这里的终止任务并不是指终止一个任务,而是指一个无论运行结果如何最后都会被执行的任务.
例子15.27.加入一个任务终止器
build.gradle
tasktaskX<<{
println'taskX'
}
tasktaskY<<{
println'taskY'
}
taskX.finalizedBytaskY
gradle-qtaskX的输出
>gradle-qtaskX
taskX
taskY
即使要终止的任务失败了,终止任务仍会继续执行.
例子14.28.当任务失败时k
build.gradle
tasktaskX<<{
println'taskX'
thrownewRuntimeException()
}
tasktaskY<<{
println'taskY'
}
taskX.finalizedBytaskY
gradle-qtaskX的输出
终止tasks
137
补充
下面补充的部分原本是第14章,最新的Gradle文档将其移除,
所以将其作为补充放到这一章节。
补充
139
Gradle属性和system属性
Gradle提供了多种的方法让您可以在构建脚本中添加属性.使用-D命令选项,您可以向运行
Gradle的JVM传递一个system属性.Gradle命令的-D选项和Java命令的-D选项有些
相同的效果.
您也可以使用属性文件向您的Project对象中添加属性.您可以在Gradle用户目录(如果您没
有在USER_HOME/.gradle配置默认设置,则由"GRADLE_USER_HOME"环境变量定义)或
者项目目录放置一个gradle.properties文件.如果是多项目的话,您可以在每个子目录里都放
置一个gradle.properties文件.gradle.properties文件内容里的属性能够被Project对象访问
到.不过有一点,用户目录中的gradle.properties文件优先权大于项目目录中的
gradle.properties文件.
您也可以通过-P命令选项直接向Project对象中添加属性.
另外,当Gradle看到特别命名的system属性或者环境变量时,Gradle也可以设置项目属性.
比如当您没有管理员权限去持续整合服务,还有您需要设置属性值但是不容易时,这个特性
非常有用.出于安全的原因,在这种情况下,您没法使用-P命令选项,您也不能修改系统级
别的文件.确切的策略是改变您持续继承构建工作的配置,增加一个环境变量设置令它匹配一
个期望的模式.对于当前系统来说,这种方法对于普通用户来说是不可见的.[6]
如果环境变量的名字是ORG_GRADLE_PROJECT=somevalue,Gradle会使用值为
somevalue在您的Project对象中设定一个支持属性.另外Gradle也支持system属性,但是
使用不同的名字模式,例如org.gradle.project.prop.
您也可以在gradle.properties文件中设置system属性.如果一个属性名的前缀为
“systemProp”,那么这个属性和它的属性值会被设置为system属性.如果没有这个前缀,在
多项目构建中,除了根项目会被忽略外,“systemProp.”属性会在任何项目中设置.也就是说仅
仅根项目的gradle.properties文件会被检查其属性的前缀是否是“systemProp”.
**例子14.2.通过gradle.properties文件设置属性
gradle.properties
gradlePropertiesProp=gradlePropertiesValue
sysProp=shouldBeOverWrittenBySysProp
envProjectProp=shouldBeOverWrittenByEnvProp
systemProp.system=systemValue
build.gradle
Gradle属性和system属性
140
taskprintProps<<{
printlncommandLineProjectProp
printlngradlePropertiesProp
printlnsystemProjectProp
printlnenvProjectProp
printlnSystem.properties['system']
}
[6].Jenkins,Teamcity,orBamboo都是提供这个功能的CI服务.
使用gradle-q-PcommandLineProjectProp=commandLineProjectPropValue-
Dorg.gradle.project.systemProjectProp=systemPropertyValueprintProps输出
>gradle-q-PcommandLineProjectProp=commandLineProjectPropValue-Dorg.gradle.proje
ct.systemProjectProp=systemPropertyValueprintProps
commandLineProjectPropValue
gradlePropertiesValue
systemPropertyValue
envPropertyValue
systemValue
Gradle属性和system属性
141
使用其他的脚本配置项目
您还可以使用其他的构建脚本来配置当前的项目,Gradle构建语言的所有的内容对于其他的
脚本都是可以使用的.您甚至可以在别的脚本中再使用其他的脚本.
例子14.3.使用其他的构建脚本配置项目
build.gradle
applyfrom:'other.gradle'
other.gradle
println"configuring$project"
taskhello<<{
println''helloformothersrcipt'
}
使用gradle-qhello输出
>gradle-qhello
configuringrootproject'configureProjectUsingScript'
hellofromotherscript
使用其他的脚本配置项目
142
使用其他的脚本配置任意对象
您也可以使用其他的构建脚本配置任意的对象.
例子:14.5.使用别的脚本配置配置对象
build.gradle
taskconfig<<{
defpos=newjava.text.FieldPosition(10)
//使用另一个脚本
applyfrom:'other.gradle',to:pos
printlnpos.beginIndex
printlnpos.endIndex
}
other.gradle
beginIndex=1
endIndex=5
使用gradle-qconfigure输出
>gradle-qconfigure
1
5
使用其他的脚本配置任意对象
143
配置任意对象
您可以使用下面方法配置任意的对象.
例子14.4.配置任意对象
build.gradle
taskconfigure<<{
defpos=configure(newjava.text.FieldPosition(10)){
beginIndex=1
endIndex=5
}
printlnpos.beginIndex
printlnpos.endIndex
}
使用gradle-qconfigure输出
>gradle-qconfigure
1
5
配置任意对象
144
缓存
为了提高响应能力,Gradle默认缓存了所有编译后的脚本.包括所有的构建脚本,初始化脚
本,还有其他脚本.Gradle创建了一个.gradle目录来存放编译后的脚本,下次您运行构建脚
本时,如果这个脚本自从它被编译后就再也没有被改动过,Gradle会先使用编译后的脚本.否
则Gradle会重新编译脚本,然后将新编译后的文件缓存起来.如果您使用Gradle--
recompile--scripts运行脚本,缓存的脚本就会被删除,然后新编译后的文件就会再被缓存.这
种方法可以强制Gradle重新编译脚本并缓存.
缓存
145
文件操作
大多数构建工作需要操作文件,Gradle增加了一些API帮助您处理这些工作。
文件操作
146
Locatingfiles
使用Project.file()方法能够相对项目目录定位一个文件
例16.1.定位文件
build.gradle
//使用一个相对路径
FileconfigFile=file('src/config.xml')
//使用一个绝对路径
configFile=file(configFile.absolutePath)
//使用一个项目路径的文件对象
configFile=file(newFile('src/config.xml'))`
file()方法接收任何形式的对象参数.它会将参数值转换为一个绝对文件对象,一般情况下,
你可以传递一个String或者一个File实例.如果传递的路径是个绝对路径,它会被直接构
造为一个文件实例.否则,会被构造为项目目录加上传递的目录的文件对象.另外,file()函数也
能识别 URL,例如file:/some/path.xml.
这个方法非常有用,它将参数值转换为一个绝对路径文件.所以请尽量使用newFile(somePath)
,因为file()总是相对于当前项目路径计算传递的路径,然后加以矫正.因为当前工作区间目
录依赖于用户以何种方式运行Gradle.
定位文件
147
文件集合
文件集合表示一组文件,Gradle使用FileCollection接口表示文件集合,GradleAPI中的许
多项目都实现了这个接口,例如dependencyconfigurations.
获取FileCollection实例的一种方法是使用Project.files()方法.你可以传递任何数量的
对象参数,这个方法能将你传递的对象集合转换为一组文件对象.files()方法接收任何类型对
象参数.每一个file()方法都依赖于项目目录(在第15章,第一小节中介绍). files()方法也
接收collections, iterables, maps和arrays类型参数.这些参数的内容会被解析,然后
被转换为文件对象.
例15.2创建文件集合
build.gradle
FileCollectioncollection=files('src/file1.txt',
newFile('src/file2.txt'),
['src/file3.txt','src/file4.txt'])
文件集合可以被迭代器,使用迭代操作能够将其转换为其他的一些类型.你可以使用+操作将
两个文件集合合并,使用-操作能够对一个文件集合做减法.下面一些例子介绍如何操作文件
集合.
例15.3使用文件集合
build.gradle
//对文件集合进行迭代
collection.each{Filefile->
printlnfile.name
}
//转换文件集合为其他类型
Setset=collection.files
Setset2=collectionasSet
Listlist=collectionasList
Stringpath=collection.asPath
Filefile=collection.singleFile
Filefile2=collectionasFile
//增加和减少文件集合
defunion=collection+files('src/file3.txt')
defdifferent=collection-files('src/file3.txt')
文件集合
148
你也可以向files()方法专递一个闭合或者可回调的实例参数.当查询集合的内容时就会调用
它,然后将返回值转换为一些文件实例.返回值可以是files()方法支持的任何类型的对象.下
面有个简单的例子来演示实现FileCollection接口
例15.4实现一个文件集合
build.gradle
tasklist<<{
FilesrcDir
//使用闭合创建一个文件集合
collection=files{srcDir.listFiles()}
srcDir=file('src')
println"Contentsof$srcDir.name"
collection.collect{relativePath(it)}.sort().each{printlnit}
srcDir=file('src2')
println"Contentsof$srcDir.name"
collection.collect{relativePath(it)}.sort().each{printlnit}
}
使用gradle-qlist输出结果
>gradle-qlist
Contentsofsrc
src/dir1
src/file1.txt
Contentsofsrc2
src2/dir1
src2/dir2
另外, files()方法也接收其他类型的参数:
FileCollection
内容损坏的文件包含在文件集合中.
Task
任务的输出文件包含在文件集合中.
TaskOutputs
TaskOutputs的输出文件包含在文件集合中
值得注意的是当有需要时文件集合的内容会被被惰性处理,就比如一些任务在需要的时候会创
建一个FileCollecion代表的文件集合.
文件集合
149
文件集合
150
文件树
文件树就是一个按照层次结构分布的文件集合,例如,一个文件树可以代表一个目录树结构或者
一个ZIP压缩文件的内容.它被抽象为FileTree结构,FileTree继承自FileCollection,所
以你可以像处理文件集合一样处理文件树,Gradle有些对象实现了 FileTree接口,例如源集
合.使用Project.fileTree()方法可以得到FileTree的实例,它会创建一个基于基准目录的
对象,然后视需要使用一些Ant-style的包含和去除规则.
例15.5创建文件树build.gradle
/以一个基准目录创建一个文件树
FileTreetree=fileTree(dir:'src/main')
//添加包含和排除规则
tree.include'**/*.java'
tree.exclude'**/Abstract*'
//使用路径创建一个树
tree=fileTree('src').include('**/*.java')
//使用闭合创建一个数
tree=fileTree('src'){
include'**/*.java'
}
//使用map创建一个树
tree=fileTree(dir:'src',include:'**/*.java')
tree=fileTree(dir:'src',includes:['**/*.java','**/*.xml'])
tree=fileTree(dir:'src',include:'**/*.java',exclude:'**/*test*/**')
就像使用文件集合一样,你可以访问文件树的内容,使用Ant-style规则选择一个子树。
例15.6使用文件树build.gradle
文件树
151
//遍历文件树
tree.each{Filefile->
printlnfile
}
//过滤文件树
FileTreefiltered=tree.matching{
include'org/gradle/api/**'
}
//合并文件树A
FileTreesum=tree+fileTree(dir:'src/test')
//访问文件数的元素
tree.visit{element->
println"$element.relativePath=>$element.file"
}
文件树
152
使用一个归档文件的内容作为文件树
你可以使用ZIP或者TAR等压缩文件的内容作为文件树, Project.zipTree()和
Project.tarTree()方法返回一个FileTree实例,你可以像使用其他文件树或者文件集合一
样使用它.例如,你可以使用它去扩展一个压缩文档或者合并一些压缩文档.
例15.7使用压缩文档作为文件树build.gradle
//使用路径创建一个ZIP文件
FileTreezip=zipTree('someFile.zip')
//使用路径创建一个TAR文件
FileTreetar=tarTree('someFile.tar')
//tartree能够根据文件扩展名得到压缩方式,如果你想明确的指定压缩方式,你可以使用下面方法
FileTreesomeTar=tarTree(resources.gzip('someTar.ext'))
使用一个归档文件的内容作为文件树
153
指定一组输入文件
在Gradle中有一些对象的某些属性可以接收一组输入文件.例如,JavaComplile任务有一个
source属性,它定义了编译的源文件,你可以设置这个属性的值,只要files()方法支持.这意
味着你可以使用File, String, collection, FileCollection甚至是使用一个闭合去设置
属性的值.
例15.8指定文件
build.gradle
//使用一个File对象设置源目录
compile{
source=file('src/main/java')
}
//使用一个字符路径设置源目录
compile{
source='src/main/java'
}
//使用一个集合设置多个源目录
compile{
source=['src/main/java','../shared/java']
}
//使用FileCollection或者FileTree设置源目录
compile{
source=fileTree(dir:'src/main/java').matching{include'org/gradle/api/**'}
}
//使用一个闭合设置源目录
compile{
source={
//Usethecontentsofeachzipfileinthesrcdir
file('src').listFiles().findAll{it.name.endsWith('.zip')}.collect{zipTree(i
t)}
}
}
Usually,thereisamethodwiththesamenameastheproperty,whichappendstothesetof
files.Again,thismethodacceptsanyofthetypessupportedbythefiles()method.
通常情况下,会有一个方法名和属性名相同的方法能够附加一组文件,这个方法接收files()
方法支持的任何类型的值.
指定一组输入文件
154
例15.9指定文件
build.gradle
compile{
//使用字符路径添加源目录
source'src/main/java','src/main/groovy'
//使用File对象添加源目录
sourcefile('../shared/java')
//使用闭合添加源目录
source{file('src/test/').listFiles()}
}
指定一组输入文件
155
复制文件
你可以使用复制任务( Copy)去复制文件.复制任务扩展性很强,能够过滤复制文件的内容,映
射文件名.
使用复制任务时需要提供想要复制的源文件和一个目标目录,如果你要指定文件被复制时的转
换方式,可以使用复制规则.复制规则被CopySpec接口抽象,复制任务实现了这个接口.使用
CopySpec.from()方法指定源文件.使用CopySpec.into()方法指定目标目录.
例15.10.使用复制任务复制文件
build.gradle
taskcopyTask(type:Copy){
from'src/main/webapp'
into'build/explodedWar'
}
from()方法接收任何files()方法支持的参数.当参数被解析为一个目录时,在这个目录下
的任何文件都会被递归地复制到目标目录(但不是目录本身).当一个参数解析为一个文件时,该
文件被复制到目标目录中.当参数被解析为一个不存在的文件时,这个参数就会忽略.如果这个参
数是一个任务,任务的输出文件(这个任务创建的文件)会被复制,然后这个任务会被自动添加为
复制任务的依赖.
例15.11指定复制任务的源文件和目标目录
build.gradle
taskanotherCopyTask(type:Copy){
//复制src/main/webapp目录下的所有文件
from'src/main/webapp'
//复制一个单独文件
from'src/staging/index.html'
//复制一个任务输出的文件
fromcopyTask
//显式使用任务的outputs属性复制任务的输出文件
fromcopyTaskWithPatterns.outputs
//复制一个ZIP压缩文件的内容
fromzipTree('src/main/assets.zip')
//最后指定目标目录
into{getDestDir()}
}
你可以使用 Ant-style规则或者一个闭合选择要复制的文件.
复制文件
156
例15.12选择要复制文件
build.gradle
taskcopyTaskWithPatterns(type:Copy){
from'src/main/webapp'
into'build/explodedWar'
include'**/*.html'
include'**/*.jsp'
exclude{details->details.file.name.endsWith('.html')&&
details.file.text.contains('staging')}
}
你也可以使用Project.copy()方法复制文件,它的工作方式有一些限制,首先该方法不是增量
的,请参考第14.9节跳过最新的任务.第二,当一个任务被用作复制源时(例如from()方法的参
数), copy()方法不能够实现任务依赖,因为它是一个普通的方法不是一个任务.因此,如果你使
用copy()方法作为一个任务的一部分功能,你需要显式的声明所有的输入和输出以确保获得
正确的结果.
例15.13不使用最新检查方式下用copy()方法复制文件
build.gradle
taskcopyMethod<<{
copy{
from'src/main/webapp'
into'build/explodedWar'
include'**/*.html'
include'**/*.jsp'
}
}
例15.14使用最新的检查方式下用copy()方法复制文件
build.gradle
复制文件
157
taskcopyMethodWithExplicitDependencies{
//对输入做最新检查,添加copyTask作为依赖
inputs.filecopyTask
outputs.dir'some-dir'//对输出做最新检查
doLast{
copy{
//复制copyTask的输出
fromcopyTask
into'some-dir'
}
}
}
建议尽可能的使用复制任务,因为它支持增量化的构建和任务依赖推理,而不需要去额外的费
力处理这些.不过copy()方法可以用作复制任务实现的一部分.即该方法被在自定义复制任
务中使用,请参考第60章编写自定义任务.在这样的场景下,自定义任务应该充分声明与复制
操作相关的输入/输出。
15.6.1重命名文件
例15.15在复制时重命名文件
build.gradle
taskrename(type:Copy){
from'src/main/webapp'
into'build/explodedWar'
//使用一个闭合映射文件名
rename{StringfileName->
fileName.replace('-staging-','')
}
//使用正则表达式映射文件名
rename'(.+)-staging-(.+)','$1$2'
rename(/(.+)-staging-(.+)/,'$1$2')
}
15.6.2过滤文件
例15.16在复制时过滤文件
build.gradle
复制文件
158
importorg.apache.tools.ant.filters.FixCrLfFilter
importorg.apache.tools.ant.filters.ReplaceTokens
taskfilter(type:Copy){
from'src/main/webapp'
into'build/explodedWar'
//在文件中替代属性标记
expand(copyright:'2009',version:'2.3.1')
expand(project.properties)
//使用Ant提供的过滤器
filter(FixCrLfFilter)
filter(ReplaceTokens,tokens:[copyright:'2009',version:'2.3.1'])
//用一个闭合来过滤每一行
filter{Stringline->
"[$line]"
}
//使用闭合来删除行
filter{Stringline->
line.startsWith('-')?null:line
}
}
在源文件中扩展和过滤操作都会查找的某个标志token,如果它的名字是tokenName,它的格
式应该类似于@tokenName@.
15.6.3使用CopySpec类
复制规范来自于层次结构,一个复制规范继承其目标路径,包括模式,排除模式,复制操作,名称
映射和过滤器.
例15.17.嵌套复制规范
build.gradle
tasknestedSpecs(type:Copy){
into'build/explodedWar'
exclude'**/*staging*'
from('src/dist'){
include'**/*.html'
}
into('libs'){
fromconfigurations.runtime
}
}
复制文件
159
复制文件
160
使用同步任务
同步任务( Sync)任务继承自复制任务( Copy),当它执行时,它会复制源文件到目标目录
中,然后从目标目录中的删除所有非复制的文件,这种方式非常有用,比如安装一个应用,创建一
个文档的副本,或者维护项目的依赖关系副本.
下面有一个例子,维护build/libs目录下项目在运行时的依赖
例15.7使用Sync任务复制依赖关系
build.gradle
tasklibs(type:Sync){
fromconfigurations.runtime
into"$buildDir/libs"
}
使用同步任务
161
创建归档文件
一个项目可以有很多JAR文件,你可以向项目中添加WAR,ZIP和TAR文档,使用归档任务可
以创建这些文档:Zip,Tar,Jar,War和Ear.它门都以同样的机制工作.
例15.19创建一个ZIP文档
build.gradle
applyplugin:'java'
taskzip(type:Zip){
from'src/dist'
into('libs'){
fromconfigurations.runtime
}
}
所有的归档任务的工作机制和复制任务相同,它们都实现了CopySpec接口,和Copy任务一
样,使用from()方法指定输入文件,可以选择性的使用into()方法指定什么时候结束.你还
可以过滤文件内容,重命名文件等等,就如同使用复制规则一样.
重命名文档
projectName-vsersion.type格式可以被用来生成文档名,例如:
例15.20创建压缩文档
build.gradle
applyplugin:'java'
version=1.0
taskmyZip(type:Zip){
from'somedir'
}
printlnmyZip.archiveName
printlnrelativePath(myZip.destinationDir)
printlnrelativePath(myZip.archivePath)
使用gradle-qmyZip命令进行输出:
创建归档文件
162
>gradle-qmyZip
zipProject-1.0.zip
build/distributions
build/distributions/zipProject-1.0.zip
上面例子使用一个名为myZip的Zip归档任务生成名称为zipProject-1.0.zip的ZIP文档,区分
文档任务名和最终生成的文档名非常的重要
可以通过设置项目属性archivesBaseName的值来修改生成文档的默认名.当然,文档的名称也
可以通过其他方法随时更改.
归档任务中有些属性可以配置,如表15.1所示:
表15.1归档任务-属性名
属性名 类型 默认值 描述
archiveName String
baseName-appendix-version-
classifier.extension,如果其中任何一个都是空
的,则不添加名称
归档文件
的基本文
件名
archivePath File destinationDir/archiveName
生成归档
文件的绝
对路径。
destinationDir File
取决于文档类型,JAR和WAR使用
project.buildDir/distributions.
project.buildDir/libraries.ZIP和TAR
归档文件
的目录
baseName String project.name
归档文件
名的基础
部分。
appendix String null
归档文件
名的附加
部分。
version String project.version
归档文件
名的版本
部分。
classifier String null
归档文件
名的分类
部分
extension String 取决于文档类型和压缩类型:zip,jar,war,tar,
tgz或者tbz2.
归档文件
的扩展名
例15.21配置归档文件-自定义文档名
build.gradle
创建归档文件
163
applyplugin:'java'
version=1.0
taskmyZip(type:Zip){
from'somedir'
baseName='customName'
}
printlnmyZip.archiveName
使用gradle-qmyZip命令进行输出:
>gradle-qmyZip
customName-1.0.zip
更多配置:
例15.22配置归档任务-附加其他后缀
build.gradle
applyplugin:'java'
archivesBaseName='gradle'
version=1.0
taskmyZip(type:Zip){
appendix='wrapper'
classifier='src'
from'somedir'
}
printlnmyZip.archiveName
使用gradle-qmyZip命令进行输出:
>gradle-qmyZip
gradle-wrapper-1.0-src.zip
多个文档共享内容
你可以使用Project.copySpec()在不用归档文件间共享内容.如果你想发布一个文档,然后在
另一个项目中使用,这部分将在第53章发布文档中讲述.
创建归档文件
164
创建归档文件
165
在Gradle中使用Ant
Gradle出色的集成了Ant.你可以在Gradle构建时使用单独的Ant任务或完整的Ant构建.事
实上,你会发现在Gradle构建脚本中使用Ant任务远比直接使用Ant的XML格式更加容易和
强大.你甚至可以将Gradle仅仅作为一个强大的Ant脚本工具.
Ant可以分为两层.第一层是Ant语言.它给build.xml文件,处理目标,像macrodefs的特殊构
造等提供语法支持.换句话说,除了Ant任务和类型,其余一切都支持.Gradle了解这种语言,
并允许用户直接导入Ant的build.xml到Gradle的项目下.然后,你可以像Gradle任务一样
直接使用这些Ant构建.
第二层是Ant丰富的任务和类型,如javac,copy或jar.Gradle提供了基于Groovy的集成以
及梦幻般的AntBuilder.
最后,由于构建脚本是Groovy脚本,你总是可以执行一个Ant构建作为一个外部进程.构建
脚本可能会含有类似的语句:“antcleancompile”.execute().
你可以使用Gradle的Ant集成,作为迁移路径将构建Ant迁移到Gradle.例如,你可以从通
过导入现有的Ant构建开始,然后将你的依赖声明从Ant脚本迁移到你的build文件.最后,你
可以将任务迁移到你的构建文件,或者用一些Gradle插件代替他们.随着时间的推移,这部分
工作会一步一步地完成,并且你可以在整个进程中有一个运行良好的Gradle构建.
[7]
使用Ant插件
166
16.1.使用Ant任务和Ant类型的构建
在构建脚本中,Ant属性是由Gradle提供的.这是一个用于参考的AntBuilder实例.AntBuilder
用于从构建脚本访问Ant任务,类型和属性.下面的例子解释了从Ant的build.xml格式到
Grooy的映射.
你可以通过调用AntBuilder实例的方法执行Ant任务.你可以使用任务名称作为方法名,比如,
可以通过调用anto.echo()任务执行Ant echo任务.Ant任务属性通过Map参数传递给方
法.下面是一个echo任务的例子,注意我们也可以混合使用Groovy代码和Ant任务标记,这
点个功能非常强大.
例16.1.使用Ant任务
build.gradle
taskhello<<{
Stringgreeting='hellofromAnt'
ant.echo(message:greeting)
}
gradlehello的输出
>\>gradlehello
>:hello
>[ant:echo]hellofromAnt
>
>BUILDSUCCESSFUL
>
>Totaltime:1secs
你可以添加嵌套元素添加到一个封闭的Ant任务的内部.定义嵌套元素跟定义任务的方式相同,
通过与调用我们要定义的元素名相同的方法.
例16.3.添加嵌套元素到一个Ant任务
build.gradle
使用Ant任务和Ant类型的构建
167
taskzip<<{
ant.zip(destfile:'archive.zip'){
fileset(dir:'src'){
include(name:'**.xml')
exclude(name:'**.java')
}
}
}
你可以像访问任务一样访问Anttype,只需要将type名作为方法名即可.该方法调用返回的
Ant数据类型,可以直接在构建脚本中使用.下面的例子中,我们创建了一个 antpath对象,然
后遍历他的内容.
例16.4.使用Ant类型
build.gradle
tasklist<<{
defpath=ant.path{
fileset(dir:'libs',includes:'*.jar')
}
path.list().each{
printlnit
}
}
更多关于AntBuilder的信息可以参考'GroovyinAction'8.4或者GroovyWiki.
使用Ant任务和Ant类型的构建
168
在构建中使用自定义Ant任务
为了让你的构建可以自定义任务,你可以使用taskdef(通常更容易)或者typedefAnt任务,就
像你在一个 build.xml文件中一样.然后,你可以参考内置Ant任务去定制Ant任务.
例16.5.使用自定义Ant任务
build.gradle
taskcheck<<{
ant.taskdef(resource:'checkstyletask.properties'){
classpath{
fileset(dir:'libs',includes:'*.jar')
}
}
ant.checkstyle(config:'checkstyle.xml'){
fileset(dir:'src')
}
}
你可以使用Gradle的依赖管理去组装你自定义任务所需要的classpath.要做到这一点,你需
要定义一个自定义配置类路径,然后添加一些依赖配置.在Section51.4,“Howtodeclareyour
dependencies”部分有更详细的说明.
例16.6.声明类路径的自定义Ant任务
build.gradle
configurations{
pmd
}
dependencies{
pmdgroup:'pmd',name:'pmd',version:'4.2.5'
}
要使用classpath配置,使用自定义配置的 asPath属性。
例16.7.一起使用自定义Ant任务和依赖管理
build.gradle
在构建中使用自定义Ant任务
169
taskcheck<<{
ant.taskdef(name:'pmd',
classname:'net.sourceforge.pmd.ant.PMDTask',
classpath:configurations.pmd.asPath)
ant.pmd(shortFilenames:'true',
failonruleviolation:'true',
rulesetfiles:file('pmd-rules.xml').toURI().toString()){
formatter(type:'text',toConsole:'true')
fileset(dir:'src')
}
}
在构建中使用自定义Ant任务
170
导入一个Ant构建
你可以在你的gradle项目中通过 ant.importBuild()来导入一个ant构建,当你导入了一个ant构
建,每一个 anttarget都会被视为一个Gradle任务.这意味着你可以像操作,执行gradle任务一
样操作,执行 anttarget
例16.8.导入ant构建
build.gradle
ant.importBuild'build.xml'
build.xml
<project>
<targetname="hello">
<echo>Hello,fromAnt</echo>
</target>
</project>
gradlehello的输出
>\>gradlehello
>:hello
>[ant:echo]Hello,fromAnt
>
>BUILDSUCCESSFUL
>
>Totaltime:1secs
你可以添加一个依赖于 anttarget的任务:例16.9.依赖于anttarget的任务
build.gradle
ant.importBuild'build.xml'
taskintro(dependsOn:hello)<<{
println'Hello,fromGradle'
}
导入一个Ant构建
171
gradleintro的输出>gradleintro:hello[ant:echo]Hello,fromAnt:introHello,from
Gradle
BUILDSUCCESSFUL
Totaltime:1secs
或者,你可以为 anttarget添加动作
例16.10.为Anttarget添加动作
build.gradle
ant.importBuild'build.xml'
hello<<{
println'Hello,fromGradle'
}
gradlehello的输出
>gradlehello:hello[ant:echo]Hello,fromAntHello,fromGradle
BUILDSUCCESSFUL
Totaltime:1secs
当然,一个 anttarget也可以依赖于gradle的任务
例16.11.为Anttarget添加动作
build.gradle
ant.importBuild'build.xml'
taskintro<<{
println'Hello,fromGradle'
}
build.xml
<project>
<targetname="hello"depends="intro">
<echo>Hello,fromAnt</echo>
</target>
</project>
gradlehello的输出
导入一个Ant构建
172
>gradlehello:introHello,fromGradle:hello[ant:echo]Hello,fromAnt
BUILDSUCCESSFUL
Totaltime:1secs
有时候可能需要'重命名'anttarget以避免与现有的gradle任务冲突.需要使
用AntBuilder.importBuilder())方法.
例16.12.重命名导入的anttarget
build.gradle
ant.importBuild('build.xml'){antTargetName->
'a-'+antTargetName
}
build.xml
<project>
<targetname="hello">
<echo>Hello,fromAnt</echo>
</target>
</project>
gradlea-hello的输出
>gradlea-hello:a-hello[ant:echo]Hello,fromAnt
BUILDSUCCESSFUL
Totaltime:1secs
注意,方法的二个参数应该是一个TransFormer,在Groovy编程的时候,由于Groovy的支持自动
闭包单抽象方法的类型。我们可以简单地使用闭包取代匿名内部类,
导入一个Ant构建
173
Ant的属性与引用
有许多方法可以设定Ant属性,可以通过Ant任务使用属性.您可以直接在 AntBuilder的实例设
置属性。Ant的属性也可以作为一个可改变的 Map.也可以使用Ant的任务属性,如下例所示:
例16.13.设置Ant属性
build.gradle
ant.buildDir=buildDir
ant.properties.buildDir=buildDir
ant.properties['buildDir']=buildDir
ant.property(name:'buildDir',location:buildDir)
build.xml
<echo>buildDir=${buildDir}</echo>
许多任务会在执行时设置属性.下面有几种方法来获取属性值,可以直接从 AntBuilder实例获
得属性,如下所示,ant的属性仍然是作为一个map:
例16.14.获取Ant属性
build.xml
<propertyname="antProp"value="apropertydefinedinanAntbuild"/>
build.gradle
printlnant.antProp
printlnant.properties.antProp
printlnant.properties['antProp']
设置一个ant引用的方法:
例16.15.设置一个Ant引用
build.gradle
ant.path(id:'classpath',location:'libs')
ant.references.classpath=ant.path(location:'libs')
ant.references['classpath']=ant.path(location:'libs')
Ant的属性与引用
174
build.xml
<pathrefid="classpath"/>
获取Ant引用的方法:
例16.16.获取一个Ant引用
build.xml
<pathid="antPath"location="libs"/>
build.gradle
printlnant.references.antPath
printlnant.references['antPath']
Ant的属性与引用
175
Logging
Log是构建的主要"UI"工具.如果日志太过冗长,那么真正的警告和问题会隐藏其中,另一方面,
如果你出错了,你又需要搞清楚相关错误信息.Gradle提供了6个等级的log,如表17.1.Logs
Level所示.出了那些你可能经常看到的,还有两个是Gradle特定级别的日志,被称为QUIET和
LIFECYCLE.后者是默认的,并用于报告生成进度.
表17.1.LogsLevel
Level 用途
ERROR 错误信息
QUIET 重要消息信息
WARNING 警告信息
LIFECYCLE 进度消息信息
INFO 信息消息
DEBUG 调试信息
Logging
177
Choosingaloglevel
你可以在命令行中选择如表17.2.Log等级命令行选项所示的选项选择不同的日志级别.如表
17.3.堆栈信息选项中所示的选项来选择堆栈信息.
表17.2.Log等级命令行选项
选项 输出日志等级
nologgingoptions LIFECYCLE及更高
-qor--quiet QUIET及更高
-ior--info INFO及更高
-dor--debug DEBUG及更高(所有日志信息)
表17.3.堆栈信息选项
选项 含义
No
stacktrace
options
无堆栈踪迹输出到控制台的情况下生成错误信息(如编译错误),仅在内部异
常时打印堆栈信息.如果选择DEBUG日志等级,总会打印截断堆栈信息
-sor--
stacktrace
打印截断堆栈信息,我们推荐这个而不是 fullstacktrace,Groovy的full
stacktrace非常详细.(由于底层的动态调用机制。然而,他们通常不包含你
的代码出了什么错的相关信息)
-Sor--
full-
stacktrace
打印全部堆栈信息
选择日志等级
178
编写自己的日志信息
用于记录在你的构建文件的简单方法是将消息写入标准输出.Gradle重定向任何东西写入到标
准输出到它的log系统作为 QUITE级别的log.
例17.1.使用标准输出写入log信息
build.gradle
println'AmessagewhichisloggedatQUIETlevel'
摇篮还提供了一个 logger属性来构建脚本,这是Logger的一个实例.这个接口继承
自SLF4J接口并且加入了一F些Gradle的具体方法.下面是如何在构建脚本中使用此方法的例
子:
例17.2.写入自己的log信息
build.gradle
logger.quiet('Aninfologmessagewhichisalwayslogged.')
logger.error('Anerrorlogmessage.')
logger.warn('Awarninglogmessage.')
logger.lifecycle('Alifecycleinfologmessage.')
logger.info('Aninfologmessage.')
logger.debug('Adebuglogmessage.')
logger.trace('Atracelogmessage.')
你还可以在构建中将其他类直接挂接到Gradle的log系统中(例如 buildSrc目录下的类).只使
用SLF4Jlogger,使用这个 logger的方式与构建脚本提供的 logger方式相同.
例17.3.使用SLF4J写入log信息
build.gradle
importorg.slf4j.Logger
importorg.slf4j.LoggerFactory
Loggerslf4jLogger=LoggerFactory.getLogger('some-logger')
slf4jLogger.info('AninfologmessageloggedusingSLF4j')
编写自己的日志信息
179
从外部工具和库记录日志
在内部,Gradle使用Ant和lvy,都有自己的log系统,Gradle重定向他们的日志输出到
Gradle日志系统.除了Ant/lvy的TRACE级别的日志,映射到Gradle的DEBUG级别,其余的都会
有一个1:1的映射从Ant/lvy的日志等级到Gradle的日志等级.这意味着默认的Gradle日志级
别将不会显示任何的Ant/lvy的输出,除非它是一个错误或警告.
有许多工具仍然使用标准输出记录,默认的,Gradle将标准输出重定向到 QUIET的日志级别和标
准错误的 ERROR级别.该行为是可配置的.该项目对象提供了一个LoggerManager,当你构建脚
本进行评估的时候,允许你改变标准输出或错误重定向的日志级别。
例17.4.配置标准输出捕获
build.gradle
logging.captureStandardOutputLogLevel.INFO
println'AmessagewhichisloggedatINFOlevel'
任务同样提供了LoggingManager去更改任务执行过程中的标准输出或错误日志级别。
例17.5.为任务配置标准输出捕获
build.gradle
tasklogInfo{
logging.captureStandardOutputLogLevel.INFO
doFirst{
println'AtaskmessagewhichisloggedatINFOlevel'
}
}
Gradle同样提供了 JavaUtilLogging,JakartaCommonsLogging和Log4jlogging的集成工
具.
使用这些工具包编写的构建的类的记录的任何日志消息都将被重定向到Gradle的日志记录系
统。
外部工具和库的log
180
改变Gradle的记录内容
你可以用自己的内容取代大部分摇篮的UI记录.你可以这样做,例如,如果你想以某种方式定
制UI,如:记录更多或更少的信息,或更改log的格式.你可以使用Gradle.useLogger())方法替换
日志.可从一个构建脚本或初始化脚本,或通过嵌入API替换.注意,这会完全禁用Gradle的默
认输出.下面的初始化脚本改变任务执行和完成构建后日志的输出.
例17.6.定制Gradlelogs
init.gradle
useLogger(newCustomEventLogger())
classCustomEventLoggerextendsBuildAdapterimplementsTaskExecutionListener{
publicvoidbeforeExecute(Tasktask){
println"[$task.name]"
}
publicvoidafterExecute(Tasktask,TaskStatestate){
println()
}
publicvoidbuildFinished(BuildResultresult){
println'buildcompleted'
if(result.failure!=null){
result.failure.printStackTrace()
}
}
}
gradle-Iinit.gradlebuild的输出
>gradle-Iinit.gradlebuild[compile]compilingsource
[testCompile]compilingtestsource
[test]runningunittests
[build]
buildcompleted
你的 logger可以实现下面列出的任何监听器接口.仅它实现接口被替换,其他接口保持不变。
你可以在Section56.6,“Respondingtothelifecycleinthebuildscript”中找到更多相关信息.
BuildListener
改变Gradle记录的内容
181
TheGradleDaemon
Gradle的守护进程
183
什么是Gradle的守护进程
维基百科中守护进程的解释
守护进程是一个运行后台进程,非交互式用户直接控制的在计算机程序
Gradle守护进程是一个后台进程,它运行着繁重的构建,然后在构建等待下一次构建的之间保
持自身存在.这使得数据和代码在下一次构建前已经准备好,并存入内存中.这显著的提高了后
续构建的性能.启用Gradle守护进程是一种节约构建时间的廉价方式.
强烈建议在所有开发机器上启用Gradle的守护进程.但是不推荐在持续集成和构建服务器环境
下启用守护进程(参见Section18.3,“WhenshouldInotusetheGradleDaemon?”).
Gradle自动管理守护进程.如果构建环境配置为利用后台程序,如果在没有可用守护进程,就会自
动启动一个守护进程,或者使用现有的空闲的兼容守护进程.如果一个守护进程在3个小时内没
有使用,它将会自我终结.一旦配置开发环境为使用的守护进程,守护进程通常是隐形的,容易被
遗忘的.
什么是Gradle的守护进程
184
管理和配置
管理和配置
185
如何启动Gradle的守护进程
在使用Gradle命令行接口时,--daemon和--no-daemon命令行选项调用在单个构建时选择启用
或禁用后台守护进程.通常,允许后台守护进程在一个环境中(例如一个用户账户)更为方便,可以
使所有构建使用守护进程,而不需要记住 --daemon开关.
有两种推荐的方式使守护进程持续与环境:
1. 通过环境变量-给GRADLE_OPTS环境变量添加 -Dorg.gradle.daemon=true标识
2. 通过属性文件-给<<GRADLE_USER_HOME>>/gradle.properties文件添
加org.gradle.daemon=true
注意:<<GRADLE_USER_HOME>>默认为 <<USER_HOME>>/.gradle,<<USER_HOME>>为当前用户home目
录,这个位置可以通过 -g和-gradle-user-home命令行选项,以及由 GRADLE_USER_HOME环
境变量 org.gradle.user.homeJVM系统属性配置。
这两种方法有同样的效果,使用哪一个是由个人喜好.大多数Gradle用户选择第二个方式,给
gradle.properties并添加条目.
在Windows中,该命令将使当前用户启用守护:
(ifnotexist"%HOMEPATH%/.gradle"mkdir"%HOMEPATH%/.gradle")&&(echofoo>>
"%HOMEPATH%/.gradle/gradle.properties")
在类Unix操作系统,以下的Bashshell命令将使当前用户启用守护进程:
touch~/.gradle/gradle.properties&&echo"org.gradle.daemon=true">>
~/.gradle/gradle.properties
一旦以这种方式在构建环境中启用了守护进程,所有的构建将隐含一个守护进程.
如何启用的摇篮守护进程
186
如何禁用Gradle的守护进程
一般Gradle守护进程默认不启用.然而,一旦它被启用,有事希望对某些项目或某些构建禁用守护
进程.
--no-daemon命令行选项可用于强制守护进程不能用于该构建.这很少使用,但是在调试具有一
定的构建或Gradle插件问题时,有时会很有用.在构建环境中,此命令行选项具有最高优先级.
如何禁用Gradle的守护进程
187
怎样抑制“pleaseconsiderusingtheGradle
Daemon”消息
Gradle可能会在构建结束时发出建议您使用Gradle守护进程的末尾警告.为了避免这个警告,您
可以通过上述的这些方法使用守护进程,或者明确禁用守护进程.您可以通过上述的 --no
daemon的命令行选项明确禁用守护进程,或使用上述的 org.gradle.deamon的值设置
为false代替 trie.
因为不建议在持续集成构建中使用守护进程,如果 CI环境变量已存在,Gradle不会发出此消息.
怎样抑制“pleaseconsiderusingtheGradleDaemon”消息
188
为什么会在机器上出现不只一个守护进程
有几个原因Gradle会创建一个新的守护进程代替使用一个已存在的守护进程.如果守护进程没
有闲置,兼容,则会启动一个新的守护进程.
空闲的守护进程是当前未执行构建或做其他有用的工作.
兼容的守护进程是一个可以(或者可以达到)满足要求的编译环境的要求。Java安装程序运
行的构建是构建环境方面的一个例子。构建运行时所需的JVM系统属性是另一个例子。
一个已经运行的Java进程可能不能满足所需的构建环境的某些方面。如果守护进程由Java7启
动,但要求的环境要求为Java8,则守护进程是不兼容的,必须另外启动。再者,在运行的
JVM不能改变一个运行时的某些性能。如内存分配(如-Xmx1024m),默认文本编码运行的
JVM中,默认的语言环境,等等一个JVM不能改变的运行环境。
"Requiredbuildenvironment"通常在构建客户端(如Gradle命令行,IDE等)方面隐含构建环境,并
明确通过命令行选项设置.参见Chapter20,TheBuildEnvironment有关如何指定和控制构建环
境的详细信息.
一下JVM系统属性是有效不变的.如果需求编译环境需要这些属性,不同的守护进程JVM在下列
属性中有不同的值时,守护进程不兼容.
file.encoding
user.language
user.country
user.variant
com.sun.management.jmxremote
下列JVM属性,通过启动参数控制,也是有效不变的.在需求构建环境和守护进程环境的对应属性
必须按顺序完全匹配,才可兼容.
最大堆大小(即-XmxJVM参数)
最小堆大小(即-XmsJVM参数)
引导类路径(即-XbootclasspathJVM参数)
"assertion"状态(即-ea参数)
所需的Gradle版本是需求构建环境的另一个方面.守护进程被耦合到特定Gradle运行时,多个正
在运行的守护进程产生的原因是使用使用不同版本的Gradle会在会话过程中处理多个项目.
为什么会在机器上出现不只一个守护进程
189
如何停止守护进程
守护进程会在闲置3小时后自动终止.如果想在这之前停止守护进程,也可以通过操作系统运
行gradle--stop命令终止后台进程.--stop选项会要求所有运行相同版本的守护进程终止.
如何停止守护进程
191
守护进程何时会出错
许多工程设计已经加入守护进程使得守护进程在日常的开发中变得更加健壮,透明和不起眼.无
论如何,守护进程偶尔也会损坏或者枯竭.一个Gradle构建执行源自多个来源的任意代码.即使
Gradle本身与守护进程的设计是经过严格的测试的,但是用户的构建脚本,或第三方插件可以通
过诸如内存泄露,或腐化全局声明等缺陷来动摇守护进程.
另外,也可以通过构建时进行不正确的资源释放,也可能会动摇守护进程(构建环境正常).在在
MicrosoftWindows下是一个特别尖锐的问题,在程序读写文件后关闭失败的处理是非常随意
的.
如果出现守护进程不稳定情况,可以简单的终止.回顾一下 --no-daemon的选项可以用于构建阻
止使用守护进程,这对于检验一个问题的罪魁祸首是不是守护进程很有帮助.
守护进程何时会出错
192
什么时候不使用Gradle守护进程
建议在开发环境中使用Gradle的守护进程,不建议在持续集成环境和构建服务器环境中使用守
护进程.
守护进程可以更快的构建,这对于一个正坐在椅子前构建项目的人来说非常重要.对于CI构建来
说,稳定性和可预见性是最重要的.为每个构建运行时用一个新的,完全孤立于以前的版本的程
序,更加可靠。
什么时候使用Gradle守护进程
193
摇篮守护进程如何使构建速度更快
Gradle守护进程是一个常驻构建进程.在两个构建之间的空闲期间会等待着下次构建.与每个构
建加载Gradle到内存相比,对于多个构建只需要加载一次Gradle到内存具有明显的好处.这本身
就是对性能的显著优化,但是不止这些.
现代JVM的显著优化是运行时代码优化.例如,热点(HotSpot)(由Oracle提供并作为OpenJDK的
基础的JVM实现)适用于优化运行时代码.优化是渐进的,而不是瞬间的。也就是说,代码在运行
期间逐步优化,这意味着后续版本纯粹是基于这个优化过程变得更快.HotSpot实验表明,它需要
5至10某处构建以优化至稳定.在一个守护进程的第一个构建和第十之间感知的编译时间的差
异可以说是相当巨大的.
守护程序还允许更有效地在内存中缓存整个构建。例如,需要构建(如插件,构建脚本)的
类可以在内存中举行的构建。同样,摇篮可保持在内存中缓存的构建数据的诸如的任务输入
和输出的哈希值,用于增量构建。
摇篮守护进程如何使构建速度更快
195
未来可能的改进
目前,守护使构建速度更快有效地支持在内存中缓存和由JVM优化使代码更快。在未来的
Gradle版本中,守护进程将变得更加聪明,预先完成工作。它可能,例如,在编辑完构建脚
本后就开始下载依赖生成是将要运行的假设下后立即和新改变或添加的依赖性是必需的。
有许多方式使得在未来的版本的gradle的gradle守护进程。
未来可能的改进
196
插件的作用是什么
应用插件到项目允许插件来扩展项目的能力。它可以做的事情,如:
扩展摇篮模型(如:添加可配置新的DSL元素)
按照惯例配置项目(如:添加新的任务或配置合理的默认值)
应用特定的配置(如:增加组织库或执行标准)
通过应用插件,而不是向项目构建脚本添加逻辑,我们可以收获很多好处.应用插件:
促进重用和减少维护在多个项目类似的逻辑的开销
允许更高程度的模块化,提高综合性和组织
封装必要的逻辑,并允许构建脚本尽可能是声明性地
插件的作用
198
脚本插件
Example21.1.Applyingascriptplugin
build.gradle
applyfrom:'other.gradle'
脚本插件可以从本地文件系统或在远程位置的脚本中应用.文件系统的位置是相对于项目目录,
而远程脚本位置的是由一个 HTTPURL指定的.多个脚本插件(两种形式之一)可以被应用到给
定的构建。
脚本插件
201
二进制插件
Example21.2.Applyingabinaryplugin
build.gradle
applyplugin:'java'
插件可以使用插件ID应用.插件的id作为给定的插件的唯一标识符.核心插件注册一个可以用作
插件的id的短名称.在上述情况下,我们可以使用简称 java的插件以应用JavaPlugin.社区插件,
一方面会使用一个完全合格的形式的插件id(如com.github.foo.bar),但还是有一些传统的插件
可能仍然使用很短的,不合格的格式.
不使用一个插件的id,插件也可以通过简单地指定类来应用插件:
Example21.3.Applyingabinarypluginbytype
build.gradle
applyplugin:JavaPlugin
在上面的例子中,JavaPlugin是指JavaPlugin,此类不是严格需要导入org.gradle.api.plugins包中
的所有自动导入构建脚本(见:附录E,现有的IDE支持,以及如何没有它应付).此外,这是没
有必要追加的.class以识别一个类常量在Groovy,因为它是在Java中。
二进制插件
202
二进制插件的位置
一个插件是一个简单的实现了插件接口的类.Gradle提供的核心插件作为其分布的一部分,因此,
你需要做的仅仅是应用上述的插件.然而,非核心二进制插件需要到构建类路径才能应用它们.这
可以以多种方式,包括以下方式实现:
定义插件作为构建脚本中内嵌类的声明.
定义插件为在项目中buildSrc目录下的一个源文件.(参见Section62.4,“Buildsourcesin
thebuildSrcproject”)
包含来自外部jar定义的构建脚本依赖插件(参见Section21.4,“Applyingpluginswiththe
buildscriptblock”)
包含插件DSL语言组成的插件门户网站(Section21.5,“Applyingpluginswiththeplugins
DSL”)
欲了解有关自定义插件的跟多信息,参见Chapter61,WritingCustomPlugins
二进制插件
203
使用构建脚本块应用插件
项目可以通过添加向构建脚本中加入插件的类路径然后在应用插件,添加作为外部JAR文件的
二进制插件.外部jar可以使用 buildscrip{}块添加到构建脚本类路径就像Section62.6,
“Externaldependenciesforthebuildscript”中描述的一样.
Example21.4.Applyingapluginwiththebuildscriptblock
build.gradle
buildscript{
repositories{
jcenter()
}
dependencies{
classpath"com.jfrog.bintray.gradle:gradle-bintray-plugin:0.4.1"
}
}
applyplugin:"com.jfrog.bintray"
使用构建脚本块应用插件
204
使用插件的插件DSL
插件DSL正在孵化(incubating)中,请注意,在以后的Gradle版本中,DSL和其它配置可能会
改变.
新的插件DSL提供了更为简洁,方便的方式来声明插件的依赖关系。它的适用于与新的Gradle
PluginPortal,同时提供了方便的核心和社区插件.该插件脚本块配
置PluginDependenciesSpec的实例.
要应用的核心插件,可以使用短名称:
Example21.5.Applyingacoreplugin
build.gradle
plugins{
id'java'
}
要从插件门户应用一个社区插件,必须使用插件的完全限定id:
Example21.6.Applyingacommunityplugin
build.gradle
plugins{
id"com.jfrog.bintray"version"0.4.1"
}
不必要进行进一步的配置,就是说没有必要配置buildscript的类路径,Gradle会从插件门户找到
该插件,并使构建可用.
参见PluginDependenciesSpec查看关于使用插件DSL的更多信息。
使用插件的插件DSL
205
插件DSL的限制
想项目中添加插件的新方法不仅仅是一种更为方便的语法.新的DSL语法处理与老方法有很大
的不同.新的机制允许Gradle更早更快的确定插件.这允许做更智能的事,如:
优化插件类的加载和重用.
允许不同的插件使用不同版本的依赖关系.
为编辑构建脚本提供关于隐含属性和值的详细信息
这要求插件被指定使Gradle在执行之前剩下的构建脚本前可以很容易并且很快的提取它.还要
求插件使用一些静态的定义。
新的插件机制与"传统的"apply()方法有一些关键区别.也有一些限制,其中一些限制是临时的,
随着机制的成熟会逐渐没有,还有一些是方法固有的.
插件DSL的限制
206
约束语法
新插件 {}块不支持任意Groovy代码.被限制的原因是为幂等(每次产生相同的结果)和无副作
用(为了Gradle随时执行的安全).
形式是:
plugins{
id«pluginid»version«pluginversion»
}
«pluginid»和«pluginversion»必须是常量,字面量,字符串.其他语句都是不允许的;他们的
存在会导致编译错误.
插件 {}块也必须在构建脚本的顶部声明.它不能被嵌套在另一个结构(例如,if语句或for循环).
约束语法
207
只能在构建脚本中使用
插件 {}块目前只能在一个项目的构建脚本中使用.他不能在脚本插件,settings.gradle和出
书画脚本中使用.
Gradle的未来版本将删除此限制.
只能在构建脚本中使用
208
StandardGradleplugins
许多包括在Gradle分布的插件。这些在下面列出。
Gradle插件规范
212
语言插件
这些插件添加了可以被编译并在JVM中执行的各种语言的支持
Table22.1.Languageplugins
Plugin
Id 自动应用
协
同
工
作
描述
java java-
base -为项目添加java编译,测试及绑定能力,作为许多Gradle插
件的基础.参见Chapter7,JavaQuickstart
groovy
java,
groovy-
base
-为Groovy项目构建增加支持,参见Chapter9,Groovy
Quickstart
scala
java,
scala-
base
-增加了对Scala项目构建的支持
antlr java - 增加了对使用ANTLR的生成解析器的支持.
语言插件
213
集成插件
这些插件提供的各种运行时的技术的集成.
Table22.3.Integrationplugins
PluginId 自动应用 协同
工作 描述
application java,
distribution -增加了对运行绑定Java项目作为命令行应用的任
务.
ear - java 增加了对构建J2EE应用程序的支持.
jetty war - 在构建中嵌入Jettyweb容器可以部署web应用.参
见Chapter10,WebApplicationQuickstart
maven - java,
war 增加了对发布artifacts到Maven仓库的支持.
sogi java-base java 增加了对构建OSGi支持
war java - 增加了对组装Web应用程序WAR文件的支持.参见
Chapter10,WebApplicationQuickstart
集成插件
215
孵化中的集成插件
这些插件提供的各种运行时的技术的集成.
Table22.4.Incubatingintegrationplugins
PluginId 自动应用 协同
工作 描述
distribution - - 对构建增加对ZIP和TAR的支持
java-library-
distribution
java,
distribution -增加了对建筑ZIP和TAR的一个Java库的支持.
ivy-publish -java,
war
这个插件提供了一个新的DSL支持发布artifacts
ivy存储库,它改善了现有的DSL.
maven-
publish -java,
war
这个插件提供了一个新的DSL支持发布artifacts
Maven仓库,它改善了现有的DSL。
孵化中的集成插件
216
软件开发插件
这些插件在您的软件开发过程中提供帮助.
Table22.5.Softwaredevelopmentplugins
PluginId 自动应用 协同工作 描述
announce - - 消息发布到自己喜欢的平台,如Twitter
或Growl.
build-
announcements announce - 发送本地通知关于有趣的事件在构建生
命周期到你的桌面.
checkstyle java-base - 使用Checksytle对项目的Java源码执行
质量检测,并生成报告.
codenarc groovy-
base -使用CodeNarc对项目的Groovy的源文
件进行质量检测,并生成检测报告
eclipse -java,groovy,
scala
生成EclipseIDE的文件,从而能够以导
入项目到Eclipse.参见Chapter7,Java
Quickstart
eclipse-wtp - ear,war
与eclipse插件一样,生成eclipse
WPT(WebToolsPlatform)配置文件,导
入到Eclipse中war/ear项目应配置与
WTP工作.参见参见Chapter7,Java
Quickstart
findbugs java-base - 使用FindBugs执行项目的Java源文件
质量检测,并生成检测报告
idea - java 生成IntellijIDEAIDE配置文件,从而可
以将项目导入IDEA。
jdepend java-base - 使用JDepend执行项目的源文件质量检
测,并生成检测报告
pmd java-base - 使用PMD执行项目的源文件质量检测,
并生成检测报告
project-report reporting-
base -生成一个包含关于您的Gradle构建有用
信息的报告。
signing base - 添加数字签名档案和artifacts的能力。
sonar -java-base,
java,jacoco
与Sonar代码质量平台整合.由sonar-
runner插件提供
软件开发插件
217
软件开发插件
218
孵化中的软件开发插件
这些插件在您的软件开发过程中提供帮助.
Table22.6.Softwaredevelopmentplugins
PluginId 自动应用 协同工作 描述
build-
dashboard
reporting-
base -生成构建仪表板报告.
build-init wrapper - 对Gradle初始化一个新构建提供支持.将一个
Maven构建转换为Gradle构建
cnuit - - 提供运行CUnit测试支持
jacoco reporting-
base java 对面向Java的JaCoCo代码库整合
sonar-
runner -java-base,
java,jacoco
提供与Sonar代码质量平台的整合.取代
sonar插件
visual-
studio - - 增加了与VisualStudio集成.
wrapper - - 增加一个Wrapper任务来生成Gradle打包文
件.
java-
gradle-
plugin
java - 通过提供标准的插件生成配置和验证,协助
Gradle发展.
孵化中的软件开发插件
219
基础插件
这些插件是形成其他插件的基本构建模块.你可以在你的构建文件中使用它们,在下面李处完整
地列表,然而,注意它们还不是Gradle的公用API的一部分.因此,这些插件未记录在用户指南中.
你可能会参考他们的API文档,详细了解它们.
Table22.7.Baseplugins
base
添加标准的生命周期任务和配置合理的默认归档任务:
增加ConfigurationName任务.这些任务组装指定配置的工件。
增加了上传ConfigurationName任务,这些任务组装并上传指定配置的工件。
对所有归档任务配置合理的默认值(如继承AbstractArchiveTask的任务).如归档类型的任
务:Jar,Tar,Zip.特别的,归档的 destinationDir,baseName和version属性是预先配置的默
认值.这是非常有用的,因为它推动了跨项目的一致性;关于档案的命名规则和完成构建后的
位置的一致性。
java-base
增加资源集的概念到项目中.不添加特定的资源.
groovy-base
增加了Groovy的源集理念到项目中.
scala-base
添加scala源集合概念到项目中.
reporting-base
为项目增加了一些涉及到生产报告的公约性质的属性,
译者注:实在不会使用MarkDown在表格中加入列表,好在只表格只有两列,故本节不能按照
官网的UserGuide表格给出.而是以列表形式完成
基础插件
220
基础插件
221
第三方插件
你可以在GradlePluginssite找到外部插件.
第三方插件
222
第22章Java插件
Java插件给项目增加了编译,测试以及打包的能力,Gradle的许多其他插件都需要以Java插
件为基础.
Java插件
223
用法
要使用Java插件,需要在构建脚本中加入如下内容
例子22.1.使用Java插件
bulid.gradle
applyplugin:'java'
使用
224
资源设置
Java插件引入了资源设置(SourceSet)的概念,资源设置就是一组被编译和执行在一起的源
文件.这些源文件可能包含Java的源文件以及一些资源文件.其他的插件可能还会在资源设置
中包含Groovy和Scala的源文件.资源设置有一个与之关联的关于编译的classpath和有关
运行的classpath.
资源设置的用法之一就是将源文件归档到描述它们目的的各个逻辑组,举个例子,你可以使用
一个资源设置来定义一个集成测试套件也可以使用另外的资源设来定义你项目的API和实现
类.
Java插件定义了两个标准资源设置,分别称为 main和test, main资源设置中包含最终面向
客户的代码,也就是编译和集成为一个Jar文件. test资源设置包括了测试阶段的代码,也就
是使用JUnit或者TestNG编译和执行的代码.它们可以是单元测试,集成测试,验收测试或者
任何对你有用的测试集.
资源集
225
任务
Java插件引入了许多任务到项目当中,具体如下表所示
表22.1java插件-任务
任务
226
任务名 依赖 类型 描述
compileJava
所有产生编译
classpath的任务,包
括编译配置项目的所依
赖的jar文件
JavaCompile
使用javac命令编
译产生java源文
件
processResources - Copy 复制生产资源到生
产class文件目录
classes
compileJava任务和
processResources任
务。有一些插件添加额
外的编译任务
Task 组装生产class文
件目录
compileTestJava
compile任务加上所有
产生测试编译的
classpath的任务
JavaCompile
使用javac编译产
生java测试源文
件
processTestResources - Copy 复制测试资源到测
试class文件目录
testClasses
compileTestJava和
processTestResources
任务。一些插件会添加
额外的测试编译任务
Task 组装测试class文
件目录
jar compile Jar 组装Jar文件
javadoc compile javadoc
使用javadoc命令
为Java源码生产
API文档
test
compile,
compileTest,加上所
有产生testruntime
classp的任务
Test
使用JUnit或者
TestNG进行单元
测试
uploadArchives
在archives配置中产生
信息单元的文件,包括
了jar
Upload
上传信息单元在
archives配置中,
包括Jar文件
clean - Delete 删除项目构建目录
cleanTaskName -Delete
删除指定任务名所
产生的项目构建目
录,CleanJar会删
除jar任务创建的
jar文件,
cleanTest将会删
除由test任务创
建的测试结果
对于添加到项目中的每个资源设置,java插件将会加入以下编译任务
表22.2.java插件-资源设置任务
任务
227
任务名 依赖 类型 描述
compileSourceSetJava 产生资源设置编译classpath
的所有任务 JavaCompile
使用
javac
命令编
译给定
资源设
置的
Java
源文件
processSourceSetResources - Copy
复制给
定资源
设置的
资源到
classes
目录
下。
sourceSetClasses
compileSourceSetJava任务
和
processSourceSetResources
任务。一些插件给资源设置添
加额外的编译工作。
Task
组装资
源设置
的class
目录
Java插件同时也增加了一些为项目生命周期服务的任务
表22.3.java插件-生命周期任务
任务
228
任务名 依赖 类型 描述
assemble
项目中的所有归档任务,包括
jar任务。一些插件给项目增加
的额外归档任务
Task 组装项目的所
有档案
check
项目中的所有验证任务,包括
test任务。一些插件给项目增
加的额外验证任务
Task 执行项目中的
所有验证任务
build assemble任务和check任务 Task
构
建
完
整
地
项
目
buildNeeded
build任务和buildNeeded任务
的testRuntime任务配置的所有
项目的依赖库
Task
构建完整地项
目并且构建该
项目依赖的所
有项目
buildDependents
buildandbuildDependents
tasksinallprojectswitha
projectlibdependencyonthis
projectinatestRuntime
configuration.
Task
构建完整项目
并且构建所有
依赖该项目的
项目
buildConfigName 产生由ConfigName配置的信
息单元的任务。 Task
根据指定的配
置组装信息单
元。这个任务
是由Java插
件隐式添加的
基础插件添加
的。
uploadConfigName 上传由ConfigName配置的信
息单元的任务。 Upload
根据指定的配
置组装并上传
信息单元。
。这个任务是由Java插件隐式添加的基础插件添加的。
下图显示了这些任务之间的关系
图22.1.java插件-任务
任务
229
任务
230
项目布局
Java插件的默认布局如下图所示,无论这些文件夹中有没有内容,Java插件都会编译里面的内
容,并处理任何缺失的内容.
表22.4.java插件-默认布局
目录|含义--—|---src/main/java|主要Java源码src/main/resources|主要资源
src/test/java|测试Java源码src/test/resources|测试资源src/sourceSet/java|指定资源设
置的Java源码src/sourceSet/resources|指定资源设置的资源
1.改变项目布局
可以通过配置适当的资源设置来配置项目布局,更多细节会在后面的章节中讨论,下面是一个
配置了java和resource的简单的例子
例22.2.自定义Java源码布局
build.gradle
sourceSet{
main{
java{
srcDir'src/java'
}
resources{
srcDir'src/resources'
}
}
}
项目布局
231
依赖管理
Java插件给项目增加了许多关于依赖的配置,如下所示,这些配置被分配给许多任务,比如
compileJava和test等配置
表22.5.Java插件-依赖配置
名称 扩展 被使用时运行的
任务 含义
compile - compileJava 编译时的依赖
runtime compile - 运行时的依赖
testCompile compile compileTestJava 编译测试所需的额外依赖
testRuntime runtime test 仅供运行测试的额外依赖
archives - uploadArchives 项目产生的信息单元(如:jar包)
default runtime - 使用其他项目的默认依赖项,包括该项
目产生的信息单元以及依赖
图22.2.Java插件-依赖配置
对于每个添加到该项目的资源设置,java插件会添加以下的依赖配置
表22.6.Java插件-资源设置依赖关系配置
名称 扩展 被使用时运行的任务 含义
sourceSetCompile - compileSourceSetJava 编译时给定资源设置的依赖
sourceSetRuntime - - 运行时给定资源设置的依赖
依赖管理
232
公共属性
Java插件会为项目添加一系列的公共属性,如下所示,你可以在构建脚本中像项目属性那样直
接使用它们(see???).
表22.7.Java插件-目录属性
属性名称 类型 默认值 描述
reportsDirName String reports
在构建目录
的生成报告
的文件夹名
reportsDir
File
(read-
only)
buildDir/reportsDirName 该目录下会
生成报告
testResultsDirName String test-results
在构建目录
的测试结果
的result.xml
的存放目录
名
testResultsDir
File
(read-
only)
buildDir/testResultsDirName
测试结果的
result.xml
文件会存放
在该文件夹
中
testReportDirName String tests
在构建目录
的测试报告
的文件夹名
testReportDir
File
(read-
only)
reportsDir/testReportDirName
测试的测试
报告会存放
在该目录下
libsDirName String libs
在构建目录
下的类库文
件夹名
libsDir
File
(read-
only)
buildDir/libsDirName 该目录下存
放类库
distsDirName String distributions
在构建目录
下的
distributions
文件夹名
distsDir
File
(read- buildDir/distsDirName
该目录下存
放生成的
公共配置
233
only) distributions
docsDirName String 在构建目录下的doc文件夹名
docsDir
File
(read-
only)
buildDir/docsDirName
该目录下存
放生成的文
档
dependencyCacheDirName String dependency-cache
在构建目录
下的依赖缓
存文件夹名
dependencyCacheDir
File
(read-
only)
buildDir/dependencyCacheDirName
该目录用来
缓存源依赖
信息。
表22.8.Java插件-其他配置
属性名称 类型 默认值 描述
sourceSets SourceSetContainer Notnull
包含项
目的资
源设置
sourceCompatibility
JavaVersion.也可以使用String
类型或Number类型,如'1.5'或
1.5
当前使用的JVM版
本
编译
Java源
码时所
使用的
Java兼
容版本
targetCompatibility
JavaVersion.也可以使用String
类型或Number类型,如'1.5'或
1.5
sourceCompatibility
生成
class
文件的
Java版
本
archivesBaseName String projectName
用
于.jar
文件或
者.zip
存档的
基本名
称
manifest Mainfest anemptymanifest
该清单
中包括
所有的
JAR文
件
按照JavaPluginConvention和BasePluginConvention类型提供这些属性.
公共配置
234
公共配置
235
使用资源设置
你可以通过sourceSets属性来使用资源设置.它其实是一个项目资源设置的容器,它的类型是
SourceSetContainer.同时也有sourceSets{}脚本模块,在这个模块里,你可以通过闭包配置
资源设置容器.资源设置容器的工作方式和其余容器几乎一模一样,比如tasks.
例22.3.进入资源设置
build.gradle
//Variouswaystoaccessthemainsourceset
printlnsourceSets.main.output.classesDir
printlnsourceSets['main'].output.classesDir
sourceSets{
printlnmain.output.classesDir
}
sourceSets{
main{
printlnoutput.classesDir
}
}
//Iterateoverthesourcesets
sourceSets.all{
printlnname
}
为了配置一个已经存在的资源集,你只需要使用上面提及的访问方法来设置资源集的属性.下
面就是一个配置Java资源目录的例子:
例22.4.配置资源集的源目录
build.gradle
sourceSets{
main{
java{
srcDir'src/java'
}
resources{
srcDir'src/resources'
}
}
}
使用资源集工作
236
使用资源集工作
237
22.7.1.SourceSet属性
下表列出了SourceSet的一些重要属性,更多细节请查看SourceSet的API文档.
表22.9.java插件-SourceSet属性
配置名称 类型 默认值
name String(read-only) Notnull
用来识别
source
set的名称
output SourceSetOutput(read-
only) Notnull
source
set的输出
文件,包含
其编译的
classes和
resources
output.classesDir File buildDir/classes/name
在该目录
下生成存
放这个
source
set的
classes文
件
output.resourcesDir File buildDir/resources/name
在该目录
下生成存
放这个
source
set的
resources
文件
compileClasspath FileCollection compileSourceSet
configuration
这个
source
set编译时
使用的
classpath
runtimeClasspath FileCollection output+runtimeSourceSet
configuration
执行当前
source
set的
classes文
件时的
classpath
SourceDirectorySet(read-
当前
source
set的java
源文件,仅
包含存在
资源集属性
238
java SourceDirectorySet(read-
only)
Notnull 于java目
录下的所
有.java文
件,排除其
他任何文
件.
java.srcDirs
Set.可以设置为在Section
15.5,“Specifyingasetof
inputfiles”中描述的任何
值
[projectDir/src/name/java]
该source
set的包含
java源文
件的目录
resources SourceDirectorySet(read-
only) Notnull
该source
set的资
源,只包含
存在于
resource
目录吓得
资源文件,
会排除在
resource
下的所
有.java文
件,其他插
件,如
Groovy插
件会在该
集合中排
除一些其
他的文件.
resources.srcDirs
Set.可以设置为在Section
15.5,“Specifyingasetof
inputfiles”中描述的任何
值
[projectDir/src/name/resources]
该source
set的包含
资源文件
的目录
allJava SourceDirectorySet(read-
only) java
该source
set的所
有.java文
件。一些
插件,如
Groovy插
件,添加
额外的
Java源文
件到这个
集合。
该source
set的所有
源文件。
这包括所
有的资源
文件和所
资源集属性
239
22.7.2.定义一个新的sourceset
要定义一个新的源组,sourceSets{}块中引用它.下面是一个例子:
例22.5.定义一个新的sourceset
build.gradle
sourceSets{
intTest
}
当你定义一个新的sourceset,java插件会为该sourceset添加一些如Table22.6,"Java
plugin-sourcesetdependencyconfigurations"中所示的依赖配置关系.可以使用这些配置来
定义sourceset的编译和运行时依赖。
例22.6.定义sourceset的依赖
build.gradle
sourceSets{
intTest
}
dependencies{
intTestCompile'junit:junit:4.12'
intTestRuntime'org.ow2.asm:asm-all:4.0'
}
java插件增加了一些如Table22.2,"Javaplugin-sourcesettasks"为该sourceset组装
classes文件的任务,例如,对于一个叫intTest的sourceset,为此sourceset编译classes任务
运行 gradleintTestClasses完成。
例22.7.编译一个sourceset
gradleintTestClasses命令的输出
>gradleintTestClasses
:compileIntTestJava
:processIntTestResources
:intTestClasses
BUILDSUCCESSFUL
Totaltime:1secs
定义新的资源集
241
定义新的资源集
242
22.7.3.一些sourceset的例子
加入含有类文件的sorceset的JAR:
例22.8.为sourceset组装JAR
build.gradle
taskintTestJar(type:Jar){
fromsourceSets.intTest.output
}
为sourceset生成javadoc:
例22.9.为sourceset生成javadoc
build.gradle
taskintTestJavadoc(type:Javadoc){
sourcesourceSets.intTest.allJava
}
为sourceset添加一个测试套件运行测试:
例22.8.sourceset运行测试
build.gradle
taskintTest(type:Test){
testClassesDir=sourceSets.intTest.output.classesDir
classpath=sourceSets.intTest.runtimeClasspath
}
资源集例子
243
22.8.Javadoc
Javadoctask是Javadoc的一个实例.它支持Javadoc的核心选项和可执行的Javadoc的
referencedocumentation中描述的标准JavaTOC的选项.有关支持Javadoc选项的完整列
表,请参阅以下类的API文档:CoreJavadocOptions和StandardJavadocDocletOptions.
表22.10.java插件-javadoc配置
任务属性 类型 默认值
classpath FileCollection sourceSets.main.output+
sourceSets.main.compileClasspath
source
FileTree.可以设置为在Section
15.5,“Specifyingasetofinput
files”中描述的任何值
sourceSets.main.allJava
destinationDir File docsDir/javadoc
title String project的name和version
Javadoc
244
22.10.资源
Java插件使用Copy任务处理资源.它为项目每个sourceset都增加了一个实例.可以参
考Section15.6,"Copyingfiles"获取关于copy任务的信息.
表22.12.java插件-ProcessResources的属性
任务属性 类型 默认值
srcDirs
Object.可以在Section15.5,
“Specifyingasetofinputfiles”中查
看使用什么
sourceSet.resources
destinationDir File.可以再Section15.1,“Locating
files”查看使用什么 sourceSet.output.resourcesDir
资源
246
22.11.编译java
java插件为项目的每一个sourceset增加了一个JavaCompile实例,最常见的配置选项如下
所示:
表22.13.java插件-编译配置
任务属性 类型 默认值
classpath FileCollection sourceSet.compileClasspath
source FileTree,可以在Section15.6,
“Copyingfiles”中查看可以设置什么.sourceSet.java
destinationDir File. sourceSet.output.classesDir
默认情况下java的编译运行在Gradle中的进程.设置option.fork为true会使编译在一个单
独的进程中运行,在Ant中运行javac任务意味着一个新进程将被拆封为多个编译任务,这会减
慢编译。相反的,Gradle的直接编译集成(见上文)在编译过程中将尽可能地重复使用相同的
进程.在所有情况下由options.forkOptions指定的选项会被实现.
编译Java
247
22.12.增量Java编译
从Gradle2.1开始,可以使用Java增量编译,此功能正在孵化,参见JavaCompile如何启用这个功
能.增量编译的主要目标如下:
避免在没必要编译的java编译资源上浪费时间.这意味着更快构建,尤其是在改变一些class
与jar的时候,不需要再次编译那些不依赖这些class与jar的文件.
尽可能地少输出class.类不需要重新编译意味着保持输出目录不变。一个示例场景中,真
正使用JRebel的真正有用的是-越少的输出类被改变,JVM可以使用越快刷新。
更高级的增量编译:
检测陈旧类的设置是否正确是以牺牲速度为代价的,该算法分析字节码并与编译器直接交
互(非私有常量内联),依赖传递等.举个例子:当一个类的公共常量改变后,我们希望避免由编
译器编译内联常数产生的问题,我们将调整算法和缓存以便增量Java编译可以是每编译任
务的默认设置。
为了使增量编译快,我们缓存会分析class的结果和jar快照。最初的增量编译应该会慢于
coldcaches.
增量Java编译
248
22.13.1.执行测试
测试从main构建过程中分离出来的,运行在一个单独的JVM中执行.Test任务允许控制这些如何
发生.有许多属性用于控制测试过程如何启动.这包括使用诸如系统属性,JVM参数和Java可
执行文件。
可以指定是否要并行执行测试.Gradle通过同时运行多个测试进程提供并行执行测试.每个测试
进程在同一时间只能执行一个测试,为了充分利用这一特性,一般不需要为tests任务做什么特别
的设置,maxParallelForks属性指定测试进程在同一时间运行的最大进程数.默认值是1,意味着
不执行并行测试.
测试过程中设置org.gradle.test.worker系统属性为该测试过程的唯一标识符,例如,在文件名
或其他资源标识符的唯一标识符。
你可以指定一些测试任务在已执行了一定数量的测试后重新运行.这可能是一个非常好的方式
替代测试进程中的大量的堆.forkEvery属性指定测试类的在测试过程执行的最大数目。默认的
是执行在各测设进程中不限数量的测试。
该任务有一个ignoreFailures属性来控制在测试失败时的行为。测试任务总是执行每一个检测
试验.它停止构建之后,如果ignoreFailures是false,说明有失败的测试。ignoreFailures的默
认值是false。
testLogging属性允许你配置哪个测试事件将被记录,并设置其log等级。默认情况下,将记录
每一个失败的测试简明消息。详见TestLoggingContainer如何按需求调整测试记录。
测试执行
250
22.13.3.测试过滤
从Gradle1.10开始,可以根据测试任务名进行特点的任务测试,过滤与在构建脚本的段落中引入/
排除测试任务(-Dtest.single,test.includeandfriends)是两种不同的机制.后者是基于文件,如测
试实现类的物理位置.选择文件级的测试会不支持那些被测试等级过滤掉的一些有趣的测试脚
本.下面的这些有些已经被实现,有些是将来会实现的:
过滤特定等级的试验方法;执行单个测试方法
通配符"*"支持任意字符匹配
命令行选项"--tests"用以设置测试过滤器.对经典的'执行单一测试方法'用例尤其有用.当使
用命令行选项的时候,在构建脚本中声明的包含过滤器被忽略。
Gradle过滤测试框架API的限制.一些高级的综合性测试无法完全兼容测试过滤,但是,绝大
多是测试和用例可以被熟练地处理.
测试过滤取代基于选择文件的测试,后者在未来可能会完全被取代.我们会完善测试过滤的
API,并增加不同的过滤器
例22.11.过滤测试的构建脚本吗build.gradle
test{
filter{
//包括在测试的任何具体方法
includeTestsMatching"*UiCheck"
//包括所有包种的测试
includeTestsMatching"org.gradle.internal.*"
//包括所有的集成测试
includeTestsMatching"*IntegTest"
}
}
要了解更多详细信息和示例,请参见TestFilter。使用命令行选项的一些例子:
gradletest--testsorg.gradle.SomeTest.someSpecificFeature
gradletest--tests*SomeTest.someSpecificFeature
gradletest--tests*SomeSpecificTest
gradletest--testsall.in.specific.package*
gradletest--tests*IntegTest
gradletest--tests*IntegTest*ui*
gradlesomeTestTask--tests*UiTestsomeOtherTestTask--tests*WebTest*ui
测试过滤
252
22.13.4.通过系统属性执行单独测试
如上所述该机制已经被测试过滤取代
译者注:被取代的东西就先不翻译了
SettingasystempropertyoftaskName.single=testNamePatternwillonlyexecuteteststhat
matchthespecifiedtestNamePattern.ThetaskNamecanbeafullmulti-projectpathlike
“:sub1:sub2:test”orjustthetaskname.ThetestNamePatternwillbeusedtoformaninclude
patternof“**/testNamePattern*.class”;.Ifnotestswiththispatterncanbefoundan
exceptionisthrown.Thisistoshieldyoufromfalsesecurity.Iftestsofmorethanone
subprojectareexecuted,thepatternisappliedtoeachsubproject.Anexceptionisthrownif
notestscanbefoundforaparticularsubproject.Insuchacaseyoucanusethepath
notationofthepattern,sothatthepatternisappliedonlytothetesttaskofaspecific
subproject.Alternativelyyoucanspecifythefullyqualifiedtasknametobeexecuted.You
canalsospecifymultiplepatterns.Examples:
gradle-Dtest.single=ThisUniquelyNamedTesttest
gradle-Dtest.single=a/b/test
gradle-DintegTest.single=*IntegrationTestintegTest
gradle-Dtest.single=:proj1:test:Customerbuild
gradle-DintegTest.single=c/d/:proj1:integTest
通过系统属性执行单独测试
253
22.13.5.测试检测
测试任务检测哪些类是通过检查编译测试类的测试类。默认情况下它会扫描所有.calss文件.可
以自定义包含/排除哪些类需不要要被扫描.所使用不同的测试框架(JUnit/TestNG)时测试类
检测使用不同的标准。当使用JUnit,我们扫描的JUnit3和JUnit4的测试类。如果任一下列条
件匹配,类被认为是一个JUnit测试类:
类或父类集成自TestCase或GroovyTestCase
类或父类有@RunWith注解
类或者父类中的方法有@Test注解
当使用TestNG的,我们扫描注解了@Test的方法。
需要注意的是抽象类不执行。Gradle还扫描了继承树插入测试classpath中的jar文件。
如果你不想使用测试类的检测,可以通过设置scanForTestClasses为false禁用它。这将使得
测试任务只使用包含/排除找到测试类。如果scanForTestClasses是false而且额并没有包含/排
除指定模式,"**/*Tests.class","**/*Test.class",与"**/*Abstract*.class"分别为包含/排除的默认值.
测试检测
254
22.13.6.测试分组
JUnit和TestNG允许为测试方法精密分组.
对于分组JUnit的测试类与测试方法,JUnit4.8引入了类别的概念.该测试任务允许您设定JUnit
包括或者排除某些类的规范。
例22.12.JUnit分类build.gradle
test{
useJUnit{
includeCategories'org.gradle.junit.CategoryA'
excludeCategories'org.gradle.junit.CategoryB'
}
}
TestNG的框架有一个非常类似的概念。在TestNG的,你可以指定不同的测试组。测试分
组应包括或排除在测试任务进行配置了的测试执行.
例22.13.TestNG分组测试build.gradle
test{
useTestNG{
excludeGroups'integrationTests'
includeGroups'unitTests'
}
}
9
[10]
测试分组
255
22.13.7.测试报告
测试任务默认生成以下结果.
一份HTML测试报告
一个与Ant的JUnit测试报告任务兼容的XML.这个格式与许多其他服务兼容,如CIserves
结果是有效的二进制,测试任务会从这些二进制结果生成其他结果。有一个独立的
TestReport任务类型会根据一些Test任务实例生成的二进制源码生成一个HTML报告.使用
这种测试类型,需要定义一个destinationDir,里面包括测试结果的报告.下面是一个示例,它
产生一个从子项目的单元测试组合而成的报告:例22.14.创建单元测试报告子项目
build.gradle```subprojects{applyplugin:'java'
//Disablethetestreportfortheindividualtesttasktest{
reports.html.enabled=false
}}
tasktestReport(type:TestReport){destinationDir=file("$buildDir/reports/allTests")//Include
theresultsfromthe testtaskinallsubprojectsreportOnsubprojects*.test}```应该注意的
是,TestReport型组合来自多个测试任务的结果,需要聚集个别测试类的结果。这意味着,如
果一个给定的测试类是由多个测试任务执行时,测试报告将会包括那些类,但是很难区分该输
出结果分别是出自哪个类.
测试报告
256
22.14.1.Manifest
每个jar或war对象有一个manifest属性做为Manifest单独的实例,当生成存档,一个对应
MANIFEST.MF文件被写入到档案中.
例22.15.MANIFEST.MF的定制
build.gradle
jar{
manifest{
attributes("Implementation-Title":"Gradle",
"Implementation-Version":version)
}
}
你可以创建一个manifest的独立实例.您可以使用如共享jar之间的manifest的信息.
例22.16.创建一个manifest对象
build.gradle
ext.sharedManifest=manifest{
attributes("Implementation-Title":"Gradle",
"Implementation-Version":version)
}
taskfooJar(type:Jar){
manifest=project.manifest{
fromsharedManifest
}
}
您可以合并其他manifest到任何Manifest对象.其它清单可能是通过文件路径描述或着像上
所述,引用另一个Manifest对象.
例22.17.独立的MANIFEST.MF一个特定的归档
build.gradle
Manifest
260
taskbarJar(type:Jar){
manifest{
attributeskey1:'value1'
fromsharedManifest,'src/config/basemanifest.txt'
from('src/config/javabasemanifest.txt',
'src/config/libbasemanifest.txt'){
eachEntry{details->
if(details.baseValue!=details.mergeValue){
details.value=baseValue
}
if(details.key=='foo'){
details.exclude()
}
}
}
}
}
清单合并的顺序与声明语句的顺序相同,如果基本清单和合并的清单都为相同的密钥定义值,
那么那么合并清单将会被合并,您可以通过添加在其中您可以使用一个ManifestMergeDetails
实例为每个条目实体完全自定义的合并行为。声明不会立即被来自触发合并。这是延迟执行
的,要么产生jar时,或要求写入effectiveManifest时.你可以很容易地写一个清单到磁盘。例
22.17.独立的MANIFEST.MF一个特定的存档build.gradle
jar.manifest.writeTo("$buildDir/mymanifest.mf")
Manifest
261
War插件(未完成)
WAR插件扩展了Java插件,支持web应用组装成War文件.它默认禁用了Java插件JAR归档任
务,并增加了一个默认的WAR归档任务。
25.1.使用
使用war插件需要在构建脚本下包括以下内容
例25.1.使用war插件
build.gradle
applyplugin'war'
25.2.任务
War插件会添加下列任务到项目.
表25.1.War插件-任务
任务名 依赖 类型 描述
war compile War 组装应用程序War文件
War插件由Java插件添加下列依赖任务.
表25.2.War插件-附加的依赖任务
任务名 依赖
assemble war
图25.1.War插件-任务
25.3.项目布局
表25.3.War插件-项目布局文件夹|含义--------|------src/main/webapp|Web应用资源
War插件
263
25.4.依赖管理
War插件增加了名为providedCompile和providedRuntime的两个依赖配置.这两个配置有相同
的作用域在编译或者运行时的配置,不同之处在于是否会将war文件归档.很重要的一点是它们
都会提供配置传递.比如在任意的provided配置中添加了 commons-httpclient:commons-
httpclient:3.0,该依赖依赖于 commons-codec,因为这个一个"provided"的配置,意味着这两个依
赖都不会被加入你的WAR中,即使 commons-codec库是一个显式的编译配置.如果不希望出现这
种传递行为,commons-httpclient:commons-httpclient:3.0@jar这样声明provided依赖即可.
25.5.公共配置
表25.4.War插件-目录配置
属性名称 类型 默认值 描述
webAppDirName String src/main/webapp 在项目目录的web应
用的资源文件夹名
webAppDir File(read-
only) projectDir/webAppDirName Web应用的资源路径
这些属性由一个WarPluginConvention公共对象提供
25.6.War
War任务默认会把 src/main/webapp的内容复制到归档目录的根目录.webapp文件夹下会包含
一个 WEB-INF子文件夹,里面可能会有一个web.xml文件.编译后的class文件会在 WEB-
INF/classes下,所有runtime 的依赖配置会被拷贝至 WEB-INF/lib下.
API文档中有更多关于War的信息.
25.7.定制War
下面的例子中有一些重要的自定义选项
例25.2.定制War插件
build.gradle
[13]
War插件
264
configuration{
moreLibs
}
respositories{
faltDir{dirs"lib"}
mavenCentral()
}
dependencies{
compilemodule(":compile:1.0"){
dependency":compile-transitive-1.0@jar"
dependency":providedCompile-transitive:1.0@jar"
}
providedCompile"javax.servlet:servlet-api:2.5"
providedCompilemodule(":providedCompile:1.0"){
dependency":providedCompile-transitive:1.0@jar"
}
runtime":runtime:1.0"
providedRuntime":providedRuntime:1.0@jar"
testCompile"junit:junit:4.12"
moreLibs":otherLib:1.0"
}
war{
from'src/rootContent'//增加一个目录到归档根目录
webInf{from'src/additionalWebInf'}//增加一个目录到WEB-INF下
classpathfileTree('additionalLibs')//增加一个目录到WEB-INF/lib下.
classpathconfigurations.moreLibs//增加更多地设置到WEB-INF/lib下.
webXml=file('src/someWeb.xml')//复制xml文件到WEB-INF/web.xml.
}
当然,可以用一个封闭的标签定义一个文件是否存打包到War文件中.
[13]runtime配置将会继承 compile配置.
War插件
265
使用
<<<<<<<HEAD
使用war插件需要在构建脚本下包括以下内容
例25.1.使用war插件
build.gradle
applyplugin'war'
a655fa4be4421004591827ae70fee579703794dd
使用
266
项目布局
表25.3.War插件-项目布局
文件夹 含义
src/main/webapp Web应用资源
项目布局
268
依赖管理
War插件增加了名为providedCompile和providedRuntime的两个依赖配置.这两个配置有相同
的作用域在编译或者运行时的配置,不同之处在于是否会将war文件归档.很重要的一点是它们
都会提供配置传递.比如在任意的provided配置中添加了 commons-httpclient:commons-
httpclient:3.0,该依赖依赖于 commons-codec,因为这个一个"provided"的配置,意味着这两个依
赖都不会被加入你的WAR中,即使 commons-codec库是一个显式的编译配置.如果不希望出现这
种传递行为,commons-httpclient:commons-httpclient:3.0@jar这样声明provided依赖即可.
依赖管理
269
定制War
下面的例子中有一些重要的自定义选项
例25.2.定制War插件
build.gradle
configuration{
moreLibs
}
respositories{
faltDir{dirs"lib"}
mavenCentral()
}
dependencies{
compilemodule(":compile:1.0"){
dependency":compile-transitive-1.0@jar"
dependency":providedCompile-transitive:1.0@jar"
}
providedCompile"javax.servlet:servlet-api:2.5"
providedCompilemodule(":providedCompile:1.0"){
dependency":providedCompile-transitive:1.0@jar"
}
runtime":runtime:1.0"
providedRuntime":providedRuntime:1.0@jar"
testCompile"junit:junit:4.12"
moreLibs":otherLib:1.0"
}
war{
from'src/rootContent'//增加一个目录到归档根目录
webInf{from'src/additionalWebInf'}//增加一个目录到WEB-INF下
classpathfileTree('additionalLibs')//增加一个目录到WEB-INF/lib下.
classpathconfigurations.moreLibs//增加更多地设置到WEB-INF/lib下.
webXml=file('src/someWeb.xml')//复制xml文件到WEB-INF/web.xml.
}
当然,可以用一个封闭的标签定义一个文件是否存打包到War文件中.
定制War
272
Ear插件(未完成)
Ear插件添加了对组装Web应用程序EAR文件的支持。它将添加默认EAR文件生成任务。它
不需要Java插件,对于使用Java插件的项目,它会禁用默认的JAR文件生成。
51.1.使用
使用ear插件需要在构建脚本下包括以下内容
例51.1.使用ear插件
build.gradle
applyplugin'ear'
51.2.任务
War插件会添加下列任务到项目.
表51.2.War插件-任务
任务名 依赖于 类型 描述
ear compile(仅在java插件存在时)Ear 组装应用程序Ear文件
Ear插件由其他已存在的插件添加下列依赖任务.
表51.2.Ear插件-附加的依赖任务
任务名 依赖于
assemble ear
51.3.项目布局
表25.3.Ear插件-项目布局
文件夹 含义
src/main/application Ear资源,比如META-INF目录
Ear插件
273
51.4.依赖管理
Ear插件添加了两个依赖配置:deploy和earlib。deploy中的所有依赖关系都放在EAR存档
的根目录中,并且是不可传递的(nottransitive)。earlib配置中的所有依赖关系都放在EAR
存档中的'lib'目录中,并且是可传递的(transitive)。
51.5.常用配置
表51.4.Ear插件-目录配置
属性名称 类型
appDirName String src/main/application
libDirName String lib
deploymentDescriptor org.gradle.plugins.ear.descriptor.DeploymentDescriptor
在一个默认的构部
署配置文件里:
application.xml
这些属性由一个EarPluginConvention公共对象提供
51.6.Ear
War任务默认会把 src/main/application的内容复制到归档目录的根目录。如果配置文件
META-INF/application.xml不存在,它将被自动生成。
API文档中有更多关于Ear的信息.
51.7.定制Ear
下面的例子中有一些重要的自定义选项
例25.2.定制Ear插件
build.gradle
Ear插件
274
pplyplugin:'ear'
applyplugin:'java'
repositories{mavenCentral()}
dependencies{
//Thefollowingdependencieswillbetheearmodulesand
//willbeplacedintheearroot
deployproject(path:':war',configuration:'archives')
//Thefollowingdependencieswillbecomeearlibsandwill
//beplacedinadirconfiguredviathelibDirNameproperty
earlibgroup:'log4j',name:'log4j',version:'1.2.15',ext:'jar'
}
ear{
appDirName'src/main/app'//useapplicationmetadatafoundinthisfolder
//putdependentlibrariesintoAPP-INF/libinsidethegeneratedEAR
libDirName'APP-INF/lib'
deploymentDescriptor{//customentriesforapplication.xml:
//fileName="application.xml"//sameasthedefaultvalue
//version="6"//sameasthedefaultvalue
applicationName="customear"
initializeInOrder=true
displayName="CustomEar"//defaultstoproject.name
//defaultstoproject.descriptionifnotset
description="MycustomizedEARfortheGradledocumentation"
//libraryDirectory="APP-INF/lib"//notneeded,abovelibDirNamesettingdoes
this
//module("my.jar","java")//won'tdeployasmy.jarisn'tdeploydependency
//webModule("my.war","/")//won'tdeployasmy.warisn'tdeploydependency
securityRole"admin"
securityRole"superadmin"
withXml{provider->//addacustomnodetotheXML
provider.asNode().appendNode("data-source","my/data/source")
}
}
}
你也可以使用一些Ear任务提供的自定义选项,如from和metaInf.
51.8.使用自定义的配置文件
-/-/-/-/-//-/-/-/--/-/-/-/-/-当前进度↑↑-/-/-/-/-/--/-/-/-/-/-/-/-/-/
Ear插件
275