Gradle Task Dependencies and Ordering: Difference between revisions

From NovaOrdis Knowledge Base
Jump to navigation Jump to search
 
(46 intermediate revisions by the same user not shown)
Line 2: Line 2:
* https://docs.gradle.org/current/dsl/org.gradle.api.Task.html#N18D13
* https://docs.gradle.org/current/dsl/org.gradle.api.Task.html#N18D13
* https://docs.gradle.org/current/javadoc/org/gradle/api/Task.html#dependencies
* https://docs.gradle.org/current/javadoc/org/gradle/api/Task.html#dependencies
* https://docs.gradle.org/current/userguide/more_about_tasks.html#sec:adding_dependencies_to_tasks


=Internal=
=Internal=
Line 10: Line 11:
May times, a task requires another task to run first, especially if the task depends on the produced output of its dependency task. A task may declared its dependencies [[#Explicit_Dependencies|explicitly]]. A task may depend on other tasks implicitly, as described in the [[#Implicit_Dependencies|Implicit Dependencies]] section.
May times, a task requires another task to run first, especially if the task depends on the produced output of its dependency task. A task may declared its dependencies [[#Explicit_Dependencies|explicitly]]. A task may depend on other tasks implicitly, as described in the [[#Implicit_Dependencies|Implicit Dependencies]] section.


In Gradle, task execution order is automatically determined taking into account explicit dependencies and implicit dependencies, and a specific execution order of all tasks involved is not guaranteed. The only thing that is guaranteed is that the dependencies will be honored. This architectural decision has several benefits: you don't need to know the whole chain of task dependencies to make a change, and because the tasks don't have to be executed strictly sequentially, they can be parallized.
In Gradle, task execution order is automatically determined taking into account explicit dependencies and implicit dependencies, and a specific execution order for the tasks that declared dependencies among themselves is not guaranteed. The only thing that is guaranteed is that the dependencies will be honored. This architectural decision has several benefits: you don't need to know the whole chain of task dependencies to make a change, and because the tasks don't have to be executed strictly sequentially, they can be parallelized.
 
However, Gradle also offers a way to request an [[#Ordering_Tasks|execution order]] between tasks, in absence of dependency.
 
If tasks belonging to different sub-projects that are part of the same multi-project define dependencies among themselves, that automatically introduces a dependency between the owning projects: {{Internal|Gradle_Multi-Project_Builds#Task_Dependencies|Multi-project builds | Inter-project Dependencies}}


=Explicit Dependencies=
=Explicit Dependencies=
The Task API used to declare explicit task dependencies is [https://docs.gradle.org/current/javadoc/org/gradle/api/Task.html#dependsOn-java.lang.Object...- Task.dependsOn(Object... paths)], which surfaces in the DSL as:
A task's explicit dependencies are maintained and can be configured with the task's "dependsOn" [[Gradle_Task#Task_Properties|property]]. The Task API used to declare explicit task dependencies is [https://docs.gradle.org/current/javadoc/org/gradle/api/Task.html#dependsOn-java.lang.Object...- Task.dependsOn(Object... paths)], which surfaces in the DSL as:
<syntaxhighlight lang='groovy'>
<syntaxhighlight lang='groovy'>
task red {
task red {
   dependsOn 'blue'
   dependsOn 'blue', 'green' // dependency expressed using task names, comma is required for more than one dependencies
   ...
   ...
}
}
</syntaxhighlight>
</syntaxhighlight>
<syntaxhighlight lang='groovy'>
<syntaxhighlight lang='groovy'>
// dependency expressed using task objects
task red(dependsOn: [blue, green] {
task red(dependsOn: [blue, green] {
   ...
   ...
Line 29: Line 35:
   ...
   ...
}
}
red.dependsOn(blue, green) // dependency expressed using task objects
</syntaxhighlight>
<syntaxhighlight lang='groovy'>
task red {
  ...
}
red.dependsOn blue // dependency expressed using task objects
red.dependsOn green // dependency expressed using task objects
</syntaxhighlight>


red.dependsOn(blue, green)
</syntaxhighlight>
Note that a task dependency may be another task name, the task instance itself or another objects.  
Note that a task dependency may be another task name, the task instance itself or another objects.  


If multiple tasks are provides as argument of dependsOn(...), the order in which they are declared does not influence the order in which they are executed. The only thing that is guaranteed is that all will be executed before the task that declares the dependency.
If multiple tasks are provides as argument of dependsOn(...), the order in which they are declared does not influence the order in which they are executed. The only thing that is guaranteed is that all will be executed before the task that declares the dependency.
==Adding Explicit Dependencies using a Lazy Block==
Task dependencies can be defined using a lazy block. When evaluated, the block is passed the task whose dependencies are being calculated. The block should return one task or a collection of tasks, which are then treated as dependencies of the task:
<syntaxhighlight lang='groovy'>
task red {
  ...
}
red.dependsOn {
  tasks.findAll { task -> task.name.startsWith('b') }
}
</syntaxhighlight>
==Other Methods to Set Explicit Dependencies==


Other methods that can be used to set explicit dependencies are:
Other methods that can be used to set explicit dependencies are:
* [https://docs.gradle.org/current/javadoc/org/gradle/api/Task.html#setDependsOn-java.lang.Iterable- Task.setDependsOn(Iterable)]
* [https://docs.gradle.org/current/javadoc/org/gradle/api/Task.html#setDependsOn-java.lang.Iterable- Task.setDependsOn(Iterable)]
* [https://docs.gradle.org/current/javadoc/org/gradle/api/Task.html#mustRunAfter-java.lang.Object...- Task.mustRunAfter(Object...)]
 
* [https://docs.gradle.org/current/javadoc/org/gradle/api/Task.html#setMustRunAfter-java.lang.Iterable- Task.setMustRunAfter(Iterable)]
==Explicit Dependencies between Tasks belonging to Different Projects==
* [https://docs.gradle.org/current/javadoc/org/gradle/api/Task.html#shouldRunAfter-java.lang.Object...- Task.shouldRunAfter(Object ...)]
In a multi-project situation, tasks belonging to two different projects can express the dependency on one another by using [[Gradle_Task#Name_and_Path|task paths]]:
* [https://docs.gradle.org/current/javadoc/org/gradle/api/Task.html#setShouldRunAfter-java.lang.Iterable- Task.setShouldRunAfter(Iterable)]
<syntaxhighlight lang='groovy'>
red.dependsOn(':project-a:yellow')
</syntaxhighlight>
<syntaxhighlight lang='groovy'>
task red(..., dependsOn: [':project-a:yellow', ':project-b:brown']) {
  ...
}
</syntaxhighlight>
<syntaxhighlight lang='groovy'>
task red {
  dependsOn ':project-a:yellow', ':project-b:brown' // comma is required
}
</syntaxhighlight>
 
This explicit dependency also introduces a [[Gradle_Multi-Project_Builds#Inter-Project_Dependencies|dependency between projects]], causing dependency project ("project-a") to be configured before the current project in the [[Gradle_Concepts#Configuration_Phase|configuration phase]].


==Finalizer Tasks==
==Finalizer Tasks==
Line 52: Line 94:
a.finalizedBy b
a.finalizedBy b
</syntaxhighlight>
</syntaxhighlight>
=Ordering Tasks=
{{External|https://docs.gradle.org/current/userguide/more_about_tasks.html#sec:ordering_tasks}}
It is possible to request a specific order in which two tasks execute, without introducing a dependency relationship between tasks: if both tasks are executed, they will be executed in the requested order; if only one of the tasks is executed, but not the other, the order between tasks becomes irrelevant. Dependency is mandatory execution of both task, in the required order, so dependency implies order. Order does not imply mandatory execution, just ordered execution if both tasks are executed; order does not imply dependency. If there is an order relationship between two tasks A and B, it is still possible to execute task independently: execute task A without task B and vice-versa. The ordering rule only has an effect when both tasks are scheduled for execution.
<span id='continue'></span>Note that when there's an order relationship between tasks, and the tasks are run with [[Gradle_Command_Line#--continue|--continue]], it is possible for B to execute in the event that A fails.
Task ordering can be useful for example in the situation when both unit and integration tests are executed and it is useful to run the unit tests first and get feedback faster. There is no dependency relationship between tasks, one can be executed without the other. However, it is useful if they execute in a specific order, if they both execute.
There are two ordering rules: [[#mustRunAfter|mustRunAfter]] and [[#shouldRunAfter|shouldRunAfter]]. In both cases, the arguments are task names, task instances or any argument that is accepted by [https://docs.gradle.org/current/javadoc/org/gradle/api/Task.html#dependsOn-java.lang.Object...- Task.dependsOn(Object ...)].
===mustRunAfter===
"must run after" ordering rule specifies that a task must always run after other task, whenever both tasks are run.
<syntaxhighlight lang='groovy'>
task a { ... }
task b { ... }
b.mustRunAfter a
</syntaxhighlight>
<syntaxhighlight lang='bash'>
gradle b
# b executes
</syntaxhighlight>
<syntaxhighlight lang='bash'>
gradle b a
# a executes
# b executes
</syntaxhighlight>
The rule can be invoked with the following API:
* [https://docs.gradle.org/current/javadoc/org/gradle/api/Task.html#mustRunAfter-java.lang.Object...- Task.mustRunAfter(Object...)]
* [https://docs.gradle.org/current/javadoc/org/gradle/api/Task.html#setMustRunAfter-java.lang.Iterable- Task.setMustRunAfter(Iterable)]
===shouldRunAfter===
"should run after" ordering rule specifies that a task should run after other task, whenever both tasks are run, but it is less strict than [[#mustRunAfter|mustRunAfter]] and it will be ignored in two situations:
* If using the rule introduces an ordering cycle.
* When using parallel execution and all dependencies of a task have been satisfied apart from "should run after", then this task will be run regardless of whether its "should run after" dependencies have been run or not. "should run after" should be used where the ordering is helpful but not strictly required.
<syntaxhighlight lang='groovy'>
task a { ... }
task b { ... }
b.shouldRunAfter a
</syntaxhighlight>
The rule can be invoked with the following API:
* [https://docs.gradle.org/current/javadoc/org/gradle/api/Task.html#shouldRunAfter-java.lang.Object...- Task.shouldRunAfter(Object ...)]
* [https://docs.gradle.org/current/javadoc/org/gradle/api/Task.html#setShouldRunAfter-java.lang.Iterable- Task.setShouldRunAfter(Iterable)]


=Implicit Dependencies=
=Implicit Dependencies=
==Task Inputs and Outputs==
Implicit dependencies are determined base on [[Gradle_Incremental_Builds#Task_Inputs_and_Outputs|task inputs and outputs]].
Gradle determines whether a task is up to date if the task's inputs and outputs have not changed since the last execution. If the input and output snapshots are identical to the previous ones, the task is not executed. An input can be a directory, one or more files or a property. An output is a directory or one or more files. Inputs and outputs are fields of the DefaultTask class. Task inputs and outputs are obtained with [https://docs.gradle.org/current/javadoc/org/gradle/api/Task.html#getInputs-- Task.getInputs()] and [https://docs.gradle.org/current/javadoc/org/gradle/api/Task.html#getOutputs-- Task.getOutputs()], which return [https://docs.gradle.org/current/javadoc/org/gradle/api/tasks/TaskInputs.html TaskInputs] and [https://docs.gradle.org/current/javadoc/org/gradle/api/tasks/TaskOutputs.html TaskOutputs] instances, respectively.
 
<font color=darkgray>Task inputs and outputs are evaluated during the configuration phase to wire up the task dependencies. That is why they need to be defined in a configuration block. To avoid unexpected behavior, make sure that the value you assign to inputs and outputs is accessible at configuration time. If you need to implement programmatic output evaluation, the method upToDateWhen(Closure) on TaskOutputs comes in handy. In contrast to regular inputs/outputs evaluation, this method is evaluated at execution time. If the closure returns true, the task is considered up to date.</font>

Latest revision as of 02:33, 9 November 2020

External

Internal

Overview

May times, a task requires another task to run first, especially if the task depends on the produced output of its dependency task. A task may declared its dependencies explicitly. A task may depend on other tasks implicitly, as described in the Implicit Dependencies section.

In Gradle, task execution order is automatically determined taking into account explicit dependencies and implicit dependencies, and a specific execution order for the tasks that declared dependencies among themselves is not guaranteed. The only thing that is guaranteed is that the dependencies will be honored. This architectural decision has several benefits: you don't need to know the whole chain of task dependencies to make a change, and because the tasks don't have to be executed strictly sequentially, they can be parallelized.

However, Gradle also offers a way to request an execution order between tasks, in absence of dependency.

If tasks belonging to different sub-projects that are part of the same multi-project define dependencies among themselves, that automatically introduces a dependency between the owning projects:

Multi-project builds | Inter-project Dependencies

Explicit Dependencies

A task's explicit dependencies are maintained and can be configured with the task's "dependsOn" property. The Task API used to declare explicit task dependencies is Task.dependsOn(Object... paths), which surfaces in the DSL as:

task red {
  dependsOn 'blue', 'green' // dependency expressed using task names, comma is required for more than one dependencies
  ...
}
// dependency expressed using task objects
task red(dependsOn: [blue, green] {
  ...
}
task red {
  ...
}
red.dependsOn(blue, green) // dependency expressed using task objects
task red {
  ...
}
red.dependsOn blue // dependency expressed using task objects
red.dependsOn green // dependency expressed using task objects

Note that a task dependency may be another task name, the task instance itself or another objects.

If multiple tasks are provides as argument of dependsOn(...), the order in which they are declared does not influence the order in which they are executed. The only thing that is guaranteed is that all will be executed before the task that declares the dependency.

Adding Explicit Dependencies using a Lazy Block

Task dependencies can be defined using a lazy block. When evaluated, the block is passed the task whose dependencies are being calculated. The block should return one task or a collection of tasks, which are then treated as dependencies of the task:

task red {
  ...
}

red.dependsOn {
  tasks.findAll { task -> task.name.startsWith('b') }
}

Other Methods to Set Explicit Dependencies

Other methods that can be used to set explicit dependencies are:

Explicit Dependencies between Tasks belonging to Different Projects

In a multi-project situation, tasks belonging to two different projects can express the dependency on one another by using task paths:

red.dependsOn(':project-a:yellow')
task red(..., dependsOn: [':project-a:yellow', ':project-b:brown']) {
  ...
}
task red {
  dependsOn ':project-a:yellow', ':project-b:brown' // comma is required
}

This explicit dependency also introduces a dependency between projects, causing dependency project ("project-a") to be configured before the current project in the configuration phase.

Finalizer Tasks

A finalizer task is a task that will be scheduled to run after the task that requires finalization, regardless of whether the task succeeds or fails.

task a { ... }
task b { ... }
a.finalizedBy b

Ordering Tasks

https://docs.gradle.org/current/userguide/more_about_tasks.html#sec:ordering_tasks

It is possible to request a specific order in which two tasks execute, without introducing a dependency relationship between tasks: if both tasks are executed, they will be executed in the requested order; if only one of the tasks is executed, but not the other, the order between tasks becomes irrelevant. Dependency is mandatory execution of both task, in the required order, so dependency implies order. Order does not imply mandatory execution, just ordered execution if both tasks are executed; order does not imply dependency. If there is an order relationship between two tasks A and B, it is still possible to execute task independently: execute task A without task B and vice-versa. The ordering rule only has an effect when both tasks are scheduled for execution.

Note that when there's an order relationship between tasks, and the tasks are run with --continue, it is possible for B to execute in the event that A fails.

Task ordering can be useful for example in the situation when both unit and integration tests are executed and it is useful to run the unit tests first and get feedback faster. There is no dependency relationship between tasks, one can be executed without the other. However, it is useful if they execute in a specific order, if they both execute.

There are two ordering rules: mustRunAfter and shouldRunAfter. In both cases, the arguments are task names, task instances or any argument that is accepted by Task.dependsOn(Object ...).

mustRunAfter

"must run after" ordering rule specifies that a task must always run after other task, whenever both tasks are run.

task a { ... }
task b { ... }
b.mustRunAfter a
gradle b
# b executes
gradle b a
# a executes
# b executes

The rule can be invoked with the following API:

shouldRunAfter

"should run after" ordering rule specifies that a task should run after other task, whenever both tasks are run, but it is less strict than mustRunAfter and it will be ignored in two situations:

  • If using the rule introduces an ordering cycle.
  • When using parallel execution and all dependencies of a task have been satisfied apart from "should run after", then this task will be run regardless of whether its "should run after" dependencies have been run or not. "should run after" should be used where the ordering is helpful but not strictly required.
task a { ... }
task b { ... }
b.shouldRunAfter a

The rule can be invoked with the following API:

Implicit Dependencies

Implicit dependencies are determined base on task inputs and outputs.