Gradle学习(十四)——任务详解

原创 2018年01月08日 17:34:47

转载请注明出处:/lastsweetop/article/details/79005417

之前的文章已经将了一下任务的基础知识,包括创建简单的任务,以及任务的依赖,本文将进一步的讲解一些任务的详细知识

任务结果

当Gradle执行任务时,就会因为不同的结果给这些任务打上不同的标签,这些标签表示任务是否有动作需要执行,任务是否应该执行了动作,任务是否执行了这些动作,这些动作是否发生了变化。

结果标签 结果描述 产生结果的前提
没有标签或者EXECUTED 任务执行了动作 1.任务有动作,Gradle也确定该动作必须被执行
2.任务没有动作但是有依赖,这些依赖都要被执行
UP-TO-DATE 任务输出没有改变 1.当任务有输入输出但是没有变化
2当任务有action,但是任务告诉Gradle输出没有变化
3.当任务没有动作,而是有依赖,但是所有的依赖都是UP-TO-DATE,SKIP或者从缓存取出的
4.任务没有动作也没有依赖
FROM-CACHE 任务输出可以在之前的执行缓存中被找到 任务在构建缓冲中可以找到相应的输出
SKIPPED 任务没有执行动作 1.在命令行显示指定了被跳过
2.任务的onlyIf判断返回false
NO-SOURCE 任务不需要执行动作 任务有输入输出,但是没有sources,比如JavaCompile任务的source就是.java文件

定义任务

之前的章节我们已经学会如何使用keyword方式定义任务,还有一些别的方式来定义任务,因为keyword方式不支持表达式。

task(hello) {
    doLast {
        println 'hello'
    }
}

task(copy, type: Copy) {
    from file('src')
    into buildDir
}

你也可以通过string来定义任务

task('hello') {
    doLast {
        println 'hello'
    }
}

task('copy', type: Copy) {
    from file('src')
    into buildDir
}

还可以通过tasks来创建任务

tasks.create('hello') {
    doLast {
        println 'hello'
    }
}

tasks.create(name: 'copy', type: Copy) {
    from file('src')
    into buildDir
}

检索任务

你有时候需要检索在构建文件中定义的任务,比如对任务进行配置或者添加依赖,有很多种方式可以检索。

首先,所有的任务对project对象来说就是可用属性,你可以像使用属性一样来检索这些任务。

task hello
println hello.name
println project.hello.name

也可以通过tasks集合来检索

task hello
println tasks.hello.name
println tasks['hello'].name

也可以通过tasks.getByPath()来访问任意工程的任务,可以使用任务名,相对路径,绝对路径来调用getByPath()方法。

task hello
project(':projectA') {
    task hello
}
println tasks.getByPath('hello').path
println tasks.getByPath(':hello').path
println tasks.getByPath('projectA:hello').path
println tasks.getByPath(':projectA:hello').path

然后执行hello任务:

:hello
:hello
:projectA:hello
:projectA:hello

配置任务

我们从一个copy任务的示例来看如何配置Gradle的任务

task myCopy(type:Copy)

这是一个没有任何默认行为的copy任务,我们有各种各样的方法可以配置它。

myCopy.from 'resource'
myCopy.into 'build'
myCopy.include '**/*.txt', '**/*.xml', '**/*.properties'

首先就是最普通的方式,像配置java或者groovy对象那样调用方法进行配置,myCopy需要重复好多次,可读性并不好。
还有一种方法可以不用重复myCopy,看起来可读性更好些:

myCopy {
    from 'resource'
    into 'build'
    include '**/*.txt', '**/*.xml', '**/*.properties'
}

这种方式其实是调用了tasks.getByName(String var1, Closure var2)方法,你看到的是它的缩写,这个方法完成了使用Closure对任务进行了配置。

你也可以在定义的时候就进行配置:

task myCopy(type:Copy) {
    from 'resource'
    into 'build'
    include '**/*.txt', '**/*.xml', '**/*.properties'
}

给任务添加依赖

