任务结果标签

当 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 不会被执行通常是在两个场景下

  1. 陷入顺序循环
  2. 在执行任务依赖的时候如果满足了这个规则,将不会再次执行了。例如 再执行 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-任务的更多相关文章

  1. Gradle配置APK自动签名完整流程

    转载请注明出处:http://www.cnblogs.com/LT5505/p/6256683.html 一.生成签名 1.命令行生成签名,输入命令keytool -genkey -v -keysto ...

  2. gradle学习笔记(1)

    1. 安装     (1) 下载最新gradle压缩包,解压到某处.地址是:Gradle web site:     (2) 添加环境变量:             1) 变量名:GRADLE_HOM ...

  3. Gradle 实现 Android 多渠道定制化打包

    Gradle 实现 Android 多渠道定制化打包 版权声明:本文为博主原创文章,未经博主允许不得转载. 最近在项目中遇到需要实现 Apk 多渠道.定制化打包, Google .百度查找了一些资料, ...

  4. 解决 Could not find com.android.tools.build:gradle 问题

    今天拉同事最新的代码,编译时老是报如下错误: Error:Could not find com.android.tools.build:gradle:2.2.0.Searched in the fol ...

  5. React Native Android gradle下载慢问题解决

    很多人会遇到 初次运行 react-native run android的时候 gradle下载极慢,甚至会失败的问题 如下图 实际上这个问题好解决的 首先 把对应版本的gradle下载到本地任意一个 ...

  6. Android studio使用gradle动态构建APP(不同的包,不同的icon、label)

    最近有个需求,需要做两个功能相似的APP,大部分代码是一样的,只是界面不一样,以前要维护两套代码,比较麻烦,最近在网上找资料,发现可以用gradle使用同一套代码构建两个APP.下面介绍使用方法: 首 ...

  7. 对Maven、gradle、svn、spring 3.0 fragment、git的想法

    1.Maven Maven可以构建项目,采用pom方式配置主项目和其他需要引用的项目.同时可结合spring3.0的新特性web  fragment. 从现实出发,特别是对于管理不到位,程序员整体素质 ...

  8. 项目自动化建构工具gradle 入门1——输出helloWorld

    先来一个简单的例子,4个步骤: 1.进入D:\work\gradle\java 目录  ,您电脑没这目录? 那辛苦自己一级一级建立起来吧 新建文件build.gradle,文件内容是: apply p ...

  9. 用IntelliJ IDEA创建Gradle项目简单入门

    Gradle和Maven一样,是Java用得最多的构建工具之一,在Maven之前,解决jar包引用的问题真是令人抓狂,有了Maven后日子就好过起来了,而现在又有了Gradle,Maven有的功能它都 ...

  10. 通过Gradle为APK瘦身

    引言:在过去几年中,APK 文件的大小曾急剧增长态势.一般来说,其原因如下:Android开发者获取了更多的依赖库,添加了更多的密度,Apps 增加了更多的功能.但实际上我们应该让APKs 尽可能的小 ...

随机推荐

  1. 题解 P3954 【成绩】

    题目评级: ★ (水题) 内容及算法: 无,简单模拟计算即可 代码: /** *@author little_frog */ #include <bits/stdc++.h> using ...

  2. Mysql数据库调优和性能优化的21条最佳实践

    Mysql数据库调优和性能优化的21条最佳实践 1. 简介 在Web应用程序体系架构中,数据持久层(通常是一个关系数据库)是关键的核心部分,它对系统的性能有非常重要的影响.MySQL是目前使用最多的开 ...

  3. Spring(Bean)3

    bean的继承<!-- bean 的继承 作为模板来使用. 可以通过abstract="true"来指定把该bean配置为·抽象的. 通过abstract="tru ...

  4. centOS系统安装-RabbitMq

    前言 消息通知机制是我们在日常业务开发总常常都会遇到:在微服务架构里,消息也是必不可少的,我们可以借助它异步实现很多业务,就拿我们日常的购物需求来说,在我们下单支付之后,我们就可以通过消息机制来异步处 ...

  5. css隐藏页面元素的多种方法

    在平常的样式排版中,我们经常遇到将某个模块隐藏,下面我整理了一下隐藏元素的多种方法以及对比(有的占据空间,有的不占据空间.有的可以点击,有的不能点击.): ( 一 )  display:  none; ...

  6. Codeforces Round #452 (Div. 2) A B C

    Codeforces Round #452 (Div. 2) A Splitting in Teams 题目链接: http://codeforces.com/contest/899/problem/ ...

  7. 如何使用Git命令克隆仓库代码

    今天我的电脑装了新系统,刚装了Git到电脑上,突然有一个大胆的想法,以后不适用可视化工具了. 要逐步锻炼我的命令的操作能力,不能太依赖可视化工具. 今天先记录一下如何使用git命令克隆仓库代码 git ...

  8. powerline字体安装

    安装命令 git clone https://github.com/powerline/fonts ./install.sh 了解powerline ->美化自己的命令行环境,增加漂亮的状态行, ...

  9. 区块链学习笔记:D03 区块链在各行业领域的应用(一)

    今天主要是学习了区块链在金融和供应链领域的应用,重点体现了区块链多方参与.透明可信.防篡改防抵赖的技术优势 区块链的应用场景最早是在金融行业应用较多,后续逐步扩展到传统行业,如:供应链.政务服务.物联 ...

  10. Object类和@Data注解

    特别说明:若是有不对的地方欢迎指正 简要概述: Object类是java中所有类默认继承的一个类.下面介绍一下Object类中的一些重要的方法,面试中也是经常会被问到的.尤其是==和equals的区别 ...