KSPのProcessor実装でハマったこと備忘録

特に下調べせずKSPを触り始めたところ、色々ハマりました。
大体は公式ドキュメントをちゃんと読めば解決する内容です。

ハマりながら書いたSymbolProcessor： https://github.com/tkhskt/ShaderView/tree/feature/kotlin_dsl_interface/processor

# 環境

Kotlin: 1.6.10
KSP: 1.6.10-1.0.4
KotlinPoet: 1.10.2

# ハマったこと

## コード生成されない

SymbolProcessorを実装するモジュールでは、`resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider`を作成する必要があります。
`resources/services/com.google.devtools.ksp.processing.SymbolProcessorProvider`(META-INFが抜けてる)だと、ProcessorProviderは認識されるようでしたが、Processorを使用するモジュールにコードが生成されませんでした。

## インクリメンタルビルドすると生成されたコードが消える

KSP関連の記事を見ていると、こんな感じ↓でファイル(コード)を生成していることが多いですが、これだとIncremental Buildを行った際に前回生成したコードが消えてしまいます。

```kotlin
val file = codeGenerator.createNewFile(
            Dependencies(false),
            "com.hoge",
            "Hoge"
        )

        file.write(code.toByteArray())
        file.close()
```

これを回避するためには、`Dependencies`に`source: KSFile`を渡します。

```kotlin
val file = codeGenerator.createNewFile(
            Dependencies(false, classDeclaration.containingFile!!), // KSFileを渡す
            "com.hoge",
            "Hoge"
        )

        file.write(code.toByteArray())
        file.close()
```

このあたりの挙動は下記の動画が非常に参考になります。
https://youtu.be/sJfURbrdau0

KotlinPoetだと下記のようになります。

```kotlin
val file = FileSpec.builder(packageName, className)
            ...
            .build()
        file.writeTo(codeGenerator, Dependencies(false, classDeclaration.containingFile!!))
```


## 生成されたコードがIDE(AndroidStudio)で認識されない

KSPを使用するモジュールの`build.gradle`のandroidブロックに`sourceSets`の設定を追加します。

```gradle
android {
    applicationVariants.all { variant ->
        kotlin.sourceSets {
            def name = variant.name
            getByName(name) {
                kotlin.srcDir("build/generated/ksp/$name/kotlin")
            }
        }
    }
}
```

## パッケージ名をどうやって管理するか問題

KSPとは直接関係ないのですが、Processorで使うパッケージ名/クラス名をどうやって管理するか悩みました。
[airbnb/epoxy](https://github.com/airbnb/epoxy)のProcessorで採用されている方法が良い感じでした。([airbnb/epoxy/processor/ClassNames.kt](https://github.com/airbnb/epoxy/blob/master/epoxy-processor/src/main/java/com/airbnb/epoxy/processor/ClassNames.kt))

```kotlin
object ClassNames {
    private const val PKG_SHADER_VIEW = "com.appspell.shaderview"
    private const val PKG_SHADER_VIEW_GL_PARAMS = "com.appspell.shaderview.gl.params"
    private const val PKG_SHADER_VIEW_KTX_ANNOTATION = "com.appspell.shaderview.ktx.annotation"
    ...

    val SHADER_VIEW = ClassName(PKG_SHADER_VIEW, "ShaderView")
    val SHADER_PARAMS = ClassName(PKG_SHADER_VIEW_GL_PARAMS, "ShaderParams")
    val SHADER_PARAMS_BUILDER = ClassName(PKG_SHADER_VIEW_GL_PARAMS, "ShaderParamsBuilder")
    val SHADER_PARAMETERS = ClassName(PKG_SHADER_VIEW_KTX_ANNOTATION, "ShaderParameters")
   ...
}
```

# 参考

- KSPドキュメント
    - https://kotlinlang.org/docs/ksp-overview.html
- KSPの使い方
    - https://star-zero.medium.com/ksp%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%A6%E3%82%B3%E3%83%BC%E3%83%89%E7%94%9F%E6%88%90%E3%81%97%E3%81%A6%E3%81%BF%E3%82%8B-1c2f421aaaa9
    - https://www.droidcon.com/2021/08/18/so-how-do-i-write-a-kotlin-symbol-processor-ksp/
- IDEで認識されない問題について
    - https://github.com/google/ksp/issues/37
- KotlinPoet
    - https://square.github.io/kotlinpoet/interop-ksp/