写点什么

深入理解 gradle 中的 task

发布于: 2021 年 02 月 15 日

简介

在之前的文章中,我们讲到了如何使用 gradle 创建一个简单的 task,以及 task 之间怎么依赖,甚至使用了程序来创建 task。在本文中,我们会更加深入的去了解一下 gradle 中的 task。


定义 task

定义一个 task 可以有很多种方式,比如下面的使用 string 作为 task 的名字:


task('hello') {    doLast {        println "hello"    }}
task('copy', type: Copy) { from(file('srcDir')) into(buildDir)}
复制代码

还可以使用 tasks 容器来创建:


tasks.create('hello') {    doLast {        println "hello"    }}
tasks.create('copy', Copy) { from(file('srcDir')) into(buildDir)}
复制代码

上面的例子中,我们使用 tasks.create 方法,将新创建的 task 加到 tasks 集合中。


我们还可以使用 groovy 特有的语法来定义一个 task:


task(hello) {    doLast {        println "hello"    }}
task(copy, type: Copy) { from(file('srcDir')) into(buildDir)}
复制代码

tasks 集合类

上面我们在创建 task 的时候,使用了 tasks 集合类来创建 task。


实际上,tasks 集合类是一个非常有用的工具类,我们可以使用它来做很多事情。


直接在 build 文件中使用 tasks,实际上是引用了 TaskContainer 的一个实例对象。我们还可以使用 Project.getTasks() 来获取这个实例对象。


我们看下 TaskContainer 的定义:


public interface TaskContainer extends TaskCollection<Task>, PolymorphicDomainObjectContainer<Task> 
复制代码

从定义上,我们可以看出 TaskContainer 是一个 task 的集合和域对象的集合。


taskContainer 中有四类非常重要的方法:


第一类是定位 task 的方法,有个分别是 findByPath 和 getByPath。两个方法的区别就是 findByPath 如果没找到会返回 null,而 getByPath 没找到的话会抛出 UnknownTaskException。


看下怎么使用:


task hello
println tasks.getByPath('hello').pathprintln tasks.getByPath(':hello').path
复制代码

输出:


:hello:hello
复制代码

第二类是创建 task 的方法 create,create 方法有多种实现,你可以直接通过名字来创建一个 task:


task('hello') {    doLast {        println "hello"    }}
复制代码

也可以创建特定类型的 task:


task('copy', type: Copy) {    from(file('srcDir'))    into(buildDir)}
复制代码

还可以创建带参数的构造函数的 task:


class CustomTask extends DefaultTask {    final String message    final int number
@Inject CustomTask(String message, int number) { this.message = message this.number = number }}
复制代码

上面我们为 CustomTask 创建了一个带参数的构造函数,注意,这里需要带上 @javax.inject.Inject 注解,表示我们后面可以传递参数给这个构造函数。


我们可以这样使用:


tasks.create('myTask', CustomTask, 'hello', 42)
复制代码

也可以这样使用:


task myTask(type: CustomTask, constructorArgs: ['hello', 42])
复制代码

第三类是 register,register 也是用来创建新的 task 的,不过 register 执行的是延迟创建。也就是说只有当 task 被需要使用的时候才会被创建。


我们先看一个 register 方法的定义:


TaskProvider<Task> register​(String name,                            Action<? super Task> configurationAction)                     throws InvalidUserDataException 
复制代码

可以看到 register 返回了一个 TaskProvider,有点像 java 多线程中的 callable,当我们调用 Provider.get()获取 task 值的时候,才会去创建这个 task。


或者我们调用 TaskCollection.getByName(java.lang.String)的时候也会创建对应的 task。


最后一类是 replace 方法:


Task replace​(String name)<T extends Task> T replace​(String name,                           Class<T> type)
复制代码

replace 的作用就是创建一个新的 task,并且替换掉同样名字的老的 task。


Task 之间的依赖

task 之间的依赖关系是通过 task name 来决定的。我们可以在同一个项目中做 task 之间的依赖:


task hello {    doLast {        println 'Hello www.flydean.com!'    }}task intro {    dependsOn hello    doLast {        println "I'm flydean"    }}
复制代码

也可以跨项目进行 task 的依赖,如果是跨项目的 task 依赖的话,需要制定 task 的路径:


project('project-a') {    task taskX {        dependsOn ':project-b:taskY'        doLast {            println 'taskX'        }    }}
project('project-b') { task taskY { doLast { println 'taskY' } }}
复制代码

