Gradle を完全に理解した人が、何も分からなくなるための第一歩

## 自己紹介
<img src="https://qiita-image-store.s3.amazonaws.com/0/28302/63d28421-104b-3066-2712-768e6aded5f7.jpeg" width="200px" />

- opengl-8080
- 主に Qiita で技術メモを書いたり
- 関西の SIer 勤務

---
## タイトルの元ネタ

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">エンジニア用語の「完全に理解した」「何も分からない」「チョットデキル」は「ダニング・クルーガー効果」で簡単に説明ができます。これは一種の認知バイアスで能力の低い段階では自分の能力の低さを認識できないためです（過大評価しがち）。その反面で能力が高くなると過少評価しがちです。 <a href="https://t.co/LGaJ4E5hWo">pic.twitter.com/LGaJ4E5hWo</a></p>&mdash; おちゃめ (@ochame_nako) <a href="https://twitter.com/ochame_nako/status/1115270046794653696?ref_src=twsrc%5Etfw">April 8, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

※スライドモードでは画像が表示されないみたいなので、スライドモードを解除して記事本文を直接参照してください。

---
## お話しすること
- 「Gradle 完全に理解した」レベルの人が、「Gradle 何も分からん」レベルになるためのお話
- 自分自身、最近 Gradle のことが何も分からなくなってきた
- 完全に理解していた頃によく分からなかったことを解説することで、  
  同じように完全に理解している人たちが何も分からなくなる一助になれば幸い

