loosebazooka
loosebazooka

Reputation: 2423

Only run task if another isn't UP-TO-DATE in gradle

I want to automatically add a serverRun task when doing functional tests in Gradle, so I add a dependency :

funcTestTask.dependsOn(serverRun)

Which results in the task running whether or not the funcTestTask even runs

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

OR

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

The cost of starting the server is pretty high and I only want it to start if the functionalTest isn't UP-TO-DATE, I'd like to do or something :

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

So I know I can't know the up-to-date status of funcTestTask until all it's inputs/outputs are decided BUT can I inherit it's uptoDate checker?

serverRun.outputs.upToDateWhen(funcTestTask.upToDate)

The alternative is to "doFirst" the ServerRun in the FuncTest, which I believe is generally frowned upon?

funcTestTask.doFirst { serverRun.execute() }

Is there a way to conditionally run a task before another?

UPDATE 1
Tried settings inputs/outputs the same

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

and this seems to rerun the server on recompiles (good), skips reruns after successful unchanged functional tests (also good), but wont rerun tests after a failed test like the following

:compile
:serverRun
:funcTestTask FAILED

then

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

Upvotes: 13

Views: 6897

Answers (5)

Denis Itskovich
Denis Itskovich

Reputation: 4523

Assuming you have task1 and task2 which depends on task1 and you need to run task2 only if task1 is not up-to-date, the following example may be used:

task task1 {
  // task1 definition
}

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

In this case task2 will run only when task1 is not up-to-date. It's important to use didWork only for tasks which are defined in dependsOn, in order to ensure that didWork is evaluated after that task (task1 in our example) had chance to run.

Upvotes: 1

loosebazooka
loosebazooka

Reputation: 2423

I ended up writing to a 'failure file' and making that an input on the serverRun task:

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()
  }
}

With information from an answer to my question on the Gradle forums : http://forums.gradle.org/gradle/topics/how-can-i-start-a-server-conditionally-before-a-functionaltestrun

Upvotes: 3

Mike Mitterer
Mike Mitterer

Reputation: 7180

I had the same problem but the solution I came up is much simpler. This starts up the server only if testing is necessary

test {
    doFirst {
        exec {
            executable = 'docker/_ci/run.sh'
            args = ['--start']
        }
    }

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

Upvotes: 1

Oleg Sklyar
Oleg Sklyar

Reputation: 10082

Having faced the same problem I found a very clean solution. In my case I want an eclipse project setup to be generated when the build is run, but only at the times when a new jar is generated. No project setup should be executed when the jar is up to date. Here is how one can accomplish that:

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

build {
  dependsOn tasks.eclipse
}

Upvotes: 7

KevinO
KevinO

Reputation: 1030

Since the task is a dependent tasks of the one you are trying to control then you can try:

tasks {
    onlyIf {
        dependsOnTaskDidWork()
    }
}

Upvotes: 3

Related Questions