Gradle 任务详述


当前第2页 返回上一页

重写任务

build.gradle

task copy(type: Copy)
task copy(overwrite: true) << {
    println('I am the new one.')
}  

gradle -q copy 的输出结果

> gradle -q copy
I am the new one.  

在这里我们用一个简单的任务替换 Copy 类型的任务。当创建这个简单的任务时,您必须将 overwrite 属性设置为 true。否则 Gradle 将抛出异常,说这种名称的任务已经存在。

跳过任务

Gradle 提供多种方式来跳过任务的执行。

使用断言

你可以使用 onlyIf()方法将断言附加到一项任务中。如果断言结果为 true,才会执行任务的操作。你可以用一个闭包来实现断言。闭包会作为一个参数传给任务,并且任务应该执行时返回 true,或任务应该跳过时返回 false。断言只在任务要执行前才计算。

使用断言跳过一个任务

build.gradle

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

gradle hello -PskipHello 的输出结果

> gradle hello -PskipHello
:hello SKIPPED
BUILD SUCCESSFUL
Total time: 1 secs  

使用 StopExecutionException

如果跳过任务的规则不能与断言同时表达,您可以使用 StopExecutionException。如果一个操作(action)抛出了此异常,那么这个操作(action)接下来的行为和这个任务的其他 操作(action)都会被跳过。构建会继续执行下一个任务。

使用 StopExecutionException 跳过任务

build.gradle

task compile << {
    println 'We are doing the compile.'
}
compile.doFirst {
    // Here you would put arbitrary conditions in real life. But we use this as an integration test, so we want defined behavior.
    if (true) { throw new StopExecutionException() }
}
task myTask(dependsOn: 'compile') << {
   println 'I am not affected'
}  

gradle -q myTask 的输出结果

> gradle -q myTask
I am not affected  

如果您使用由 Gradle 提供的任务,那么此功能将非常有用。它允许您向一个任务的内置操作中添加执行条件。

启用和禁用任务

每一项任务有一个默认值为 true 的 enabled 标记。将它设置为 false,可以不让这个任务的任何操作执行。

启用和禁用任务

build.gradle

task disableMe << {
    println 'This should not be printed if the task is disabled.'
}
disableMe.enabled = false  

Gradle disableMe 的输出结果

> gradle disableMe
:disableMe SKIPPED
BUILD SUCCESSFUL
Total time: 1 secs  

跳过处于最新状态的任务

如果您使用 Gradle 自带的任务,如 Java 插件所添加的任务的话,你可能已经注意到 Gradle 将跳过处于最新状态的任务。这种行在您自己定义的任务上也有效,而不仅仅是内置任务。

声明一个任务的输入和输出

让我们来看一个例子。在这里我们的任务从一个 XML 源文件生成多个输出文件。让我们运行它几次。

一个生成任务

build.gradle

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"
        }
    }
}  

gradle transform 的输出结果

> gradle transform
:transform
Transforming source file.  

gradle transform的输出结果

> gradle transform
:transform
Transforming source file.  

请注意 Gradle 第二次执行执行这项任务时,即使什么都未作改变,也没有跳过该任务。我们的示例任务被用一个操作(action)闭包来定义。Gradle 不知道这个闭包做了什么,也无法自动判断这个任务是否为最新状态。若要使用 Gradle 的最新状态(up-to-date)检查,您需要声明这个任务的输入和输出。

每个任务都有一个 inputs 和 outputs 的属性,用来声明任务的输入和输出。下面,我们修改了我们的示例,声明它将 XML 源文件作为输入,并产生输出到一个目标目录。让我们运行它几次。

声明一个任务的输入和输出

build.gradle

task transform {
    ext.srcFile = file('mountains.xml')
    ext.destDir = new File(buildDir, 'generated')
    inputs.file srcFile
    outputs.dir 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"
        }
    }
}  

gradle transform 的输出结果

> gradle transform
:transform
Transforming source file.  

gradle transform 的输出结果

> gradle transform
:transform UP-TO-DATE  

现在,Gradle 知道哪些文件要检查以确定任务是否为最新状态。

任务的 inputs 属性是 TaskInputs 类型。任务的 outputs 属性是 TaskOutputs 类型。

一个没有定义输出的任务将永远不会被当作是最新的。对于任务的输出并不是文件的场景,或者是更复杂的场景, TaskOutputs.upToDateWhen() 方法允许您以编程方式计算任务的输出是否应该被判断为最新状态。

一个只定义了输出的任务,如果自上一次构建以来它的输出没有改变,那么它会被判定为最新状态。