---
## 前提知識・対象者のイメージ
- ユーザマニュアル通りなら `build.gradle` を書ける
- でも、自力でゼロからタスクを作るのは無理
- `build.gradle` を読むための最低限の知識はある
    - [Groovyを知らない人のためのbuild.gradle読み書き入門](https://qiita.com/opengl-8080/items/a0bb31fb20cb6505188b) にかかれている知識を有している程度

---
## 初心者の頃によく分からなかったこと
- タスクの作り方がよく分からん:thinking:
- ファイルの選択方法がよく分からん:thinking:

---
## このあたりが分かってくると、初心者を脱出できる（たぶん）
### タスクの作り方
- ゼロからタスクを作ることができる
- 既存のタスクをカスタマイズできる

### ファイルの選択
- ビルドタスクは、だいたい何らかのファイル操作が伴う
- ファイル操作はビルドの基本であり中核（おそらく）

---
## タスクの作り方がよく分からん:thinking:

---
## タスクに関する疑問アレコレ
- `doFirst`, `doLast` って２つあるけど、どっちを使うの？
- タスクの順番ってどうやって制御するの？
- ファイルコピーって結局どうやって実装するのがいいの？
    - Java の API
    - Groovy の API
    - Project の copy() メソッド
    - `task(type: Copy)`
        - `type: Copy` ってなんなん？

---
## doFirst, doLast、どっち使えばいいの？
```gradle:build.grdle
task hoge {
    doFirst { println "doFirst" }
    doLast { println "doLast" }
}
```

```text:実行結果
$ gradle hoge
...
doFirst
doLast
```

- どちらも、タスクの処理を記述するためのモノ
- 使い分けは？

---
## タスクの正体
![gradle.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28302/d51cc987-685c-1672-7ab6-eadf2518e582.jpeg)

- [org.gradle.api.Task](https://docs.gradle.org/current/javadoc/org/gradle/api/Task.html) のインスタンス
- 内部に [Action](https://docs.gradle.org/current/javadoc/org/gradle/api/Action.html) のリストを持つ
- `Action` は、何らかの処理を持つ
- `Task` は、 `Action` のリストを先頭から順番に実行していく

---
## doFirst と doLast は Action を追加するメソッド
![gradle.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28302/80135906-723c-f1e9-2fac-849c63b8f177.jpeg)

- [doFirst](https://docs.gradle.org/current/javadoc/org/gradle/api/Task.html#doFirst-groovy.lang.Closure-) と [doLast](https://docs.gradle.org/current/javadoc/org/gradle/api/Task.html#doLast-groovy.lang.Closure-) は、 `Task` に定義されたメソッド
- 引数に渡したクロージャを `Action` にして `Task` に追加する
- `doFirst` は、 `Action` リストの先頭に追加する
- `doLast` は、 `Action` リストの末尾に追加する

---
## どちらを使うのがいいの？
- 自作タスクなら、どちらでもいい
- 統一感が出るので、どちらかに揃えるくらいはしておいたほうがいい

---
## 既存タスクに処理を追加できる
```gradle:build.gradle
plugins { id "java" }

compileJava {
    doFirst { println "Before compileJava" }
    doLast { println "After compileJava" }
}
```

```text:実行結果
$ gradle compileJava
...
Before compileJava
After compileJava
```

- `doFirst`, `doLast` の使い分けが重要になるのは、既存タスクを修正するような場面
- 組み込みタスクに、任意の事前（事後）処理を追加できる

---
## タスクの実行順序の制御

---
## タスクＡの後でタスクＢを実行したい
```gradle:build.gradle
task taskA {
    doFirst {
        println "Task A"
        tasks.taskB.execute() // 注：実際はできない
    }
}

task taskB {
    doFirst { println "Task B" }
}
```

- 自分が初心者の頃は、指定したタスクを実行する方法を探していた

---
## Gradle のタスクは依存関係で順序を制御する
考え方の向きを逆転させる

<font color="red">×</font> 「～～したら、・・・する」（実行順序）
<font color="blue">○</font> 「・・・するためには、先に～～が必要」（依存関係）

![gradle.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28302/79ceac41-5d56-282d-2ce7-fc017adc812f.jpeg)


---
## タスクの依存関係を定義する
```gradle:build.gradle
task taskA {
    doFirst { println "Task A" }
}

task taskB {
    // ★ taskB が taskA に依存していることを定義する
    dependsOn "taskA"
    doFirst { println "Task B" }
}
```

```text:実行結果
$ gradle taskB
...
> Task :taskA
Task A

> Task :taskB
Task B
```

- `Task` の `dependsOn` で依存するタスクを指定する

---
## とは言っても順序を指定したくなる場面はある
典型的な例は、 `clean` と `build` を同時に実行したとき

```
$ gradle build clean
...
> Task :build
> Task :clean
```

- `build` してから `clean` してしまっている
- `dependsOn` で依存関係として定義すると、 `build` したときは常に `clean` されてしまう
- 依存関係（必ず実行するもの）ではないが、同時に実行されたら順序を制御したい

---
## タスクの実行順序を定義する
```gradle:build.gradle
plugins { id "java" }

build.mustRunAfter clean
```

```text:実行結果
$ gradle build clean
...
> Task :clean
> Task :build
```

- [mustRunAfter](https://docs.gradle.org/current/javadoc/org/gradle/api/Task.html#mustRunAfter-java.lang.Object...-) を使うと、複数のタスクが同時に指定されたときに、実行する順序を制御できる
- 依存関係はないけど同時に実行されたときは順序を制御したい、といったときに利用できる

---
## ファイルコピーの実装方法

---
## 実現方法がいくつもある
- Java の標準 API を使う
- Groovy の API を使う
- [Project](https://docs.gradle.org/current/javadoc/org/gradle/api/Project.html) の [copy()](https://docs.gradle.org/current/javadoc/org/gradle/api/Project.html#copy-groovy.lang.Closure-) メソッドを使う
- [Copy](https://docs.gradle.org/current/javadoc/org/gradle/api/tasks/Copy.html) タスクを使う

---
## Java の標準 API を使う
```gradle:build.gradle
import java.nio.file.*

task copyFile {
    doFirst {
        Path from = Paths.get("${projectDir}/foo.txt")
        Path to = Paths.get("${buildDir}/foo.txt")
        Files.copy(from, to)
    }
}
```

- `build.gradle` は実質 Groovy のスクリプトファイルなので、 Java のプログラムを書くことができる
- つまり、 Java の標準 API （[Files.copy(Path, Path, CopyOption...)](https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/nio/file/Files.html#copy(java.nio.file.Path,java.nio.file.Path,java.nio.file.CopyOption...))）を使用できる

---
## Groovy の API を使う
```gradle:build.gradle
task copyFile {
    doFirst {
        def from = new File("${projectDir}/foo.txt")
        def to = new File("${buildDir}/build/foo.txt")
        to.bytes = from.bytes
    }
}
```

- Groovy でのみ利用できる書き方も可能
- ※この書き方はメモリに一旦全データを読み込んでいるので注意（あくまで例）

---
## Project の copy() メソッドを使う
```gradle:build.gradle
task copyFile {
    doFirst {
        copy {
            from "./foo.txt"
            into buildDir
        }
    }
}
```

- [Project](https://docs.gradle.org/current/javadoc/org/gradle/api/Project.html) の [copy()](https://docs.gradle.org/current/javadoc/org/gradle/api/Project.html#copy-groovy.lang.Closure-) メソッドを使うことでファイルのコピーが可能

---
## Copy タスク
```gradle:build.gradle
task copyFile(type: Copy) {
    from "./foo.txt"
    into buildDir
}
```

- [Copy](https://docs.gradle.org/current/javadoc/org/gradle/api/tasks/Copy.html) タスクを使うことで `Project.copy()` を使った場合と似たような記述でコピーを実現できる

---
## どれを使うのがいいの？

---
## より簡潔で宣言的な記述になる方法を選択するのがいいと思う

---
## 宣言的な記述
- 何をするか（目的・やりたいこと・what）にフォーカスした記述
- どうやって実現するか（手段・方法・how）は書かない

---
## やりたいこと
ファイル `foo.txt` を `build` ディレクトリの下にコピーしたい。

---
## Groovy の例
```gradle:build.gradle
task copyFile {
    doFirst {
        // 1. foo.txt の File オブジェクトを構築して、 from 変数に代入
        def from = new File("${projectDir}/foo.txt")
        // 2. コピー先となる File オブジェクトを構築して、 to 変数に代入
        def to = new File("${buildDir}/foo.txt")
        // 3. from の内容をバイトで読み込んで、 to にそのまま書き込む
        to.bytes = from.bytes
    }
}
```

- 実現方法が書かれている
    - `File` オブジェクトの準備
    - バイトを読み込んで、書き込む

---
## Java の例
```gradle:build.gradle
import java.nio.file.*

task copyFile {
    doFirst {
        // 1. foo.txt の Path オブジェクトを構築して、 from 変数に代入
        Path from = Paths.get("${projectDir}/foo.txt")
        // 2. コピー先となる Path オブジェクトを構築して、 to 変数に代入
        Path to = Paths.get("${buildDir}/foo.txt")
        // 3. from から to にコピー
        Files.copy(from, to)
    }
}
```

- `Files.copy()` のおかげで少し宣言的になった
- でも雑音が多い
    - `Path` の構築
    - `Files` というユーティリティクラスの存在

---
## Project の copy() メソッド
```gradle:build.gradle
task copyFile {
    doFirst {
        // 1. コピーする
        copy {
            // 2. foo.txt を
            from "./foo.txt"
            // 3. buildDir へ
            into buildDir
        }
    }
}
```

- やりたいことだけが書かれている
    - 「ファイル `foo.txt` を `build` ディレクトリの下にコピーしたい。」
- 雑音もなくて簡潔

---
## 宣言的な記述のメリット
- 目的にフォーカスすることで、やりたいこと（意図）が読み取りやすくなる
- 詳細な手段を省くことで記述が簡潔になる

---
## Copy と copy() の違いは？
Copy タスクと Project の copy() を使った場合の違いは？

```gradle:build.gradle
task copyTask(type: Copy) {
    from "./foo.txt"
    into buildDir
}

task projectCopy {
    doFirst {
        copy {
            from "./foo.txt"
            into buildDir
        }
    }
}
```

- 行数は `Copy` を使ったほうが短くなっているが、 `from` や `into` などの記述は同じ感じ
- 何が違う？

---
## タスクの型が違う
```gradle:build.gradle
task copyTask(type: Copy) {
    from "./foo.txt"
    into buildDir
}

task projectCopy {
    doFirst {
        copy {
            from "./foo.txt"
            into buildDir
        }
    }
}

println "copyTask.class = ${copyTask.class}"
println "projectCopy.class = ${projectCopy.class}"
```

```text:実行結果
$ gradle
...
copyTask.class = class org.gradle.api.tasks.Copy_Decorated
projectCopy.class = class org.gradle.api.DefaultTask_Decorated
```

### 補足

- `type` を指定してタスクを定義した場合、タスクのインスタンスは `type` で指定したクラスから生成される
    - `copyTask` タスクは `Copy` のインスタンス
    - `projectCopy` タスクは `DefaultTask` のインスタンス

```gradle:build.gradle
task copyFile(type: Copy) { ...

// Java 風に書くと
task(Map.of("type", Copy.class), "copyFile");
```

- `type: ` は、名前付き引数
- `Copy` は、 Java のコードになおすと `Copy.class` になる
- つまり、 `Copy` クラスの `Class` オブジェクトを `type` という名前付き引数で渡していることになる

---
## 型が違うだけ？<br>使い方に差はある？

---
## どちらも CopySpec を使用している
```gradle:build.gradle
task copyTask(type: Copy) {
    from "./foo.txt"
    into buildDir

    println "copyTask > ${delegate instanceof CopySpec}"
}

task projectCopy {
    doFirst {
        copy {
            from "./foo.txt"
            into buildDir
            println "projectCopy > ${delegate instanceof CopySpec}"
        }
    }
}
```

```text:実行結果
$ gradle projectCopy
...
copyTask > true
projectCopy > true
```

- [Copy](https://docs.gradle.org/current/javadoc/org/gradle/api/tasks/Copy.html) タスクは [CopySpec](https://docs.gradle.org/current/javadoc/org/gradle/api/file/CopySpec.html) を実装している
- [Project.copy(Closure)](https://docs.gradle.org/current/javadoc/org/gradle/api/Project.html#copy-groovy.lang.Closure-) メソッドは、引数のクロージャの `delegate` が `CopySpec` になっている

---
## コピーの内容を定義するためのインターフェース
```java:CopySpec
public interface CopySpec extends ... {
    ...
    CopySpec from(Object... sourcePaths);
    ...
    CopySpec into(Object destPath);
    ...
}
```

- [CopySpec](https://docs.gradle.org/current/javadoc/org/gradle/api/file/CopySpec.html) は、その名の通りコピーの仕様を定義するメソッドを定義している
- コピーの内容を定義する方法が `CopySpec` で統一されている
- つまり、使い方（書き方）は同じということになる

---
## 書き方が CopySpec で統一されているということは、<br>結局両者に違いは無いということ？

---
## 複数回実行すると違いが見えてくる
```text:Copyを利用したタスクを複数回実行した場合
# １回目
$ gradle copyTask
> Task :copyTask

BUILD SUCCESSFUL in 4s
1 actionable task: 1 executed

# ２回目
$ gradle copyTask
> Task :copyTask UP-TO-DATE

BUILD SUCCESSFUL in 3s
1 actionable task: 1 up-to-date
```

２回目は `UP-TO-DATE` でタスクが実行されなかった。

```text:Project.copy()を利用したタスクを複数回実行した場合
# １回目
$ gradle projectCopy
> Task :projectCopy

BUILD SUCCESSFUL in 3s
1 actionable task: 1 executed

# ２回目
$ gradle projectCopy
> Task :projectCopy

BUILD SUCCESSFUL in 3s
1 actionable task: 1 executed
```

２回目も実行された。

---
## 不必要なタスクの再実行を防ぐ仕組み
- Gradle には、タスクの不必要な再実行を防ぐ仕組みがある
    - タスクの入出力を定義する
    - タスクが実行されると、入出力の内容が記録される
    - 入出力が前回と同じ場合、タスクは実行されない（`UP-TO-DATE`）
- `Copy` タスクは、 `from` や `into` で指定した内容がタスクの入出力になる
- `Project.copy()` は、純粋にファイルをコピーするだけ
    - タスクの入出力は、別途自力で設定する必要がある

---
## Project.copy() と Copy の違い
### Project.copy()
- 純粋にコピーだけを行う
- 「コピー＝タスクの処理内容の一部」の場合に便利

### Copy
- コピーだけでなく、 Gradle タスクとして便利な設定が施されている
- 「コピー＝タスクの処理内容そのもの」の場合に便利

---
## ところで ～～Spec はいろいろある（ちょっと脱線）
- [CopySpec](https://docs.gradle.org/current/javadoc/org/gradle/api/file/CopySpec.html) ：コピーの仕様
- [DeleteSpec](https://docs.gradle.org/current/javadoc/org/gradle/api/file/DeleteSpec.html) ：削除の仕様
- [ExecSpec](https://docs.gradle.org/current/javadoc/org/gradle/process/ExecSpec.html) ：コマンド実行の仕様
- [JavaExecSpec](https://docs.gradle.org/current/javadoc/org/gradle/process/JavaExecSpec.html)：Javaコマンド実行の仕様

同じ `～～Spec` を使用している部分は、同じ書き方ができる。

---
## CopySpec はところどころに出てくる（ちょっと脱線）
`CopySpec` の書き方を覚えておくと、いろいろなところで応用できる。

### delegate
```gradle:build.gradle
task syncSrc {
    doFirst {
        project.sync {
            // delegate が CopySpec
            from "src"
            into "${buildDir}/sync"
        }
    }
}
```

- [Project.sync(Action<? super CopySpec>)](https://docs.gradle.org/current/javadoc/org/gradle/api/Project.html#sync-org.gradle.api.Action-)

### タスク
```java
task zipTask(type: Zip) {
    // Zip が CopySpec を実装している
    from "src"

    destinationDirectory = buildDir
    archiveBaseName = "zipTask"
}
```

- [Jar](https://docs.gradle.org/current/javadoc/org/gradle/jvm/tasks/Jar.html)
- [War](https://docs.gradle.org/current/javadoc/org/gradle/api/tasks/bundling/War.html)
- [Tar](https://docs.gradle.org/current/javadoc/org/gradle/api/tasks/bundling/Tar.html)
- [Zip](https://docs.gradle.org/current/javadoc/org/gradle/api/tasks/bundling/Zip.html)

---
## タスクの作り方・まとめ
### doFirst, doLast って２つあるけど、どっちを使うの？
- タスクの実体は `Task` のインスタンス
- `Task` は `Action` のリストを持ち、先頭から順番に実行していく
- `doFirst`, `doLast` は、 `Action` のリストの先頭か末尾に `Action` を追加する
- 自作タスクならどちらでもいい
- 既存タスクに処理を追加するときに意識する


### タスクの順番ってどうやって制御するの？
- タスクの順序は依存関係で定義する
- 依存関係は無いが順番を制御したいときは `mustRunAfter` を使う


### ファイルコピーって結局どうやって実装するのがいいの？
- 目的を達成できる方法が複数ある場合は、より宣言的で簡潔になる方法を選ぶ
- `Project.copy()` は、純粋なコピー処理のみ
- `Copy` は、タスクの入出力まで定義する
- 使い方を統一するために `～～Spec` というインターフェースが存在する

---
## ファイルの選択方法がよく分からん:thinking:

---
## ファイル選択に関する疑問アレコレ
- `file()` とか `files()` とか `fileTree()` の使い方がよくわからない
- `file()` は、まぁなんとか分かる
- `files()`, `fileTree()` になってくると違いが分からなくなってくる

---
## file()
```gradle:build.gradle
task foo {
    doFirst {
        File file = file("./foo.txt")
        file.text = "Hello Foo!!"
        println file.text
    }
}
```

```text:実行結果
$ gradle foo
> Task :foo
Hello Foo!!
```

- [file()](https://docs.gradle.org/current/javadoc/org/gradle/api/Project.html#file-java.lang.Object-) は、引数で指定したパスを表す `java.io.File` オブジェクトを返す
- 文字列だけでなく `File` や `java.nio.file.Path`, `java.net.URL`, `java.net.URI` などを渡しても、よしなに判断してくれる

---
## files()
```gradle:build.gradle
task foo {
    doFirst {
        ConfigurableFileCollection files
            = files("hoge.txt", "fuga.txt", "piyo.txt")

        files
            .findAll { it.name.contains("g") }
            .each { println it }

        println "files.asPath = ${files.asPath}"
    }
}
```

```text:実行結果
C:\work\hoge.txt
C:\work\fuga.txt

files.asPath = C:\work\hoge.txt;C:\work\fuga.txt;C:\work\piyo.txt
```

- [files()](https://docs.gradle.org/current/javadoc/org/gradle/api/Project.html#files-java.lang.Object...-) の戻り値の型は [ConfigurableFileCollection](https://docs.gradle.org/current/javadoc/org/gradle/api/file/ConfigurableFileCollection.html)
- [FileCollection](https://docs.gradle.org/current/javadoc/org/gradle/api/file/FileCollection.html) を継承している
    - 名前の通り `File` のコレクション
- `Iterable<File>` を継承しているので、 `findAll()` や `each()` などの Groovy のコレクション用メソッドを使用できる
- また、 [getAsPath()](https://docs.gradle.org/current/javadoc/org/gradle/api/file/FileCollection.html#getAsPath--) のような便利メソッドも定義されている
    - 各パスを環境ごとのパス区切り文字で連結した文字列を返す
    - Windows なら `;`、 Linux 系の環境なら `:` で連結した文字列になる
    - CUI ツールを起動するときのオプション指定で活用できる

---
## fileTree()
```gradle:build.gradle
task foo {
    doFirst {
        ConfigurableFileTree tree = fileTree("src")

        tree.each { println it }

        println "tree.asPath = ${tree.asPath}"
    }
}
```

```text:実行結果
$ gradle foo
C:\work\src\main\java\fuga\Fuga.java
C:\work\src\main\java\hoge\Hoge.java
C:\work\src\main\java\Main.java
C:\work\src\main\resources\fuga\fuga.xml
C:\work\src\main\resources\hoge\hoge.xml
C:\work\src\main\resources\main.xml

tree.asPath = C:\work\src\main\java\fuga\Fuga.java;C:\work\src\main\java\hoge\Hoge.java;C:\work\src\main\java\Main.java;C:\work\src\main\resources\fuga\fuga.xml;C:\work\src\main\resources\hoge\hoge.xml;C:\work\src\main\resources\main.xml
```

- [fileTree(Object)](https://docs.gradle.org/current/javadoc/org/gradle/api/Project.html#fileTree-java.lang.Object-) の戻り値の型は [ConfigurableFileTree](https://docs.gradle.org/current/javadoc/org/gradle/api/file/ConfigurableFileTree.html)
- [FileTree](https://docs.gradle.org/current/javadoc/org/gradle/api/file/FileTree.html) を継承している
    - 名前の通り、 `File` の木構造（階層構造）を表している
    - 引数で指定したディレクトリ以下の階層を取得できる
- `FileCollection` を継承しているので、 `FileCollection` と同じ操作が可能

---
## 階層構造内のファイルを絞り込む
```gradle:build.gradle
task foo {
    doFirst {
        ConfigurableFileTree tree = fileTree("src")
        tree.include "**/hoge/**"

        tree.each { println it }
    }
}
```

```text:実行結果
$ gradle foo
C:\work\src\main\java\hoge\Hoge.java
C:\work\src\main\resources\hoge\hoge.xml
```

- [include()](https://docs.gradle.org/current/javadoc/org/gradle/api/tasks/util/PatternFilterable.html#include-java.lang.String...-) や [exclude()](https://docs.gradle.org/current/javadoc/org/gradle/api/tasks/util/PatternFilterable.html#exclude-java.lang.String...-) で、階層構造内のファイル（ディレクトリ）を絞り込むことができる
- 引数の文字列には、**Ant スタイルのパターン** を指定する

### Ant スタイルのパターン
- [Apache Ant](https://ant.apache.org/) で使用されていたファイルやディレクトリを絞り込むときのパターン
- `*`：任意の文字列
    - `foo/*`[^1]
    - `*.java`[^2]
    - `Test*`[^3]
- `**`：任意のサブディレクトリ
    - `**/foo`[^4]
    - `**/bar/*`[^5]
    - `fizz/**/*.java`[^6]

[^1]: `foo` ディレクトリ直下の全ファイル
[^2]: 拡張子が `java` のファイル
[^3]: `Test` で始まるファイル
[^4]: 任意のサブディレクトリ内の `foo` ディレクトリ（ファイル）
[^5]: 任意のサブディレクトリ内の `bar` ディレクトリ直下の全ファイル
[^6]: `fizz` ディレクトリ以下の任意のサブディレクトリにある拡張子が `java` のファイル

---
## fileTree() には書き方がいくつかある
```gradle:build.gradle
task foo {
    doFirst {
        // 1. トップのディレクトリを指定するだけ
        ConfigurableFileTree tree = fileTree("src")
        tree.include "**/hoge/**"

        // 2. 第二引数にクロージャを渡して ConfigurableFileTree の設定を記述できる
        fileTree("src") {
            include "**/hoge/**"
        }

        // 3. ConfigurableFileTree のプロパティに設定したい値を名前付き引数で指定する
        fileTree(dir: "src", include: "**/hoge/**")
    }
}
```

- [fileTree(Object)](https://docs.gradle.org/current/javadoc/org/gradle/api/Project.html#fileTree-java.lang.Object-) は、階層構造のトップとなるディレクトリを指定するのみ
- [fileTree(Object, Closure)](https://docs.gradle.org/current/javadoc/org/gradle/api/Project.html#fileTree-java.lang.Object-groovy.lang.Closure-) は、第二引数にクロージャを渡す
    - クロージャの `delegate` は対象ディレクトリの `ConfigurableFileTree` なので、ファイルの絞り込みなどを記述できる
- [fileTree(Map)](https://docs.gradle.org/current/javadoc/org/gradle/api/Project.html#fileTree-java.util.Map-) は `ConfigurableFileTree` のプロパティにセットする値を名前付き引数で指定できる

---
## file(), files(), fileTree() の違いを整理
- `file()` は、単一のファイル（ディレクトリ）を指定して、 `File` を作る
- `files()` は、複数のファイル（ディレクトリ）を指定して、 `File` のコレクションを作る
- `fileTree()` は、ディレクトリを指定して階層構造で取得する

---
## files() の引数に指定できる値
```gradle:build.gradle
import java.nio.file.Paths

task foo {
    doFirst {
        def files = files(
            "foo.txt",
            new File("foo.txt"),
            file("file.txt"),
            Paths.get("file.txt"),
            ["hoge.txt", file("fuga.txt"), Paths.get("piyo.txt")],
            files("foo.txt", "bar.txt"),
            fileTree(dir: "src", include: "**/*.java")
        );
    }
}
```

- ファイルっぽいものなら、だいたい渡せる
    - [CharSequence](https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/lang/CharSequence.html), [String](https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/lang/String.html), [GString](http://docs.groovy-lang.org/latest/html/gapi/groovy/lang/GString.html), [File](https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/io/File.html), [Path](https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/nio/file/Path.html), [URI](https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/net/URI.html), [URL](https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/net/URL.html), [Directory](https://docs.gradle.org/current/javadoc/org/gradle/api/file/Directory.html), [RegularFile](https://docs.gradle.org/current/javadoc/org/gradle/api/file/RegularFile.html)
- [Collection](https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/util/Collection.html) や [Iterable](https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/lang/Iterable.html) を渡した場合は中身を再帰的に解析してくれる
- [FileCollection](https://docs.gradle.org/current/javadoc/org/gradle/api/file/FileCollection.html) も OK なので、他の `files()` や `fileTree()` で取得した結果を渡すことも可能

---
## file(), files() は様々なところで利用されている
- 例えば `CopySpec` の [from()](https://docs.gradle.org/current/javadoc/org/gradle/api/file/CopySpec.html#from-java.lang.Object...-) メソッドは、引数の値を `files()` に渡している

> **The given paths are evaluated as per Project.files(Object...).**

- Gradle の API でファイルやファイルのコレクションを受け取るところは、だいたい `file()` または `files()` を使っている
    - API ドキュメントを見ると書いてある

---
# files() を使っている = FileTree を渡せる<br>これが分かると応用の幅が広がる

---
## zip を解凍する方法
```gradle:build.gradle
task foo {
    doFirst {
        copy {
            from zipTree("foo.zip")
            into "${buildDir}/foo"
        }
    }
}
```

- [zipTree()](https://docs.gradle.org/current/javadoc/org/gradle/api/Project.html#zipTree-java.lang.Object-) は、指定された zip ファイルの内容を `FileTree` にして取得するメソッド
- zip 内のファイルをコピーする = zip を解凍する
- コピーだけでなく、 `files()` を使ってファイル情報を受取る API に対して `zipTree()` の結果を渡すことができる

---
## 自作タスクの設定で利用する
```gradle:build.gradle
task foo(type: MyTask) {
    inputFiles "foo.txt", file("bar.txt"), Paths.get("fizz.txt")
}

class MyTask extends DefaultTask {
    Object inputFiles

    @TaskAction
    def execute() {
        println "<<inputFiles>>"
        project.files(this.inputFiles).each { println it }
    }

    void inputFiles(Object... args) {
        this.inputFiles = args
    }
}
```

```text:実行結果
<<inputFiles>>
C:\work\foo.txt
C:\work\bar.txt
```

- 自作タスクのオプションでファイルの情報を受け取る場合は、内部で `file()`, `files()` を使って解析する
- こうすることで、柔軟なファイル指定が可能なタスクを作ることができる

---
## ファイルの選択・まとめ
### ファイルを選択するための API
- `file()` は、単一のファイルを指定して `File` を取得する
- `files()` は、複数のファイルを指定して `FileCollection` を取得する
- `fileTree()` は、ディレクトリを指定して `FileTree` を取得する
    - Ant 形式のパターンで柔軟な絞り込みが可能

### file(), files() の柔軟性
- `file()`, `files()` は、ファイルっぽいものなら何でも渡せる
- `FileCollection` も渡すことができるので、 `fileTree()` の結果を渡すこともできる
- Gradle の API の多くは `file()`, `files()` を使って引数を解析している
- `fileTree()` の結果を渡すことで柔軟な組み合わせが可能となる
- 自作タスクも `file()`, `files()` を使うことで、柔軟性が増す

---
## さらに何も分からなくなるために
- タスクの入出力
- タスクルール
- プラグイン
- 拡張プロパティ
- Configuration
- Property
- etc...

今日の話も含め、もうちょっと詳しい話が知りたい場合は↓を参照
[Gradle のタスク定義のあれこれ - Qiita](https://qiita.com/opengl-8080/items/0a192b62ee87d8ac7578)