有各种各样的方式可以给任务添加依赖。常用的是通过任务名进行添加依赖,不仅可以在相同项目中可以通过任务名添加依赖,也可以添加其他项目的任务作为依赖。添加其他项目的任务作为依赖时,必须要把其他工程的项目名作为前缀,下面是个跨项目依赖的例子:

project(':projectA') {
    task taskX(dependsOn:':projectB:taskY') {
        doLast {
            println it.name
        }
    }
}

project(':projectB') {
    task taskY {
        doLast {
            println it.name
        }
    }
}

然后执行taskX任务:

% gradle taskX -q                                                       
taskY
taskX

除了使用任务名外,你还可以通过Task对象进行添加依赖:

task taskX {
    doLast {
        println it.name
    }
}

task taskY {
    doLast {
        println it.name
    }
}
taskX.dependsOn taskY

然后执行taskX任务:

% gradle taskX -q                                                       
taskY
taskX

再扩展一下,你甚至可以定义闭包作为依赖,这个闭包应该返回一个task或者多个task列表,这些task会被当做任务的依赖,示例如下:

task taskX {
    doLast {
        println it.name
    }
}

taskX.dependsOn {
    tasks.findAll {
        it.name.startsWith('lib')
    }
}

task lib1 {
    doLast {
        println it.name
    }
}

task lib2 {
    doLast {
        println it.name
    }
}

然后执行taskX任务:

± % gradle taskX -q   
lib1
lib2
taskX

任务排序

控制两个任务的执行顺序而不是显示的声明他们之间的依赖关系,在某些用例下它是非常有用的。任务排序和任务依赖的不同在于,任务排序不会影响哪个任务将被执行,仅仅只是影响将被任务的排序。

任务排序适用的场景:

  • 强制任务的执行顺序:比如build必须在clean任务后面执行
  • 在build之后才执行校验:比如需要在release构建之后才进行认证校验
  • 在长校验之前先进行短校验:比如先单元测试再集成测试
  • 一个任务是其他所有任务的总结:比如测试报告任务需要其他测试任务都做完的情况下

有两个排序规则:must run aftershould run after

当你运行must run after规则时,taskB.mustRunAfter(taskA)表示taskB和taskA同事运行时,taskB必然在taskA之后运行。

should run after就没那么严格了,它在两种情况下就会被忽略掉:
第一种情况就是任务排序构成了循环,比如taskB.shouldRunAfter taskA之后又taskA.shouldRunAfter taskB
第二种情况声明的依赖和should run after的顺序不一样,should run after就会被忽略掉
使用should run after应该在有更好,没有也行的情况下才使用。
看下面的例子:

task taskA {
    doLast {
        println it.name
    }
}

task taskB {
    doLast {
        println it.name
    }
}
taskB.mustRunAfter taskA

然后执行taskA和taskB任务:

± % gradle taskB taskA -q                                                 
taskA
taskB

should run after的情况:

task taskA {
    doLast {
        println it.name
    }
}

task taskB {
    doLast {
        println it.name
    }
}
taskB.shouldRunAfter taskA

然后执行taskA和taskB任务:

± % gradle taskB taskA -q                                                 
taskA
taskB

上面的例子中,如果单独执行taskB任务:

± % gradle taskB  -q
taskB

可以看到taskA任务并没有执行

如果有--continue选项,如果A失败B也会继续执行。

task taskA {
  doLast {
      throw new Exception()
      println it.name
  }
}

task taskB {
    doLast {
        println it.name
    }
}
taskB.shouldRunAfter taskA

然后执行taskA和taskB任务:

± % gradle --continue -q taskA taskB                                      
taskB

FAILURE: Build failed with an exception
···

和依赖关系冲突的情况下,shouldRunAfter会被忽略

task taskA {
    doLast {
        println it.name
    }
}

task taskB {
    doLast {
        println it.name
    }
}
taskA.dependsOn taskB
taskB.shouldRunAfter taskA

然后执行

± % gradle -q taskA taskB                                                 
taskB
taskA

如果是mustRunAfter和依赖冲突,那么就会直接报错

任务描述

你可以为任务增加描述信息,他会在gradle tasks显示出来。

