A RetroSearch Logo

Home - News ( United States | United Kingdom | Italy | Germany ) - Football scores

Search Query:

Showing content from https://docs.gradle.org/current/userguide/controlling_task_execution.html below:

Controlling Task Execution

Task dependencies allow tasks to be executed in a specific order based on their dependencies. This ensures that tasks dependent on others are only executed after those dependencies have completed.

Task dependencies can be categorized as either implicit or explicit:

Implicit dependencies

These dependencies are automatically inferred by Gradle based on the tasks' actions and configuration. For example, if taskB uses the output of taskA (e.g., a file generated by taskA), Gradle will automatically ensure that taskA is executed before taskB to fulfill this dependency.

Explicit dependencies

These dependencies are explicitly declared in the build script using the dependsOn, mustRunAfter, or shouldRunAfter methods. For example, if you want to ensure that taskB always runs after taskA, you can explicitly declare this dependency using taskB.mustRunAfter(taskA).

Both implicit and explicit dependencies play a crucial role in defining the order of task execution and ensuring that tasks are executed in the correct sequence to produce the desired build output.

Task dependencies

Gradle inherently understands the dependencies among tasks. Consequently, it can determine the tasks that need execution when you target a specific task.

Let’s take an example application with an app subproject and a some-logic subproject:

settings.gradle.kts

rootProject.name = "gradle-project"
include("app")
include("some-logic")

settings.gradle

rootProject.name = 'gradle-project'
include('app')
include('some-logic')

Let’s imagine that the app subproject depends on the subproject called some-logic, which contains some Java code. We add this dependency in the app build script:

app/build.gradle.kts

plugins {
    id("application")                       // app is now a java application
}

application {
    mainClass.set("hello.HelloWorld")       // main class name required by the application plugin
}

dependencies {
    implementation(project(":some-logic"))  // dependency on some-logic
}

app/build.gradle

plugins {
    id('application')                       // app is now a java application
}

application {
    mainClass = 'hello.HelloWorld'          // main class name required by the application plugin
}

dependencies {
    implementation(project(':some-logic'))  // dependency on some-logic
}

If we run :app:build again, we see the Java code of some-logic is also compiled by Gradle automatically:

$./gradlew :app:build

> Task :app:processResources NO-SOURCE
> Task :app:processTestResources NO-SOURCE
> Task :some-logic:compileJava UP-TO-DATE
> Task :some-logic:processResources NO-SOURCE
> Task :some-logic:classes UP-TO-DATE
> Task :some-logic:jar UP-TO-DATE
> Task :app:compileJava
> Task :app:classes
> Task :app:jar UP-TO-DATE
> Task :app:startScripts
> Task :app:distTar
> Task :app:distZip
> Task :app:assemble
> Task :app:compileTestJava UP-TO-DATE
> Task :app:testClasses UP-TO-DATE
> Task :app:test
> Task :app:check
> Task :app:build

BUILD SUCCESSFUL in 430ms
9 actionable tasks: 5 executed, 4 up-to-date
Adding dependencies

There are several ways you can define the dependencies of a task.

