只有在gradle中另一个任务不是“UP-TO-DATE”时才运行任务

13

我希望在Gradle中进行功能测试时自动添加一个serverRun任务,因此我添加了一个依赖项:

funcTestTask.dependsOn(serverRun)

这将导致任务运行,无论 funcTestTask 是否运行

:compile
:serverRun
:funcTestTask (and associate compile tasks... etc)
:serverStop

OR

:compile UP-TO-DATE
:serverRun <-- unnecessary 
:funcTestTask UP-TO-DATE
:serverStop

启动服务器的成本很高,我只想在functionalTest不是UP-TO-DATE时才启动它,我想要做什么:

if(!funcTestTask.isUpToDate) {
    funcTestTask.dependsOn(serverRun)
}

我知道在决定所有输入/输出之前,我无法了解funcTestTask的最新状态,但是我能继承它的“是否最新”检查器吗?

serverRun.outputs.upToDateWhen(funcTestTask.upToDate)

另外一个选择是在FuncTest中先运行ServerRun,但我认为这种做法通常不被赞同?

funcTestTask.doFirst { serverRun.execute() }

有没有一种方法可以在另一个任务之前有条件地运行一个任务?

更新1
尝试将输入/输出设置为相同

serverRun.inputs.files(funcTestTask.inputs.files)
serverRun.outputs.files(funcTestTask.outputs.files)

这似乎会在重新编译时重新运行服务器(很好),在功能测试未更改并成功后跳过重新运行(同样不错),但不会像以下示例一样在测试失败后重新运行测试

:compile
:serverRun
:funcTestTask FAILED

then

:compile UP-TO-DATE
:serverRun UP-TO-DATE <-- wrong!
:funcTestTask FAILED

你可以通过将它们的输入设置为相同来有效地模拟这个过程。我假设 funcTestTask 是某种类型为 Test 的任务?还必须有一些任务来编译这些测试吧? - Mark Vieira
是的,它是Test类型。但是如果测试失败了,那么信息会被缓存到哪里呢?因为输入保持不变,但之前的任务失败了,我能获取到任务失败的信息吗? - loosebazooka
您可以轻松地检查任务是否失败。表达式 task.state.failure == null 将在成功的任务执行中为 true - Mark Vieira
这让我有了一些进展,但我不认为我完全理解gradle如何计算输出目录更改。更多信息请参见问题的UPDATE 1部分。 - loosebazooka
Gradle 可能会在上次执行失败时内部地认为任务不是最新的。在这种情况下,您可以手动解析测试结果输出以确定上次执行是否失败。 - Mark Vieira
显示剩余4条评论
5个回答

7

我曾经遇到过同样的问题,找到了一种非常干净的解决方案。在我的情况下,我希望当构建运行时生成一个eclipse项目设置,但仅在生成新jar时执行。当jar文件是最新的时候,不应该执行任何项目设置。以下是如何实现它的方法:

tasks.eclipse {
  onlyIf {
    !jar.state.upToDate
  }
}

build {
  dependsOn tasks.eclipse
}

4
这对我似乎不起作用。即使任务实际上被视为是最新的,state.upToDate始终返回false。 - Felipe Lima
对我来说,当执行 onlyIf 时状态尚未设置。 - Baptiste Mesta

3
我最终写了一个“失败文件”,并将其作为服务器运行任务的输入:
File serverTrigger = project.file("${buildDir}/trigger") 

project.gradle.taskGraph.whenReady { TaskExecutionGraph taskGraph ->
  // make the serverRun task have the same inputs/outputs + extra trigger
  serverRun.inputs.files(funcTestTask.inputs.files, serverTrigger)
  serverRun.outputs.files(funcTestTask.outputs.files)
}

project.gradle.taskGraph.afterTask { Task task, TaskState state ->
  if (task.name == "funcTestTask" && state.failure) {
    serverRun.trigger << new Date()
  }
}

通过 Gradle 论坛上的一个回答提供的信息: http://forums.gradle.org/gradle/topics/how-can-i-start-a-server-conditionally-before-a-functionaltestrun

3

由于该任务是您尝试控制的任务的依赖任务,因此您可以尝试:

tasks {
    onlyIf {
        dependsOnTaskDidWork()
    }
}

2
这个对我起作用:只是 dependsOnTaskDidWork 被弃用了,但可以用 task.didWork 替换。 - Denis Itskovich

1
我曾经遇到过同样的问题,但是我想出来的解决方案要简单得多。 只有在需要测试时才启动服务器。
test {
    doFirst {
        exec {
            executable = 'docker/_ci/run.sh'
            args = ['--start']
        }
    }

    doLast {
        exec {
            executable = 'docker/_ci/run.sh'
            args = ['--stop']
        }
    }
}

1
假设您有依赖于task1task2,并且只有在task1不是最新的情况下才需要运行task2,则可以使用以下示例:
task task1 {
  // task1 definition
}

task task2(dependsOn: task1) {
  onlyIf { task1.didWork }
}

在这种情况下,只有当任务1不是最新的时,任务2才会运行。重要的是仅对在“dependsOn”中定义的任务使用“didWork”,以确保在该任务(例如我们的“task1”)有机会运行之后才评估“didWork”。请注意保留HTML标记。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接