task myCopy(type: Copy) {
    description 'Copy the resource directory to the build directory'
    from 'resource'
    into 'build'
    include '**/*.txt', '**/*.xml', '**/*.properties'
}

替代任务

有时候你需要替代任务,必须你想自定义java插件的某个任务,可以参照下面这个例子:

task copy(type:Copy){
    doLast {
        println 'copy1'
    }
}
task copy(overwrite: true) {
    doLast {
        println 'copy2'
    }
}

任务输出:

± % gradle copy -q                                                       
copy2

如果直接定义名字相同的任务,Gradle会抛出任务已经被定义的异常,因此你需要增加overwrite:true的设置。

跳过任务

Gradle提供了多种方法可以跳过任务的执行

使用predicate

你可以用onlyIf()方法给任务附加一个predicate,只有当predicate为true时,任务的动作才会执行.predicate可以用closure去实现,closure将会作为一个参数传入到OnlyIf方法中,只有当闭包返回true,任务的action才会执行,否则将会跳过,示例如下:

task hello {
    doLast {
        println 'hello world'
    }
}
hello.onlyIf {
    !project.hasProperty('skipHello')
}

然后执行任务:

± % gradle hello -PskipHello -q

任务跳过,没有任何输出

使用StopExecutionException

如果跳过的逻辑很难用predicate协程表达式,那么可以考虑下StopExecutionException,这个异常可以通过一个action抛出来,然后该任务之后的action都会跳过,但是会执行下一个任务:

task compile {
    doLast {
        println 'compile'
    }
}
compile.doFirst {
    if(true){
        throw new StopExecutionException()
    }
}
task myTask(dependsOn: compile) {
    doLast {
        println 'myTask'
    }
}

然后执行任务:

± % gradle myTask -q                                                     
myTask

这个特性非常有用,因为它的力度是基于action的,灵活使用可以写出很多有趣的代码

启用禁用任务

每个任务的enabled标签默认都是true,如果设为false,它所以的action都不会被执行,任务结果也会变成SKIPPED

task disabledMe {
    doLast {
        println '反正写什么也不会被执行'
    }
}
disabledMe.enabled = false

然后执行任务:

± % gradle disabledMe -q

任务规则

有时候你需要任务可以包含无穷的参数,而你不想为每个都单独写一个task的话,你可以试试任务规则:

tasks.addRule('Pattern:ping<ID>') { String taskName ->
    if (taskName.startsWith('ping')) {
        task(taskName) {
            doLast {
                println "Panding: " + (taskName - 'ping')
            }
        }
    }
}

你可以这样执行任务:

± % gradle pingServer1 -q                                                 
Panding: Server1

addRule的第一个参数是描述参数,用于gradle tasks的显示,在命令行下可以看到,目前idea的gradle插件还不支持。

任务规则不仅仅可以在命令行下调用,而且还可以与其他规则建立以来关系

tasks.addRule('Pattern:ping<ID>') { String taskName ->
    if (taskName.startsWith('ping')) {
        task(taskName) {
            doLast {
                println "Panding: " + (taskName - 'ping')
            }
        }
    }
}

task groupPing {
    dependsOn pingServer1,pingServer2
}

然后执行任务:

± % gradle groupPing -q                                                   
Panding: Server1
Panding: Server2

Finalizer任务

被终结的任务执行后,Finalizer任务必然会跟着执行

task taskC {
    doLast {
        println it.name
    }
}

task taskD {
    doLast {
        println it.name
    }
}
taskC.finalizedBy taskD

执行任务:

± % gradle taskC -q                                                       !1
taskC
taskD

即使被终结的任务失败了,Finalizer任务一样会执行

task taskC {
    doLast {
        println it.name
        throw new RuntimeException()
    }
}

task taskD {
    doLast {
        println it.name
    }
}
taskC.finalizedBy taskD

执行任务:

± % gradle taskC -q                                                       
taskC
taskD

FAILURE: Build failed with an exception.
...

仅仅有一种情况下Finalizer任务不会被执行,就是被终极的任务根本没有工作,比如up to date或者依赖失败了。

