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 6章ビルドスクリプトの基本. 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.
Gradleは2種類のタスクをサポートしています。 そのひとつは単純タスクで、アクションクロージャでタスクを定義します。 これらは6章ビルドスクリプトの基本で説明してきたものです。 このタイプのタスクに対しては、タスクのふるまいはアクションクロージャによって決まります。 このタイプのタスクはビルドスクリプトで1回限りのタスクを実装するのに適しています。
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 15章タスク詳解. 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.
もうひとつは拡張タスクで、ふるまいがタスクの中に組み込まれており、ふるまいを制御することのできるプロパティが提供されます。 これらは15章タスク詳解で説明してきたものです。 ほとんどのGradleプラグインは拡張タスクを利用します。 拡張タスクでは、単純タスクのようにふるまいを実装する必要はありません。 単にタスクを宣言し、プロパティを利用してタスクを制御するだけです。 この方法により、拡張タスクではふるまいの断片を多くの場所で再利用したり、場合によっては異なるビルドで横断的に再利用したりできます。
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.
拡張タスクのふるまいやプロパティはタスクのクラスによって定義されます。 拡張タスクを宣言するときは、タスクのタイプやクラスを指定します。
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.
Gradleで独自のカスタムタスククラスを実装するのは簡単です。 カスタムタスククラスは、最終的にバイトコードにコンパイルされるなら、どんな言語でも好きなもので実装できます。 このサンプルでは実装言語としてGroovyを使いますが、例えばJavaやScalaを使うこともできます。 Gradle APIはGroovyとの親和性が高いように設計されているので、一般論としてGroovyが一番簡単な選択肢ではありますが。
There are several places where you can put the source for the task class.
タスククラスのソースを配置できる箇所はいくつかあります。
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.
タスククラスをビルドスクリプトに直接含めることができます。 この方法は、特に何もしなくてもタスククラスが自動的にコンパイルされ、ビルドスクリプトのクラスパスに追加されるというメリットがあります。 しかし、タスククラスがビルドスクリプトの外部からは参照できないため、タスククラスを定義しているビルドスクリプトの外部では再利用できません。
buildSrc
プロジェクトbuildSrc
projectYou can put the source for the task class in the
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 rootProjectDir
/buildSrc/src/main/groovybuildSrc
project approach separates
the task declaration - that is, what the task should do - from the task implementation - that is,
how the task does it.
タスククラスのソースを
ディレクトリに配置できます。
Gradleがタスククラスのコンパイルとテストを実行し、ビルドスクリプトのクラスパス上で有効になるように取り計らってくれます。
タスククラスはビルドで利用されるすべてのビルドスクリプトから参照可能です。
しかし、ビルドの外部からは参照できないので、タスクが定義されているビルドの外部でタスククラスを再利用することはできません。
rootProjectDir
/buildSrc/src/main/groovybuildSrc
プロジェクトを使うアプローチでは、タスクの宣言「タスクが何を実行すべきか」と、タスクの実装「タスクがそれをどうやって実行するか」を分離できます。
See 60章ビルドロジックの体系化 for more details about the buildSrc
project.
buildSrc
プロジェクトの詳細については、60章ビルドロジックの体系化を参照してください。
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.
タスククラスのために独立したプロジェクトを作ることもできます。 このプロジェクトはJARを生成して発行するので、複数のビルドで利用したり、他のユーザーと共有することができます。 一般に、このJARはカスタムプラグインを含むか、関連するいくつかのタスククラスを単一のライブラリにバンドルする場合があります。 あるいは、その両方の組み合わせです。
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.
サンプルでは、簡単のためにビルドスクリプト内のタスククラスからはじめます。 その後、スタンドアロンプロジェクトについて説明します。
To implement a custom task class, you extend DefaultTask
.
カスタムタスククラスを実装するには、DefaultTask
を拡張します。
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 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 doFirst()
or doLast()
with a closure in the
task constructor to add behaviour.
このタスクは何の役にも立たないので、ふるまいを追加してみましょう。
そのためには、タスクにメソッドを追加し、TaskAction
アノテーションでマークします。
タスクが実行されたときに、Gradleがこのメソッドを呼びます。
タスクのふるまいを定義するには、必ずしもメソッドを使う必要はありません。
例えば、タスクのコンストラクタでクロージャを引数としてdoFirst()
やdoLast()
を呼ぶことでふるまいを追加することもできます。
例58.2 hello worldタスク
build.gradle
task hello(type: GreetingTask) class GreetingTask extends DefaultTask { @TaskAction def greet() { println 'hello from GreetingTask' } }
gradle -q hello
の出力
> gradle -q hello hello from GreetingTask
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 greeting
property, and set the value when we declare the greeting
task.
タスクにプロパティを追加して、カスタマイズできるようにしてみましょう。
タスクは単純なPOGOsで、タスクを宣言するときにプロパティをセットしたり、タスクオブジェクトのメソッドを呼んだりできます。
ここでは、greeting
プロパティを追加して、greeting
タスクを宣言するときにその値をセットしています。
例58.3 カスタマイズ可能なhello worldタスク
build.gradle
// Use the default greeting task hello(type: GreetingTask) // Customize the greeting task greeting(type: GreetingTask) { greeting = 'greetings from GreetingTask' } class GreetingTask extends DefaultTask { String greeting = 'hello from GreetingTask' @TaskAction def greet() { println greeting } }
gradle -q hello greeting
の出力
> gradle -q hello greeting hello from GreetingTask greetings from GreetingTask
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.
それでは、タスクをスタンドアロンプロジェクトに移動して、発行したり他ユーザーと共有したりできるようにしてみましょう。 このプロジェクトは単純なGroovyプロジェクトで、タスククラスを含むJARを生成します。 プロジェクトに対する簡単なビルドスクリプトがこちらです。
例58.4 カスタムタスクのビルド
build.gradle
apply plugin: 'groovy'
dependencies {
compile gradleApi()
compile localGroovy()
}
ノート: 本例のソースコードは、Gradleのバイナリ配布物またはソース配布物に含まれています。以下の場所をご参照ください。samples/customPlugin/plugin
We just follow the convention for where the source for the task class should go.
タスククラスのソースを配置する場所は規約に従いました。
例58.5 カスタムタスク
src/main/groovy/org/gradle/GreetingTask.groovy
package org.gradle import org.gradle.api.DefaultTask import org.gradle.api.tasks.TaskAction class GreetingTask extends DefaultTask { String greeting = 'hello from GreetingTask' @TaskAction def greet() { println greeting } }
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 buildscript { }
block, as described in 「ビルドスクリプトで外部ライブラリを使うときの依存関係設定External dependencies for the build script」.
The following example shows how you might do this when the JAR containing the task class has been published
to a local repository:
ビルドスクリプトでタスククラスを使うためには、ビルドスクリプトのクラスパスにタスククラスを追加する必要があります。
これを実行するには、「ビルドスクリプトで外部ライブラリを使うときの依存関係設定External dependencies for the build script」で説明されているとおり、buildscript { }
ブロックを使います。
次のサンプルでは、タスククラスを含むJARをローカルリポジトリに発行した場合にどのようにすればよいかを示しています:
例58.6 カスタムタスクを別のプロジェクトで使う
build.gradle
buildscript { repositories { maven { url uri('../repo') } } dependencies { classpath group: 'org.gradle', name: 'customPlugin', version: '1.0-SNAPSHOT' } } task greeting(type: org.gradle.GreetingTask) { greeting = 'howdy!' }
You can use the ProjectBuilder
class to create
Project
instances to use when you test your task class.
タスククラスのテストのために、ProjectBuilder
クラスを利用して
Project
のインスタンスを生成することができます。
例58.7 カスタムタスクのテスト
src/test/groovy/org/gradle/GreetingTaskTest.groovy
class GreetingTaskTest { @Test public void canAddTaskToProject() { Project project = ProjectBuilder.builder().build() def task = project.task('greeting', type: GreetingTask) assertTrue(task instanceof GreetingTask) } }
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 付録C 機能のライフサイクル The Feature Lifecycle), exists for this purpose of ensuring high quality final implementations 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 「更新されていないタスクをスキップする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.
例58.8 Defining an incremental task action
build.gradle
class IncrementalReverseTask extends DefaultTask { @InputDirectory def File inputDir @OutputDirectory def File outputDir @Input def inputProperty @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 = new File(outputDir, change.file.name) targetFile.text = change.file.text.reverse() } inputs.removed { change -> println "removed: ${change.file.name}" def targetFile = new File(outputDir, change.file.name) targetFile.delete() } } }
ノート: 本例のソースコードは、Gradleのバイナリ配布物またはソース配布物に含まれています。以下の場所をご参照ください。samples/userguide/tasks/incrementalTask
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 for 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. 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.
First, consider the IncrementalReverseTask
executed against a set of inputs for the first time.
In this case, all inputs will be considered “out of date”:
例58.9 Running the incremental task for the first time
build.gradle
task incrementalReverse(type: IncrementalReverseTask) { inputDir = file('inputs') outputDir = file("$buildDir/outputs") inputProperty = project.properties['taskInputProperty'] ?: "original" }
Build layout
incrementalTask/ build.gradle inputs/ 1.txt 2.txt 3.txt
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 the entire task is up to date and no files are reported to the task action:
例58.10 Running the incremental task with unchanged inputs
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()
:
例58.11 Running the incremental task with updated input files
build.gradle
task updateInputs() << { file('inputs/1.txt').text = "Changed content for existing file 1." file('inputs/4.txt').text = "Content for new file 4." }
gradle -q updateInputs incrementalReverse
の出力
> gradle -q updateInputs 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 in that file being reported to IncrementalTaskInputs.removed()
:
例58.12 Running the incremental task with an input file removed
build.gradle
task removeInput() << {
file('inputs/3.txt').delete()
}
gradle -q removeInput incrementalReverse
の出力
> gradle -q removeInput 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:
例58.13 Running the incremental task with an output file removed
build.gradle
task removeOutput() << {
file("$buildDir/outputs/1.txt").delete()
}
gradle -q removeOutput incrementalReverse
の出力
> gradle -q removeOutput incrementalReverse ALL inputs considered out of date out of date: 1.txt out of date: 2.txt out of date: 3.txt
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, all input files are reported to
the IncrementalTaskInputs.outOfDate()
action,
and no input files are reported to the IncrementalTaskInputs.removed()
action:
例58.14 Running the incremental task with an input property changed
gradle -q -PtaskInputProperty=changed incrementalReverse
の出力
> gradle -q -PtaskInputProperty=changed incrementalReverse ALL inputs considered out of date out of date: 1.txt out of date: 2.txt out of date: 3.txt