buildSrcにKotlinで処理を書きGradle Tasksとして実行する

この記事は[Android Advent Calendar 2022](https://qiita.com/advent-calendar/2022/android)のカレンダー2の12月25日の記事となります。

私はAndroidアプリのビルド処理の際にassetsファイルなどをapiから自動で取得したりといった処理をbuild.gradle(.kts)にtasksとして直接書いていました。

ただこの方法だと普段KotlinやJavaでやっているようなpackageやファイルの分割などが難しくてコードの整理ができず、ぐちゃぐちゃな状態になっていました。
これを普段KotlinやJavaで書いているようにできると便利だなと思い調べてみました。

いろいろ応用が効きそうで便利なため使ってみてください。

# 前準備(任意)

* `settings.gradle`, `projectroot/build.gradle`, `app/build.gradle`をそれぞれkts化(kts化することを何と呼べばいいかわからない)しておく
    * 必須ではないですが本記事ではkts化前提で記述しています.

# buildSrcの追加

1. project rootに `buildSrc` ディレクトリを追加
1. `buildSrc/build.gradle.kts` を追加
1. gradle sync
    * これでbuildSrcディレクトリが認識されるはず
1. build.gradle.kts に以下を記述
    ```build.gradle.kts
    plugins {
        kotlin("jvm") version "1.6.10" //おそらくアプリ全体でばーじょんを合わせないとだめ
    }
    
    buildscript {
        repositories {
            mavenCentral()
        }
    
        dependencies {
            classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10")
        }
    }
    
    repositories {
        mavenCentral()
    }
    
    dependencies {
        implementation(gradleApi())
        implementation(localGroovy())
    
        //taskで必要なものを任意で追加. 
        //通信処理を書きたい場合はktorを追加するなど
    }
    ```

# 処理を記述するKotlinファイルを追加

1. `buildSrc/src` ディレクトリを追加
1. AndroidStudio上から 
    1. `src` を右クリック
    1. [New] -> [Directory] -> [main/kotlin]
1. `main/kotlin` に任意のpackageを追加 (本記事では `jp.pmcoffee.tasks`)
1. 任意でpackageを分ける. 今回は hoge, fugaを追加
    1. `jp.pmcoffee.tasks.hoge`
    1. `jp.pmcoffee.tasks.fuga`
1. それぞれのpackageにclassを追加. 今回はそれぞれ`HogeTask`, `FugaTask`
1. HogeTaskに以下のように記述(FugaTaskも同様に)
    ```kotlin
    package jp.pmcoffee.tasks.hoge
    
    import org.gradle.api.DefaultTask
    import org.gradle.api.tasks.TaskAction
    
    abstract class HogeTask: DefaultTask() {
    
        @TaskAction
        fun runTask() {
            println("run HogeTask")
        }
    }
    ```
    * DefaultTaskを継承することとtaskとして実行したい関数に@TaskActionアノテーションをつけることが大事.

# gradle taskとして登録

1. `project_root/build.gradle.kts` に以下を追記
    
    ```build.gradle.kts
    tasks.register("runHogeTask", jp.pmcoffee.tasks.hoge.HogeTask::class)
    tasks.register("runHogeTask", jp.pmcoffee.tasks.fuga.FugaTask::class)
    ```
1. gradle sync

これで gradle taskに`runHogeTask`, `runFugaTask` が登録される

# 登録したgradle taskを実行
1. 画像のようにGradleの実行設定を追加
    * Runの項目には先程登録した `runHogeTask` を記述
    * ![スクリーンショット 2022-12-25 13.56.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41839/9490ba11-c84f-c319-11c4-dd195bce1053.png)

1. 作成した実行設定を実行
    * "run HogeTask" が出力されれば成功
    ![スクリーンショット 2022-12-25 14.02.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41839/03f91ed7-7084-eeaf-ad20-e38cdf699094.png)

# ProjectRoot Dirと引数

## ProjectRoot Dirの取得

何かと必要になるProjectRoot Dirの取得方法です。

* `project.rootDir.absolutePath`で ProjectRoot Dirを取得できる
    ```kotlin
    package jp.pmcoffee.tasks.fuga
    
    import org.gradle.api.DefaultTask
    import org.gradle.api.tasks.TaskAction
    
    abstract class FugaTask: DefaultTask() {
    
        private val projectRootDir = project.rootDir.absolutePath
    
        @TaskAction
        fun runTask() {
            println("run FugaTask. projectRootDir = $projectRootDir")
        }
    }
    ```




## 引数

### build.gradle.ktsから引数を受け取る

@Inputアノテーションを使う

```kotlin
abstract class FugaTask: DefaultTask() {

    private val projectRootDir = project.rootDir.absolutePath

    @Input
    var arg1: String = ""

    @TaskAction
    fun runTask() {
        println("run FugaTask. projectRootDir: $projectRootDir")
        println("arg1: $arg1")
    }
}
```

```projectRoot/build.gradle.kts
tasks.register("runFugaTask", jp.pmcoffee.tasks.fuga.FugaTask::class){
    arg1 = "from build.gradle.kts"
}
```

### 実行設定で引数を指定する

* アクセストークンなどユーザー毎に固有の値が必要な場合、build.gradleに引数を書くのはセキュリティ上良くないので, そういう場合はこちらの方法を使うといいかと思います.

```kotlin
abstract class FugaTask: DefaultTask() {

    private val projectRootDir = project.rootDir.absolutePath

    private val arg2Name = "arg2"
    private val arg2: String = if(project.hasProperty(arg2Name)) project.property(arg2Name) as String else ""

    @TaskAction
    fun runTask() {
        println("run FugaTask. projectRootDir: $projectRootDir")
        println("arg2: $arg2")
    }
}
```

* Run/Debug Configurations
    ![スクリーンショット 2022-12-25 14.43.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41839/ef92c88f-ea4d-8ed7-249d-42312574e218.png)


# まとめ

* gradle taskを普段androidアプリ開発をしている時と同じようにpackageおよびファイルを分けて書くことができた
* 引数を指定して任意で実行できる

# 感想

久々にアドベントカレンダー書きました。
当日(今日)の朝に書いてなかったことを思い出して、だいぶ焦ったり。
突貫で書いたのでわかりにくい部分などあるかもしれませんので、その際は質問などしてもらえればと思います。

# 追記

タイトルが誤字ってるのを指摘してもらいました。ありがとうございます。
自分だとなかなか気づかないもんですね。
(間違いがショボすぎて恥ずかしいわぁ)

```diff
- Koltin
+ Kotlin
```