Gradleでカスタムタスクを定義する

## はじめに
Androidアプリ開発をしてる時に、主にビルドやライブラリの管理の時にbuild.gradleを触るかと思います。

私自身はこれまで雰囲気で使っていたのですが、それは良くないなと思っています…
そもそもGradleってなんなんだということすらしっかり理解できてなかったので、
今回はGradleをインストール・カスタムタスクの作成・実行というところをやってみたので、まとめていきたいと思います。

## Gradleとは
JavaやKotlin等を使ったプロジェクトのビルド自動化ツールになります。
コードのコンパイルやテスト、プロジェクトで使われている依存関係の管理を担って、ビルド作業を自動で行ってくれます。

対応する言語としては、以下の言語をサポートしているようです。([Gradle ユーザーマニュアル](https://docs.gradle.org/current/userguide/userguide.html))
- Android Java
- Kotlin Multiplatform
- Groovy
- Scala
- Javascript
- C/C++ 

また、私が普段使っているAndroid Studio以外にも、IntelliJ IDEAやVSCode等でも使えるみたいです。

## Gradleのビルドプロセスの全体像
以下の画像は[Gradleのガイド](https://docs.gradle.org/current/userguide/gradle_basics.html#gradle)から拝借したものになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/512668/6153eab8-96cf-4013-7d83-3372bdabcf1b.png)

ProjectとOutputの間にGradleがあるのがわかります。
また、Gradleのなかを見てみると、依存関係の管理をしてくれているのもわかります。

Androidアプリ開発で外部のライブラリを導入する際に、初めにbuild.gradleのdependenciesに依存関係を追加するかと思いますが、
ビルド時に記載したライブラリはGradleの方でインターネットから引っ張ってきて用意してくれています。

## Gradleを記述する言語
Gradleにスクリプトを記載する際に使用できる言語は２つあります。
- Groovy（動的型付言語）
- Kotlin DSL（静的型付け言語）

２つの大きな違いは、Groovyは「動的型付言語」に対し、Kotlin DSLは「静的型付け言語」であるという点です。
もしコードにエラーが含まれている場合、Groovyは実行時にエラーとなるのですが、Kotlin DSLはコンパイル時にエラーとなります。
エラーを早い段階で気づける分、Kotlin DSLの方がメリット大きそうですね。
また、エディターによってはエラーをコード上で警告してくれます。

## プロジェクトを作ってみる
実際にプロジェクトを作ってみて、どんなものか触れていきたいと思います。
### Gradleのインストール
インストール方法はOSによって異なります。詳しくは[こちら](https://docs.gradle.org/current/userguide/installation.html)を見ていただければと思います。
私のようにmacをお使いの方は以下のHomebrewを使うと簡単にインストールできます。
``` terminal
brew install gradle
```

### プロジェクトの作成
Gradleもインストールできたので、実際にGradleのコマンドを使ってプロジェクトを作っていきます。
今回はKotlinのプロジェクトで、スクリプトの記述言語はKotlin DSLを選択したいと思います。
まずは、Gradleプロジェクトを作成するフォルダを用意します。
今回は「GradleSample」というフォルダを作成しました。

ターミナルを起動し、作成したフォルダに移動してGradleの初期化コマンドを実行します。

``` terminal
cd GradleSample

gradle init --type kotlin-application  --dsl kotlin
```

上記を実行するといくつかの質問が与えられます。
今回はほとんどデフォルトを選択しました。（回答内容を以下に記載します）
<details><summary>回答内容</summary>


```terminal
GradleSample % gradle init --type kotlin-application  --dsl kotlin
Starting a Gradle Daemon (subsequent builds will be faster)

Enter target Java version (min: 7, default: 21): 

Project name (default: GradleSample): 

Select application structure:
  1: Single application project
  2: Application and library project
Enter selection (default: Single application project) [1..2] 1

Select test framework:
  1: kotlin.test
  2: JUnit Jupiter
Enter selection (default: kotlin.test) [1..2] 1

Generate build using new APIs and behavior (some features may change in the next minor release)? (default: no) [yes, no] 


> Task :init
Learn more about Gradle by exploring our Samples at https://docs.gradle.org/8.9/samples/sample_building_kotlin_applications.html

BUILD SUCCESSFUL in 21s
1 actionable task: 1 executed
```
</details>

上記実行すると、作成したフォルダに色々出来上がるかと思います。
IntelliJで開くとこんな感じです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/512668/04b72900-214e-51a1-622f-e1c632b53f23.png)

## Kotlinをビルドして実行してみる
ここでKotlinのファイルをビルドし、実行してみたいと思います。
```terminal
// ビルド
./gradlew build 

// 実行
./gradlew run
```
./gradlew build を実行すると、app配下に「build」というフォルダが出来上がるかと思います。
./gradlew run を実行し、buildにあるビルドされたファイルを実行しています。
（恐らくTerminalにHello World!と出ているかと思います。）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/512668/351551a3-0cdd-bdd7-d536-fe2a5e2822eb.png)

## タスクについて
Gradleに実行してもらうアクションのまとまりを「タスク」と言います。
先ほど使った build や run もタスクです。
そのほかにも使えるタスクはいくつかあり、tasksコマンドで確認することができます。
``` terminal
GradleSample % ./gradlew tasks

> Task :tasks

------------------------------------------------------------
Tasks runnable from root project 'GradleSample'
------------------------------------------------------------

Application tasks
-----------------
run - Runs this project as a JVM application

Build tasks
-----------
assemble - Assembles the outputs of this project.
build - Assembles and tests this project.
buildDependents - Assembles and tests this project and all projects that depend on it.
buildKotlinToolingMetadata - Build metadata json file containing information about the used Kotlin tooling
buildNeeded - Assembles and tests this project and all projects it depends on.
classes - Assembles main classes.
clean - Deletes the build directory.
jar - Assembles a jar archive containing the classes of the 'main' feature.
kotlinSourcesJar - Assembles a jar archive containing the sources of target 'kotlin'.
testClasses - Assembles test classes.

Build Setup tasks
-----------------
init - Initializes a new Gradle build.
updateDaemonJvm - Generates or updates the Gradle Daemon JVM criteria.
wrapper - Generates Gradle wrapper files.

Distribution tasks
------------------
assembleDist - Assembles the main distributions
distTar - Bundles the project as a distribution.
distZip - Bundles the project as a distribution.
installDist - Installs the project as a distribution as-is.

Documentation tasks
-------------------
javadoc - Generates Javadoc API documentation for the 'main' feature.

Help tasks
----------
buildEnvironment - Displays all buildscript dependencies declared in root project 'GradleSample'.
dependencies - Displays all dependencies declared in root project 'GradleSample'.
dependencyInsight - Displays the insight into a specific dependency in root project 'GradleSample'.
help - Displays a help message.
javaToolchains - Displays the detected java toolchains.
kotlinDslAccessorsReport - Prints the Kotlin code for accessing the currently available project extensions and conventions.
outgoingVariants - Displays the outgoing variants of root project 'GradleSample'.
projects - Displays the sub-projects of root project 'GradleSample'.
properties - Displays the properties of root project 'GradleSample'.
resolvableConfigurations - Displays the configurations that can be resolved in root project 'GradleSample'.
tasks - Displays the tasks runnable from root project 'GradleSample' (some of the displayed tasks may belong to subprojects).

Verification tasks
------------------
check - Runs all checks.
checkKotlinGradlePluginConfigurationErrors - Checks that Kotlin Gradle Plugin hasn't reported project configuration errors, failing otherwise. This task always runs before compileKotlin* or similar tasks.
test - Runs the test suite.

To see all tasks and more detail, run gradlew tasks --all

To see more detail about a task, run gradlew help --task <task>

BUILD SUCCESSFUL in 389ms
1 actionable task: 1 executed
```

## カスタムタスクを定義する
タスクは開発者自身で定義することもできます。
また、タスクに依存関係を持たせて実行させることも可能です。（タスクBの前にタスクAを実行させたい等）
簡単にカスタムタスクを定義してみようと思います。

カスタムタスクはbuild.gradle.ktsに追加していきます。
tasks.register（タスクの名前）を宣言し、中身に実行したい内容を記載します。
``` build.gradle.kts
tasks.register("taskA") {
    println("taskA")
}
```
greetingタスクを追加することができました。

実際にterminalで実行してみます。
``` terminal
GradleSample % ./gradlew taskA   

BUILD SUCCESSFUL in 893ms
taskA
```

無事taskAタスクが実行できましたね。

次に、taskBを作り、taskA→taskBと動くような依存関係を持たせたいと思います。
```build.gradle.kts
tasks.register("taskB") {
    dependsOn("taskA")
    doLast {
        println("taskB")
    }
}
```
taskBの実行前にtaskAを実行するという依存関係を持たせることができます。

``` terminal
GradleSample % ./gradlew taskB
taskA

> Task :app:taskB
taskB

```

## おわりに
Gradleのインストール ~ カスタムタスクの定義というところをやってみました。
依存関係持たせながら色々なアクションを作って定義できるのは便利そうですね。

Gradleの学習範囲が広く、まだ全然理解が追いついていないところがありますので、引き続きGradleの学習をしていきたいと思います。

## 参考
- [ユーザーマニュアル](https://docs.gradle.org/current/userguide/userguide.html)
- [新しい Gradle ビルドで Kotlin DSL がデフォルトに](https://blog.jetbrains.com/ja/kotlin/2023/05/kotlin-dsl-is-the-default-for-new-gradle-builds/)