Gradle-任务
任务结果标签
当 Gradle 执行一个任务时,它会在控制台和 Tooling API 根据任务结果给任务打标签。
这些标签是根据任务是否有操作,是否应该执行操作,是否执行了操作以及这些操作做了哪些改变 来标记的。
下面是 Gradle 的标签以及对应的条件
(无标签)或者 EXECUTED
任务执行了它的操作。
- 任务有操作并且 Gradle 已经决定作为构建的一部分来执行
- 任务没有操作但有些依赖,并且执行了某些依赖项。参考下面的生命周期任务。
UP-TO-DATE
任务输出没有变化
- 任务有输入和输出,但没有改变。另行参考增量构建
- 任务有操作,但是没有改变它的输出。
- 任务没有操作但是有些依赖,但所有的依赖都是最新的,忽略的或者来自缓存。参考下面的生命周期任务
FROM-CACHE
任务的输出能够在先前的任务执行中被找到
- 任务输出能够从缓存中恢复。具体的可另行参考 构建缓存
SKIPPED
任务没有执行它的操作
- 任务已经明确的在命令行中被排除。
- 任务的 onlyIf 返回 false
NO-SOURCE
任务不需要执行它的操作
- 任务有输出和输入,但是没有来源。例如输入是空的。
在Android studio 的 build console 就可以看到这个标签
Tooling API 是指 Gradle 提供的编程 API,通常给 IDE,CI 服务器使用的
创建任务
最简单方便的定义方式:定义一个 hello 任务
这是使用 DSL 的语法
task hello {
doLast {
println 'Hello world!'
}
}
这里的 task 看着像一个关键字,实际上是一个方法,这个方法的原型是 TaskContainer.create()
任务的创建就是使用这个方法给 Project 添加一个 Task 类型的属性,名字就是我们的任务名字;所以才能使用任务名字引用一些API,例如为任务添加额外的属性。
也可以使用一个字符串当做任务名称,指定一个类型
task('hello') {
doLast {
println "hello"
}
}
task('copy', type: Copy) {
from(file('srcDir'))
into(buildDir)
}
或者是直接使用 tasks.create 方法; tasks 是 Project的一属性 类型是 TaskContainer
tasks.create('hello') {
doLast {
println "hello"
}
}
tasks.create('copy', Copy) {
from(file('srcDir'))
into(buildDir)
}
任务配置
在创建任务的的时候,可以传入参数对任务进行配置,比如 任务分组,任务描述等等
task(hello,group:'Hello',description:'这是一个 Hello。'){
doLast{
println "name:$name; group:$group; description:$description;"
}
}
输出如下
> Task :hello
name:hello; group:Hello; description:这是一个 Hello。;
BUILD SUCCESSFUL in 694ms
也可以在闭包中对任务进行配置
task taskX{
group 'Hello'
description '在闭包里配置'
dependsOn hello
}
tasks.create('taskY',Copy){
description '使用 tasks.create()'
dependsOn hello
group 'Hello'
}
使用 gradle help --task 任务名
查看任务详情
可以配置的参数如下
配置项 | 描述 | 默认值 |
---|---|---|
type | 基于一个存在的 Task 来创建,和我们的类继承差不多 | DefaultTask |
dependsOn | 用于配置任务的依赖 | [] |
action | 添加到任务的一个 Action 或者一个闭包 | null |
description | 任务描述 | null |
group | 任务分组 | null |
任务访问
通常是在配置任务或者是使用任务依赖的时候访问任务。
下面是几种获取任务引用的方式
使用 DSL 语法的方式
task hello
task copy(type: Copy)
// Access tasks using Groovy dynamic properties on Project
println hello.name
println project.hello.name
println copy.destinationDir
println project.copy.destinationDir
使用 tasks 集合访问
task hello
task copy(type: Copy)
println tasks.hello.name
println tasks.named('hello').get().name
println tasks.copy.destinationDir
println tasks.named('copy').get().destinationDir
使用 tasks.getByPath() 方法
可以在构建文件的任何地方使用这个方法,它接受任务名字,任务相对路径或者绝对路径。
project(':projectA') {
task hello
}
task hello
println tasks.getByPath('hello').path
println tasks.getByPath(':hello').path
println tasks.getByPath('projectA:hello').path
println tasks.getByPath(':projectA:hello').path
输出如下
gradle -q hello
:hello
:hello
:projectA:hello
:projectA:hello
当我们拿到这个任务的引用的时候,就可以按照我们的任务逻辑去操作它,比如配置任务依赖,配置任务的一些属性,调用方法等。
添加操作
可以使用 Task.doLast 和 Task.doFirst 为任务添加操作
其中 doFirst 是在任务之前执行,doLast 在任务之后执行
task taskZ
taskZ.configure {
group 'Custom'
description '添加操作的实验'
}
taskZ.doFirst {
println 'add doFirst. first'
}
taskZ.doLast {
println 'add doLast. first'
}
taskZ.doLast {
println 'add doLast. second'
}
taskZ.doFirst {
println 'add doFirst. second'
}
执行任务 gradle taskZ
gradle taskZ
> Task :taskZ
add doFirst. second
add doFirst. first
add doLast. first
add doLast. second
BUILD SUCCESSFUL in 608ms
从输出可以看到是先执行 doFirst 然后是 doLast 。
执行分析
任务的执行其实就是执行它的 actions 列表。
这个列表保存在 Task 对象实例中的 actions 成员变量中,其类型是一个 List
private List<InputChangesAwareTaskAction> actions;
现在我们把 Task 之前执行、Task 本身执行以及 Task 之后执行分别称为 doFirst,doSelf,doLast。下面做个演示
class CustomTask extends DefaultTask{
@TaskAction
def doSelf(){
println 'Task 自己本身在执行 in doSelf.'
}
}
task taskA(type:CustomTask)
taskA.doFirst {
println "do First. Task 之前执行"
}
taskA.doLast {
println "do Last. Task 之后执行"
}
例子中定义了一个 CustomTask 并声明了一个 doSelf 方法,使用 @TaskAction 标注,意思是这是任务本身要执行的方法。
执行 taskA 输出
> Task :taskA
do First. Task 之前执行
Task 自己本身在执行 in doSelf.
do Last. Task 之后执行
BUILD SUCCESSFUL in 726ms
前面说过任务执行就是执行它的 actions List。那么要达到这种 doFirst、doSelf、doLast 顺序的目的,就必须把 doFirst 的 actions 放在 actions List 的前面,把 doSelf 的 actions 放在 List 的中间,把 doLast 的 actions 放在 List 最后面,这样才能达到按约定顺序执行的目的。
当我们使用 task 方法创建 taskA 这个任务的时候,Gradle 会解析其带有 Task Action 标注的方法作为其 Task 执行的 Action,然后通过 Task 的 prependParallelSafeAction 方法把该 Action 添加到 actions List 里。
@Override
public void prependParallelSafeAction(final Action<? super Task> action) {
if (action == null) {
throw new InvalidUserDataException("Action must not be null!");
}
getTaskActions().add(0, wrap(action));
}
这个时候任务刚刚被创建,不会有 doFirst 的 Action, actions List 一般是空的。
只有在创建任务时,传入了配置参数中的 action 选项配置的时候才会有。(上面配置任务有提到)
这个时候 actions List 就有了任务本身的 Action了。
再来看 doFirst 和 doLast 两个方法的实现代码
@Override
public Task doFirst(final Action<? super Task> action) {
return doFirst("doFirst {} action", action);
}
@Override
public Task doFirst(final String actionName, final Action<? super Task> action) {
hasCustomActions = true;
if (action == null) {
throw new InvalidUserDataException("Action must not be null!");
}
taskMutator.mutate("Task.doFirst(Action)", new Runnable() {
@Override
public void run() {
getTaskActions().add(0, wrap(action, actionName));
}
});
return this;
}
@Override
public Task doLast(final Action<? super Task> action) {
return doLast("doLast {} action", action);
}
@Override
public Task doLast(final String actionName, final Action<? super Task> action) {
hasCustomActions = true;
if (action == null) {
throw new InvalidUserDataException("Action must not be null!");
}
taskMutator.mutate("Task.doLast(Action)", new Runnable() {
@Override
public void run() {
getTaskActions().add(wrap(action, actionName));
}
});
return this;
}
看最重要的 actions.add 这部分
doFirst 永远都是在 actions List 第一位添加,保证其添加的 Action 在现有的 actions List 的最前面。
doLast 永远都是在 actions List 末尾添加,保证其添加的 Action 在现有的 actions List 元素的最后面。
一个往最前面添加,一个往最后面添加,最后这个 actions List 就形成了 doFirst,doSelf,doLast三部分的 actions.
也就保证了 doFirst,doSelf,doLast三部分的 Action 顺序执行的目的。
这个任务执行分析在 《Android Gradle 权威指南》 中有很详细的解释。
任务排序
任务依赖也能够达到让任务排序的目的,但是还是有些区别的。
主要区别是排序不影响任务执行,只影响执行顺序。
有两种排序规则 “must run after” and “should run after”.
taskA.mustRunAfter(taskB) 表示 taskA 必须总是在 TaskB 之后运行。
taskA.shouldRunAfter(taskB) 表示 taskA 应该在 taskB 之后运行,并不一定必须运行,没有那么严格。
should run after 不会被执行通常是在两个场景下
- 陷入顺序循环
- 在执行任务依赖的时候如果满足了这个规则,将不会再次执行了。例如 再执行 taskB 的依赖时将 TaskA 给执行了,那么在 taskB 完成后将不会再执行 taskA。
must run after
task taskX {
doLast {
println 'taskX'
}
}
task taskY {
doLast {
println 'taskY'
}
}
taskY.mustRunAfter taskX
> gradle -q taskY taskX
taskX
taskY
should run after
task taskX {
doLast {
println 'taskX'
}
}
task taskY {
doLast {
println 'taskY'
}
}
taskY.shouldRunAfter taskX
> gradle -q taskY taskX
taskX
taskY
如果只执行一个任务的话顺序规则就没用了
gradle -q taskY
taskY
任务跳过
Gradle 提供了多种方式跳过任务,任务被跳过将不会执行。
使用断言 onlyIf
这个方法接收一个闭包参数,闭包返回 false 就不会执行,返回 true 将执行任务
这个方法是在执行任务前被调用的,不是在配置阶段。
task hello {
doLast {
println 'hello world'
}
}
hello.onlyIf { !project.hasProperty('skipHello') }
gradle hello -PskipHello
> Task :hello SKIPPED
BUILD SUCCESSFUL in 0s
使用 StopExecutionException
如果断言不能满足你的话,就可以使用这个 StopExecutionException ,使用这个异常可以执行时根据逻辑进行判断。
这个异常可以在一个操作中抛出,抛出后直接跳过这个任务进行下一个任务。
task compile {
doLast {
println 'We are doing the compile.'
}
}
compile.doFirst {
// Here you would put arbitrary conditions in real life.
// But this is used in an integration test so we want defined behavior.
if (true) { throw new StopExecutionException() }
}
task myTask {
dependsOn('compile')
doLast {
println 'I am not affected'
}
}
gradle -q myTask
I am not affected
使用 enabled = false
每个任务都有一个 enabled 标志,默认是 true,如果设置为 false 这个任务将会被标记为 SKIPPED,直接跳过
task disableMe {
doLast {
println 'This should not be printed if the task is disabled.'
}
}
disableMe.enabled = false
执行 gradle disableMe
gradle disableMe
Task :disableMe SKIPPED
BUILD SUCCESSFUL in 0s
使用 超时时间
每个任务都有一个 timeout 属性用来限制执行时间。
当任务执行超时,任务执行线程就会被终止,任务将会被标记失败。
如果使用了 --continues 其他任务将会继续执行。
如果任务不能响应超时,任务将不会被终止。
Gradle 所有的内置任务都会响应超时。
task hangingTask() {
doLast {
Thread.sleep(100000)
}
timeout = Duration.ofMillis(500)
}
任务规则
正常情况下在使用 gradle 执行任务时,如果任务不存在就会抛出异常。
而任务规则就是在 Gradle 找不到任务时应用的规则,例如我们可以在找不到任务时打印一句话或者执行其他操作。
任务规则的添加和创建一样都是由 TaskContainer 完成,其实这个实在 NamedDomainObjectCollection 接口中的,不过 TaskContainer 继承了这个接口。
tasks.addRule("这就是一个任务规则描述"){ taskName ->
task (taskName){
println "$taskName 不存在"
}
}
这个 方法有多个重载,详情可查看 API。
API 传送门
生命周期任务
生命周期任务通常是没有操作的,通常是表达一个概念,例如下面几个:
- 一个步骤,例如 check 检查,build 构建;
- 一个可构建的东西,例如一个可执行文件
- 一个组合了多个逻辑任务的空壳任务,例如 使用 compileAll 任务执行所有编译任务
Base Plugin 中定义了标准生命周期任务:
- check
- assemble
- build
几乎所有的核心语言插件都应用了 Base Plugin, 例如 Java Plugin。
所以它的生命周期任务几乎所有语言插件都有。
除非生命周期任务有操作,否则它的结果标记应该是由它的依赖的执行结果决定的。
如果所有的依赖都被执行了,那么就应该标记 EXECUTED
如果所有的依赖都是最新的,跳过的或来自缓存,那么就应该被标记为 UP-TO-DATE
Gradle-任务的更多相关文章
- Gradle配置APK自动签名完整流程
转载请注明出处:http://www.cnblogs.com/LT5505/p/6256683.html 一.生成签名 1.命令行生成签名,输入命令keytool -genkey -v -keysto ...
- gradle学习笔记(1)
1. 安装 (1) 下载最新gradle压缩包,解压到某处.地址是:Gradle web site: (2) 添加环境变量: 1) 变量名:GRADLE_HOM ...
- Gradle 实现 Android 多渠道定制化打包
Gradle 实现 Android 多渠道定制化打包 版权声明:本文为博主原创文章,未经博主允许不得转载. 最近在项目中遇到需要实现 Apk 多渠道.定制化打包, Google .百度查找了一些资料, ...
- 解决 Could not find com.android.tools.build:gradle 问题
今天拉同事最新的代码,编译时老是报如下错误: Error:Could not find com.android.tools.build:gradle:2.2.0.Searched in the fol ...
- React Native Android gradle下载慢问题解决
很多人会遇到 初次运行 react-native run android的时候 gradle下载极慢,甚至会失败的问题 如下图 实际上这个问题好解决的 首先 把对应版本的gradle下载到本地任意一个 ...
- Android studio使用gradle动态构建APP(不同的包,不同的icon、label)
最近有个需求,需要做两个功能相似的APP,大部分代码是一样的,只是界面不一样,以前要维护两套代码,比较麻烦,最近在网上找资料,发现可以用gradle使用同一套代码构建两个APP.下面介绍使用方法: 首 ...
- 对Maven、gradle、svn、spring 3.0 fragment、git的想法
1.Maven Maven可以构建项目,采用pom方式配置主项目和其他需要引用的项目.同时可结合spring3.0的新特性web fragment. 从现实出发,特别是对于管理不到位,程序员整体素质 ...
- 项目自动化建构工具gradle 入门1——输出helloWorld
先来一个简单的例子,4个步骤: 1.进入D:\work\gradle\java 目录 ,您电脑没这目录? 那辛苦自己一级一级建立起来吧 新建文件build.gradle,文件内容是: apply p ...
- 用IntelliJ IDEA创建Gradle项目简单入门
Gradle和Maven一样,是Java用得最多的构建工具之一,在Maven之前,解决jar包引用的问题真是令人抓狂,有了Maven后日子就好过起来了,而现在又有了Gradle,Maven有的功能它都 ...
- 通过Gradle为APK瘦身
引言:在过去几年中,APK 文件的大小曾急剧增长态势.一般来说,其原因如下:Android开发者获取了更多的依赖库,添加了更多的密度,Apps 增加了更多的功能.但实际上我们应该让APKs 尽可能的小 ...
随机推荐
- day29 文件的上传和下载 socketserver(并发)
文件上传的讲解: import subprocess res=subprocess.Popen("dir", shell=True, stderr=subprocess.PIPE, ...
- Activiti工作流框架学习(一)之通用数据表详细介绍
文/朱季谦 Activiti工作流引擎自带了一套数据库表,这里面有一个需要注意的地方: 低于5.6.4的MySQL版本不支持时间戳或毫秒级的日期.更糟糕的是,某些版本在尝试创建此类列时将引发异常,而其 ...
- JAVA合并多个word文档根据文章标题生成目录
此产品版本是免费版的,我也是在用免费,除了只能单次识别25张一下的word和生成pdf有限制,其他的功能都和正式版差不多. 如果你几十个文档,每个文档几页,输出出来超过25页,那没关系,依然可以使用. ...
- Maven搭建SpringMvc
Maven搭建SpringMvc,只需跟着一步步操作 项目结构 1 创建Maven项目 index,jsp报错不用管,配置完pom就好了,也可以直接删除掉 2 pom.xml添加依赖 <depe ...
- JS的静态类型检测,有内味儿了
我们知道 TypeScript 2.3 以后的版本支持使用--checkJs对.js文件进行类型检查和错误提示. 但是由于 JavaScript 是弱类型语言,在编写代码的时候,是无法检测变量的类型的 ...
- sync.Map(在并发环境中使用的map)
sync.Map 有以下特性: 需要并发读写时,一般的做法是加锁,但这样性能并不高,Go语言在 1.9 版本中提供了一种效率较高的并发安全的 sync.Map,sync.Map 和 map 不同,不是 ...
- Docker 02 - 向 Docker 的 Tomcat 镜像中部署 Web 应用
目录 1 下载 Docker 镜像 2 部署Web项目 2.1 通过Dockerfile自定义项目镜像 2.2 启动自定义镜像, 生成一个容器 2.3 另一种启动方式: 交互式启动 3 (附) 向镜像 ...
- SpringBoot整合邮件发送
本节介绍SpringBoot项目如何快速配置和发送邮件,包括简单的邮件配置.发送简单邮件.发送HTML邮件.发送携带附件的邮件等. 示例源码在:https://github.com/laolunsi/ ...
- Xcode10.0: NO BUNDLE URL PRESENT
目录 解决方案 1.删除build, 重新运行, 没有work 2.删除node_modules, npm i, 重新运行, 没有work 3.删除端口占用 4.代理设置, 可能work了 解决方案 ...
- openstack网络(四)-虚机流量分析
几种网络名词解释 使用LinuxBridge时虚机流量分析 VLAN FLAT Local VXLAN 使用OVS时虚机流量分析 几种网络名词解释 1.local网络:local网络是与其他网络和节点 ...