美文网首页Gradle
Gradle初级教程

Gradle初级教程

作者: 坚持到底v2 | 来源:发表于2017-12-28 08:57 被阅读403次

一、安装

https://gradle.org/install

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很好地集成.

相关文章

网友评论

    本文标题:Gradle初级教程

    本文链接:https://www.haomeiwen.com/subject/ynkxpxtx.html