// Copyright (C) 2024 Gradle, Inc.
//
// Licensed under the Creative Commons Attribution-Noncommercial-ShareAlike 4.0 International License.;
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      https://creativecommons.org/licenses/by-nc-sa/4.0/
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

[[controlling_task_execution]]
= 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.

image::writing-tasks-2.png[]

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.

[[sec:more_task_dependencies]]
== 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:

====
[.multi-language-sample]
=====
.settings.gradle.kts
[source,kotlin]
----
rootProject.name = "gradle-project"
include("app")
include("some-logic")
----
=====
[.multi-language-sample]
=====
.settings.gradle
[source,groovy]
----
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:

====
[.multi-language-sample]
=====
.app/build.gradle.kts
[source,kotlin]
----
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
}
----
=====
[.multi-language-sample]
=====
.app/build.gradle
[source,groovy]
----
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:

[source,text]
----
$./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
----

[[sec:adding_dependencies_to_tasks]]
== Adding dependencies

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

Defining dependencies using task names and the link:{javadocPath}/org/gradle/api/DefaultTask.html#dependsOn-java.lang.Object...-[dependsOn()`] method is simplest.

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

====
[.multi-language-sample]
=====
[source,kotlin]
----
tasks.register("taskX") {
    dependsOn("taskY")
}
----
=====
[.multi-language-sample]
=====
[source,groovy]
----
tasks.register("taskX") {
    dependsOn "taskY"
}
----
=====
====

====
----
$ gradle -q taskX
include::{snippetsPath}/tasks/addDependencyUsingPath/tests/addDependencyUsingPath.out[]
----
====

For more information about task dependencies, see the link:{javadocPath}/org/gradle/api/Task.html[Task] API.

[[sec:ordering_tasks]]
== 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:

* Enforce sequential ordering of tasks (e.g., `build` never runs before `clean`).
* Run build validations early in the build (e.g., validate I have the correct credentials before starting the work for a release build).
* Get feedback faster by running quick verification tasks before long verification tasks (e.g., unit tests should run before integration tests).
* A task that aggregates the results of all tasks of a particular type (e.g., test report task combines the outputs of all executed test tasks).

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 link:{javadocPath}/org/gradle/api/Task.html#mustRunAfter-java.lang.Object...-[Task.mustRunAfter(java.lang.Object++...++)] and link:{javadocPath}/org/gradle/api/Task.html#shouldRunAfter-java.lang.Object...-[Task.shouldRunAfter(java.lang.Object++...++)] methods.
These methods accept a task instance, a task name, or any other input accepted by link:{javadocPath}/org/gradle/api/Task.html#dependsOn-java.lang.Object...-[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)`.

====
include::sample[dir="snippets/tasks/mustRunAfter/kotlin",files="build.gradle.kts[]"]
include::sample[dir="snippets/tasks/mustRunAfter/groovy",files="build.gradle[]"]

----
$ gradle -q taskY taskX
include::{snippetsPath}/tasks/mustRunAfter/tests/mustRunAfter.out[]
----
====

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:

====
include::sample[dir="snippets/tasks/shouldRunAfter/kotlin",files="build.gradle.kts[]"]
include::sample[dir="snippets/tasks/shouldRunAfter/groovy",files="build.gradle[]"]

----
$ gradle -q taskY taskX
include::{snippetsPath}/tasks/shouldRunAfter/tests/shouldRunAfter.out[]
----
====

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

====
----
$ gradle -q taskY
include::{snippetsPath}/tasks/mustRunAfter/tests/mustRunAfterSingleTask.out[]
----
====

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

====
include::sample[dir="snippets/tasks/shouldRunAfterWithCycle/kotlin",files="build.gradle.kts[]"]
include::sample[dir="snippets/tasks/shouldRunAfterWithCycle/groovy",files="build.gradle[]"]

----
$ gradle -q taskX
include::{snippetsPath}/tasks/shouldRunAfterWithCycle/tests/shouldRunAfterWithCycle.out[]
----
====

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

* It is possible to execute `taskX` and `taskY` independently. The ordering rule only has an effect when both tasks are scheduled for execution.
* When run with `--continue`, it is possible for `taskY` to execute if `taskX` fails.

