一、安装
1. 前提 JDK 7+
2. 下载安装
tmpHomePath="/home"
tarName="gradle-4.0-all.zip"
baseUrl="https://services.gradle.org/distributions"
# 下载
curl -o "${tmpHomePath}/${tarName}" ${baseUrl}/${tarName}
# 解压
unzip -d ${tmpHomePath} ${tmpHomePath}/${tarName}
dirName=$(unzip -l -qq ${tmpHomePath}/${tarName} | head -1 | awk '{print $4}')
dirName=${dirName/%\//}
# 设置环境变量
echo "
export GRADLE_HOME=${tmpHomePath}/${dirName}
export PATH=\$GRADLE_HOME/bin:\$PATH
" >> /etc/bashrc
sed -i '1a\GRADLE_OPTS="\${GRADLE_OPTS} -Xmx1024m"' ${tmpHomePath}/${dirName}/bin/gradle
# 验证
gradle -v
2.1 使用gradlew
使用gradlew是为了保证编译项目时使用的gradle版本一致
# cd 到项目目录
# 生成 gradle wrapper 文件,设定期望的 gradle版本
gradle wrapper --gradle-version=4.0 --distribution-type=bin
# 或者如果已经生成过gradlew,也可以使用gradlew命令
./gradlew wrapper --gradle-version=4.0 --distribution-type=bin
# 然后使用gradlew命令时会下载期望版本的gradle到./.gradle/中
./gradlew tasks
也可以在build.gradle中设定wrapper task如下(当然除了版本信息还可以设置配置文件中的其他信息)
task wrapper(type: Wrapper) { gradleVersion='4.0' }
然后执行
gradle wrapper # 或
./gradlew wrapper
2.2 设置代理
针对单个项目设置gradle代理或针对所有的gradle项目设置代理,
可以编辑或新建 项目中的./gradle.properties文件或~/.gradle/gradle.properties ,
增加如下配置项:(不能使用变量,只能直接设定)
systemProp.http.proxyHost=10.0.86.71
systemProp.http.proxyPort=31888
systemProp.https.proxyHost=10.0.86.71
systemProp.https.proxyPort=31888
当然也可以在命令行上指定要使用的代理
proxyipiponly=10.0.86.71
proxyport=31888
./gradlew -Dhttps.proxyHost=${proxyipiponly} -Dhttps.proxyPort=${proxyport} \
-Dhttp.proxyHost=${proxyipiponly} -Dhttp.proxyPort=${proxyport} \
--no-daemon \
tasks
gradlew 如果想使用本地的gradle软件,而不用再去网上下载,
可以修改 ./gradle/wrapper/gradle-wrapper.properties
文件中的distributionUrl属性,
该属性定义了从哪里获取gradle的distribution文件,
同时该文件也定义了zip存储的位置和distribution存储的位置,
一般是 GRADLE_USER_HOME/wrapper/dists
。一般而言 GRADLE_USER_HOME
是 ~/.gradle
目录
二、开始使用gradle
1. 基本命令和参数
显示帮助
gradle help
列出依赖情况
gradle dependencies
# 解释在依赖图中一个依赖如何被选择,为什么会被选择
gradle dependencyInsight
# 检查特定依赖
gradle dependencyInsight --dependency apache-commons
# 检查compile依赖以外的依赖
gradle dependencyInsight --configuration
查看可用的tasks
gradle tasks
# -q 表示以安静模式运行
gradle -q tasks
# --all 表示所有的tasks和更多细节
gradle -q task --all
查看项目可用的属性
有些属性是由Gradle的Project对象提供的。
其他属性是用户自定义的属性,可能来自于属性文件、属性命令行选项或直接在构建脚本中定义
gradle properties
查看 task的更多细节
gradle help --task <task>
运行task
gradle <task> ...
构建设置类 task
# 通过创建build.gradle和settings.gradle来初始化一个Gradle项目,设置wrapper文件. Gradle会尝试从pom.xml中获取项目信息
gradle setupBuild
# 为Java项目创建一个标准的build.gradle,项目中不能有pom.xml
gradle generateBuildFile
显示命令行选项
gradle --help
# 安静模式
gradle -q | --quiet
# 指定编译文件,默认是 build.gradle
gradle --build-file | -b build.gradle
# 指定设置文件,默认是 settings.gradle
gradle --settings-file | -c settings.gradle
# 设定JVM系统参数
gradle -D | --system-prop key=value
# 设定项目参数
gradle -P | --project-prop key=value
# 输出更多的日志信息
gradle -i | --info | -d | --debug
# 打印异常的堆栈跟踪信息
gradle -s | --stacktrace | -S | --full-stacktrace
# 在一个task执行失败后继续执行
# 在这多项目构建中很有用,它会让你在构建时发现所有可能的问题,并一起修复它们
gradle --continue
# 指定 gradle-user-home,默认是~/.gradle目录
gradle --gradle-user-home | -g ~/.gradle
# 设置一个初始化脚本,这个脚本会在每个task执行之前执行
gradle --init-script | -I someScript
# 设置构建目录,默认是当前目录
gradle --project-dir | -p someDir
# 并行构建参与多项目的子项目
gradle --parallel
# 指定并行构建时的线程数
gradle --parallel-threads
# 打印task的执行顺序,而不是真正执行它们
gradle --dry-run | -m
# 重新运行task,忽略task的UP-TO-DATE状态
gradle --rerun-task
# 告诉Gradle不要在父目录寻找设置文件(settings.file),从而节约时间
gradle --no-search-upwards | -u
# 指定排除运行的task,例如不想执行单元测试可以 gradle -x test build
gradle --exclude-task | -x
# 每次初始化一个构建都要启动一次JVM
# 所以gradle守护进程应运而生
gradle --daemon
# 然后可以使用 ps -ef|grep gradle
# daemon进程会在3小时空闲期后自动过期
# 不使用守护进程编译
gradle --no-daemon
# 关闭守护进程
gradle --stop
# 注意!!: gradle默认就是使用的daemon模式
# 所以使用-D设定系统属性时要考虑使用--no-daemon或先关闭daemon,以防止-D设置的系统属性(例如代理属性)不生效
# 以前台形式运行Gradle守护进程,用于调试和监控
gradle --foreground
# 缓存选项
# 仅仅在本地缓存中检查依赖
gradle --offline
# 强制检查依赖的版本
gradle --refresh-dependencies
# 设置项目缓存目录,默认为~/.gradle
gradle --project-cache-dir
# 清空缓存
gradle --recompile-scripts
2. Groovy简单介绍
PDF下载: 链接:http://pan.baidu.com/s/1bo1bQmr 密码:hj63
2.1 优化:
- 表达式后面的分号是可选的
- 类、构造器和方法默认都是public的
- 方法体中的最后一个表达式的值会被作为返回值,return语句是可选的
- getter和setter是自动生成的. 类的属性通过.来获取,但实际执行的是getter和setter方法。
- == 实际调用的是 equals()方法,避免了可能的NullPointerExceptions.
2.2 Groovy Web终端
http://groovyconsole.appsot.com
2.3 assert
def version=12
assert version==12
如果assert失败,Groovy会提供有用的输出用于查找问题的根源
2.4 变量类型
Groovy不强制你显式声明变量类型、方法参数和返回类型.
如果一个方法没有返回值,则应该声明void而不是def作为返回类型,
def表示Object占位符.
当然你也可以使用强类型来声明
def aStr='Gradle'
assert aStr.class==java.lang.String
def add(a,b) {
return a+b
}
2.5 可选的括号
如果方法的参数至少有一个时,可以省略括号,例如
add 2,3
2.6 字符串
可以使用 "
或 '
括起来,
也可以使用 """
或 '''
声明多行字符串,
当然你也可以使用 \n?
在行尾加 \
来生成多行字符串,
但也许你会喜欢'''这种方式
2.6.1 ${}引用
GString 支持类似于shell中的$var的引用方式,
字符串必须使用 "
而不是 '
, 例如
aa='aa';
bb="$aa bb";
println bb
其实$的完整用法应该是 ${}
,花括号中是Groovy表达式.
2.7 List
使用[],例如
def aList=['a','b']
assert aList.class==java.util.ArrayList
assert aList.size()==2
assert aList[1]=='b'
//追加元素
aList << 'c'
//遍历aList
aList.each { tmpStr ->
println tmpStr
}
def aList=(1..4)
//等价于[1,2,3,4]
aList=aList+[1]
// [1,2,3,4,1]
aList=aList-[1]
//[2,3,4]
aList=[2,3,4]*2
//[2,3,4,2,3,4]
aList=[1,[2,3]].flatten()
//[1,2,3]
2.8 Map:
例子:
def aMap=[key1:1,"key2":2]
//表示key的字符串的引号可省略,表示value的字符串的引号不可以省略
assert aMap.getClass()==java.util.LinkedHashMap
assert aMap.size() == 2
assert aMap.key1==1
assert aMap['key1']==1
//增加kv
aMap['key3']=3
//遍历Map
aMap.each { tmpKey,tmpVal ->
println "key:$tmpKey value:$tmpVal"
}
2.9 命名参数
如果类没有定义构造器,那么Groovy默认实现了一个接收Map的构造器,
它会遍历map中的key然后调用类中对应的setter函数.
例如
class TestClass {
Integer major
}
TestClass test=new TestClass(major:1)
//其实接收的是一个map参数
//test=new TestClass('major':1)
assert test.major==1
2.10 闭包
闭包总是会返回一个值(最后一条语句的值),
例子:
def add1= {Integer aInt ->
//声明闭包参数,Integer声明了参数为强类型,可以省略
++aInt
//最后一条语句的值作为返回值
}
def add1={
++it
//不声明闭包参数时,it代表了第一个传入的参数
}
闭包的类型为Closure
2.11 闭包委托
闭包代码在委托的闭包上执行,默认这个委托就是闭包的所有者,
例如你在Groovy脚本中定义了一个闭包,那么所有者就是一个groovy.lang.Script实例.
闭包的隐式变量delegate允许你重新定义所有者.
例子:
class ProjectVersion {
Integer major
void increment(Closure closure) {
closure.resolveStrategy=Closure.DELEGATE_ONLY
closure.delegate=this
closure()
}
}
ProjectVersion pv=new ProjectVersion(major:1)
pv.increment { major+=1 }
2.12 Groovy开发工具库 (GDK)
网址: http://groovy.codehaus.org/groovy-jdk/
有很多关于String、Collection、File和Stream相关的有用的方法。
例子:
assert 'gradle'.capitalize()=='Gradle'
new File('build.gradle').eachLine { line ->
println line
}
2.13 Gradle脚本中的Groovy
每个构建脚本都至少有一个对应的org.gradle.api.Project实例。
大多数情况下,在构建脚本中调用的属性和方法都自动委托给了这个Project实例。
例子:
apply plugin:'java'
//等价于调用Project的apply方法,该方法接收一个只有单键值对的map
project.apply ['plugin':'java']
version='0.1'
//等价于 project.setVersion('0.1')
repositories {
mavenCentral()
}
//等价于调用 project.repositories 方法,并传递一个闭包给它.
dependencies {
compile 'commons-codec:commons-codec:1.6'
}
//等价于调用 project.dependencies方法,并传递一个闭包给它
三、gradle脚本
1. 脚本示例
创建一个名为build.gradle的文件
task helloWorld {
//doLast和doFirst可以多次添加,执行顺序是doLast后添加的后执行,doFirst后添加的先执行
doLast {
println 'Hello world!'
}
}
helloWorld.doFirst {print "first"}
helloWorld << {
//在task方法中可以直接访问project的logger实例和vesion属性
//如果要访问group和description属性,需要使用project.group,因为Task实例也有自己的group属性和description属性
logger.quiet "Version:$version"
}
task hello2(denpendsOn: helloWorld)
task hello3
hello3.dependsOn hello2,helloWorld
2. 构建脚本
2.1 构件块
每个构建脚本都包含3个基本构建块: project、task、property
project
project 对应org.gradle.api.Project类,并可以通过project变量使其隐式可用.
也就是说不需要指定project变量,
例如可以直接使用setDescription("xx")
Project类的方法有:
apply(options:Map<String,?>),
buildscript(config:Closure),
dependencies(config:Closure),
configurations(config:Closure),
getDependencies(),
getConfigurations(),
getAnt(),
getName(),
getDescription(),
getGroup(),
getPath(),
getVersion(),
getLogger(),
setDescription(descString),
setVersion(version:Object),
file(path:Object),
files(paths:Object...),
fileTree(baseDir:Object),
task(nameStr,Closure),
task(nameStr),
task(args:argsMap,nameStr,Closure)
task
task对应org.gradle.api.Task接口的实例,
默认为org.gradle.api.DefaultTask类实例,
该接口的方法有:
dependsOn(tasks:Object...)
doFirst(action:Closure),
doLast(action:Closure),
getActions()
getInputs(),
getOutputs()
getAnt(),
getDescription(),
getEnabled(),
getGroup(),
setDescription(descStr),
setEnabled(boolean),
setGroup(grpStr)
2.2 扩展属性
//只有在初始声明的时候需要使用ext命名空间,引用属性时可以不加ext
project.ext.myProp='myValue'
ext { someOtherProp=123 }
通过属性文件提供扩展属性
可以在 ~/.gradle/gradle.properties 或 ./gradle.properties 中直接声明属性,如下所示:
exampleProp=myValue
someOtherProp=123
然后可以直接在构建脚本中使用该属性,不用加ext.
通过命令行提供属性
-P=项目属性 -D=系统属性
通过环境变量设置
ORG_GRADLE_PROJECT_propertyName=someValue
2.3 task依赖
设置依赖
task hello4(dependsOn:[hello1,hello2]);
增加依赖
hello4.dependsOn hello3;//或者 hello4.dependsOn(hello3) ,引号可以不加
多个依赖的执行顺序不是添加顺序,
它们的执行顺序是内置的,可以认为是不确定的
task依赖还可以使用隐式引用的方式,
例如在task中使用其他task的输出作为自己的输入,
也可以让Gradle来推断task之间的依赖
2.4 task终结器
hello1.finalizedBy hello2
2.5 task配置块
配置块永远在task动作执行之前被执行。
无论何时执行Gradle构建,都会运行3个不同的生命周期阶段: 初始化、配置和执行
在初始化阶段,Gradle为项目创建了一个Project实例.
在多项目构建中,Gradle会找出哪些项目依赖需要参与到构建中。
在配置阶段,Gradle构造了一个模型来表示任务,并参与到构建中来.
增量式构建特性决定了模型中的task是否需要被运行.
这个阶段非常适用于为项目或指定task设置所需的配置.
在执行阶段,task被执行.
如果任务被认为没有修改过,将被跳过. 即 声明task的inputs和outputs.
DefaultTask类的inputs对应的是TaskInputs接口,
该接口有4个方法:
dir(dirPath:Object),
file(path:Object),
files(paths:Object...),
property(name:String,value:Object)
即输入可以是一个目录,一个或多个文件或一个任意属性.
DefaultTask类的outputs对应的是TaskOutputs接口,
该接口有3个方法:
dir(dirPath:Object),
file(path:Object),
files(paths:Object...),
upToDateWhen(Closure)
即输出可以是一个目录,一个或多个文件,或一个闭包执行的结果
(该闭包是在task执行阶段才执行,如果返回true则表明outputs是upToDate状态).
第一次执行task后,生成inputs和outputs内容后,只要输入和输出都没有发生过变化,那么task就会被认为是UpToDate状态,并且会跳过执行.
2.6 编写和使用自定义Task
class MyTask extends DefaultTask {
//使用注解声明task的inputs和outputs
@Input Boolean release;
@OutputFile File destFile;
//使用注解声明将被执行的方法
@TaskAction
void start() {
//...
}
}
task myTask(type:MyTask) {
release=version.release
destFile=versionFile
}
2.7 Gradle内置的task类型
都是DefaultTask的派生类,例如Zip和Copy,
例如:
task createZip(type:Zip,dependsOn:makeReleaseVersion) {
//使用war这个task的outputs作为自己的输入,达到隐式依赖
from war.outputs.file;
//...other code...
}
2.8 task规则命名模式 动态生成task
tasks.addRule("规则的描述信息") {String taskName ->
if (taskName.startsWith("incre") && taskName.endsWith("Version")) { //根据预定义模式检查task名称
task(taskName) << { //动态生成task,并增加一个doLast方法
String classifier=(taskName-'incr'-'Version').toLowerCase();//从完整的taskName中抽出动态的部分,以便进行不同的task处理
//...
}
}
}
使用tasks.addRule增加的task在 gradle tasks中位于 Rules下
2.9 自定义的类放在buildSrc目录下!
Gradle会自动编译 buildSrc/src/main/groovy/目录下的文件,并加入到构建脚本的classpath中.
2.10 编写构建生命周期的回调事件
编写构建生命周期的回调事件 有2种方式:
闭包(注册钩子回调)
或 通过Gradle API所提供的监听器接口.
闭包实现的方式
例如
在配置阶段之前的回调 gradle.beforeProject{project -> }
在执行阶段之前的 gradle.taskGraph.whenReady{ graph -> }
在执行阶段之后的 gradle.buildFinished(result -> }
在配置阶段Gradle决定了在执行阶段task的执行顺序DAG,
注意这个DAG没有一个闭环,即每个task只能被执行一次.
这个DAG可以使用一个TaskExecutionGraph接口表示.
project实例有一个方法 getGradle()
该方法返回一个Gradle接口实例,
Gradle接口有一个方法 getTaskGraph()
该方法返回一个 TaskExecutionGraph接口实例,
TaskExecutionGraph接口有一个方法 whenReady(Closure)
该方法实现了生命周期钩子.
例子:
gradle.taskGraph.whenReady(TaskExecutionGraph taskGraph ->
if (taskGraph.hasTask(release) { //判断task执行图中是否包含release,如果包含
if (!version.release) { //如果version.release属性不是true
version.release=true
...
}
}
}
如何通过Gradle API所提供的监听器接口挂接到构建生命周期呢?
Gradle接口有一个方法 addListener(listener:Object) 、
TaskExecutionGraph接口有一个方法 addTaskExecutionGraphListener(listener:TaskExecutionGraphListener) 用于实现监听器的注册,
所以你需要做的就是实现TaskExecutionGraphListener这个接口,并注册.
例子:
class ReleaseVersionListener implements TaskExecutionGraphListener {
final static String releaseTaskPath=':release'
@Override
void graphPopulated(TaskExecutionGraph taskGraph){ //实现接口方法
if (taskGraph.hasTask(releaseTaskPath)) {
List<Task> allTasks=taskGraph.allTasks;
Task releaseTask=allTasks.find {it.path==releaseTaskPath}
Project project=releaseTask.project;
if (!project.version.release) {
project.version.release=true;
...
}
}
}
}
def listener=new ReleaseVersionListener()
gradle.taskGraph.addTaskExecutionGraphListener(listener)
2.11 初始化构建环境:
例子: Gradle有一个核心插件 build-announcements ,这个插件可以在构建完成后发送通告到本地通知系统.
你可以在每个 build.gradle 中都增加如下内容来应用这个插件
apply plugin:'build-announcements'
但是你可以有更好的方式,那就是在 ~/.gradle/init.d/
文件夹下创建初始化脚本,
Gradle会执行该目录下所有以 .gradle
为扩展名的初始化脚本.
我们可以新建一个 build-announcements.gradle
脚本文件,
然后在其中增加如下内容:
gradle.projectsLoaded { Gradle gradle ->
gradle.rootProject {
apply plugin:'build-announcements'
}
}
3. 依赖管理
3.1 理解配置
配置可以直接在项目的根级别添加和访问;
你可以使用插件所提供的配置,或声明自己的配置.
project实例的 configurations(Closure)
方法可以添加配置,
getConfigurations()
获取一个 ConfigurationContainer 接口实例.
ConfigurationContainer接口提供了 add(name:String)
和 getByName(name:String)
方法来增加或获取一个 Configuration 接口实例.
Configuration接口提供了 getDependencies()
等方法.
project实例的 dependencies(c:Closure)
可以为配置增加依赖, getDependencies()
获取一个DependencyHandler 接口实例.
获取一个DependencyHandler接口提供了 add(configName:String,depNotation:Object,c Closure)
来为某个配置增加依赖...
3.2 排除传递性依赖
dependencies {
cargo 'org.codehaus.cargo:cargo-ant:1.3.1' {
exclude group:'xml-apis',module:'xml-apis'
}
cargo 'xml-apis:xml-apis:2.0.2'
}
3.3 排除所有的传递性依赖
dependencies {
cargo 'org.codehaus.cargo:cargo-ant:1.3.1' {
transitive=false
}
}
3.4 动态版本声明
dependencies {
cargo 'org.codehaus.cargo:cargo-ant:1.+'
}
3.4 文件依赖
dependencies {
compile fileTree(dir: 'libs', include: '*.jar')
}
3.5 配置仓库
repositories {
mavenCentral()
mavenLocal()
maven {
name 'Custom Maven Repository'
url 'http://.../release/'
}
flatDir (dir:'libs',name:'local libs dir')
}
3.6 Gradle缓存的依赖存储在哪里了?
~/.gradle/caches/artifacts-15/filestore/ 目录下,
artifacts-15是一个标识符,用来指定Gradle版本,可以打印如下内容来查看
project.configurations.getByName('cargo').each { dependency ->
println dependency
}
3.7 应对依赖版本冲突1: 遇到冲突问题是让构建失败
configurations.cargo.resolutionStrategy {
failOnVersionConflict()
}
3.8 强制指定一个版本
configurations.cargo.resolutionStrategy {
force 'org.codehaus.cargo:cargo-ant:1.3.0'
}
3.9 手动刷新缓存
命令行选项 --refresh-dependencies
3.10 对依赖的SNAPSHOT版本和动态版本模式声明的依赖
gradle 针对 SNAPSHOT版本和动态版本模式声明的依赖 的默认的缓存策略是 缓存24小时,
你可以更改它:
configurations.cargo.resolutionStrategy {
cacheDynamicVersionsFor 0,'seconds' //动态版本模式声明的依赖的缓存 0秒超时
cacheChangingModulesFor 0,'seconds' //SNAPSHOT版本的依赖的缓存 0秒超时
}
4. 多项目构建
在多项目构建中是通过 settings.gradle 文件来声明子项目的.
其内容一般是:
include 'model','repository','web'
//传递的是子项目路径,不是文件路径
include 'model:todo:items'
//构建更深层次的项目结构
settings.gradle对应的是Settings接口实例.
你可以在settings.gradle文件中面向Settings接口进行编码,
Settings中的任何方法都可以被直接调用,就像include一样.
settings.gradle被加载和解析后,如果你需要在build.gradle中访问Settings实例,可以注册一个生命周期闭包或监听器.
gradle.settingsEvaluated(Closure)是一个不错的起点,它提供了Settings对象作为闭包参数.
4.1 settings 是在初始化阶段被执行
4.2 找到settings.gradle
只要项目根目录或任何子项目目录中包含build.gradle,Gradle就允许你从相应位置进行构建.
那么Gradle如何知道一个子项目是一个多项目构建的一部分呢?
首先在当前目录下寻找settings.gradle,
如果没有找到就在父目录中查找,
如果找到了settings.gradle,并在它的定义中包含了这个项目,那么该项目就被认为是多项目构建的一部分,
否则这个项目将作为一个单项目构建.
你可以通过命令行参数来控制该搜索行为:
--no-search-upward:不去父目录查找settings.gradle,
--settings-file:指定settings文件名称.
4.3 分层布局与扁平布局
分层布局是指子项目作为根项目的子目录存在;
扁平布局是指子项目和根项目平级存在,
相应的扁平布局的settings.gradle文件内容一般是:
//使用includeFlat方法
includeFlat 'model','repository','web'
4.4 配置子项目:
- (1) 根项目和所有子项目应该使用相同的group和version属性
- (2) 在子项目之间建模依赖关系
4.5 与多项目构建相关的Project API:
(1) 特定的项目配置:
project(path:String)
project(path:String,config:Closure)
path使用 :model 在子项目前加:的方式
(2) 公共的项目配置:
allprojects(action:Action<? super Project>)
allprojects(action:config:Closure)
subprojects(action)
subprojects(action:config)
(3) 项目解析顺序:
evaluationDependsOn(path:String)
evaluationDependsOnChildren()
在多项目构建中,项目的默认执行顺序是基于它们的字母名称,
为了显式地控制在构建生命周期的配置阶段的执行顺序,你可以使用前面的2个方法.
4.6 属性继承
在一个项目中定义的属性会自动被其子项目继承.
4.7 执行子项目的task
例子:
执行model子项目的build task
gradle :model:build
4.8 声明项目依赖:例子:
project (':repository'){
//...
dependencies {
compile project(':model')
}
}
有了依赖声明,Gradle在初始化阶段之后就有了一个项目依赖的内部模型,
你就不需要从一个特定的子项目执行task,你可以为构建的所有子项目执行task.
假设你只执行了
//为所有子项目执行 build task
gradle build
你会看到期望的执行顺序:
先执行 :model 子项目的task到build,
然后是 :repository 子项目的task到build
4.9 部分构建
通过命令行选项 --no-rebuild,例子:
gradle :repository:build --no-rebuild
#你会看到不再执行 :model 子项目的构建
#同时构建和测试当前项目所依赖的项目
gradle :repository:buildNeeded
#同时构建和测试依赖当前项目的项目
gradle :repository:buildDependents
4.10 如果多个子项目中有相同名称的task,而且它们之间没有依赖关系,那么它们的执行顺序是什么样的?
首先执行的是根项目的task,
然后是按项目名称的字母顺序执行.
你可以通过跨项目的task依赖来确定task执行顺序,
例如:
project(':model') {
task hello(dependsOn:':repository:hello') << {
println 'hello from model project'
}
}
4.11 定义公共行为:
allprojects {
group='com.manning.gia'
version='0.1'
}
subprojects {
apply plugin:'java'
}
4.12 到目前为止,我们所定义的多项目构建只包含一个build.gradle和settings.gradle.
你可以为每个子项目创建单独的build.gradle.
4.13 自定义 build.gradle 文件名称.
settings.gradle 例子:
include 'model','repository','web'
rootProject.name='todo'
rootProject.children.each {
it.buildFileName=it.name+'.gradle'
}
5. Gradle测试
5.1 测试配置
java插件引入了2个新的配置: testCompile和testRuntime,
例子:
dependencies {
testCompile 'junit:junit:4.11'
}
testCompile不影响编译后的classpath,但是它扩展了compile依赖,
同样,testRuntime扩展了runtime依赖,同时testRuntime也扩展了testCompile依赖
6. 插件开发:
插件分为2类:
1类是脚本插件 使用 apply from:'cloudbees.gradle' //脚本插件可以是任何uri,包括http和https
2类是对象插件 实现 org.gradle.api.Plugin 接口,对象插件的源代码一般放在buildSrc目录下.
6.1 脚本插件
脚本插件其实就是把task定义到其他脚本文件中,
然后使用 apply from:'cloudbees.gradle' 引入其脚本,
然后就可以直接使用脚本中定义的task
buildscript(Closure)
buildscript的作用是为了在构建脚本中直接使用外部类库(不是编译工程文件时使用的外部类库).
该依赖定义在classpath配置分组中,
例如:
buildscript {
repositories {
mavenCentral();
}
dependencies {
classpath 'com.cloudbees:cloudbees-api-client:1.4.0'
}
}
6.2 定制task
继承DefaultTask,实现自己的Task类,
在 buildSrc目录下创建自己的Task类,
其下的目录有src/main/groovy和src/main/java,
还有自己的build.gradle文件,
例如如果需要使用外部类库则需要在build.gradle中加入:
repositories {
mavenCentral();
}
dependencies {
compile 'com.cloudbees:cloudbees-api-client:1.4.0'
}
然后继承DefaultTask类并使用@TaskAction声明task的动作,
例如:
package com.test.ludq
class CloudBeesAppInfo extends DefaultTask {
@taskAction
void start() { //函数名称可以任意,只要不是execute()
println "hello"
//...
}
}
使用自定义的Task类时,如下所示:
import com.test.ludq.CloudBeesAppInfo //导入需要使用的类
task cloudBeesAppInfo(type:CloudBeesAppInfo) {
//...some configure code...
}
6.3 定制plugin:
继承Plugin<Project>接口
package com.ludq.test
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.DefaultTask
class CloudBeesPlugin implments Plugin<Project> {
@Override
void apply(Project project) {
//可以对现有的任务进行赋值
//可以创建新的task
}
}
还可以对约定和配置进行扩展
在构建脚本 build.gradle 中声明一个闭包(扩展可以被添加到许多Gradle对象中,例如Project和Task都是扩展可知的),
cloudBees {
apiUrl='https://api.cloudbees.com/api'
apiKey=project.apiKey
}
然后在自定义Plugin中使用
project.extensions.create('cloudBees',CloudBeesPluginExtension) // CloudBeesPluginExtension是一个POJO类
def extension=project.extensions.findByName('cloudBees');
conventionMapping.apiUrl= { extension.apiUrl } //每个继承DefaultTask类的Task都有conventionMapping这个属性,使用这个属性将扩展模型的值赋给task的输入或输出字段.通过将扩展模型值包装成一个闭包,实现惰性赋值,为了获取存储在约定映射中的一个属性值,需要显式使用getter方法,否则只会返回一个null值.
还可以给插件定义一个简称,
可以在 src/main/META-INF/gradle-plugins/ 目录下新建一个文件,
例如 helloplugin.properties ,该文件暴露的就是插件简称,
其内容是
implementation-class=com.ludq.test.HelloPlugin
使用自定义的Plugin类,
apply plugin:'java' //使用插件的简称要加引号
apply plugin:org.gradle.api.plugins.JavaPlugin //使用插件的全称不要加引号
Gradle标准类库提供在Gradle安装目录下的 lib/plugins 目录中
非标准的外部插件需要使用 buildscript 声明repositories和dependencies
7. 代码质量管理和监测
7.1 JaCoCo插件:
JavaCodeCoverage,通过运行时二进制代码检测方法来测量代码覆盖率.
JaCoCo在JVM类加载器上附加了一个Java代理,这个代理收集指令执行信息,并将这些信息写入到文件中.
支持Java7项目. 执行test类型的task的时候,JaCoCo的代理会针对运行的测试类来收集相关的运行时信息.
7.2 Cobertura插件:
采用对编译后的二进制代码添加指令的方式来工作.
不支持Java7项目.
该插件引入了两个新的task,
其中一个用来将检测指令插入到编译后的类文件中;
另一个用来生成代码覆盖率报告. 执行check task.
7.3 Checkstyle插件:
定义统一的源代码格式、结构和注解,以便代码可读、可维护,这就是Checkstyle的作用.
7.4 PMD插件:
关注无用代码或重复代码、过于复杂的代码以及可能的bug.
7.5 FindBugs插件:
它要发现的潜在bug包括:
equals/hashCode实现问题、
多余的null检查,甚至是性能问题.
FindBugs操作的是Java二进制代码,而不是源代码,这带来的结果就是更耗时.
7.6 JDepend插件:
用来衡量代码的质量设计产生分析结果,
它会扫描Java代码的所有包、计算类和接口的数量,这个信息会帮助你识别不必要的组件或强耦合部分.
7.7 集成Sonar:
你已经知道了如何使用各种代码分析工具为项目做代码检查和分析,这些工具所提供的报告都需要单独做检查. 在每个构建当中,已经存在的报告可能会被删除,并且有新的报告被创建,所以你很难看出代码在一段时间内是提高了还是降低了,你需要一个工具能够集中监控、可视化和整合报告信息.Sonar应运而生.
Sonar能和大部分工具很好的集成,
对于非常规的工具或者语言,Sonar也可以通过插件的形式扩展.
Gradle通过Sonar Runner插件和Sonar很好地集成.
网友评论