Gradle Task: Difference between revisions

From NovaOrdis Knowledge Base
Jump to navigation Jump to search
 
(393 intermediate revisions by the same user not shown)
Line 1: Line 1:
=External=
=External=


* https://docs.gradle.org/current/dsl/org.gradle.api.Task.html
* Task API: [https://docs.gradle.org/current/javadoc/org/gradle/api/Task.html org.gradle.api.Task]
* Task DSL: https://docs.gradle.org/current/dsl/org.gradle.api.Task.html
* DefaultTask API: https://docs.gradle.org/current/javadoc/org/gradle/api/DefaultTask.html
* Authoring Tasks | https://docs.gradle.org/current/userguide/more_about_tasks.html


=Internal=
=Internal=
Line 9: Line 12:
=Overview=
=Overview=


A ''task'' is a core Gradle concept. It represents a single atomic piece of work for a build, such as compiling classes or generating javadoc. Task may depend on each other. Any build ends up executing a sequence of tasks in succession, or [[#Parallel_Execution|in parallel if explicitly configured]], after all tasks have been arranged in a [[Graph#Directed_Acyclic_Graph|directed acyclic graph]]. Understanding how the directed acyclic graph is built and how tasks are scheduled for execution is key to understanding how gradle works.
A task is a core Gradle concept, representing well defined work to be executed during a [[Gradle_Concepts#Build_Lifecycle|build]], such as compiling Java classes, generating javadoc or creating a distribution file. The work executed by a task is further divided into individual [[#Task_Action|task actions]], defined and managed by the task. Task may depend on each other. The goal of a Gradle [[Gradle_Concepts#Build_Lifecycle|build]] is to execute a set of tasks in sequence, after the tasks have been arranged in a [[Graph_Concepts#Directed_Acyclic_Graph|directed acyclic graph]] (DAG), computed based on each task's [[Gradle_Task_Dependencies_and_Ordering#Overview|implicit and explicit dependencies]], before any task is executed. During a build execution, a task is executed only once. Understanding how the directed acyclic graph is built and how tasks are scheduled for execution is key to understanding how Gradle works.  


Tasks can be explicitly declared in a build configuration file, or can be exposed by plugins.
A task belongs to a specific [[Gradle Project#Overview|project]]. The association is made when the task is declared in [[build.gradle]] or when the plugin that declares the task is applied to the project in [[build.gradle]]. Tasks are maintained in the project's [[Gradle_Project#Task_Container|task container]], and the can be looked up and manipulated from the project's build.gradle. The [[#Locating_Tasks|Locating Tasks]] section below gives more details on how tasks can be located.


Each task '''belongs to a specific project'''. The association is made when the task is declared in [[Gradle_Project_and_Build_Script#Overview|build.gradle]] or when the plugin that declares the task is applied to the project in [[Gradle_Project_and_Build_Script#Overview|build.gradle]]. A project is essentially a collection of tasks. The tasks of a project can be accessed in a build script with:
Some tasks are [[#Built-in_Tasks|built into Gradle core]] and are available to any project. Other tasks are contributed by [[Gradle Plugin Concepts#Overview|plugins]]. New tasks can be defined by the project itself. For more details see [[#Sources_of_Tasks|Sources of Tasks]] section below.  


''project-reference''.tasks
New [[#Simple_Task|simple tasks]] can be defined in-line in [[build.gradle]] or in [[Gradle_Plugin_Concepts#Script_Plugin|script plugins]]. Tasks whose implementations are available on the build's classpath, and that usually have their own properties and methods, can be declared and configured in [[build.gradle]], as [[#Enhanced_Task|enhanced tasks]], and thus made available for use during the build. Complex tasks can be even fully defined in-line or in script plugins, by providing the task code in-line in the script, though this is not a recommended practice, as it does not encourage modularization, encapsulation and sharing.


This will return the project's [[#Task Container|TaskContainer]].  
Any task instance implements [https://docs.gradle.org/current/javadoc/org/gradle/api/Task.html org.gradle.api.Task] interface. By default, every newly created task is of type [https://docs.gradle.org/current/javadoc/org/gradle/api/DefaultTask.html org.gradle.api.DefaultTask], the standard implementation of [https://docs.gradle.org/current/javadoc/org/gradle/api/Task.html org.gradle.api.Task].


Example:
Declaring, configuring and defining tasks is done via corresponding Project API methods, directly accessible from [[build.gradle]]:
 
<syntaxhighlight lang='groovy'>
println "" + project.tasks
</syntaxhighlight>


<span id='Task_Name'></span>Each task has a '''name''', which can be used to refer to the task within its project, and a <span id='Task_Fully_Qualified_Path'></span>'''fully qualified path''', which is unique across all projects. The path is the concatenation of the owning project's path and the task name, separated by the ":" character:
[https://docs.gradle.org/current/javadoc/org/gradle/api/Project.html#task-java.lang.String- Project.task(String name)]
[https://docs.gradle.org/current/javadoc/org/gradle/api/Project.html#task-java.lang.String-groovy.lang.Closure- Project.task(String name, Closure configureClosure)]
[https://docs.gradle.org/current/javadoc/org/gradle/api/Project.html#task-java.lang.String-org.gradle.api.Action- Project.task(String name, Action<? super Task> configureAction)]
[https://docs.gradle.org/current/javadoc/org/gradle/api/Project.html#task-java.util.Map-java.lang.String- Project.task(Map<String, ?> args, String name)]
[https://docs.gradle.org/current/javadoc/org/gradle/api/Project.html#task-java.util.Map-java.lang.String-groovy.lang.Closure- Project.task(Map<String, ?> args, String name, Closure configureClosure)]


:sub-project-A:child-B:taskC
A Gradle build executes the task specified on command line, including all explicit and implicit dependencies of the task.


A build consists in executing a sequence of tasks in succession. Gradle computes the [[Graph#Directed_Acyclic_Graph|Directed Acyclic Graph]] of tasks necessary to be executed to fulfill the tasks specified on command line, and then executes the [[Gradle_Task#Task_Action|task actions]], honoring inter-task dependencies and insuring the fact that a task '''is executed only once'''. Gradle builds the complete dependency graph before any tasks is executed.
<syntaxhighlight lang='text'>
 
./gradlew build
{{Warn|Behavior must be declared in a [[#Task_Action|task action]] to be executed at the [[Gradle_Concepts#Build_Execution_Phase|execution phase]]. Anything declared in a task that does not belong to an action is executed at the [[Gradle_Concepts#Build_Configuration_Phase|configuration phase]]. For more details see [[#Explicit_Task_Declaration|Explicit Task Declaration]].}}
</syntaxhighlight>
 
=Displaying Tasks=
 
Some of the common tasks for a project can be displayed with:
 
gradle [:''project-path'':]tasks
 
In case of the root project, the project path is empty:
 
gradle tasks
 
For a sub-project:
 
gradle :sub-project-A:tasks
 
Custom tasks declared in the project's [[Gradle_Concepts#Overview|build.gradle]] are not present in the output.
 
All tasks of a project, including the custom tasks and the tasks of its sub-projects, if any, can be displayed with:
 
gradle [:''project-path'':]tasks --all
 
For the root project:
 
gradle tasks --all
 
Details about a specific task can be obtained with:


gradle help --task <''task-name''>
=Task Lifecycle=
The tasks are instantiated and configured in the [[Gradle_Concepts#Build_Configuration_Phase|build configuration phase]], by executing their [[#Task_Configuration_Closure|configuration closures]]. The task's actions are executed in the [[Gradle_Concepts#Execution_Phase|execution phase]]. A task does not necessarily have to have actions, it could only exist to execute the configuration closure at the build configuration phase. Such a task is called "task configuration".


==Display Tasks in Task Execution Graph in Execution Order==
==Task Configuration Closure==
Task configuration closures declare code that configures the task during the [[Gradle_Concepts#Build_Configuration_Phase|build configuration phase]]. The configuration closure usually initializes properties, declares task dependencies and [[#Task_Action|adds actions]]. Note that the statements that are not part of a [[#Task_Action|task action]], declared outside [[#Action|doFirst]] and [[#Action|doLast]] closures, will be executed in the configuration phase, when the task is initialized and configured, not in the [[Gradle_Concepts#Build_Execution_Phase|execution phase]]. For more details, see [[#Task_Lifecycle|Task Lifecycle]], above.


To display all tasks in the task execution graph, use:
The task configuration closure can be applied at the same time the task is declared, as shown in the [[Gradle_Task#Simple_Task|Simple Task]] section below. Alternatively, the task can be declared, then configured by applying the configuration closure later, as shown in the [[#Configuration_Separated_from_Declaration|Enhanced Task &#124; Configuration separated from declaration]] below.


<syntaxhighlight lang='groovy'>
=Task Structure=
println "Tasks in Task Execution Graph, in execution order: " + gradle.taskGraph.allTasks
==<span id='Task_Name_and_Path'></span>Name and Path==
Each task has a <span id='Task_Name'></span>'''name''', which is unique within the context of the task's project. The name must not contain dashes. Since different projects may contain tasks with the same name, a task is uniquely identified by its <span id='Task_Path'></span>'''path''' in the context of a multi-project build. The path is the concatenation of the name of the project that owns the task and the task name, separated by the ":" character:
<syntaxhighlight lang='text'>
:web:build
</syntaxhighlight>
</syntaxhighlight>


To hook this into a default task execution:  
==<span id='Other_Task_State'></span>Configuration==
 
A task has configuration and [[#Actions|actions]]. The code defined in the [[#Task_Configuration_Closure|configuration section]] of the task will be executed during the [[Gradle_Concepts#Build_Configuration_Phase|configuration phase]] of the build.
===Task Properties===
{{External|https://docs.gradle.org/current/dsl/org.gradle.api.Task.html#N18DE0}}
A task has a number of properties, such as [[#Group|group]], [[#Description|description]], [[#Enabled|enabled]], etc.
====Group====
A task group defines the logical grouping of tasks. The task group is available via the "group" property of the task instance, and can be set within the [https://docs.gradle.org/current/javadoc/org/gradle/api/Project.html#task-java.util.Map-java.lang.String-groovy.lang.Closure- task constructor] or directly setting the property:
<syntaxhighlight lang='groovy'>
<syntaxhighlight lang='groovy'>
task debugPublish {
task example {
   dependsOn 'publish'
   group = "experimental"
   doFirst {
   doLast {
    println "Tasks in Task Execution Graph, in execution order: " + gradle.taskGraph.allTasks
    ...
   }
   }
}
}
</syntaxhighlight>
task example(group: "experimental") {
 
  doLast {
==Dry Run==
     ...
 
  }
Display the tasks that would be executed, without actually doing anything
 
[[Gradle_Operations#-m.2C_--dry-run|gradle -m|--dry-run]] ...
 
This is useful in understanding task execution order.
 
=<span id='Get_the_Reference_of_a_Task_by_Name'></span>Obtaining a Task Reference=
 
{{Internal|Gradle_Programming#Obtaining_a_Task_Reference|Gradle Programming - Obtaining a Task Reference}}
 
=Task Execution Order=
 
A task may have dependencies on other tasks or might be scheduled to always run after another task. Gradle ensures that all task dependencies and ordering rules are honored during the [[Gradle_Concepts#Execution|execution phase]], so that the task is executed after all of its dependencies and any "must run after" tasks have been executed.
 
==Command Line to Task Execution Graph==
 
The execution graph is built starting from task names specified on command line.
 
The names specified on command line are looked up in '''all''' projects of the build, and if they exist in any project, they will be added to the execution graph, for as many times they appear. For example, if tasks "t" was declared in the root project and "sub-project-A" and "sub-project-B", then the execution graph looks like:
 
task ':t', task ':subproject-A:t', task ':subproject-B:t'
 
More tasks are added to the execution graph if the initial tasks have [[#Declaring_Dependencies_between_Tasks|dependencies on other tasks]].
 
==<span id='Declaring_Dependencies_between_Custom_Tasks'></span>Declaring Dependencies between Tasks==
 
{{External|https://docs.gradle.org/current/dsl/org.gradle.api.Task.html#N1790C}}
 
Dependencies can be declared directly inside new task declarations:
 
<syntaxhighlight lang='groovy'>
task a {
 
     // ...
}
 
task b {
 
    // ....
 
    dependsOn 'a'
}
</syntaxhighlight>
 
{{Warn|The order of the declaration matters. The dependency task must be declared '''before''' the dependent task.}}
 
Dependencies can also be declared inside the script block that configures an existing task:
 
<syntaxhighlight lang='groovy'>
docker {
 
  dependsOn build
  ...
}
}
</syntaxhighlight>
</syntaxhighlight>
The task grouping is reflected in the output of the  [[Gradle tasks Task|tasks]] task, which is one of the built-in tasks.


Dependencies can be declared between tasks that do not belong to the same project. This makes sense as Gradle manages a global task dependency graph. In "project-B":
====Description====
 
Descriptions are displaying when executing the [[Gradle tasks Task|tasks]] built-in task.
<syntaxhighlight lang='groovy'>
<syntaxhighlight lang='groovy'>
docker {
task someTask(type: SomeTask) {
 
  // configuration closure
   dependsOn rootProject.project(':project-A').getTasksByName("build", false).asList().get(0)
   description "A task that does this and that"
   ...
   ...
}
}
</syntaxhighlight>
</syntaxhighlight>


Functions that can be used to declare dependencies between tasks are:
====Enabled====


dependsOn '<''task-name''>', '<''task-name''>', ...  
===Inputs and Outputs===
In most cases, tasks have input and outputs, and whether those inputs and outputs change between successive builds is monitored by Gradle and could be used to optimize builds. A tasks' inputs and outputs are also [[#Task_Properties|task properties]]. More details about declaring and using task inputs and outputs is available here:
{{Internal|Gradle Incremental Builds#Task_Inputs_and_Outputs|Task Inputs and Outputs}}


mustRunAfter '<''task-name''>', ...
===<span id='Dependencies'></span><span id='Task_Dependencies'></span><span id='Task_Dependencies_and_Ordering'></span><span id='Explicit_Dependencies'></span><span id='Implicit_Dependencies'></span><span id='Dependencies_and_Ordering'></span>dependsOn, Dependencies and Ordering===
Tasks can declare explicit dependencies on other tasks, and also have implicit dependencies given by the relationship between their outputs and inputs. A task's explicit dependencies can be configured with the "dependsOn" task property. Gradle offers a way to declare ordering without declaring dependency. More details are available here:
{{Internal|Gradle Task Dependencies and Ordering|Task Dependencies and Ordering}}


Unfortunately, there's no "mustRunBefore".
==<span id='Task_Action'></span><span id='Action'></span>Actions==
An action is the place within a task that contains build logic. Each task contains a list of actions, which are all implementing [https://docs.gradle.org/current/javadoc/org/gradle/api/Action.html org.gradle.api.Action] and are executed when the task is executed, in order in which they were added to the task's internal list of actions.  


finalizedBy '<''task-name''>', ...
Existing tasks have their own actions, defined when the task is coded. Additional actions can also be added in-line when the task is configured, with corresponding DSL keywords <tt>doFirst</tt> and <tt>doLast</tt>, followed by closure that contain executable code, as shown in [[#Simple_Task|Defining a Simple Task]] section. Alternatively, actions can be added to the task by invoking the Task API methods [https://docs.gradle.org/current/javadoc/org/gradle/api/Task.html#doFirst-groovy.lang.Closure- doFirst(Action|Closure)], which adds the action at the head of the list, and [https://docs.gradle.org/current/javadoc/org/gradle/api/Task.html#doLast-groovy.lang.Closure doLast(Action|Closure)], which adds the action at the tail of the list, on the task reference. The last doFirst() invocation will configure the action that is executed first. The last doLast() invocation will configure the action that is executed last. The execution order is easy to figure out if you think of an action queue to which doFirst() and doLast() add actions at the head or at the tail.


<font color=darkgray>Describe 'mustRunAfter' and 'finalizedBy'.</font>
Actions can be anonymous or named.


The task name may be a [[#Task_Fully_Qualified_Path|fully qualified path]], thus allowing us to declare dependencies on tasks from another projects, in a [[Gradle_Multi-Project_Builds#Overview|multi-project build]].  
=Task Outcome=
{{External|https://docs.gradle.org/current/userguide/more_about_tasks.html#sec:task_outcomes}}
After execution, a task has one of the following outcomes, depending on whether a task has actions to execute, it should execute those actions, it did execute its actions and whether those actions made any changes: [[#EXECUTED|EXECUTED]], [[#UP-TO-DATE|UP-TO-DATE]], [[#FROM-CACHE|FROM-CACHE]], [[#SKIPPED|SKIPPED]], [[#NO-SOURCE|NO-SOURCE]].
====<span id='EXECUTED'></span>(no label) or EXECUTED====
The task has this outcome if:
* the task has actions that should have been executed, and they were [[#EXECUTED|executed]].
* the task has no actions, but has dependencies and any of its dependencies has been [[#EXECUTED|executed]].


If a task declares a dependency on a task that was declared in multiple projects, only the dependency task that was declared in the same project as the dependent task will be executed, and not all of them.
====UP-TO-DATE====
The task has this outcome if:
* the task has no actions and no dependencies.
* the task has no actions, it has dependencies, but none of its dependencies have been executed because they are [[#UP-TO-DATE|up-to-date]], [[#SKIPPED|skipped]] or [[#FROM-CACHE|from cache]].
* the task has actions, but the task tells Gradle that it did not change its outputs. <Font color=darkgray>How does a task tell Gradle it did not change its outputs?</font>
* the task has inputs and outputs and [[Gradle_Incremental_Builds#Task_Inputs_and_Outputs|they have not changed]].


Aside from task names, the following can be used:
====FROM-CACHE====
* A '''closure'''. The closure may take a task as a parameter. It may return any of the types mentioned below, and its return value is recursively converted into a task. null is treated as an empty collection. For example, the closure shown below returns a set of tasks. Note that the closure is executed '''after''' the [[Gradle_Concepts#Configuration|configuration phase]], at the time the closure is executed, all build configuration script have been evaluated.
The task has this outcome if its output have been generated from a previous execution and have been restored from the [[#Gradle_Build_Cache#Overview|build cache]].


<syntaxhighlight lang='groovy'>
====SKIPPED====
dependsOn {
The task has this outcome if:
* The task has actions, but did not executed its actions because an [[#onlyIf|onlyIf]] predicate returns false.
* The task has been [[Gradle_Command_Line#-x|explicitly excluded from command line]].


  Set<Task> tasks = new HashSet<>();
====NO-SOURCE====
  subprojects.each {
The task has this outcome if it did not execute its actions because even if it has inputs and outputs, it has no sources.
      tasks.add(it.tasks.findByName('t'))
  }
  return tasks;
}
</syntaxhighlight>


* An absolute and relative path, as String.
=Sources of Tasks=
* A [https://docs.gradle.org/current/dsl/org.gradle.api.Task.html Task].
Each project comes with a set of pre-defined, [[#Built-in_Tasks|built-in tasks]], which are part of Gradle core. Each declared plugin [[#Tasks_and_Plugins|may add its own tasks]]. Simple tasks may be declared by the build, as shown in the [[Gradle_Task#Defining_Custom_Tasks|Defining Custom Tasks]] section, below. Also, tasks whose implementations are available on the build's classpath, and that usually have their own properties and methods, can be declared and configured in [[build.gradle]], as [[#Enhanced_Task|enhanced tasks]], and thus made available for use during the build.  
* A [https://docs.gradle.org/current/javadoc/org/gradle/api/tasks/TaskDependency.html TaskDependency].
* A [https://docs.gradle.org/current/dsl/org.gradle.api.Buildable.html Buildable].  
* A [https://docs.gradle.org/current/javadoc/org/gradle/api/file/RegularFileProperty.html RegularFileProperty] or [https://docs.gradle.org/current/javadoc/org/gradle/api/file/DirectoryProperty.html DirectoryProperty].
* An Iterable, Collection, Map or array that contains the types listed here.
* A Callable.


Gradle will detect circular dependencies, if they exist, and fail:
==Built-in Tasks==
Each build exposes a set of built-in tasks. The built-in tasks are part of Gradle core and can be used right away without any additional configuration or loading any plugin:
* [[Gradle init Task|init]]
* [[Gradle wrapper Task|wrapper]]
* [[Gradle buildEnvironment Task|buildEnvironment]]
* [[Gradle components Task|components]]
* [[Gradle dependencies Task|dependencies]]
* [[Gradle dependencyInsight Task|dependencyInsight]]
* [[Gradle dependentComponents Task|dependentComponents]]
* [[Gradle help Task|help]]
* [[Gradle model Task|model]]
* [[Gradle projects Task|projects]]
* [[Gradle properties Task|properties]]
* [[Gradle tasks Task|tasks]]


<font color=red>FAILURE: Build failed with an exception.</font>
==Tasks and Plugins==
Plugins usually add specialized tasks. As per this writing (Gradle 6.7), there is no command line command to list the tasks contributed by a certain plugin. However, they can be listed by doing a bit of programming:{{Internal|Gradle_Plugin_Concepts#Listing_Tasks_introduced_by_a_Certain_Plugin|Listing Tasks introduced by a Certain Plugin}}
* What went wrong:
Circular dependency between the following tasks:
:x
\--- :subproject-A:t
      \--- :x (*)
(*) - details omitted (listed previously)


===Examples of Task Dependency Declarations===
=<span id='Custom_Tasks'></span>Defining Custom Tasks=
{{External|[https://docs.gradle.org/current/userguide/custom_tasks.html Developing Custom Gradle Task Types &#124; https://docs.gradle.org/current/userguide/custom_tasks.html]}}
The simplest way of extending Gradle is to write a custom task. Custom tasks can be declared [[Extending_Gradle#In-line_in_build.gradle|in-line in the default build script build.gradle]], as [[#Simple_Task|simple tasks]]. Simple tasks can also be declared [[Extending_Gradle#In-line_in_a_script_plugin|in-line in a separate build script]], which is then included from the default build script. The code of the custom task can live in a separate source file or multiple files, which in turn can be declared in [[Extending_Gradle#In_the_Project.27s_buildSrc_Directory|buildSrc]], which is a special area of the Gradle project, or can be shared with other projects as part of a library, developed in [[Extending_Gradle#External_to_Project|its own project]]. Such a task is referred to as a [[#Enhanced_Task|enhanced task]].


====Root Project Task Depends on Specific Sub-Project Tasks====
Custom tasks is also how Gradle plugins extend the Gradle functionality. "Custom" in this context means new functionality defined either by plugins, or by Gradle users while writing the build scripts.


==Simple Task==
A simple task is an implementation of [https://docs.gradle.org/current/javadoc/org/gradle/api/DefaultTask.html org.gradle.api.DefaultTask] that can be declared in-line in [[build.gradle]] or in [[Gradle_Plugin_Concepts#Script_Plugin|script plugin]] using DSL keywords ("task", "doFirst", "doLast", etc.) corresponding to [[#Task_Action|Task API methods]]. The example below shows a syntax where the [[#Task_Name_and_Path|task name]] and a [[#Task_Configuration_Closure|task configuration closure]] are provided.
<syntaxhighlight lang='groovy'>
<syntaxhighlight lang='groovy'>
task topLevelTask {
task myTask { <configuration-closure> }
 
    dependsOn {
 
        Set<Task> tasks = new HashSet<>();


        subprojects.each {
task('myTask') { <configuration-closure> }


            tasks.add(it.tasks.findByName('someName'))
task(myTask) { <configuration-closure> } // this syntax uses Groovy dynamic keywords
        }


        return tasks;
tasks.create('myTask') { <configuration-closure> }
  }
}
</syntaxhighlight>
</syntaxhighlight>
 
Example:
<font color=darkgray>Update this if a better way is found.</font>
 
The same example can be written by declaring the closure first and then invoking it:
 
<syntaxhighlight lang='groovy'>
<syntaxhighlight lang='groovy'>
def allSubProjectTasksNamedSomeName = { Task t ->
task customSimpleTask {


    Set<Task> tasks = new HashSet<>();
  // this is the task configuration closure, it is executed during the configuration phase
  println "this will be displayed during configuration phase"


     subprojects.each {
  // will add the action defined in the closure at the head of the action list
  doFirst {
     println "this will be executed second when the action is executed in the build execution phase"
  }


        tasks.add(it.tasks.findByName('someName'))
  doFirst {
     }
     println "this will be executed first when the action is executed in the build execution phase"
  }


    return tasks;
  // will add the action defined in the closure at the tail of the action list
}
  doLast {
 
    println "this will be executed last when the action is executed in the build execution phase"
task topLevelTask {
  }
 
    dependsOn allSubProjectTasksNamedSomeName
}
}
</syntaxhighlight>
</syntaxhighlight>
===Creation Options Map===


<font color=darkgray>Update this if a better way is found.</font>
Simple task state elements can be set by using the [https://docs.gradle.org/current/javadoc/org/gradle/api/Project.html#task-java.util.Map-java.lang.String-groovy.lang.Closure- task constructor that accepts a map], usually referred to as "map of creation options":
 
==Declaring a Dependency between an Existing Task and a Custom Task==
 
Declare the custom task with:
 
<syntaxhighlight lang='groovy'>
<syntaxhighlight lang='groovy'>
task myTask {
task example(group: "experimental",
            description: "Example task",
            dependsOn: [task-a, task-b]) {
   doFirst {
   doFirst {
    ...
    println "example"
   }
   }
}
}
</syntaxhighlight>
</syntaxhighlight>


Then configure the existing task to depend on the custom task:
The following options are available:
{|
| '''Option''' || '''Description''' || '''Default Value'''
|-
| "type" || The class of the task to create. || DefaultTask
|-
| "overwrite" || Replace an existing task? || false
|-
| "dependsOn" || A task name or set of task names which this task depends on || []
|-
| "action" || A closure or Action to add to the task. || null
|-
| "description" || A description of the task. || null
|-
| "group" || A task group which this task belongs to. || null
|}


<syntaxhighlight lang='groovy'>
===Simple Task Examples===
assemble {
* [https://github.com/ovidiuf/playground/tree/master/gradle/extending-gradle/01-in-line-build-gradle-simple-task Custom simple task declared in-line in build.gradle]
  dependsOn 'myTask'
* [https://github.com/ovidiuf/playground/tree/master/gradle/extending-gradle/02-script-plugin-simple-task Custom simple task declared in-line in a script plugin]
}
 
</syntaxhighlight>
==Enhanced Task==


==Parallel Execution==
An enhanced task is a Gradle task that has two distinct components: the [[#Task_Type|task type]] and [[#Task_Instance|task instances]].


{{External|https://docs.gradle.org/current/dsl/org.gradle.api.Task.html#N179C7}}
The <span id='Task_Type'></span>'''task type''' is a class written in Java or Groovy, which encapsulates the task logic. Gradle comes with a number of [[#Predefined_Task_Types|predefined task types]], but new types can be defined: the code can be declared [[Extending_Gradle#In-line_in_build.gradle|in-line in build.gradle]] or [[Extending_Gradle#In-line_in_a_script_plugin|a script plugin file]], or preferably into a source code file (or files) maintained in [[Extending_Gradle#In_the_Project.27s_buildSrc_Directory|the project's buildSrc]] or [[Extending_Gradle#External_to_Project|externally]]. The behavior, usually configurable, is built into the task type. Also, the task type may expose properties that can be configured from the build script.


By default, tasks are not executed in parallel unless a task is waiting on asynchronous work and another task, which is not dependent, is ready to execute. Parallel execution can be enabled by the --parallel flag when the build is initiated. In parallel mode, the tasks of different projects of a [[Gradle_Multi-Project_Builds#Overview|multi-project build]] able to be executed in parallel.
The <span id='Task_Instance'></span>'''task instance''' is a particular instantiation of the [[#Task_Type|task type]], carrying a particular configuration. During the task instantiation process, values are provided for the properties exposed by the task class, and thus the instance is configured with a specific behavior. Multiple instance of the same type, with different names and configurations, may exist in the same build script.


=Default Task=
<font color=darkgray>How do tasks that come with plugins get their default name. They are not declared in the build script, so where is that declared?</font>


A default task can be declared in the build script, so when gradle is invoked without any argument, that task is executed.
Developing enhanced task is preferred because separating the implementation code from the configuration of the task increases the maintainability, the testability and the reusability of the enhanced task.


Tasks instances can be created using one of the syntaxes shown below:
<syntaxhighlight lang='groovy'>
<syntaxhighlight lang='groovy'>
defaultTasks 'distZip'
task someTask(type: SomeTask)
</syntaxhighlight>


More than one tasks can be declared as "default".
task('someTask', type: SomeTask)


=Task Action=
// this syntax uses Groovy dynamic keywords
task(someTask, type: SomeTask)


Each task consists of a sequence of [https://docs.gradle.org/current/javadoc/org/gradle/api/Action.html Action] objects, which are executed in the order of their declaration. Actions can be added to a task by calling Task's doFirst() or doLast() methods with an action closure. When the action is executed, the closure is called with the task as parameter.
tasks.create('someTask', SomeTask)
</syntaxhighlight>


task someTask {
If the task was written to allow to be configured via its constructor, configuration parameters can be passed as such:
  doFirst|doLast { ''action-closure'' }
<syntaxhighlight lang='groovy'>
  }
tasks.create('someTask', SomeTask, 'something', ...)
</syntaxhighlight>
For more details on how to write a task that allows configuration via constructor, see: {{Internal|Extending_Gradle_with_a_Custom_Enhanced_Task#Allowing_Configuration_via_a_Task_Constructor|Extending Gradle with a Custom
  Enhanced Task &#124; Allowing Configuration via a Task Constructor}}


The last doFirst() invocation will configure the action that is executed first. The last doLast() invocation will configure the action that is executed last. The execution order is easy to figure out if you think of an action queue to which doFirst() and doLast() add actions at the head or at the tail.
Upon instantiation, the task instances may be configured using their type's API:
<syntaxhighlight lang='groovy'>
SomeTask myTask = tasks.getByName("myTask")


Actions can be anonymous or named.
myTask.someProperty = "something"
 
myTask.someOtherProperty = "something else"
=Task Failure=
 
Any task failure stops the build, with two exceptions:
 
# If the task action throws [https://docs.gradle.org/current/javadoc/org/gradle/api/tasks/StopActionException.html StopActionException], it aborts the current action, and continues at the ''next action''.
# If the task action throws [https://docs.gradle.org/current/javadoc/org/gradle/api/tasks/StopExecutionException.html StopExecutionException], it aborts the current action, and continues at the ''next task''.
 
=Conditional Execution=
 
onlyIf { ''closure'' }
 
onlyIf ( ''onlyIfSpec'' )
 
=Task Properties=
 
<font color=darkgray>TODO when needed https://docs.gradle.org/current/dsl/org.gradle.api.Task.html#N17986</font>
 
=Task Container=
 
{{External|https://docs.gradle.org/4.7/javadoc/org/gradle/api/tasks/TaskContainer.html}}
 
The project TaskContainer can be accessed from a build script with:
 
  ''project-reference''.tasks
 
The [https://docs.gradle.org/4.7/javadoc/org/gradle/api/tasks/TaskContainer.html TaskContainer] can then queried for tasks. Also see [[Gradle_Programming#Obtaining_a_Task_Reference|Gradle Programming - Obtaining a Task Reference]].
 
=Configuring Existing Tasks=
 
<font color=darkgray>TODO more, organize.</font>
 
All tasks of the same type can be configured at once:
 
<syntaxhighlight lang='groovy'>
tasks.withType(Test) {
// configure the task
}
</syntaxhighlight>
</syntaxhighlight>


Also see [[#Get_the_Reference_of_a_Task_by_Name|Get the Reference of a Task by Name]] above.
<span id='Configuration_Separated_from_Declaration'></span>Alternatively, the configuration can be separated from declaration, using  Groovy dynamic tasks configuration block.


<font color=darkgray>I believe this also works: </font>
<syntaxhighlight lang='groovy'>
<syntaxhighlight lang='groovy'>
task(myTask, type: SomeTask)


test {
myTask {
// configure the task
  someProperty = "something"
  someOtherProperty = "something else"
}
}
</syntaxhighlight>
</syntaxhighlight>
Note that the blocks used here are for configuring the tasks and are not evaluated when the tasks executes.


=Task Sources=
Instantiation and configuration can be performed at the same time:
 
==Default Tasks==
 
Any build comes with a number of pre-defined default tasks, regardless on whether plugins have been applied or not. They are available below:
 
gradle dependencies
 
Displays all dependencies (classpath) for various dependency [[Gradle_Concepts#Configurations|configurations]] of the root project or a sub-project. More details in: {{Internal|Gradle_Operations#Inspect_Dependencies|Inspecting Dependencies}}
 
gradle dependencyInsight
 
Displays the insight into a specific dependency. More details in: {{Internal|Gradle_Operations#Inspect_Dependencies|Inspecting Dependencies}}
 
gradle dependentComponents
 
Displays the dependent components of components in project ':subproject-A'.
 
gradle model
 
Displays the configuration model of project ':subproject-A'.
 
gradle projects
 
Displays the sub-projects of project ':subproject-A'.
 
gradle properties
 
Displays the properties of project ':subproject-A'.
 
gradle tasks
 
Displays the tasks runnable from project ':subproject-A'.
 
gradle buildEnvironment
 
Displays all buildscript dependencies declared in project ':subproject-A'.
 
gradle components
 
Displays the components produced by project ':subproject-A'.
 
==Tasks From Plugins==
 
[[Gradle_Plugins#Overview|Plugins]] that have been applied to the project may come with their own tasks. This is a non-exhaustive list:
 
* [[Gradle_Java_Plugin#Tasks|Java Plugin Tasks]]
 
==<span id='Explicit_Task_Declaration'></span>Explicit Task Declaration (Custom Tasks)==
 
Tasks may be explicitly declared in [[Gradle_Project_and_Build_Script#Overview|build.gradle]] using the "task" keyword (Project's task() method). There are four forms to declare custom tasks:
 
task <''task-name''> { ''configuration-closure'' }
 
task <''task-name''>(type: <''[[#Task_Types|SomeTaskType]]''>)  { ''configuration-closure'' }
 
The configuration closures [[#Task_Action|should add actions]], as described above.
 
As an example, a task can be declared as follows:
 
<syntaxhighlight lang='groovy'>
<syntaxhighlight lang='groovy'>
task sample {
task someTask(type: SomeTask) {
  // configuration closure
  someProperty = "something"
  someOtherProperty = "something else"
}


    println 'this is not an action, but a statement to be executed when the task is declared, in the build configuration phase'
task('someTask', type: SomeTask) {
  // configuration closure
}


    doFirst {
// this syntax uses Groovy dynamic keywords
 
task(someTask, type: SomeTask) {
        println 'action executed second'
  // configuration closure
    }
}
 
    doFirst {
 
        println 'action executed first'
    }
    doLast {


        println 'action executed last'
tasks.create('someTask', SomeTask) {
    }
  // configuration closure
}
}
</syntaxhighlight>
</syntaxhighlight>


Note that the statements that are not part of a [[#Task_Action|task action]], declared outside doFirst {} and doLast {} in the task configuration closure will be executed in the [[Gradle_Concepts#Build_Configuration_Phase|build configuration phase]], when the task is declared, not in the [[Gradle_Concepts#Build_Execution_Phase|build execution phase]].
===Predefined Task Types===
{{External|https://docs.gradle.org/current/dsl/#N104B9}}
These predefined task types are exposed by the [[Gradle_Plugin_Concepts#Core_Plugins|core plugins]].
* [[Gradle Copy Task Type|Copy]] 'Copy' is useful to copy files around. <font color=darkgray>What plugin introduces Copy?</font>
* [[Gradle Delete Task Type|Delete]] 'Delete' is useful to delete files and directories. <font color=darkgray>What plugin introduces Delete?</font>
* [[Gradle Exec Task Type|Exec]] 'Exec' is useful to execute arbitrary O/S commands. <font color=darkgray>What plugin introduces Exec?</font>
====Java Plugin Predefined Task Types====
{{Internal|Gradle_Java_Plugin#Java_Plugin_Predefined_Task_Types|Java Plugin &#124; Predefined Task Types}}


A task can be first declared and then configured later:
===Extending Gradle with Custom Enhanced Tasks===
{{Internal|Extending Gradle with a Custom Enhanced Task|Extending Gradle with Custom Enhanced Tasks}}


=Locating Tasks=
Tasks may need to be located in [[build.gradle]] to configure them or to declare them as dependencies of other tasks.
==By Name==
The DSL syntax supports locating tasks by name:
<syntaxhighlight lang='groovy'>
task myTask
...
println myTask.name
println project.myTask.name
</syntaxhighlight>
==Via <tt>tasks</tt> Collection==
The tasks are available through the "tasks" collection:
<syntaxhighlight lang='groovy'>
<syntaxhighlight lang='groovy'>
task something(type: SomeType)
task myTask
 
...
something {
println tasks.named('myTask').get().name
 
  // task configuration closure
}
</syntaxhighlight>
</syntaxhighlight>
Note that the [https://docs.gradle.org/current/javadoc/org/gradle/api/NamedDomainObjectCollection.html#named-java.lang.String- named()] method returns a [https://docs.gradle.org/current/javadoc/org/gradle/api/NamedDomainObjectProvider.html NamedDomainObjectProvider<Task>] instance, so to get the corresponding Task instance, we need to call [https://docs.gradle.org/current/javadoc/org/gradle/api/provider/Provider.html#get-- get()] on the result.


=Task Types=
For a multi-project build, tasks from any project can be accessed using task's path and the <tt>tasks</tt> collection's [https://docs.gradle.org/current/javadoc/org/gradle/api/tasks/TaskContainer.html#getByPath-java.lang.String- getByPath()] method. The argument can be the tasks name, a relative path or an absolute path:
 
{{External|https://docs.gradle.org/current/dsl/#N10376}}
 
===Copy===
 
{{Internal|Gradle Copy Task|Copy Task}}
 
===Delete===
 
{{Internal|Gradle Delete Task|Delete Task}}
 
===<span id='Zip'></span><span id='Jar'></span>Archive Tasks===
 
{{Internal|Gradle Archive Tasks|Archive Tasks}}
 
===Exec===
 
[https://docs.gradle.org/current/dsl/org.gradle.api.tasks.Exec.html Exec] executes a command line process.
 
<syntaxhighlight lang='groovy'>
<syntaxhighlight lang='groovy'>
task runMain(type: Exec) {
task myTask
    commandLine 'echo', 'something'
...
}
println tasks.getByPath('myTask').path
println tasks.getByPath(':myTask').path
println tasks.getByPath('myProject:myTask').path
println tasks.getByPath(':myProject:myTask').path
</syntaxhighlight>
</syntaxhighlight>
This method is convenient because it returns directly the Task instance.


===JavaExec===
Tasks of a specific type can be accessed by using [https://docs.gradle.org/current/javadoc/org/gradle/api/DomainObjectCollection.html#withType-java.lang.Class- tasks.withType()] method:
 
[https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html JavaExec] executes a Java application in a child process.
 
<syntaxhighlight lang='groovy'>
<syntaxhighlight lang='groovy'>
task runMain(type: JavaExec) {
tasks.witType(Copy).configureEach { ... }
  classpath = 'something'
  main = 'io.example.Main'
}
</syntaxhighlight>
</syntaxhighlight>


JavaExec does not require implicitly the [[Gradle_Java_Plugin#Overview|Java plugin]], but it needs a classpath, and including the [[Gradle_Java_Plugin#Overview|Java plugin]] allows us to express it as a project variable:
=Default Task=
 
A default task can be declared in the build script, to be executed by default when Gradle is invoked without any argument.
<syntaxhighlight lang='groovy'>
<syntaxhighlight lang='groovy'>
apply plugin: 'java'
defaultTasks 'distZip'
 
task runMain(type: JavaExec) {
  classpath = sourceSets.main.runtimeClasspath
  main = 'io.example.Main'
}
</syntaxhighlight>
</syntaxhighlight>
More than one tasks can be declared as "default".


The [[Gradle_Java_Plugin#Overview|Java plugin]] adds "main" to the sourceSets, and initializes its runtimeClasspath.
=Incremental Builds=
An incremental build is a build that can avoid running tasks whose inputs and outputs did not change since the previous build, making the execution of such tasks unnecessary. More details on incremental builds, [[Gradle_Incremental_Builds#Task_Inputs_and_Outputs|task inputs and output]], [[Gradle_Incremental_Builds#Incremental_Task|incremental tasks]] and the [[Gradle_Incremental_Builds#Build_Cache|build cache]] can be found in: {{Internal|Gradle_Incremental_Builds#Incremental_Build|Incremental Builds}}


===GenerateMavenPom===
=Lifecycle Tasks=
* https://docs.gradle.org/current/userguide/more_about_tasks.html#sec:lifecycle_tasks
A lifecycle task is a task that does not do work itself.


[https://docs.gradle.org/current/dsl/org.gradle.api.publish.maven.tasks.GenerateMavenPom.html GenerateMavenPom] generates a Maven module descriptor (POM) file.
==Standard Lifecycle Tasks==


===PublishToMavenRepository===
Describing a software project build lifecycle in terms of standard lifecycle tasks ([[#assemble|assemble]], [[#check|check]] and [[#publish|publish]]) is a good idea.


[https://docs.gradle.org/current/dsl/org.gradle.api.publish.maven.tasks.PublishToMavenRepository.html PublishToMavenRepository] publishes a Maven publication to a Maven artifact repository.
===<tt>assemble</tt>===
Build the code and create any distributable packages.


===CreateStartScripts===
===<tt>check</tt>===
Run tests and any other static analysis or quality checks.


{{Internal|Gradle_Application_Plugin#Configure_the_Default_.27startScripts.27_Task|CreateStartScripts}}
===<tt>publish</tt>===
Publish artifacts (libraries or applications) to an artifact repository.


===clean===
=<span id='onlyIf'></span>onlyIf Predicate=
=Task Configuration Avoidance APIs=
<font color=darkgray>TODO:
* https://docs.gradle.org/current/userguide/task_configuration_avoidance.html
* [[Extending_Gradle_with_a_Custom_Enhanced_Task#Allowing_Configuration_via_a_Task_Constructor|Extending Gradle with a Custom Enhanced Task &#124; Allowing Configuration via a Task Constructor]]
</font>


Under unclear circumstances, some Gradle deployments complain about not finding the task "clean", though others work well with the same configuration. This can be fixed by applying the "base" plugin:
=Task Operations=
* [[Gradle_tasks_Task#Listing_All_Available_Tasks|Listing all available tasks]]
* [[Gradle_tasks_Task#Listing_Tasks_for_a_Project|Listing tasks for a project]]
* [[Gradle_help_Task#In-line_Help_for_a_Specific_Task|In-line help for a specific task]]
* [[Gradle_Command_Line#-i.2C--info|Display the tasks that are executed by a build, in order in which they are executed]]
* [[Gradle_Command_Line#-x|Exclude a task from command line]]


<syntaxhighlight lang='groovy'>
=TO DEPLETE=
apply plugin:'base'
{{Internal|Gradle Task TODEPLETE|Gradle Task TODEPLETE}}
</syntaxhighlight>
 
=React to a Task's Life Cycle Events=
 
<font color=darkgray>TODO: Research necessary.</font>
 
==Task Creation==
 
<syntaxhighlight lang='groovy'>
tasks.whenTaskAdded { task ->
      task.ext.srcDir = 'src/main/java'
}
</syntaxhighlight>
 
==Task Execution==
 
<syntaxhighlight lang='groovy'>
gradle.taskGraph.beforeTask { Task task ->
      println "executing $task ..."
}
gradle.taskGraph.afterTask { Task task, TaskState state ->
      if (state.failure) {
          println "FAILED"
      }
      else {
          println "done"
      }
}</syntaxhighlight>

Latest revision as of 01:45, 19 January 2023

External

Internal

Overview

A task is a core Gradle concept, representing well defined work to be executed during a build, such as compiling Java classes, generating javadoc or creating a distribution file. The work executed by a task is further divided into individual task actions, defined and managed by the task. Task may depend on each other. The goal of a Gradle build is to execute a set of tasks in sequence, after the tasks have been arranged in a directed acyclic graph (DAG), computed based on each task's implicit and explicit dependencies, before any task is executed. During a build execution, a task is executed only once. Understanding how the directed acyclic graph is built and how tasks are scheduled for execution is key to understanding how Gradle works.

A task belongs to a specific project. The association is made when the task is declared in build.gradle or when the plugin that declares the task is applied to the project in build.gradle. Tasks are maintained in the project's task container, and the can be looked up and manipulated from the project's build.gradle. The Locating Tasks section below gives more details on how tasks can be located.

Some tasks are built into Gradle core and are available to any project. Other tasks are contributed by plugins. New tasks can be defined by the project itself. For more details see Sources of Tasks section below.

New simple tasks can be defined in-line in build.gradle or in script plugins. Tasks whose implementations are available on the build's classpath, and that usually have their own properties and methods, can be declared and configured in build.gradle, as enhanced tasks, and thus made available for use during the build. Complex tasks can be even fully defined in-line or in script plugins, by providing the task code in-line in the script, though this is not a recommended practice, as it does not encourage modularization, encapsulation and sharing.

Any task instance implements org.gradle.api.Task interface. By default, every newly created task is of type org.gradle.api.DefaultTask, the standard implementation of org.gradle.api.Task.

Declaring, configuring and defining tasks is done via corresponding Project API methods, directly accessible from build.gradle:

Project.task(String name)
Project.task(String name, Closure configureClosure)
Project.task(String name, Action<? super Task> configureAction)
Project.task(Map<String, ?> args, String name)
Project.task(Map<String, ?> args, String name, Closure configureClosure)

A Gradle build executes the task specified on command line, including all explicit and implicit dependencies of the task.

./gradlew build

Task Lifecycle

The tasks are instantiated and configured in the build configuration phase, by executing their configuration closures. The task's actions are executed in the execution phase. A task does not necessarily have to have actions, it could only exist to execute the configuration closure at the build configuration phase. Such a task is called "task configuration".

Task Configuration Closure

Task configuration closures declare code that configures the task during the build configuration phase. The configuration closure usually initializes properties, declares task dependencies and adds actions. Note that the statements that are not part of a task action, declared outside doFirst and doLast closures, will be executed in the configuration phase, when the task is initialized and configured, not in the execution phase. For more details, see Task Lifecycle, above.

The task configuration closure can be applied at the same time the task is declared, as shown in the Simple Task section below. Alternatively, the task can be declared, then configured by applying the configuration closure later, as shown in the Enhanced Task | Configuration separated from declaration below.

Task Structure

Name and Path

Each task has a name, which is unique within the context of the task's project. The name must not contain dashes. Since different projects may contain tasks with the same name, a task is uniquely identified by its path in the context of a multi-project build. The path is the concatenation of the name of the project that owns the task and the task name, separated by the ":" character:

:web:build

Configuration

A task has configuration and actions. The code defined in the configuration section of the task will be executed during the configuration phase of the build.

Task Properties

https://docs.gradle.org/current/dsl/org.gradle.api.Task.html#N18DE0

A task has a number of properties, such as group, description, enabled, etc.

Group

A task group defines the logical grouping of tasks. The task group is available via the "group" property of the task instance, and can be set within the task constructor or directly setting the property:

task example {
  group = "experimental"
  doLast {
    ...
  }
}
task example(group: "experimental") {
  doLast {
    ...
  }
}

The task grouping is reflected in the output of the tasks task, which is one of the built-in tasks.

Description

Descriptions are displaying when executing the tasks built-in task.

task someTask(type: SomeTask) {
  // configuration closure
  description "A task that does this and that"
  ...
}

Enabled

Inputs and Outputs

In most cases, tasks have input and outputs, and whether those inputs and outputs change between successive builds is monitored by Gradle and could be used to optimize builds. A tasks' inputs and outputs are also task properties. More details about declaring and using task inputs and outputs is available here:

Task Inputs and Outputs

dependsOn, Dependencies and Ordering

Tasks can declare explicit dependencies on other tasks, and also have implicit dependencies given by the relationship between their outputs and inputs. A task's explicit dependencies can be configured with the "dependsOn" task property. Gradle offers a way to declare ordering without declaring dependency. More details are available here:

Task Dependencies and Ordering

Actions

An action is the place within a task that contains build logic. Each task contains a list of actions, which are all implementing org.gradle.api.Action and are executed when the task is executed, in order in which they were added to the task's internal list of actions.

Existing tasks have their own actions, defined when the task is coded. Additional actions can also be added in-line when the task is configured, with corresponding DSL keywords doFirst and doLast, followed by closure that contain executable code, as shown in Defining a Simple Task section. Alternatively, actions can be added to the task by invoking the Task API methods doFirst(Action|Closure), which adds the action at the head of the list, and doLast(Action|Closure), which adds the action at the tail of the list, on the task reference. The last doFirst() invocation will configure the action that is executed first. The last doLast() invocation will configure the action that is executed last. The execution order is easy to figure out if you think of an action queue to which doFirst() and doLast() add actions at the head or at the tail.

Actions can be anonymous or named.

Task Outcome

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

After execution, a task has one of the following outcomes, depending on whether a task has actions to execute, it should execute those actions, it did execute its actions and whether those actions made any changes: EXECUTED, UP-TO-DATE, FROM-CACHE, SKIPPED, NO-SOURCE.

(no label) or EXECUTED

The task has this outcome if:

  • the task has actions that should have been executed, and they were executed.
  • the task has no actions, but has dependencies and any of its dependencies has been executed.

UP-TO-DATE

The task has this outcome if:

  • the task has no actions and no dependencies.
  • the task has no actions, it has dependencies, but none of its dependencies have been executed because they are up-to-date, skipped or from cache.
  • the task has actions, but the task tells Gradle that it did not change its outputs. How does a task tell Gradle it did not change its outputs?
  • the task has inputs and outputs and they have not changed.

FROM-CACHE

The task has this outcome if its output have been generated from a previous execution and have been restored from the build cache.

SKIPPED

The task has this outcome if:

NO-SOURCE

The task has this outcome if it did not execute its actions because even if it has inputs and outputs, it has no sources.

Sources of Tasks

Each project comes with a set of pre-defined, built-in tasks, which are part of Gradle core. Each declared plugin may add its own tasks. Simple tasks may be declared by the build, as shown in the Defining Custom Tasks section, below. Also, tasks whose implementations are available on the build's classpath, and that usually have their own properties and methods, can be declared and configured in build.gradle, as enhanced tasks, and thus made available for use during the build.

Built-in Tasks

Each build exposes a set of built-in tasks. The built-in tasks are part of Gradle core and can be used right away without any additional configuration or loading any plugin:

Tasks and Plugins

Plugins usually add specialized tasks. As per this writing (Gradle 6.7), there is no command line command to list the tasks contributed by a certain plugin. However, they can be listed by doing a bit of programming:

Listing Tasks introduced by a Certain Plugin

Defining Custom Tasks

Developing Custom Gradle Task Types | https://docs.gradle.org/current/userguide/custom_tasks.html

The simplest way of extending Gradle is to write a custom task. Custom tasks can be declared in-line in the default build script build.gradle, as simple tasks. Simple tasks can also be declared in-line in a separate build script, which is then included from the default build script. The code of the custom task can live in a separate source file or multiple files, which in turn can be declared in buildSrc, which is a special area of the Gradle project, or can be shared with other projects as part of a library, developed in its own project. Such a task is referred to as a enhanced task.

Custom tasks is also how Gradle plugins extend the Gradle functionality. "Custom" in this context means new functionality defined either by plugins, or by Gradle users while writing the build scripts.

Simple Task

A simple task is an implementation of org.gradle.api.DefaultTask that can be declared in-line in build.gradle or in script plugin using DSL keywords ("task", "doFirst", "doLast", etc.) corresponding to Task API methods. The example below shows a syntax where the task name and a task configuration closure are provided.

task myTask { <configuration-closure> }

task('myTask') { <configuration-closure> }

task(myTask) { <configuration-closure> } // this syntax uses Groovy dynamic keywords

tasks.create('myTask') { <configuration-closure> }

Example:

task customSimpleTask {

  // this is the task configuration closure, it is executed during the configuration phase
  println "this will be displayed during configuration phase"

  // will add the action defined in the closure at the head of the action list
  doFirst {
    println "this will be executed second when the action is executed in the build execution phase"
  }

  doFirst {
    println "this will be executed first when the action is executed in the build execution phase"
  }

  // will add the action defined in the closure at the tail of the action list
  doLast {
    println "this will be executed last when the action is executed in the build execution phase"
  }
}

Creation Options Map

Simple task state elements can be set by using the task constructor that accepts a map, usually referred to as "map of creation options":

task example(group: "experimental", 
             description: "Example task",
             dependsOn: [task-a, task-b]) {
  doFirst {
     println "example"
  }
}

The following options are available:

Option Description Default Value
"type" The class of the task to create. DefaultTask
"overwrite" Replace an existing task? false
"dependsOn" A task name or set of task names which this task depends on []
"action" A closure or Action to add to the task. null
"description" A description of the task. null
"group" A task group which this task belongs to. null

Simple Task Examples

Enhanced Task

An enhanced task is a Gradle task that has two distinct components: the task type and task instances.

The task type is a class written in Java or Groovy, which encapsulates the task logic. Gradle comes with a number of predefined task types, but new types can be defined: the code can be declared in-line in build.gradle or a script plugin file, or preferably into a source code file (or files) maintained in the project's buildSrc or externally. The behavior, usually configurable, is built into the task type. Also, the task type may expose properties that can be configured from the build script.

The task instance is a particular instantiation of the task type, carrying a particular configuration. During the task instantiation process, values are provided for the properties exposed by the task class, and thus the instance is configured with a specific behavior. Multiple instance of the same type, with different names and configurations, may exist in the same build script.

How do tasks that come with plugins get their default name. They are not declared in the build script, so where is that declared?

Developing enhanced task is preferred because separating the implementation code from the configuration of the task increases the maintainability, the testability and the reusability of the enhanced task.

Tasks instances can be created using one of the syntaxes shown below:

task someTask(type: SomeTask)

task('someTask', type: SomeTask)

// this syntax uses Groovy dynamic keywords
task(someTask, type: SomeTask)

tasks.create('someTask', SomeTask)

If the task was written to allow to be configured via its constructor, configuration parameters can be passed as such:

tasks.create('someTask', SomeTask, 'something', ...)

For more details on how to write a task that allows configuration via constructor, see:

Extending Gradle with a Custom Enhanced Task | Allowing Configuration via a Task Constructor

Upon instantiation, the task instances may be configured using their type's API:

SomeTask myTask = tasks.getByName("myTask")

myTask.someProperty = "something"
myTask.someOtherProperty = "something else"

Alternatively, the configuration can be separated from declaration, using Groovy dynamic tasks configuration block.

task(myTask, type: SomeTask)

myTask {
  someProperty = "something"
  someOtherProperty = "something else"
}

Note that the blocks used here are for configuring the tasks and are not evaluated when the tasks executes.

Instantiation and configuration can be performed at the same time:

task someTask(type: SomeTask) {
  // configuration closure
  someProperty = "something"
  someOtherProperty = "something else"
}

task('someTask', type: SomeTask) {
  // configuration closure
}

// this syntax uses Groovy dynamic keywords
task(someTask, type: SomeTask) {
  // configuration closure
}

tasks.create('someTask', SomeTask) {
  // configuration closure
}

Predefined Task Types

https://docs.gradle.org/current/dsl/#N104B9

These predefined task types are exposed by the core plugins.

  • Copy 'Copy' is useful to copy files around. What plugin introduces Copy?
  • Delete 'Delete' is useful to delete files and directories. What plugin introduces Delete?
  • Exec 'Exec' is useful to execute arbitrary O/S commands. What plugin introduces Exec?

Java Plugin Predefined Task Types

Java Plugin | Predefined Task Types

Extending Gradle with Custom Enhanced Tasks

Extending Gradle with Custom Enhanced Tasks

Locating Tasks

Tasks may need to be located in build.gradle to configure them or to declare them as dependencies of other tasks.

By Name

The DSL syntax supports locating tasks by name:

task myTask
...
println myTask.name
println project.myTask.name

Via tasks Collection

The tasks are available through the "tasks" collection:

task myTask
...
println tasks.named('myTask').get().name

Note that the named() method returns a NamedDomainObjectProvider<Task> instance, so to get the corresponding Task instance, we need to call get() on the result.

For a multi-project build, tasks from any project can be accessed using task's path and the tasks collection's getByPath() method. The argument can be the tasks name, a relative path or an absolute path:

task myTask
...
println tasks.getByPath('myTask').path
println tasks.getByPath(':myTask').path
println tasks.getByPath('myProject:myTask').path
println tasks.getByPath(':myProject:myTask').path

This method is convenient because it returns directly the Task instance.

Tasks of a specific type can be accessed by using tasks.withType() method:

tasks.witType(Copy).configureEach { ... }

Default Task

A default task can be declared in the build script, to be executed by default when Gradle is invoked without any argument.

defaultTasks 'distZip'

More than one tasks can be declared as "default".

Incremental Builds

An incremental build is a build that can avoid running tasks whose inputs and outputs did not change since the previous build, making the execution of such tasks unnecessary. More details on incremental builds, task inputs and output, incremental tasks and the build cache can be found in:

Incremental Builds

Lifecycle Tasks

A lifecycle task is a task that does not do work itself.

Standard Lifecycle Tasks

Describing a software project build lifecycle in terms of standard lifecycle tasks (assemble, check and publish) is a good idea.

assemble

Build the code and create any distributable packages.

check

Run tests and any other static analysis or quality checks.

publish

Publish artifacts (libraries or applications) to an artifact repository.

onlyIf Predicate

Task Configuration Avoidance APIs

TODO:

Task Operations

TO DEPLETE

Gradle Task TODEPLETE