gradle学习(15)-任务

最后更新于:2022-04-01 06:51:52

## 1.任务覆盖 所谓任务覆盖就是,就是2个任务名称相同,但是却还可以同时存在。这是为啥,因为有overwrite,类似于java中重写。 ~~~ task copy << { println "this is a first" } task copy(overwrite:true)<<{ println "this is a second" } ~~~ 执行命令 ~~~ D:\GRADLE~2\0112>gradle -q copy this is a second ~~~ 如果不在第二个copy中写overwrite的话,就会报错,提示任务已经存在。 ~~~ D:\GRADLE~2\0112>gradle -q copy FAILURE: Build failed with an exception. * Where: Build file 'D:\gradle_product\0112\build.gradle' line: 5 * What went wrong: A problem occurred evaluating root project '0112'. > Cannot add task ':copy' as a task with that name already exists. * Try: Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. ~~~ ## 2.跳过任务 gradle有很多种方式达到跳过任务执行的方式,下面一一列举: #### 使用断言 ~~~ task hello <<{ println 'hello world' } hello.onlyIf{!project.hasProperty('skipHello')} ~~~ 如果执行gradle -q hello命令 ~~~ D:\GRADLE~2\0112>gradle -q hello hello world ~~~ 但是在该命令后面加上一些-P参数,来添加属性,命令的执行结果就不一样了: ~~~ D:\GRADLE~2\0112>gradle -q hello -P skipHello D:\GRADLE~2\0112> ~~~ 没有打印任何信息,说明任务hello被跳过了。 #### 抛出StopExecutionException 和java类似,可以通过抛出异常,来跳过某个任务的执行 ~~~ task compile <<{ println "We are doing the compile" } compile.doFirst{ if(true){throw new StopExecutionException()} } task myTask(dependsOn:'compile')<<{ println 'I am not affected' } ~~~ 执行结果如下: ~~~ D:\GRADLE~2\0112>gradle -q myTask I am not affected ~~~ #### 任务的enabled属性 每一次任何都有一个enabled属性,默认情况下该属性是true的。但是你也可以根据自己的需要将该属性设置为false,来达到跳过任务的作用的。 ~~~ task disableMe <<{ println 'ni dou bu hui zhi xing ,ni hai shuo zhe me duo hua' } disableMe.enabled = false ~~~ 执行命令后的是没有任何输出的: ~~~ D:\GRADLE~2\0112>gradle disableMe :disableMe SKIPPED BUILD SUCCESSFUL Total time: 2.216 secs ~~~ ## 3.跳过up-to-date式的任务 ~~~ task transform{ ext.srcFile = file('mountains.xml') ext.destDir = new File(buildDir,'generated') doLast{ println "Transforming source file" destDir.mkdirs() def mountains = new XmlParser().parse(srcFile) mountains.mountain.each{ mountain -> def name = mountain.name[0].text() def height = mountain.height[0].text() def destFile = new File(destDir,"${name}.txt") destFile.text = "$name -> ${height}\n" } } } ~~~ 这个时候执行任务是会报错的: ~~~ D:\GRADLE~2\0112>gradle -q transform Transforming source file FAILURE: Build failed with an exception. * Where: Build file 'D:\gradle_product\0112\build.gradle' line: 7 * What went wrong: Execution failed for task ':transform'. > D:\gradle_product\0112\mountains.xml (系统找不到指定的文件。) * Try: Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. ~~~ 因为需要添加一个mountains.xml文件。 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-07_568e4667b1153.jpg) 那么这个xml文件里写啥呢?不能说定义一个空的xml文件吧。那就要从任务的定义来看了,因为build.gradle中要读的属性有mountain,name,height。mountain又包含name和height。那么我的xml应该定义如下: ~~~ <?xml version='1.0' encoding='UTF-8' standalone='no' ?> <mountain> &nbsp;<name>doctorq</name> &nbsp;<height>111</height> </mountain> ~~~ 这个时候执行命令: ~~~ D:\GRADLE~2\0112>gradle transform :transform Transforming source file BUILD SUCCESSFUL Total time: 3.416 secs D:\GRADLE~2\0112>gradle transform :transform Transforming source file BUILD SUCCESSFUL Total time: 2.309 secs ~~~ 从上面可以看出2次执行的结果输出的结果是一样的,没有什么差异。那么如果能让任务是一个up-to-date式呢。 我们修改一下build.gradle中transform任务的定义,添加2个属性inputs和outputs: ~~~ task transform{ ext.srcFile = file('mountains.xml') ext.destDir = new File(buildDir,'generated') inputs.file srcFile outputs.file destDir doLast{ println "Transforming source file" destDir.mkdirs() def mountains = new XmlParser().parse(srcFile) mountains.mountain.each{mountain -> def name = mountain.name[0].text() def height = mountain.height[0].text() def destFile = new File(destDir,"${name}.txt") destFile.text = "$name -> ${height}\n" } } } ~~~ 这个时候我们再执行任务的时候就会发现,第一次执行的时候是没有任何差异的,但是以后执行的时候,任务后面就会多了一个up-to-date的标识: ~~~ D:\GRADLE~2\0112>gradle transform :transform Transforming source file BUILD SUCCESSFUL Total time: 2.496 secs D:\GRADLE~2\0112>gradle transform :transform UP-TO-DATE BUILD SUCCESSFUL Total time: 2.34 secs D:\GRADLE~2\0112>gradle transform :transform UP-TO-DATE BUILD SUCCESSFUL Total time: 2.325 secs ~~~ 当你把build/generated目录删除掉以后,重新执行gradle transform任务的时候,是没有UP-TO-DATE标识的。但是一旦有build/generated目录就会显示UP-TO-DATE目录,提示你是最新生成的。 上面的2两个属性分别所属类别如下: inputs属于TaskInputs类型下的 outputs属于TaskOutputs类型下的 需要注意的是,如果一个任务没有定义outputs属性,就永远都不会出现UP-TO-DATE的。对于下面情形,task任务仍然被看作UP-TO-DATE。 1.outputs定位的不是文件 2.TaskOutpusts.upToDateWhen方法被调用 #### 原理 上面说了那么多,那么我们让任务出现UP-TO-DATE到底有什么用呢? gradle在任务第一次被执行的时候,会记住任务的输入文件以及里面的内容。执行成功后,会记住任务的输出文件以及里面的内容。在下次执行任务的时候,同样会记住这些内容,我们称这些为记忆。 在新的一次任务执行来了以后,在该任务执行前,gradle会生成新的记忆。然后将该新的记忆和之前的记忆来比对,如果输出的文件和文件中的内容没有任何变化,就标记为UP-TO-DATE且跳过该任务,只有不同的时候,才会执行该任务。这样的话可以使任务的执行更快。 ## 4.任务规则 任务的行为依赖传入的参数,这个时候需要提供任务的规则。 ~~~ tasks.addRule("Pattern:ping<ID>"){ String taskName -> if(taskName.startsWith("ping")){ task(taskName)<<{ println taskName println "Pinging: " + (taskName-'ping') } } } ~~~ 输出: ~~~ D:\GRADLE~2\0112>gradle -q pingServer1 pingServer1 Pinging: Server1 ~~~ build.gradle文件中,addRule中的参数是一个字符串,它其实是该rule的描述信息。可以通过gradle tasks查看: ~~~ Rules ----- Pattern:ping<ID> To see all tasks and more detail, run with --all. BUILD SUCCESSFUL ~~~ 上面我们是通过命令行来传入,下面来看看通过依赖的方式,调用。 ~~~ tasks.addRule("Pattern:ping<ID>"){ String taskName -> if(taskName.startsWith("ping")){ task(taskName)<<{ println "Pinging: " + (taskName-'ping') } } } task groupPing{ dependsOn pingServer1,pingServer2 } ~~~ ~~~ D:\GRADLE~2\0112>gradle -q groupPing Pinging: Server1 Pinging: Server2 ~~~ ## 5.Finalizer任务 这让我想起了java中Object类中的finalize方法。需要注意的是,该特性目前还不完善,以后可能发生改变。 在这个里面我们要理解2个概念,一个是终止的任务finalizer task,一个是被终止的任务finalized  task。 finalizer task是我们这节要讲的task,finalized task是被finalizer修饰的task。看下面的例子: ~~~ task taskX << { println 'taskX' } task taskY << { println 'taskY' } taskX.finalizedBy taskY ~~~ ~~~ D:\GRADLE~2\0112>gradle -q taskX taskX taskY ~~~ 上面的任务taskX ,taskY哪一个是finalizer,哪个是finalized呢? 被修饰的是taskX,所以taskX是finalized,taskY是finalizer,知道这两个概念是很重要的。 下面再来研究一下finalizer任务的特性: #### 被修饰任务失败,finalizer任务也会照常执行 ~~~ task taskX << { throw new RuntimeException() println 'taskX' } task taskY << { println 'taskY' } taskX.finalizedBy taskY ~~~ 执行命令后输出: ~~~ D:\GRADLE~2\0112>gradle -q taskX taskY FAILURE: Build failed with an exception. * Where: Build file 'D:\gradle_product\0112\build.gradle' line: 2 * What went wrong: Execution failed for task ':taskX'. > java.lang.RuntimeException (no error message) * Try: Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. ~~~ 这个时候你会发现在taskX执行失败的情况下,taskY却正常执行。finalizer任务是依赖于taskX的,如果taskX不执行,那么taskY也不执行,那么至于taskX执行的成不成功,不是finalizer任务所关系的。
';