KMPでKSPをやるって時の現状の制約

# 前提
- KSP2は未使用

ざっと調査した限りなので間違っている情報があればご指摘ください。

# 制約
1. commonMain 内のシンボル処理によって生成するファイルは 各プラットフォームに置かれる = commonMain では使うことができない
2. KotlinNativeを必要とするターゲットは,その 根っこのターゲットのソースセット空間でのみ コード生成のアウトプットができる


# KMPでKSPを使う方法

### Processor作成周り
- Processorの作成は通常通り

```kotlin
class TestProcessor(
    private val codeGenerator: CodeGenerator,
    private val kspLogger: KSPLogger
) : SymbolProcessor {
    override fun process(resolver: Resolver): List<KSAnnotated> {
        resolver.getSymbolsWithAnnotation(SampleAnnotation::class.qualifiedName!!)
            ...
        return emptyList()
    }
}

class TestProcessorProvider : SymbolProcessorProvider {
    override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor {
        return TestProcessor(environment.codeGenerator, environment.logger)
    }
}
...

// /resources/META-INF/services
TestProcessorProvider
```

- build.gradle.kts

ターゲットはjvmを設定する。あとは通常と同じ。
```kotlin
plugins {
    alias(libs.plugins.kotlinMultiplatform)
}

kotlin {
    jvm()
    sourceSets {
        jvmMain.dependencies {
            implementation(libs.ksp.poet)
            implementation(libs.ksp.poet.extension)
            implementation(libs.ksp.symbol.processing.api)
            implementation(projects.annotations)
        }
    }
}
...
```
これだとProcessorを使う側もマルチプラットフォームなので jvm 以外で使えないのでは？ と疑問が出るが、Gradleのconfigurationを マルチプラットフォームの文脈外でやるので 問題ない様子 (後述）

- ソースセット
jvmターゲット のため、jvmMain（or alias）となる

```
├── build.gradle.kts
└── src
    └── jvmMain
        ├── kotlin
        │   └── TestProcessor.kt
        └── resources
            └── META-INF
                └── services
                    └── com.google.devtools.ksp.processing.SymbolProcessorProvider
```

## Processorのconfiguration

Processorを受け入れる側 の設定は..

```kotlin

plugins {
    alias(libs.plugins.kotlinMultiplatform)
    alias(libs.plugins.androidLibrary)
    alias(libs.plugins.ksp)
}
kotlin {
... // ターゲットの設定等

 sourceSets {
        commonMain.dependencies {
            // put your Multiplatform dependencies here
            implementation(projects.annotations)
        }
        ... // その他 依存の追加
    }
}

// ここ
dependencies {
    add("kspCommonMainMetadata", projects.testProcessor)
    add("kspJvm", projects.testProcessor)
    add("kspAndroid", projects.testProcessor)
}

```

通常、`ksp(...)`でconfugurationを宣言するのとは違い、`add(ksp<Target>)` or `add(ksp<SourceSet>)`を使用する。
理由👇

> The universal "ksp" configuration has performance issue and is deprecated on multiplatform since 1.0.1

これでビルド時にシンボル処理 & コード生成 をができるようになる

# 制約は何か

### 1. commonMain 内のシンボル処理によって生成するファイルは 各プラットフォームに置かれる = commonMain では使うことができない

例えば 上記のconfigurationで

`annotation class SampleAnnotation` を 付与したクラスから 同名のファイルを生成する といったサンプルコードを作ってやってみる
```kotlin
// commonMain
@SampleAnnotation
class Greeting {...

// platformMain
@SampleAnnotation
class AndroidPlatform : Platform { ...

// platformMain
@SampleAnnotation
class JVMPlatform : Platform {...
```

すると生成されるソースは..
- `build/generated/ksp/android/androidDebug/kotlin/AndroidPlatform.kt`
- `build/generated/ksp/android/androidDebug/kotlin/Greeting.kt`
- `build/generated/ksp/jvm/jvmMain/kotlin/AndroidPlatform.kt`
- `build/generated/ksp/jvm/jvmMain/kotlin/Greeting.kt`

CommonのMetadataなども作られるが、使う側からは見えないので割愛

commonのシンボル処理のoutput も各プラットフォームへ入る形になる。
CommonMainでは、Greetingが見えないので基本的にはプラットフォーム固有で使うことが前提

https://github.com/google/ksp/issues/567

### 2. KotlinNativeを必要とするターゲットは,その 根っこのターゲットのソースセット空間でのみ コード生成のアウトプットができる

KMP Wizard で web desktop android ios を 作ると現状以下のような構成になっている

```kotlin
@OptIn(ExperimentalWasmDsl::class)
    wasmJs {
        browser {
            commonWebpackConfig {
                devServer = (devServer ?: KotlinWebpackConfig.DevServer()).apply {
                    static = (static ?: mutableListOf()).apply {
                        // Serve sources to debug inside browser
                        add(project.projectDir.path)
                    }
                }
            }
        }
    }
    
    androidTarget {
        @OptIn(ExperimentalKotlinGradlePluginApi::class)
        compilerOptions {
            jvmTarget.set(JvmTarget.JVM_11)
        }
    }
    
    jvm()
    
    listOf(
        iosX64(),
        iosArm64(),
        iosSimulatorArm64()
    ).forEach { iosTarget ->
        iosTarget.binaries.framework {
            baseName = "Shared"
            isStatic = true
        }
    }
    
    sourceSets {
        commonMain.dependencies {
            // put your Multiplatform dependencies here
        }
    }
```

processorのconfigurationを全部プリントしてみると

```
KSP Configuration: ksp
KSP Configuration: kspAndroid
KSP Configuration: kspAndroidAndroidTest
KSP Configuration: kspAndroidAndroidTestDebug
KSP Configuration: kspAndroidAndroidTestRelease
KSP Configuration: kspAndroidDebug
KSP Configuration: kspAndroidRelease
KSP Configuration: kspAndroidTest
KSP Configuration: kspAndroidTestDebug
KSP Configuration: kspAndroidTestFixtures
KSP Configuration: kspAndroidTestFixturesDebug
KSP Configuration: kspAndroidTestFixturesRelease
KSP Configuration: kspAndroidTestRelease
KSP Configuration: kspAppleMain
KSP Configuration: kspCommonMainKotlinMetadataProcessorClasspath
KSP Configuration: kspCommonMainMetadata
KSP Configuration: kspDebugKotlinAndroidProcessorClasspath
KSP Configuration: kspIosArm64
KSP Configuration: kspIosArm64Test
KSP Configuration: kspIosMain
KSP Configuration: kspIosSimulatorArm64
KSP Configuration: kspIosSimulatorArm64Test
KSP Configuration: kspIosX64
KSP Configuration: kspIosX64Test
KSP Configuration: kspJvm
KSP Configuration: kspJvmTest
KSP Configuration: kspKotlinIosArm64ProcessorClasspath
KSP Configuration: kspKotlinIosSimulatorArm64ProcessorClasspath
KSP Configuration: kspKotlinIosX64ProcessorClasspath
KSP Configuration: kspKotlinJvmProcessorClasspath
KSP Configuration: kspKotlinWasmJsProcessorClasspath
KSP Configuration: kspNativeMain
KSP Configuration: kspPluginClasspath
KSP Configuration: kspPluginClasspathNonEmbeddable
KSP Configuration: kspReleaseKotlinAndroidProcessorClasspath
KSP Configuration: kspWasmJs
KSP Configuration: kspWasmJsTest
```
なので、kspIosMainを入れれば良さそうだが、それではダメで..

`Configuration with name 'kspIosMain' not found.`

```kotlin
dependencies {
    add("kspCommonMainMetadata", projects.testProcessor)
    add("kspJvm", projects.testProcessor)
    add("kspAndroid", projects.testProcessor)
    add("kspWasmJs", projects.testProcessor)
    // ここ
    add("kspIosX64", projects.testProcessor)
    add("kspIosArm64", projects.testProcessor)
    add("kspIosSimulatorArm64", projects.testProcessor)
}
```

のように、内側にあるターゲットを指定する必要がある。

```kotlin
// iosMain
@SampleAnnotation
class IOSPlatform : Platform {...
```
とiOSMainのシンボルを処理した場合 生成されるコードのソースセットは以下になる。

```
├── iosArm64
│   └── iosArm64Main
│       └── kotlin
│           ├── Greeting.kt
│           └── IOSPlatform.kt
├── iosSimulatorArm64
│   └── iosSimulatorArm64Main
│       └── kotlin
│           ├── Greeting.kt
│           └── IOSPlatform.kt
├── iosX64
│   └── iosX64Main
│       └── kotlin
│           ├── Greeting.kt
│           └── IOSPlatform.kt

```

iOSMainからは 上記のターゲットは見えないので 、ios* のソースセットからしか使うことができない。

現状、Androidは その限りではない様子（Androidも`androidNativeX64`と`androidNativeArm64`が根っこにある）なので、おそらくKotlin-Native の制約

https://github.com/google/ksp/issues/929


# コメント
今の仕組みだと、使えるけど使いづらそう という印象。
KSP2 だと、仕組みがだいぶ変わっているので こちらを待つ感じになるのだろうか。