<!--
  ~ Copyright 2009 the original author or authors.
  ~
  ~ Licensed under the Apache License, Version 2.0 (the "License");
  ~ you may not use this file except in compliance with the License.
  ~ You may obtain a copy of the License at
  ~
  ~      http://www.apache.org/licenses/LICENSE-2.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.
  -->
<chapter id="custom_tasks">
    <title>Writing Custom Task Classes</title>

    <para>Gradle supports two types of task. One such type is the simple task, where you define the task with an
        action closure. We have seen these in <xref linkend="tutorial_using_tasks"/>. For this type of task, the action
        closure determines the behaviour of the task. This type of task is good for implementing one-off tasks in your
        build script.
    </para>
    <para>
        The other type of task is the enhanced task, where the behaviour is built into the task, and the task
        provides some properties which you can use to configure the behaviour. We have seen these in
        <xref linkend="more_about_tasks"/>. Most Gradle plugins use enhanced tasks. With enhanced tasks, you don't need
        to implement the task behaviour as you do with simple tasks.
        You simply declare the task and configure the task using its properties. In this way, enhanced tasks let you reuse
        a piece of behaviour in many different places, possibly across different builds.
    </para>
    <para>
        The behaviour and properties of an enhanced task is defined by the task's class. When you declare an enhanced task, you
        specify the type, or class of the task.
    </para>
    <para>
        Implementing your own custom task class in Gradle is easy.
        You can implement a custom task class in pretty much any language you like, provided it ends up compiled to bytecode.
        In our examples, we are going to use Groovy as the implementation language, but you could use, for example, Java
        or Scala. In general, using Groovy is the easiest option, because the Gradle API is designed to work well with Groovy.
    </para>

    <section id="sec:packaging_a_task_class">
        <title>Packaging a task class</title>
        <para>There are several places where you can put the source for the task class.
        </para>
        <variablelist>
            <varlistentry>
                <term>Build script</term>
                <listitem>
                    <para>You can include the task class directly in the build script. This has the benefit that the task class is
                        automatically compiled and included in the classpath of the build script without you having to do anything.
                        However, the task class is not visible outside the build script, and so you cannot reuse the task class
                        outside the build script it is defined in.
                    </para>
                </listitem>
            </varlistentry>
            <varlistentry>
                <term><filename>buildSrc</filename> project</term>
                <listitem>
                    <para>You can put the source for the task class in the
                        <filename><replaceable>rootProjectDir</replaceable>/buildSrc/src/main/groovy</filename> directory.
                        Gradle will take care of compiling and testing the task class and making it available on the
                        classpath of the build script. The task class is visible to every build script used by the build.
                        However, it is not visible outside the build, and so you cannot reuse the task class outside the
                        build it is defined in.
                        Using the <filename>buildSrc</filename> project approach separates
                        the task declaration - that is, what the task should do - from the task implementation - that is,
                        how the task does it.</para>
                    <para>
                        See <xref linkend="organizing_build_logic"/> for more details about the <filename>buildSrc</filename>
                        project.</para>
                </listitem>
            </varlistentry>
            <varlistentry>
                <term>Standalone project</term>
                <listitem>
                    <para>You can create a separate project for your task class. This project produces and
                        publishes a JAR which you can then use in multiple builds and share with others. Generally, this JAR
                        might include some custom plugins, or bundle several related task classes into a single library. Or
                        some combination of the two.
                    </para>
                </listitem>
            </varlistentry>
        </variablelist>
        <para> In our examples, we will start with the task class in the build script, to keep things simple.
            Then we will look at creating a standalone project.
        </para>
    </section>

    <section id="sec:writing_a_simple_task_class">
        <title>Writing a simple task class</title>
        <para>To implement a custom task class, you extend <apilink class="org.gradle.api.DefaultTask"/>.
        </para>
        <sample id="customTask" dir="userguide/tasks/customTask" title="Defining a custom task">
            <sourcefile file="build.gradle" snippet="define-task"/>
        </sample>

        <para>This task doesn't do anything useful, so let's add some behaviour. To do so, we add a method to the task
            and mark it with the <apilink class="org.gradle.api.tasks.TaskAction"/> annotation. Gradle will call the
            method when the task executes. You don't have to use a method to define the behaviour for the task. You
            could, for instance, call <literal>doFirst()</literal> or <literal>doLast()</literal> with a closure in the
            task constructor to add behaviour.
        </para>
        <sample id="customTaskWithAction" dir="userguide/tasks/customTask" title="A hello world task">
            <sourcefile file="build.gradle" snippet="add-action"/>
            <output args="-q hello"/>
        </sample>

        <para>Let's add a property to the task, so we can customize it. Tasks are simply POGOs, and when you declare a
            task, you can set the properties or call methods on the task object. Here we add a <literal>greeting</literal>
            property, and set the value when we declare the <literal>greeting</literal> task.
        </para>
        <sample id="customTaskWithProperty" dir="userguide/tasks/customTaskWithProperty" title="A customizable hello world task">
            <sourcefile file="build.gradle" snippet="add-property"/>
            <output args="-q hello greeting"/>
        </sample>
    </section>

    <section id="sec:custom_tasks_standalone_project">
        <title>A standalone project</title>
        <para>Now we will move our task to a standalone project, so we can publish it and share it with others.
            This project is simply a Groovy project that produces a JAR containing the task class.
            Here is a simple build script for the project. It applies the Groovy plugin, and adds the Gradle API
            as a compile-time dependency.
        </para>

        <sample id="customTaskStandalone" dir="customPlugin/plugin" title="A build for a custom task" includeLocation="true">
            <sourcefile file="build.gradle" snippet="use-plugin"/>
        </sample>

        <para>We just follow the convention for where the source for the task class should go.</para>

        <sample id="customTaskStandalone" dir="customPlugin/plugin" title="A custom task">
            <sourcefile file="src/main/groovy/org/gradle/GreetingTask.groovy"/>
        </sample>

        <section id="sec:using_your_task_class_in_another_project">
            <title>Using your task class in another project</title>
            <para>To use a task class in a build script, you need to add the class to the build script's classpath. To
                do this, you use a <literal>buildscript { }</literal> block, as described in <xref linkend="sec:build_script_external_dependencies"/>.
                The following example shows how you might do this when the JAR containing the task class has been published
                to a local repository:
            </para>
            <sample id="usingCustomTask" dir="customPlugin/consumer" title="Using a custom task in another project">
                <test args="-p../plugin uploadArchives"/>
                <test args="greeting"/>
                <sourcefile file="build.gradle" snippet="use-task"/>
            </sample>
        </section>
        <section id="sec:writing_tests_for_your_task_class">
            <title>Writing tests for your task class</title>
            <para>You can use the <apilink class="org.gradle.testfixtures.ProjectBuilder"/> class to create
                <apilink class="org.gradle.api.Project"/> instances to use when you test your task class.
            </para>
            <sample id="customTaskStandalone" dir="customPlugin/plugin" title="Testing a custom task">
                <sourcefile file="src/test/groovy/org/gradle/GreetingTaskTest.groovy" snippet="test-task"/>
            </sample>
        </section>
    </section>
    <section id="incremental_tasks">
        <title>Incremental tasks</title>
        <note>
            <para>
                Incremental tasks are an <link linkend="feature_lifecycle">incubating</link> feature.
            </para>
            <para>
                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.
            </para>
            <para>
                The feature incubation process, which is part of the Gradle feature lifecycle (see <xref linkend="feature_lifecycle"/>), exists for this purpose of ensuring high quality
                final implementations through incorporation of early user feedback.
            </para>
        </note>
        <para>
            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 <xref linkend="sec:up_to_date_checks"/>).
            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.
        </para>
        <para>
            If you'd like to optimise your build so that only out-of-date inputs are processed, you can do so with an <firstterm>incremental task</firstterm>.
        </para>
        <section id="sec:implementing_an_incremental_task">
            <title>Implementing an incremental task</title>
            <para>
                For a task to process inputs incrementally, that task must contain an <firstterm>incremental task action</firstterm>. This is a task action method that contains a
                single <apilink class="org.gradle.api.tasks.incremental.IncrementalTaskInputs"/> parameter, which indicates to Gradle that the action will process the changed inputs only.
            </para>
            <para>
                The incremental task action may supply an <apilink class="org.gradle.api.tasks.incremental.IncrementalTaskInputs" method="outOfDate"/> action for processing any input file that is out-of-date,
                and a <apilink class="org.gradle.api.tasks.incremental.IncrementalTaskInputs" method="removed"/> action that executes for any input file that has been removed since the previous execution.
            </para>
            <sample id="taskDefinition" dir="userguide/tasks/incrementalTask" title="Defining an incremental task action" includeLocation="true">
                <sourcefile file="build.gradle" snippet="incremental-task" />
            </sample>
            <para>
                If for some reason the task is not run incremental, e.g. by running with --rerun-tasks, only the
                outOfDate action is executed, even if there where deleted input files. You should consider handling this
                case at the beginning, as is done in the example above.
            </para>
            <para>
                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.
            </para>
            <para>
                A task may only contain a single incremental task action.
            </para>
        </section>
        <section id="sec:which_inputs_are_considered_out_of_date">
            <title>Which inputs are considered out of date?</title>
            <para>
                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 <apilink class="org.gradle.api.tasks.incremental.IncrementalTaskInputs" method="outOfDate"/> action will be executed for any input file that was <emphasis>added</emphasis> or <emphasis>modified</emphasis>,
                and the <apilink class="org.gradle.api.tasks.incremental.IncrementalTaskInputs" method="removed"/> action will be executed for any <emphasis>removed</emphasis> input file.
            </para>
            <para>
                However, there are many cases where Gradle is unable to determine which input files need to be reprocessed. Examples include:
            </para>
            <itemizedlist>
                <listitem>There is no history available from a previous execution.</listitem>
                <listitem>You are building with a different version of Gradle. Currently, Gradle does not use task history from a different version.</listitem>
                <listitem>An <literal>upToDateWhen</literal> criteria added to the task returns <literal>false</literal>.</listitem>
                <listitem>An input property has changed since the previous execution.</listitem>
                <listitem>One or more output files have changed since the previous execution.</listitem>
            </itemizedlist>
            <para>
                In any of these cases, Gradle will consider all of the input files to be <literal>outOfDate</literal>.
                The <apilink class="org.gradle.api.tasks.incremental.IncrementalTaskInputs" method="outOfDate"/> action will be executed for every input file, and the
                <apilink class="org.gradle.api.tasks.incremental.IncrementalTaskInputs" method="removed"/> action will not be executed at all.
            </para>
            <para>
                You can check if Gradle was able to determine the incremental changes to input files with <apilink class="org.gradle.api.tasks.incremental.IncrementalTaskInputs" method="isIncremental"/>.
            </para>
        </section>
        <section id="sec:an_incremental_task_in_action">
            <title>An incremental task in action</title>
            <para>
                Given the incremental task implementation <link linkend="taskDefinition">above</link>, we can explore the various change scenarios by example.
                Note that the various mutation tasks ('updateInputs', 'removeInput', etc) are only present for demonstration purposes: these would not normally be part of your build script.
            </para>
            <para>
                First, consider the <literal>IncrementalReverseTask</literal> executed against a set of inputs for the first time.
                In this case, all inputs will be considered “out of date”:
            </para>
            <sample id="incrementalTaskFirstRun" dir="userguide/tasks/incrementalTask" title="Running the incremental task for the first time">
                <sourcefile file="build.gradle" snippet="reverse"/>
                <layout after="originalInputs">
                    build.gradle
                    inputs/
                    inputs/1.txt
                    inputs/2.txt
                    inputs/3.txt
                </layout>
                <output args="-q incrementalReverse" ignoreLineOrder="true"/>
            </sample>
            <para>
                Naturally when the task is executed again with no changes, then the entire task is up to date and no files are reported to the task action:
            </para>
            <sample id="incrementalTaskNoChange" dir="userguide/tasks/incrementalTask" title="Running the incremental task with unchanged inputs">
                <test args="-q originalInputs incrementalReverse"/>
                <output args="-q incrementalReverse"/>
            </sample>

            <para>
                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 <apilink class="org.gradle.api.tasks.incremental.IncrementalTaskInputs" method="outOfDate"/>:
            </para>
            <sample id="incrementalTaskUpdatedInputs" dir="userguide/tasks/incrementalTask" title="Running the incremental task with updated input files">
                <sourcefile file="build.gradle" snippet="updated-inputs" />
                <test args="-q originalInputs incrementalReverse"/>
                <output args="-q updateInputs incrementalReverse" ignoreLineOrder="true"/>
            </sample>

            <para>
                When an existing input file is removed, then re-executing the task results in that file being reported to <apilink class="org.gradle.api.tasks.incremental.IncrementalTaskInputs" method="removed"/>:
            </para>
            <sample id="incrementalTaskRemovedInput" dir="userguide/tasks/incrementalTask" title="Running the incremental task with an input file removed">
                <sourcefile file="build.gradle" snippet="removed-input" />
                <test args="-q originalInputs incrementalReverse"/>
                <output args="-q removeInput incrementalReverse" ignoreLineOrder="true"/>
            </sample>

            <para>
                When an output file is deleted (or modified), then Gradle is unable to determine which input files are out of date.
                In this case, <emphasis>all</emphasis> input files are reported to the <apilink class="org.gradle.api.tasks.incremental.IncrementalTaskInputs" method="outOfDate"/> action,
                and no input files are reported to the <apilink class="org.gradle.api.tasks.incremental.IncrementalTaskInputs" method="removed"/> action:
            </para>
            <sample id="incrementalTaskRemovedOutput" dir="userguide/tasks/incrementalTask" title="Running the incremental task with an output file removed">
                <sourcefile file="build.gradle" snippet="removed-output" />
                <test args="-q originalInputs incrementalReverse"/>
                <output args="-q removeOutput incrementalReverse" ignoreLineOrder="true"/>
            </sample>

            <para>
                When a task input property is modified, Gradle is unable to determine how this property impacted the task outputs, so all input files are assumed to be out of date.
                So similar to the changed output file example, <emphasis>all</emphasis> input files are reported to
                the <apilink class="org.gradle.api.tasks.incremental.IncrementalTaskInputs" method="outOfDate"/> action,
                and no input files are reported to the <apilink class="org.gradle.api.tasks.incremental.IncrementalTaskInputs" method="removed"/> action:
            </para>
            <sample id="incrementalTaskChangedProperty" dir="userguide/tasks/incrementalTask" title="Running the incremental task with an input property changed">
                <test args="-q originalInputs incrementalReverse"/>
                <output args="-q -PtaskInputProperty=changed incrementalReverse" ignoreLineOrder="true"/>
            </sample>
        </section>
    </section>
</chapter>
