Custom Gradle exec tasks won't run in parallel - gradle

My goal is that I can run ./gradlew deployStaging, and it executes buildDockerStatic, and buildDockerBackend in parallel, and after both tasks have finished, the deployStaging will run. With the code below, the tasks run serially. I've tried a few things, but buildDockerStatic, and buildDockerBackend always run serially.
tasks.register("buildDockerStatic", BuildDockerStatic) {
workingDir rootDir
}
tasks.register("buildDockerBackend", BuildDockerBackend) {
workingDir rootDir
}
//noinspection GroovyAssignabilityCheck
task deployStaging() {
dependsOn tasks.buildDockerStatic
dependsOn tasks.buildDockerBackend
doLast {
exec {
workingDir rootDir
commandLine 'sh', './scripts/build_deploy_staging.sh'
}
}
}
class BuildDockerStatic extends AbstractExecTask<BuildDockerStatic> {
BuildDockerStatic() {
//noinspection GroovyAssignabilityCheck
super(BuildDockerStatic)
commandLine 'sh', './scripts/build_static.sh'
}
}
class BuildDockerBackend extends AbstractExecTask<BuildDockerBackend> {
BuildDockerBackend() {
//noinspection GroovyAssignabilityCheck
super(BuildDockerBackend)
commandLine 'sh', './scripts/build_backend.sh'
}
}

In order to be run in parallel, the build tasks need to be placed in sub-projects.
They should contain doLast; otherwise they will be executed when calling eg. gradle clean. And probably, a definition using the plain task {..} will be enough.
Parallel: from the docs at Gradle:parallel execution
By using the --parallel switch, you can force Gradle to execute tasks in parallel as long as those tasks are in different projects.
See a modified example below; it will run two sub-tasks (to sleep and log), in parallel.
It can be run with gradle clean deployStaging --parallel
Multi project structure
build-backend
+--build.gradle
build-static
+--build.gradle
build.gradle
settings.gradle
build-backend/build.gradle :
apply plugin: 'base'
task buildDockerBackend {
doLast{
exec {
println 'BuildDockerBackend: started at ' + new java.util.Date()
workingDir rootDir
sleep 4000
//commandLine 'sh', './scripts/build_backend.sh'
commandLine 'echo', 'task: build-backend'
println 'BuildDockerBackend: done at ' + new java.util.Date()
}
}
}
build-static/build.gradle
apply plugin: 'base'
task buildDockerStatic {
doLast{
exec {
println 'BuildDockerStatic: started at ' + new java.util.Date()
workingDir rootDir
sleep 8000
//commandLine 'sh', './scripts/build_static.sh'
commandLine 'echo', 'task: build-static'
println 'BuildDockerStatic: done at ' + new java.util.Date()
}
}
}
build.gradle
apply plugin: 'base'
task deployStaging {
doLast {
exec {
workingDir rootDir
//commandLine 'sh', './scripts/build_deploy_staging.sh'
commandLine 'echo', 'task: deploy-staging'
}
}
}
deployStaging.dependsOn ':build-static:buildDockerStatic'
deployStaging.dependsOn ':build-backend:buildDockerBackend'
settings.gradle
include 'build-static'
include 'build-backend'

Related

How to run certain task with gradle

I try to investigate a Gradle and follows some tutorials, but I have confused with the following:
I created are a couple of simple tasks:
task startProcess{
println 'startProcess'
}
task doStep2{
println 'Step2'
}
task doStep3{
println 'Step3'
}
task finishProcess{
println 'finishProcesss'
}
And try to execute one of them:
gradle finishProcess
Or with defaultTasks with command gradle build:
defaultTasks `finishProcess`
task startProcess{
println 'startProcess'
}
task doStep2{
println 'Step2'
}
task doStep3{
println 'Step3'
}
task finishProcess{
println 'finishProcesss'
}
In both options, I got the same result:
> Configure project :
startProcess
Step2
Step3
finishProcesss
BUILD SUCCESSFUL in 1s
How to execute exactly one of them?
You have to use register, I think if you did not use it, You're only asking Gradle to execute these tasks.
for example
tasks.register('startProcess') {
doLast {
println 'startProcess'
}
}
tasks.register('doStep2') {
doLast {
println 'Step2'
}
}
tasks.register('doStep3') {
doLast {
println 'Step3'
}
}
tasks.register('finishProcess') {
doLast {
println 'finishProcesss'
}
}
tasks.named("build") { finalizedBy("finishProcess") }
Registering these tasks, you will be able to call each one indivadually.
If you want to link a specific task, with a build task for example.
Then you can use finalizedBy like the following.
tasks.named("build") { finalizedBy("finishProcess") }
This will call the finishProcess task, whenever build is triggered.
I strongly recommend the official gradle documintation for more information about tasks.

How to pass command to includedproject for build.gradle

