Incremental Tasks are an incubating feature.
Since the introduction of the implementation described above (early in the Gradle 1.6 release cycle), discussions within the Gradle community have produced superior ideas for exposing the information about changes to task implementors to what is described below. As such, the API for this feature will almost certainly change in upcoming releases. However, please do experiment with the current implementation and share your experiences with the Gradle community.
The feature incubation process, which is part of the Gradle feature lifecycle (see Appendix C, The Feature Lifecycle), exists for this purpose of ensuring high quality final implementation through incorporation of early user feedback.
With Gradle, it's very simple to implement a task that gets skipped when all of it's inputs and outputs are up to date (see Section 15.9, “Skipping tasks that are up-to-date”). However, there are times when only a few input files have changed since the last execution, and you'd like to avoid reprocessing all of the unchanged inputs. This can be particularly useful for a transformer task, that converts input files to output files on a 1:1 basis.
If you'd like to optimise your build so that only out-of-date inputs are processed, you can do so with an incremental task.
For a task to process inputs incrementally, that task must contain an incremental task action. This is a task action method that contains a
single IncrementalTaskInputs
parameter, which indicates to Gradle that the action will process the changed inputs only.
The incremental task action may supply an IncrementalTaskInputs.outOfDate()
action for processing any input file that is out-of-date,
and a IncrementalTaskInputs.removed()
action that executes for any input file that has been removed since the previous execution.
Example 57.1. Defining an incremental task action
build.gradle
class IncrementalReverseTask extends DefaultTask { @InputDirectory def File inputDir @OutputDirectory def File outputDir @TaskAction void execute(IncrementalTaskInputs inputs) { println inputs.incremental ? "CHANGED inputs considered out of date" : "ALL inputs considered out of date" inputs.outOfDate({ change -> println "out of date: ${change.file.name}" def targetFile = project.file("$outputDir/${change.file.name}") targetFile.text = change.file.text.reverse() } as Action) inputs.removed({ change -> println "removed: ${change.file.name}" def targetFile = project.file("$outputDir/${change.file.name}") if (targetFile.exists()) { targetFile.delete() } } as Action) } }
Note: The code for this example can be found at samples/userguide/tasks/incrementalTask
which is in both the binary and source distributions of Gradle.
For a simple transformer task like this, the task action simply needs to generate output files for any out-of-date inputs, and delete output files for any removed inputs.
A task may only contain a single incremental task action.
When Gradle has history of a previous task execution, and the only changes to the task execution context since that execution are to input files,
then Gradle is able to determine which input files need to be reprocessed by the task.
In this case, the IncrementalTaskInputs.outOfDate()
action will be executed any input file that was added or modified,
and the IncrementalTaskInputs.removed()
action will be executed for any removed input file.
However, there are many cases where Gradle is unable to determine which input files need to be reprocessed. Examples include:
upToDateWhen
criteria added to the task returns false
.
In any of these cases, Gradle will consider all of the input files to be outOfDate
.
The IncrementalTaskInputs.outOfDate()
action will be executed for every input file, and the
IncrementalTaskInputs.removed()
action will not be executed at all.
You can check if Gradle was able to determine the incremental changes to input files with IncrementalTaskInputs.isIncremental()
.
Given the incremental task implementation above, we can explore the various change scenarios by example.
First, consider the an IncrementalReverseTask
executed against a set of inputs for the first time.
In this case, all inputs will be considered "out of date":
Example 57.2. Running the incremental task for the first time
build.gradle
task incrementalReverse(type: IncrementalReverseTask) { inputDir = file('inputs') outputDir = file("$buildDir/outputs") }
Build layout
incrementalTask/ build.gradle inputs/ 1.txt 2.txt 3.txt
Output of gradle -q incrementalReverse
> gradle -q incrementalReverse ALL inputs considered out of date out of date: 1.txt out of date: 2.txt out of date: 3.txt
Naturally when the task is executed again with no changes, then task itself is up to date and no files are reported to the task action:
Example 57.3. Running the incremental task with unchanged inputs
Output of gradle -q incrementalReverse
> gradle -q incrementalReverse
When an input file is modified in some way or a new input file is added, then re-executing the task results in those files being reported to IncrementalTaskInputs.outOfDate()
:
Example 57.4. Running the incremental task with updated input files
build.gradle
task updatedInputs() << { file('inputs/1.txt').text = "Changed content for existing file 1." file('inputs/4.txt').text = "Content for new file 4." }
Output of gradle -q updatedInputs incrementalReverse
> gradle -q updatedInputs incrementalReverse CHANGED inputs considered out of date out of date: 1.txt out of date: 4.txt
When an existing input file is removed, then re-executing the task results that file being reported to IncrementalTaskInputs.removed()
:
Example 57.5. Running the incremental task with an input file removed
build.gradle
task removedInput() << {
file('inputs/3.txt').delete()
}
Output of gradle -q removedInput incrementalReverse
> gradle -q removedInput incrementalReverse CHANGED inputs considered out of date removed: 3.txt
When an output file is deleted (or modified), then Gradle is unable to determine which input files are out of date.
In this case, all input files are reported to the IncrementalTaskInputs.outOfDate()
action,
and no input files are reported to the IncrementalTaskInputs.removed()
action:
Example 57.6. Running the incremental task with an output file removed
build.gradle
task removedOutput() << {
file("$buildDir/outputs/1.txt").delete()
}
Output of gradle -q removedOutput incrementalReverse
> gradle -q removedOutput incrementalReverse ALL inputs considered out of date out of date: 1.txt out of date: 2.txt out of date: 3.txt