Ensuring a Gradle task is executed after another task - heroku

I have the following issue with my gradle build: Heroku guarantees to execute the stage task (see below) but I need a custom cleanUp task (see below) to be executed just after the stage task.
I am not sure how to achieve this... Can anyone please help?
task cleanUp(type: Delete) {
delete 'bignibou-server/build/install'
}
//Executed/invoked by Heroku
task stage(dependsOn: [':bignibou-server:bootRepackage', ':bignibou-server:installDist'])

Basically task dependencies are configured with dependsOn and mustRunAfter, but it seems that what you need can be done with simple doLast:
stage.doLast {
project.file('bignibou-server/build/install').deleteDir()
}

you can declare a task which must always be executed after another task (regardless whether the task succeeded or not):
stage.finalizedBy "someOtherTask" //someOther task will always be executed after "stage"

Related

Is it possible to execute a task from within doLast?

I have the following:
task copyToLib(type: Copy) {
from configurations.runtime
into "$buildDir/output/lib"
doLast { copyOpcThirdParty() } // this doesnt get executed
}
task copyOpcThirdParty(type: Copy) {
from "$projectDir/libs/opc/thirdparty"
into "$buildDir/output/lib/thirdparty/"
}
How can I call copyOpcThirdParty from copyToLib.doLast?
I tried .execute(), tasks.copyOpcThirdParty, etc, nothing worked..
Is it unsupported?
In Gradle tasks are not executed directly. Instead you can register dependencies and Gradle then decides which tasks to execute in which order to achieve the execution of the tasks you specified (generally via command line). In older versions of Gradle you can call execute() directly on a task, but it should never be used.
When executing the tasks, the execution of one task must always be completely finished until another task can be executed. The execution of a task always covers running all doFirst closures, all internal task actions and all doLast closures.
For your specific example you can use the finalizedBy method. It tells Gradle that whenever a specific task runs, at some point after that another specific task also has to run:
copyToLib.finalizedBy copyOpcThirdParty

Execute more than one command in a task without breaking incremental build

We use gradle incremental builds and want to run two commands (ideally from one task). One of the solutions here worked getting the two commands running... however it breaks incremental builds.. It looks something like:
task myTask() {
inputs.files(inputFiles)
project.exec {
workingDir web
commandLine('mycmd')
}
project.exec {
workingDir web
commandLine('mysecond-cmd')
}
}
if running a single command and incremental builds is working, the task looked similar to this, the thing that seems to make the difference is the workingDir:
task myTask(type: Exec) {
workingDir myDir // this seems to trigger/enable continuos compilation
commandLine ('myCmd')
}
the best alternative so far is create 3 tasks, one for each of the cmdline tasks I want to run and a third one to group them, which seems dirty.
The question is: Is there a way to run two or more commands in one task with incremental builds still working?
I'll try to answer the question from the comments: how can I signal from a task that has no output files that the build should watch certain files. Unfortunately, this is hard to answer without knowing the exact use case.
To start with, Gradle requires some form of declared output that it can check to see whether a task has run or whether it needs to run. Consider that the task may have failed during the previous run, but the input files haven't changed since then. Should Gradle run the task?
If you have a task that doesn't have any outputs, that means you need to think about why that task should run or not in any given build execution. What's it doing if it's not creating or modifying files or interacting with another part of the system?
Assuming that incremental build is the right solution — it may not be — Gradle does allow you to handle unusual scenarios via TaskOutputs.upToDateWhen(Action). Here's an example that has a straightforward task (gen) that generates a file that acts as an input for a task (runIt) with no outputs:
task gen {
ext.outputDir = "$buildDir/stuff"
outputs.dir outputDir
doLast {
new File(outputDir, "test.txt").text = "Hurray!\n"
}
}
task runIt {
inputs.dir gen
outputs.upToDateWhen { !gen.didWork }
doLast {
println "Running it"
}
}
This specifies that runIt will only run if the input files have changed or the task gen actually did something. In this example, those two scenarios equate to the same thing. If gen runs, then the input files for runIt have changed.
The key thing to understand is that the condition you provide to upToDateWhen() is evaluated during the execution phase of the build, not the configuration phase.
Hope that helps, and if you need any clarification, let me know.

Depend on multiple gradle tasks in multi project build

