Create Gradle task that contains several tasks - gradle

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

Related

skip a task in gradle, but only if the task exists

I have a complex multi sub-project gradle project with a little kotlin multiplatform inside and a fex custom gradle plugin.
My issue is , when I want to run the build by skipping some tasks(mostly test), not all the project have the same test task name : for some it's call jsTest, for other nodeJsTest, for other jvmTest.
So when I call gradle build -x jsTest -x nodeJsTest I have error because sometime some of the tasks to skip don't exist.
How can I skip the task, and ignore-it if it don't exist?
You can modify your Gradle file to do something like (Kotlin DSL):
tasks.named("build") {
dependsOn.removeIf { it.toString().contains("flakyTest") }
}
Otherwise you will need to aggregate your tasks to what you want specifically either by doing ./gradlew myTask anotherTask anotherOne andAnotherOne or create a task that dependsOn all the tasks you want.
Instead of making your test tasks to depend on build directly, you can create generic test task task testType in the middle- build triggers task testType and then it triggers jsTest or whatever relevant test task you create in that module.
Now you can safely run gradle build -x testType.
For example (in your .gradle file):
task jsTest { ... }
task testType { dependsOn jsTest }
build.finalizedBy(testType)
Do the same for the rest of your test tasks files, you can also create task testType globaly if you want the solution to be cleaner.

Gradle: where are task dependencies defined for distZip task

I am using the gradle application plugin.
I noticed that:
distZip task get's run before the assemble task.
And I can see
from the docs (link above), and confirmed during my gradle run, that
the distZip tasks depends on: jar, startScripts tasks
So execution looks something like this:
> Task :my-app:processResources
> Task :my-app:compileJava
> Task :my-app:classes
> Task :my-app:jar
> Task :my-app:startScripts
> Task :my-app:distTar
> Task :my-app:distZip
> Task :my-app:assemble
Looking through the code in ApplicationPlugin.java and/or DistributionPlugin.java, I expected to see the task dependencies defined.
e.g. something like this:
distZipTask.dependsOn(jarTask)
distZipTask.dependsOn(createScriptsTask)
assembleTask.dependsOn(distZipTask)
...but I couldn't find anything like this in the java code.
Would very much appreciate it if someone could point me in the direction of where to look to find out how these tasks are 'linked'.
Gradle can determine implicit task dependencies from task inputs. If (the output of) a task is used as input of another task, Gradle can derive that there must be a dependency on the task. This functionality is called incremental build support, as it also supports checking for changes in the inputs or outputs to skip tasks if the inputs did not change.
Regarding your example, the task dependencies are defined implicitly in line 197 and line 208 of the file ApplicationPlugin.java:
libChildSpec.from(jar);
...
binChildSpec.from(startScripts);
The arguments in these lines are the respective tasks (to be exact: providers of the tasks). The from method can often be used to define sources of a file operation (e.g. copying, zipping ...). So we define the results (outputs) of the tasks jar and startScripts as a source of some operation (CopySpec). The resulting CopySpec objects are then passed to both tasks distZip and distTar. When a task tries to resolve the defined sources it finds the tasks jar and startScripts and defines the dependencies on them on its own.
Please note that the mentioned task assemble is a so-called lifecycle task. It does not run any action but can be used to organize tasks into build phases (just like build and check).

Task with path 'build' not found in root project

I have a multiproject and after the last subproject is built, I'd like to process all jars.
Therefore I created a task in the root-project:
task install(dependsOn: 'build', type: Copy) {
doLast {
println "exec install task"
}
}
Upon calling ./gradlew install in the root directory, I'm facing this error:
FAILURE: Build failed with an exception.
* What went wrong:
Could not determine the dependencies of task ':install'.
> Task with path 'build' not found in root project 'foo'.
However, calling ./gradlew tasks shows me these tasks:
:tasks
------------------------------------------------------------
All tasks runnable from root project
------------------------------------------------------------
Build tasks
-----------
assemble - Assembles the outputs of this project.
build - Assembles and tests this project.
...
How can I achieve the desired functionality?
I assume, that your root project organizes the build, but does not define build action taken by itself. The build task is often defined by language plugins (in most cases via apply plugin: 'java'), but if your root project does not use any of them, it won't have a build task.
The description of the help task tasks, which you used, says:
Displays the tasks runnable from root project 'projectReports' (some of the displayed tasks may belong to subprojects).
The help task followes the same logic as the task activation via the command line. You can provide a task name and any task with the name in any subproject will be executed (thats why gradle build works).
But if you define a dependsOn dependency, the given string is evaluated as task path for a single task. Since each task name can only be used once in a project, the name is unique for tasks in the root project, but many tasks could be found, if subprojects would be considered. Therefor, one can use the syntax :<projectName>:<taskName> to identify tasks in subprojects.
Now, let's face your specific problem: If the install task should depend on the build task of one single subproject, you could use dependsOn ':<mySubproject>:build'. But I assume you want the install task to depend on each subproject build task, so I'd like to propose this approach:
task install(type: Copy) {
dependsOn subprojects*.tasks*.findByName('build').minus(null)
doLast {
println "exec install task"
}
}
This way, for each registered subproject, findByName('build') is called and the result (the found task or null) is put into a list, which is then used as task dependency list. I added the .minus(null) part to remove null entries from the list, because I am not sure how Gradle handles such entries in a dependency collection. If you are sure, that each subproject provides a build task, you can use getByName('build'), too.
EDIT: OP found the optionally recursive getTasksByName method, which suits this case even better than iterating over all subprojects manually:
dependsOn getTasksByName('build', true)

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

Ensuring a Gradle task is executed after another task

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"

Resources