或者我们可以在定义好 task 之后,再处理 task 之间的依赖关系:


task taskX {    doLast {        println 'taskX'    }}
task taskY { doLast { println 'taskY' }}
复制代码

还可以动态添加依赖关系:


task taskX {    doLast {        println 'taskX'    }}
// Using a Groovy ClosuretaskX.dependsOn { tasks.findAll { task -> task.name.startsWith('lib') }}
task lib1 { doLast { println 'lib1' }}
task lib2 { doLast { println 'lib2' }}
task notALib { doLast { println 'notALib' }}
复制代码

定义 task 之间的顺序

有时候我们的 task 之间是有执行顺序的,我们称之为对 task 的排序 ordering。


先看一下 ordering 和 dependency 有什么区别。dependency 表示的是一种强依赖关系,如果 taskA 依赖于 taskB,那么执行 taskA 的时候一定要先执行 taskB。


而 ordering 则是一种并不太强列的顺序关系。表示 taskA 需要在 taskB 之后执行,但是 taskB 不执行也可以。


在 gradle 中有两种 order:分别是 must run after 和 should run after。


taskA.mustRunAfter(taskB)表示必须遵守的顺序关系,而 taskA.shouldRunAfter(taskB)则不是必须的,在下面两种情况下可以忽略这样的顺序关系:

第一种情况是如果 shouldRunAfter 引入了 order 循环的时候。


第二种情况是如果在并行执行的情况下,task 所有的依赖关系都已经满足了,那么也会忽略这个顺序。


我们看下怎么使用:


task taskX {    doLast {        println 'flydean.com'    }}task taskY {    doLast {        println 'hello'    }}taskY.mustRunAfter taskX//taskY.shouldRunAfter taskX
复制代码

给 task 一些描述

我们可以给 task 一些描述信息,这样我们在执行 gradle tasks 的时候,就可以查看到:


task copy(type: Copy) {   description 'Copies the resource directory to the target directory.'   from 'resources'   into 'target'   include('**/*.txt', '**/*.xml', '**/*.properties')}
复制代码

task 的条件执行

有时候我们需要根据 build 文件中的某些属性来判断是否执行特定的 task,我们可以使用 onlyIf :


task hello {    doLast {        println 'www.flydean.com'    }}
hello.onlyIf { !project.hasProperty('skipHello') }
复制代码

或者我们可以抛出 StopExecutionException 异常,如果遇到这个异常,那么 task 后面的任务将不会被执行:


task compile {    doLast {        println 'We are doing the compile.'    }}
compile.doFirst { if (true) { throw new StopExecutionException() }}task myTask { dependsOn('compile') doLast { println 'I am not affected' }}
复制代码

我们还可以启动和禁用 task:


myTask.enabled = false
复制代码

最后我们还可以让 task 超时,当超时的时候,执行 task 的线程将会被中断,并且 task 将会被标记为 failed。


如果我们想继续执行,那么可以使用 –continue。


注意, 只有能够响应中断的 task,timeout 才有用。


task hangingTask() {    doLast {        Thread.sleep(100000)    }    timeout = Duration.ofMillis(500)}
复制代码

task rule

如果我们想要给某些 task 定义一些规则,那么可以使用 tasks.addRule:


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

上我们定义了一个 rule,如果 taskName 是以 ping 开头的话,那么将会输出对应的内容。


看下运行结果:


> gradle -q pingServer1Pinging: Server1
复制代码

我还可以将这些 rules 作为依赖项引入:


task groupPing {    dependsOn pingServer1, pingServer2}
复制代码

Finalizer tasks

和 java 中的 finally 一样,task 也可以指定对应的 finalize task:


task taskX {    doLast {        println 'taskX'    }}task taskY {    doLast {        println 'taskY'    }}
taskX.finalizedBy taskY
> gradle -q taskXtaskXtaskY
复制代码

finalize task 是一定会被执行的,即使上面的 taskX 中抛出了异常。


总结

以上就是 gradle 中 task 的详解,希望大家能够喜欢。


本文已收录于 http://www.flydean.com/gradle-task-in-depth/

最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!

欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!


发布于: 2021 年 02 月 15 日阅读数: 19
用户头像

关注公众号:程序那些事,更多精彩等着你! 2020.06.07 加入

最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧,尽在公众号:程序那些事!

评论

发布
暂无评论
深入理解gradle中的task