I need to pass command like "build -x test" to included project, how to do it?
any hints will be more than welcome! , currently it is without -x test like below
plugins {
id "com.gradle.build-scan" version "1.6"
}
apply plugin: 'eclipse'
defaultTasks 'build'
//gradle build -Pfast=true
task build{
if(project.findProperty('fast')&&project.findProperty('fast').toLowerCase()=='true') {
println "#### build fast### " + project.findProperty('fast')
gradle.includedBuild('another-project').task(':build')
}else{
println "#### build normally###"
dependsOn gradle.includedBuild('another-project').task(':test')
dependsOn gradle.includedBuild('another-project').task(':build')
}
}
task clean{
dependsOn gradle.includedBuild('another-project').task(':clean')
}
task test{
dependsOn gradle.includedBuild('another-project').task(':clean')
dependsOn gradle.includedBuild('another-project').task(':test')
}
below wordaround will solve my problem
workingDir '../another-project'
commandLine 'gradle', 'build' ,'-x' ,'test'

Run gradle task multiple times

I have a gradle project containing two modules in subdirectories. The directory structure is as below.
root
module1
build.gradle
module2
build.gradle
build.gradle
settings.gradle
The top level settings.gradle includes the two modules. The top level build.gradle contains the following.
task runScript(type: Exec) {
workingDir 'scripts'
commandLine 'python3', 'myscript.py'
}
project(':module1') {
evaluationDependsOn(':module1')
final test = tasks.findByName('test')
test.dependsOn(runScript)
}
project(':module2') {
evaluationDependsOn(':module2')
final test = tasks.findByName('test')
test.dependsOn(runScript)
}
Task runScript sets the database to a known state and must be run before each module test task.
When I run the test task my script only executes once. How can I ensure it executes multiple times?
$ ./gradlew test
... some output
:runScript
RUNNING MY SCRIPT
:module1:test
RUNNING MODULE1 TESTS
... some output
:module2:test
RUNNING MODULE2 TESTS
Things I Tried
I tried adding outputs.upToDateWhen {false} to the task runScript so Gradle never thinks it is up to date. This didn't make any difference; I assume because the task still only occurs once in the task graph?
I tried replacing the lines containing dependsOn for each module with test.doFirst {runScript.execute()}. This changes when the task gets executed but does not result in multiple executions.
$ ./gradlew test
... some output
:module1:test
RUNNING MY SCRIPT
RUNNING MODULE1 TESTS
... some output
:module2:test
RUNNING MODULE2 TESTS
I tried creating a new task for each module. This works but it's duplicating code.
project(':module1') {
evaluationDependsOn(':module1')
final test = tasks.findByName('test')
task runScript(type: Exec) {
workingDir '../scripts'
commandLine 'python3', 'myscript.py'
}
test.dependsOn(runScript)
}
project(':module2') {
evaluationDependsOn(':module2')
final test = tasks.findByName('test')
task runScript(type: Exec) {
workingDir '../scripts'
commandLine 'python3', 'myscript.py'
}
test.dependsOn(runScript)
}
If your script is necessary for each run of each Test task, simply assure its execution before each Test task. Why even use a task then?
subprojects {
tasks.withType(Test) {
doFirst {
exec {
workingDir 'scripts'
commandLine 'python3', 'myscript.py'
}
}
}
}

A Gradle task (type: Exec) is not executed during the configuration phase

I am reading about Gradle build lifecycle
Here is my script:
task startTomcat(type:Exec) {
commandLine 'cmd', '/c', 'echo init startTomcat'
}
task stopTomcat(type:Exec) {
// on windows:
commandLine 'cmd', '/c', 'echo init stopTomcat!'
doLast {
commandLine 'cmd', '/c', 'echo doLast stopTomcat!'
}
}
task configured(type:Exec) {
println 'configured. method body'
}
task test2 {
doLast {
println 'test2 doLast'
}
}
task testBoth2 {
doFirst {
println 'testBoth2 doFirst'
}
doLast {
println 'testBoth2 doLast'
}
println 'testBoth2. method body'
}
I run task2:
gradlew test2
This is the output:
Parallel execution with configuration on demand is an incubating feature.
configured. method body
testBoth2. method body
:test2
test2 doLast
BUILD SUCCESSFUL
It looks like the calls to commandLine were ignored. Why?
The Exec task's commandLine properly only configures what to do if the task is executed. As such you don't see the actual command doing anything during the configuration phase.

Gradle, task type: Exec - commandLine not work in onLast

I want execute some command from command line in gradle task(e.g. print all files in dir):
task dir(type: Exec) {
def adbCommand = ["dir", "*.*"]
commandLine adbCommand
standardOutput = new ByteArrayOutputStream()
doLast {
println ("result = " + standardOutput)
}
}
It's work. OK. But when I put it on onLast section it's not work:
task dir(type: Exec) {
doLast {
def adbCommand = ["dir", "*.*"]
commandLine adbCommand
standardOutput = new ByteArrayOutputStream()
println ("result = " + standardOutput)
}
}
I get error:
FAILURE: Build failed with an exception.
What went wrong:
Execution failed for task ':app:dir'.
execCommand == null!
The reason is in the fact, that task of Exec should be configured during configuration phase of the build, otherwise your task will be not configured and fail.
In you first example everything works due to configuration happens at the configuratyion phase. Your second example tries to configure the task within doLast closure - right after the task is executed yet.
If you really need to execute something in doLast, you can use something like this, without creating special task:
task someTaskName {
doLast {
exec {
commandLine adbCommand
}
}
}
Here is exec-specification used to execute some command and it's configured and executed at the same time.

Resources