它是怎么实现的?

在第一次执行任务之前,Gradle 对输入进行一次快照。这个快照包含了输入文件集和每个文件的内容的哈希值。然后 Gradle 执行该任务。如果任务成功完成,Gradle 将对输出进行一次快照。该快照包含输出文件集和每个文件的内容的哈希值。Gradle 会保存这两个快照,直到任务的下一次执行。

之后每一次,在执行任务之前,Gradle 会对输入和输出进行一次新的快照。如果新的快照和前一次的快照一样,Gradle 会假定这些输出是最新状态的并跳过该任务。如果它们不一则, Gradle 则会执行该任务。Gradle 会保存这两个快照,直到任务的下一次执行。

请注意,如果一个任务有一个指定的输出目录,在它上一次执行之后添加到该目录的所有文件都将被忽略,并且不会使这个任务成为过时状态。这是不相关的任务可以在不互相干扰的情况下共用一个输出目录。如果你因为一些理由而不想这样,请考虑使用 TaskOutputs.upToDateWhen()

任务规则

有时你想要有这样一项任务,它的行为依赖于参数数值范围的一个大数或是无限的数字。任务规则是提供此类任务的一个很好的表达方式:

任务规则

build.gradle

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

Gradle q pingServer1 的输出结果

> gradle -q pingServer1
Pinging: Server1  

这个字符串参数被用作这条规则的描述。当对这个例子运行 gradle tasks 的时候,这个描述会被显示。

规则不只是从命令行调用任务才起作用。你也可以对基于规则的任务创建依赖关系:

基于规则的任务依赖

build.gradle

tasks.addRule("Pattern: ping<ID>") { String taskName ->
    if (taskName.startsWith("ping")) {
        task(taskName) << {
            println "Pinging: " + (taskName - 'ping')
        }
    }
}
task groupPing {
    dependsOn pingServer1, pingServer2
}  

Gradle q groupPing 的输出结果

> gradle -q groupPing
Pinging: Server1
Pinging: Server2  

析构器任务

析构器任务是一个孵化中的功能 。当最终的任务准备运行时,析构器任务会自动地添加到任务图中。

添加一个析构器任务

build.gradle

task taskX << {
    println 'taskX'
}
task taskY << {
    println 'taskY'
}
taskX.finalizedBy taskY  

gradle -q taskX 的输出结果

> gradle -q taskX
taskX
taskY  

即使最终的任务执行失败,析构器任务也会被执行。

执行失败的任务的任务析构器

build.gradle

task taskX << {
    println 'taskX'
    throw new RuntimeException()
}
task taskY << {
    println 'taskY'
}
taskX.finalizedBy taskY  

gradle -q taskX 的输出结果

> gradle -q taskX
taskX
taskY  

另一方面,如果最终的任务什么都不做的话,比如由于失败的任务依赖项或如果它被认为是最新的状态,析构任务不会执行。

在不管构建成功或是失败,都必须清理创建的资源的情况下,析构认为是很有用的。这样的资源的一个例子是,一个 web 容器会在集成测试任务前开始,并且在之后关闭,即使有些测试失败。

你可以使用 Task.finalizedBy()方法指定一个析构器任务。这个方法接受一个任务实例、任务名称或<a4><c5>Task.dependsOn()</c5></a4>所接受的任何其他输入作为参数。

总结

如果你是从 Ant 转过来的,像 Copy 这种增强的 Gradle 任务,看起来就像是一个 Ant 目标(target)和一个 Ant 任务(task)之间的混合物。实际上确实是这样子。Gradle 没有像 Ant 那样对任务和目标进行分离。简单的 Gradle 任务就像 Ant 的目标,而增强的 Gradle 任务还包括 Ant 任务方面的内容。Gradle 的所有任务共享一个公共 API,您可以创建它们之间的依赖性。这样的一个任务可能会比一个 Ant 任务更好配置。它充分利用了类型系统,更具有表现力而且易于维护。



标签:Gradle

返回前面的内容

相关阅读 >>

Gradle 构建环境

Gradle war 插件

Gradle 使用文件

Gradle 概述

Gradle 使用 Gradle 图形用户界面

Gradle sonar runner 插件

Gradle Gradle 插件

Gradle 编写构建脚本

Gradle 依赖管理基础

Gradle checkstyle 插件

更多相关阅读请进入《Gradle》频道 >>



打赏

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码打赏,您说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

分享从这里开始,精彩与您同在

评论

管理员已关闭评论功能...