[[sec:finalizer_tasks]]
== Finalizer tasks

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.

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 link:{javadocPath}/org/gradle/api/Task.html#finalizedBy-java.lang.Object...-[Task.finalizedBy(java.lang.Object...)] method.
This method accepts a task instance, a task name, or any other input accepted by link:{javadocPath}/org/gradle/api/Task.html#dependsOn-java.lang.Object...-[Task.dependsOn(java.lang.Object...)]:

====
include::sample[dir="snippets/tasks/finalizers/kotlin",files="build.gradle.kts[]"]
include::sample[dir="snippets/tasks/finalizers/groovy",files="build.gradle[]"]

----
$ gradle -q taskX
include::{snippetsPath}/tasks/finalizers/tests/taskFinalizers.out[]
----
====

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

====
include::sample[dir="snippets/tasks/finalizersWithFailure/kotlin",files="build.gradle.kts[]"]
include::sample[dir="snippets/tasks/finalizersWithFailure/groovy",files="build.gradle[]"]

----
$ gradle -q taskX
include::{snippetsPath}/tasks/finalizersWithFailure/tests-groovy/taskFinalizersWithFailureGroovy.out[]
----
====

[[sec:skipping_tasks]]
== Skipping tasks

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

[[sec:using_a_predicate]]
=== 1. Using a predicate

You can use link:{javadocPath}/org/gradle/api/Task.html#onlyIf-org.gradle.api.specs.Spec-[`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:

====
include::sample[dir="snippets/tutorial/taskOnlyIf/kotlin",files="build.gradle.kts[]"]
include::sample[dir="snippets/tutorial/taskOnlyIf/groovy",files="build.gradle[]"]

----
$ gradle hello -PskipHello
include::{snippetsPath}/tutorial/taskOnlyIf/tests/taskOnlyIf.out[]
----
====

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

====
----
$ gradle hello -PskipHello --info
include::{snippetsPath}/tutorial/taskOnlyIf/tests/taskOnlyIf-info.out[]
----
====

[[sec:using_stopexecutionexception]]
=== 2. Using `StopExecutionException`

If the logic for skipping a task can't be expressed with a predicate, you can use the link:{javadocPath}/org/gradle/api/tasks/StopExecutionException.html[`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:

====
include::sample[dir="snippets/tutorial/stopExecutionException/kotlin",files="build.gradle.kts[]"]
include::sample[dir="snippets/tutorial/stopExecutionException/groovy",files="build.gradle[]"]

----
$ gradle -q myTask
include::{snippetsPath}/tutorial/stopExecutionException/tests/stopExecutionException.out[]
----
====

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.footnote:[You might be wondering why there is neither an import for the `StopExecutionException` nor do we access it via its fully qualified name. The reason is that Gradle adds a set of default imports to your script (see <<default_script_imports.adoc#script-default-imports,Default imports>>).]

[[sec:enabling_and_disabling_tasks]]
=== 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`:

====
include::sample[dir="snippets/tutorial/disableTask/kotlin",files="build.gradle.kts[]"]
include::sample[dir="snippets/tutorial/disableTask/groovy",files="build.gradle[]"]

----
$ gradle disableMe
include::{snippetsPath}/tutorial/disableTask/tests/disableTask.out[]
----
====

[[sec:task_timeouts]]
=== 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`.

<<sec:finalizer_tasks,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.

====
include::sample[dir="snippets/tasks/timeout/kotlin",files="build.gradle.kts[tags=without-import]"]
include::sample[dir="snippets/tasks/timeout/groovy",files="build.gradle[]"]
====

[[sec:task_rules]]
== 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:

====
include::sample[dir="snippets/tasks/addRules/kotlin",files="build.gradle.kts[tags=task-rule]"]
include::sample[dir="snippets/tasks/addRules/groovy",files="build.gradle[tags=task-rule]"]

----
$ gradle -q pingServer1
include::{snippetsPath}/tasks/addRules/tests/taskRule.out[]
----
====

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:

====
include::sample[dir="snippets/tasks/addRules/kotlin",files="build.gradle.kts[tags=all]"]
include::sample[dir="snippets/tasks/addRules/groovy",files="build.gradle[tags=all]"]

----
$ gradle -q groupPing
include::{snippetsPath}/tasks/addRules/tests/taskRuleDependsOn.out[]
----
====

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.

[[sec:exclude_task]]
== 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.

[source,text]
----
$ ./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.