Finalizer任务在无论构建是否成功都必须要清除一些资源的情况下是非常有用的,比如测试中需要的web容器,它在集成测试前需要被开启,而无论测试是否成功,都需要被关闭。

生命周期任务

生命周期任务本身自己没有action可以运行,它有一下几个特点

  • 一个工作流(比如用check运行所以的check任务)
  • 一个可以构建是事物(比如运行debug32MainExecutable为native组件生产32位下的debug可执行程序)
  • 一个可以运行相同逻辑任务的任务(比如compileAll运行所以的编译任务)

很多Gradel插件都有自己的生命周期任务,可以方便的指定具体的操作。如果你自己开发Gradle插件时,一定要记得把相同逻辑的任务作出生命周期任务,以便于操作。

除非生命周期任务有自己的action,否则他的执行与否要看他所有的依赖是否需要执行,如果有一个需要执行,那么生命周期任务就会被执行。如果所有的依赖都是up-to-date,skipped或者从构建缓存拉取的,那么这个任务就是up-to-date的。

版权声明:本文为博主原创文章,未经博主允许不得转载。

FreeRTOS学习之任务通知

FreeRTOS 的任务通知功能实例
  • cyfhan
  • cyfhan
  • 2016年08月01日 10:13
  • 1663

Spring 整合 Quartz 任务调度框架学习笔记

定时任务怎么做。 我们首先先新建一个任务 RefreshAccessTokenTask 通过 Job 来执行上面的 Task。参考资料: http://www.mkyong.com/spring...
  • lw_power
  • lw_power
  • 2015年11月08日 04:58
  • 1530

多任务学习(Multi-task learning)

多任务学习(Multi-task learning)是和单任务学习(single-task learning)相对的一种机器学习方法。在机器学习领域,标准的算法理论是一次学习一个任务,也就是系统的输出...
  • u014221266
  • u014221266
  • 2016年10月24日 14:45
  • 6245

钱柜娱乐开户学习笔记(十四)——在运行时添加碎片

  • 2014年05月12日 20:10
  • 1.4MB
  • 下载

深度学习(十四)——Softmax详解, 目标检测, RCNN

深度学习(十四)——Softmax详解, 目标检测, RCNN
  • antkillerfarm
  • antkillerfarm
  • 2018年01月08日 09:41
  • 42

Swoole源码学习记录(十四)——Server模块详解(下)

swoole版本:1.7.6-stable 上一章已经分析了如何启动swServer的相关函数。本章将继续分析swServer的相关函数, 1.swServer函数分析 swSe...
  • ldy3243942
  • ldy3243942
  • 2014年10月28日 20:54
  • 2070

Gradle 1.12 翻译——第十五章. 任务详述

第十五章. 任务详述 在入门教程 (第 6 章,构建脚本基础) 中,你已经学习了如何创建简单的任务。之后您还学习了如何将其他行为添加到这些任务中。并且你已经学会了如何创建任务之间的依赖。这都是简单的任...
  • maosidiaoxian
  • maosidiaoxian
  • 2014年11月12日 08:51
  • 4646

Gradle 1.12用户指南翻译——第五十七章. 编写自定义任务类

Gradle 支持两种类型的任务。一种是简单的任务,你可以使用一个action闭包来定义它。这样的任务我们在第六章, 构建脚本基础已经看到过。对于这种类型的任务,action闭包决定了这个任务的行为。...
  • maosidiaoxian
  • maosidiaoxian
  • 2017年04月21日 23:54
  • 599

gradle学习(15)-任务

1.任务覆盖 所谓任务覆盖就是,就是2个任务名称相同,但是却还可以同时存在。这是为啥,因为有overwrite,类似于java中重写。 task copy ...
  • qhshiniba
  • qhshiniba
  • 2015年01月12日 14:53
  • 2634

gradle学习(14)-任务

1.任务的定义 之前我们定义任务的时候采用的是task + 任务名的方式。例如 task hello ...
  • qhshiniba
  • qhshiniba
  • 2015年01月12日 01:12
  • 2246
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Gradle学习(十四)——任务详解
举报原因:
原因补充:

(最多只允许输入30个字)