Defining dependencies using task names and the dependsOn()` method is simplest.

The following is an example which adds a dependency from taskX to taskY:

tasks.register("taskX") {
    dependsOn("taskY")
}
tasks.register("taskX") {
    dependsOn "taskY"
}
$ gradle -q taskX
taskY
taskX

For more information about task dependencies, see the Task API.

Ordering tasks

In some cases, it is useful to control the order in which two tasks will execute, without introducing an explicit dependency between those tasks.

The primary difference between a task ordering and a task dependency is that an ordering rule does not influence which tasks will be executed, only the order in which they will be executed.

Task ordering can be useful in a number of scenarios:

Two ordering rules are available: "must run after" and "should run after".

To specify a "must run after" or "should run after" ordering between 2 tasks, you use the Task.mustRunAfter(java.lang.Object...) and Task.shouldRunAfter(java.lang.Object...) methods. These methods accept a task instance, a task name, or any other input accepted by Task.dependsOn(java.lang.Object...).

When you use "must run after", you specify that taskY must always run after taskX when the build requires the execution of taskX and taskY. So if you only run taskY with mustRunAfter, you won’t cause taskX to run. This is expressed as taskY.mustRunAfter(taskX).

build.gradle.kts

val taskX by tasks.registering {
    doLast {
        println("taskX")
    }
}
val taskY by tasks.registering {
    doLast {
        println("taskY")
    }
}
taskY {
    mustRunAfter(taskX)
}

build.gradle

def taskX = tasks.register('taskX') {
    doLast {
        println 'taskX'
    }
}
def taskY = tasks.register('taskY') {
    doLast {
        println 'taskY'
    }
}
taskY.configure {
    mustRunAfter taskX
}
$ gradle -q taskY taskX
taskX
taskY

The "should run after" ordering rule is similar but less strict, as it will be ignored in two situations:

  1. If using that rule introduces an ordering cycle.

  2. When using parallel execution and all task dependencies have been satisfied apart from the "should run after" task, then this task will be run regardless of whether or not its "should run after" dependencies have been run.

You should use "should run after" where the ordering is helpful but not strictly required:

build.gradle.kts

val taskX by tasks.registering {
    doLast {
        println("taskX")
    }
}
val taskY by tasks.registering {
    doLast {
        println("taskY")
    }
}
taskY {
    shouldRunAfter(taskX)
}

build.gradle

def taskX = tasks.register('taskX') {
    doLast {
        println 'taskX'
    }
}
def taskY = tasks.register('taskY') {
    doLast {
        println 'taskY'
    }
}
taskY.configure {
    shouldRunAfter taskX
}
$ gradle -q taskY taskX
taskX
taskY

In the examples above, it is still possible to execute taskY without causing taskX to run:

The "should run after" ordering rule will be ignored if it introduces an ordering cycle:

build.gradle.kts

val taskX by tasks.registering {
    doLast {
        println("taskX")
    }
}
val taskY by tasks.registering {
    doLast {
        println("taskY")
    }
}
val taskZ by tasks.registering {
    doLast {
        println("taskZ")
    }
}
taskX { dependsOn(taskY) }
taskY { dependsOn(taskZ) }
taskZ { shouldRunAfter(taskX) }

build.gradle

def taskX = tasks.register('taskX') {
    doLast {
        println 'taskX'
    }
}
def taskY = tasks.register('taskY') {
    doLast {
        println 'taskY'
    }
}
def taskZ = tasks.register('taskZ') {
    doLast {
        println 'taskZ'
    }
}
taskX.configure { dependsOn(taskY) }
taskY.configure { dependsOn(taskZ) }
taskZ.configure { shouldRunAfter(taskX) }
$ gradle -q taskX
taskZ
taskY
taskX

Note that taskY.mustRunAfter(taskX) or taskY.shouldRunAfter(taskX) does not imply any execution dependency between the tasks:

Finalizer tasks

Finalizer tasks are automatically added to the task graph when the finalized task is scheduled to run.

To specify a finalizer task, you use the Task.finalizedBy(java.lang.Object…​) method. This method accepts a task instance, a task name, or any other input accepted by Task.dependsOn(java.lang.Object…​):

build.gradle.kts

val taskX by tasks.registering {
    doLast {
        println("taskX")
    }
}
val taskY by tasks.registering {
    doLast {
        println("taskY")
    }
}

taskX { finalizedBy(taskY) }

build.gradle

def taskX = tasks.register('taskX') {
    doLast {
        println 'taskX'
    }
}
def taskY = tasks.register('taskY') {
    doLast {
        println 'taskY'
    }
}

taskX.configure { finalizedBy taskY }
$ gradle -q taskX
taskX
taskY

Finalizer tasks are executed even if the finalized task fails or if the finalized task is considered UP-TO-DATE:

build.gradle.kts

val taskX by tasks.registering {
    doLast {
        println("taskX")
        throw RuntimeException()
    }
}
val taskY by tasks.registering {
    doLast {
        println("taskY")
    }
}

taskX { finalizedBy(taskY) }

build.gradle

def taskX = tasks.register('taskX') {
    doLast {
        println 'taskX'
        throw new RuntimeException()
    }
}
def taskY = tasks.register('taskY') {
    doLast {
        println 'taskY'
    }
}

taskX.configure { finalizedBy taskY }
$ gradle -q taskX
taskX
taskY

FAILURE: Build failed with an exception.

* Where:
Build file '/home/user/gradle/samples/build.gradle' line: 4

* What went wrong:
Execution failed for task ':taskX'.
> java.lang.RuntimeException (no error message)

* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to generate a Build Scan (Powered by Develocity).
> Get more help at https://help.gradle.org.

BUILD FAILED in 0s

Finalizer tasks are useful when the build creates a resource that must be cleaned up, regardless of whether the build fails or succeeds. An example of such a resource is a web container that is started before an integration test task and must be shut down, even if some tests fail.

Skipping tasks

Gradle offers multiple ways to skip the execution of a task.

1. Using a predicate

You can use Task.onlyIf to attach a predicate to a task. The task’s actions will only be executed if the predicate is evaluated to be true.

The predicate is passed to the task as a parameter and returns true if the task will execute and false if the task will be skipped. The predicate is evaluated just before the task is executed.

Passing an optional reason string to onlyIf() is useful for explaining why the task is skipped:

build.gradle.kts

val hello by tasks.registering {
    doLast {
        println("hello world")
    }
}

hello {
    val skipProvider = providers.gradleProperty("skipHello")
    onlyIf("there is no property skipHello") {
        !skipProvider.isPresent()
    }
}

build.gradle

def hello = tasks.register('hello') {
    doLast {
        println 'hello world'
    }
}

hello.configure {
    def skipProvider = providers.gradleProperty("skipHello")
    onlyIf("there is no property skipHello") {
        !skipProvider.present
    }
}
$ gradle hello -PskipHello
> Task :hello SKIPPED

BUILD SUCCESSFUL in 0s

To find why a task was skipped, run the build with the --info logging level.

$ gradle hello -PskipHello --info
...

> Task :hello SKIPPED
Skipping task ':hello' as task onlyIf 'there is no property skipHello' is false.
:hello (Thread[included builds,5,main]) completed. Took 0.018 secs.

BUILD SUCCESSFUL in 13s
2. Using StopExecutionException

If the logic for skipping a task can’t be expressed with a predicate, you can use the StopExecutionException.

If this exception is thrown by an action, the task action as well as the execution of any following action is skipped. The build continues by executing the next task:

build.gradle.kts

val compile by tasks.registering {
    doLast {
        println("We are doing the compile.")
    }
}

compile {
    doFirst {
        // Here you would put arbitrary conditions in real life.
        if (true) {
            throw StopExecutionException()
        }
    }
}
tasks.register("myTask") {
    dependsOn(compile)
    doLast {
        println("I am not affected")
    }
}

build.gradle

def compile = tasks.register('compile') {
    doLast {
        println 'We are doing the compile.'
    }
}

compile.configure {
    doFirst {
        // Here you would put arbitrary conditions in real life.
        if (true) {
            throw new StopExecutionException()
        }
    }
}
tasks.register('myTask') {
    dependsOn('compile')
    doLast {
        println 'I am not affected'
    }
}
$ gradle -q myTask
I am not affected

This feature is helpful if you work with tasks provided by Gradle. It allows you to add conditional execution of the built-in actions of such a task.

3. Enabling and Disabling tasks

Every task has an enabled flag, which defaults to true. Setting it to false prevents executing the task’s actions.

A disabled task will be labeled SKIPPED:

build.gradle.kts

val disableMe by tasks.registering {
    doLast {
        println("This should not be printed if the task is disabled.")
    }
}

disableMe {
    enabled = false
}

build.gradle

def disableMe = tasks.register('disableMe') {
    doLast {
        println 'This should not be printed if the task is disabled.'
    }
}

disableMe.configure {
    enabled = false
}
$ gradle disableMe
> Task :disableMe SKIPPED

BUILD SUCCESSFUL in 0s
4. Task timeouts

Every task has a timeout property, which can be used to limit its execution time. When a task reaches its timeout, its task execution thread is interrupted. The task will be marked as FAILED.

Finalizer tasks are executed. If --continue is used, other tasks continue running.

Tasks that don’t respond to interrupts can’t be timed out. All of Gradle’s built-in tasks respond to timeouts.

build.gradle.kts

tasks.register("hangingTask") {
    doLast {
        Thread.sleep(100000)
    }
    timeout = Duration.ofMillis(500)
}

build.gradle

tasks.register("hangingTask") {
    doLast {
        Thread.sleep(100000)
    }
    timeout = Duration.ofMillis(500)
}
Task rules

Sometimes you want to have a task whose behavior depends on a large or infinite number value range of parameters. A very nice and expressive way to provide such tasks are task rules:

build.gradle.kts

tasks.addRule("Pattern: ping<ID>") {
    val taskName = this
    if (startsWith("ping")) {
        task(taskName) {
            doLast {
                println("Pinging: " + (taskName.replace("ping", "")))
            }
        }
    }
}

build.gradle

tasks.addRule("Pattern: ping<ID>") { String taskName ->

    if (taskName.startsWith("ping")) {
        task(taskName) {
            doLast {
                println "Pinging: " + (taskName - 'ping')
            }
        }
    }
}
$ gradle -q pingServer1
Pinging: Server1

The String parameter is used as a description for the rule, which is shown with ./gradlew tasks.

Rules are not only used when calling tasks from the command line. You can also create dependsOn relations on rule based tasks:

build.gradle.kts

tasks.addRule("Pattern: ping<ID>") {
    val taskName = this
    if (startsWith("ping")) {
        task(taskName) {
            doLast {
                println("Pinging: " + (taskName.replace("ping", "")))
            }
        }
    }
}

tasks.register("groupPing") {
    dependsOn("pingServer1", "pingServer2")
}

build.gradle

tasks.addRule("Pattern: ping<ID>") { String taskName ->

    if (taskName.startsWith("ping")) {
        task(taskName) {
            doLast {
                println "Pinging: " + (taskName - 'ping')
            }
        }
    }
}

tasks.register('groupPing') {
    dependsOn 'pingServer1', 'pingServer2'
}
$ gradle -q groupPing
Pinging: Server1
Pinging: Server2

If you run ./gradlew -q tasks, you won’t find a task named pingServer1 or pingServer2, but this script is executing logic based on the request to run those tasks.

Exclude tasks from execution

You can exclude a task from execution using the -x or --exclude-task command-line option and provide the task’s name to exclude.

$ ./gradlew build -x test

For instance, you can run the check task but exclude the test task from running. This approach can lead to unexpected outcomes, particularly if you exclude an actionable task that produces results needed by other tasks. Instead of relying on the -x parameter, defining a suitable lifecycle task for the desired action is recommended.

Using -x is a practice that should be avoided, although still commonly observed.


RetroSearch is an open source project built by @garambo | Open a GitHub Issue

Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo

HTML: 3.2 | Encoding: UTF-8 | Version: 0.7.4