Gradle技术问题:如何在任务配置中引用另一任务的输入?
问题简述
我想在Gradle任务的配置过程中,引用另一个任务的输入值,让两个任务使用相同的输入内容。
已尝试的方案
我在/tmp/foo目录下编写了示例build.gradle文件,尝试让task2复用task1的输入:
task task1 { def myInput = projectDir // (or any other directory) inputs.dir(myInput).withPropertyName('t1Input') doLast { println myInput } } task task2 { dependsOn task1 def myInput = task1.inputs.properties['t1Input'] inputs.dir(myInput).withPropertyName('t2Input') doLast { println myInput } }
执行命令./gradlew --console=verbose --warning-mode=all task2后,出现了如下报错:
> Task :task1 /tmp/foo > Task :task2 A problem was found with the configuration of task ':task2'. Registering invalid inputs and outputs via TaskInputs and TaskOutputs methods has been deprecated and is scheduled to be removed in Gradle 5.0. - No value has been specified for property 't2Input'. null
看起来通过task1.inputs.properties['t1Input']无法获取到task1中定义的输入属性,我使用的是Gradle 4.6版本。
解决方案
方法1:抽离共享变量
最简单直接的方式是把输入值定义为项目级的共享变量,让两个任务直接引用这个变量,完全避免任务间的输入依赖:
// 把共享输入抽成项目级变量 def sharedInput = projectDir task task1 { inputs.dir(sharedInput).withPropertyName('t1Input') doLast { println sharedInput } } task task2 { dependsOn task1 inputs.dir(sharedInput).withPropertyName('t2Input') doLast { println sharedInput } }
这种方式逻辑清晰,也降低了任务间的耦合度。
方法2:通过TaskInputs的文件集合获取
如果你确实需要从task1的输入中读取值,可以使用task1.inputs.files来访问——inputs.properties并不是获取自定义输入值的正确途径,它主要用于Gradle内部属性管理,而非你通过withPropertyName设置的友好标识。修改后的代码如下:
task task1 { def myInput = projectDir inputs.dir(myInput).withPropertyName('t1Input') doLast { println myInput } } task task2 { dependsOn task1 // 因为我们知道输入是单个目录,所以用singleFile获取 def myInput = task1.inputs.files.singleFile inputs.dir(myInput).withPropertyName('t2Input') doLast { println myInput } }
如果task1有多个输入,可以遍历task1.inputs.files集合来获取全部内容。
方法3:给任务添加自定义属性
你也可以给task1添加一个自定义属性来存储输入值,让task2直接引用这个属性,这种方式的依赖关系更明确:
task task1 { // 给任务添加自定义属性存储输入值 ext.myInput = projectDir inputs.dir(ext.myInput).withPropertyName('t1Input') doLast { println myInput } } task task2 { dependsOn task1 def myInput = task1.myInput inputs.dir(myInput).withPropertyName('t2Input') doLast { println myInput } }
原方案失败的原因
你之前用的task1.inputs.properties['t1Input']之所以返回null,是因为inputs.properties这个Map并不是用来存储你通过withPropertyName设置的标识的。withPropertyName的作用只是在Gradle的增量构建报告、错误提示中给输入一个易读的名称,而非作为键来访问输入值。再加上在Gradle的配置阶段,task1的输入还未被完全解析,所以通过这种方式根本拿不到预期的值。
内容的提问来源于stack exchange,提问作者Chriki