Currently I have task which starts Google Cloud server, runs tests and stops server. It is defined at root project:
buildscript {...}
allprojects {...}
task startServer (dependsOn: "backend:appengineStart") {}
task testPaid (dependsOn: "app:connectedPaidDebugAndroidTest") {}
task stopServer (dependsOn: "backend:appengineStop") {}
task GCEtesting {
dependsOn = ["startServer",
"testPaid",
"stopServer"]
group = 'custom'
description 'Starts GCE server, runs tests and stops server.'
testPaid.mustRunAfter 'startServer'
stopServer.mustRunAfter 'testPaid'
}
I tried multiple ways to write it something like this, short with only one task. I didn't get how to refer to task from another project and call mustRunAfter on it. This doesn't work (I also tried to refer from Project.tasks.getByPath, root.tasks, etc):
task GCEtesting {
dependsOn = ["backend:appengineStart",
"app:connectedPaidDebugAndroidTest",
"backend:appengineStop"]
group = 'custom'
description 'Starts GCE server, runs tests and stops server.'
"app:connectedPaidDebugAndroidTest".mustRunAfter "backend:appengineStart"
"backend:appengineStop".mustRunAfter "app:connectedPaidDebugAndroidTest"
}
Is it possible? What is correct syntax to make this work?
It looks like your problem is that you're treating dependsOn as meaning "invoke this task". It actually means "ensure the result of the depended on task is available before the dependent task starts". That's why your first solution didn't work: statements like testPaid.mustRunAfter only affect the actions of the testPaid task itself, not its dependencies.
Anyway, you can get the behaviour you want using dependsOn and finalizedBy, but they have to be declared in the build file of the app subproject.
app/build.gradle:
task connectedPaidDebugAndroidTest {
//
//...
//
dependsOn 'backend:appengineStart' // Ensure appengineStart is run at some point before this task
finalizedBy 'backend:appendginStop' // Ensure appengineStop is run at some point after this task
}
Then, you can run your tests simply with gradle app:connectedPaidDebugAndroidTest. If you really want to define a task in the root project to run the tests, then that's easy too:
build.gradle:
task GCEtesting {
dependsOn = "app:connectedPaidDebugAndroidTest"
}

Skip a task when running another task

I added a task to my gradle project:
task deploy() {
dependsOn "build"
// excludeTask "test" <-- something like this
doFirst {
// ...
}
}
Now the build task always runs before the deploy task. This is fine because the build task has many steps included. Now I want to explicitly disable one of these included tasks.
Usually I disable it from command line with
gradle deploy -x test
How can I exclude the test task programmatically?
You need to configure tasks graph rather than configure the deploy task itself. Here's the piece of code you need:
gradle.taskGraph.whenReady { graph ->
if (graph.hasTask(deploy)) {
test.enabled = false
}
}
WARNING: this will skip the actions defined by the test task, it will NOT skip tasks that test depends on. Thus this is not the same behavior as passing -x test on the command line
I don't know what your deploy task does, but it probably just shouldn't depend on the 'build' task. The 'build' task is a very coarse grained lifecycle task that includes tons of stuff you probably don't want.
Instead it should correctly define its inputs (probably the artifacts that you wanna deploy) and then Gradle will only run the necessary tasks to build those inputs. Then you no longer need any excludes.
I ran into a similar problem. Here is how I prevent "test" from running when I run "intTest" and want the ITs to run alone:
test {
onlyIf { !gradle.startParameter.taskNames.contains("intTest") }
}
An alternative that doesn't depend on certain tasks being run explicitly:
test {
onlyIf { !gradle.taskGraph.hasTask(":intTest") || gradle.taskGraph.hasTask(":check") }
}

Create Gradle task that contains several tasks

Is it possible to create a gradle task that runs several tasks? My goal would be to to have a command cleanAndTestAll that would be executed like:
./gradlew cleanAndTestAll
and would be the equivalent of doing:
./gradlew clean :unit:test :app:connectedAndroidTestPlayDebug
One way is to define a wrapper task that depends on the tasks you want to run.
For example adding the following to the root build.gradle :
task cleanAndTestAll(dependsOn: [ clean, ':unit:test', ':app:connectedAndroidTestPlayDebug']) { }
This task will trigger the two other tasks. and give output like the following:
15:31:38: Executing external task 'cleanAndTestAll'...
:clean
:app:connectedAndroidTestPlayDebug
:unit:test
:cleanAndTestAll
BUILD SUCCESSFUL
If you want to enforce an ordering between the tasks, you could do something like:
task cleanAndTestAll(dependsOn: [clean, ':unit:test', ':app:connectedAndroidTestPlayDebug']) { }
tasks.getByPath(':app:connectedAndroidTestPlayDebug').mustRunAfter tasks.getByPath(':unit:test')
Find out more about gradle tasks at:
https://docs.gradle.org/current/userguide/more_about_tasks.html

Resources