kaptからKSPに移行しようとしてコケました

表題のままなのですが、備忘録として解決方法を残しておきます。

## Environment
- Android Studio Hedgehog
- AGP 8.2.0
- Hilt 2.50
- KSP 1.9.21-1.0.16

ちなみにプロジェクトの構造は[nowinandroid](https://github.com/android/nowinandroid)を参考に作っています。

## Problem
[HiltがKSPをサポートするようになった](https://dagger.dev/dev-guide/ksp.html)ので、公式の[マイグレーションガイド](https://developer.android.com/build/migrate-to-ksp)に従ってkaptからKSPに移行したところ、ビルドでコケてしまいました。


<details><summary>エラー内容</summary>

```
Some problems were found with the configuration of task ':core:api:kspDevelopDebugKotlin' (type 'KspTaskJvm').
- Gradle detected a problem with the following location: '/Users/myname/project/module/build/generated/source/buildConfig/develop/debug/jp/co/project/module/BuildConfig.java'.

* Try:
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.
> Get more help at https://help.gradle.org.

* Exception is:
org.gradle.internal.execution.WorkValidationException: Some problems were found with the configuration of task ':core:api:kspDevelopDebugKotlin' (type 'KspTaskJvm').
- Gradle detected a problem with the following location: '/Users/myname/project/module/build/generated/source/buildConfig/develop/debug/jp/co/project/module/BuildConfig.java'.

Reason: Task ':core:api:kspDevelopDebugKotlin' uses this output of task ':core:api:generateDevelopDebugBuildConfig' without declaring an explicit or implicit dependency. This can lead to incorrect results being produced, depending on what order the tasks are executed.

Possible solutions:
1. Declare task ':core:api:generateDevelopDebugBuildConfig' as an input of ':core:api:kspDevelopDebugKotlin'.
2. Declare an explicit dependency on ':core:api:generateDevelopDebugBuildConfig' from ':core:api:kspDevelopDebugKotlin' using Task#dependsOn.
3. Declare an explicit dependency on ':core:api:generateDevelopDebugBuildConfig' from ':core:api:kspDevelopDebugKotlin' using Task#mustRunAfter.

For more information, please refer to https://docs.gradle.org/8.2/userguide/validation_problems.html#implicit_dependency in the Gradle documentation.
- Gradle detected a problem with the following location: '/Users/myname/project/module/build/generated/source'.

Reason: Task ':core:api:kspDevelopDebugKotlin' uses this output of task ':core:api:generateDevelopDebugBuildConfig' without declaring an explicit or implicit dependency. This can lead to incorrect results being produced, depending on what order the tasks are executed.

Possible solutions:
1. Declare task ':core:api:generateDevelopDebugBuildConfig' as an input of ':core:api:kspDevelopDebugKotlin'.
2. Declare an explicit dependency on ':core:api:generateDevelopDebugBuildConfig' from ':core:api:kspDevelopDebugKotlin' using Task#dependsOn.
3. Declare an explicit dependency on ':core:api:generateDevelopDebugBuildConfig' from ':core:api:kspDevelopDebugKotlin' using Task#mustRunAfter.

For more information, please refer to https://docs.gradle.org/8.2/userguide/validation_problems.html#implicit_dependency in the Gradle documentation.
at org.gradle.internal.execution.WorkValidationException$BuilderWithSummary.build(WorkValidationException.java:133)
at org.gradle.internal.execution.WorkValidationException$BuilderWithSummary.get(WorkValidationException.java:115)
at org.gradle.internal.execution.steps.ValidateStep.throwValidationException(ValidateStep.java:162)
at org.gradle.internal.execution.steps.ValidateStep.execute(ValidateStep.java:86)
at org.gradle.internal.execution.steps.ValidateStep.execute(ValidateStep.java:49)
at org.gradle.internal.execution.steps.CaptureStateBeforeExecutionStep.execute(CaptureStateBeforeExecutionStep.java:71)
at org.gradle.internal.execution.steps.CaptureStateBeforeExecutionStep.execute(CaptureStateBeforeExecutionStep.java:45)
at org.gradle.internal.execution.steps.SkipEmptyWorkStep.executeWithNonEmptySources(SkipEmptyWorkStep.java:177)
at org.gradle.internal.execution.steps.SkipEmptyWorkStep.execute(SkipEmptyWorkStep.java:86)
at org.gradle.internal.execution.steps.SkipEmptyWorkStep.execute(SkipEmptyWorkStep.java:53)
at org.gradle.internal.execution.steps.RemoveUntrackedExecutionStateStep.execute(RemoveUntrackedExecutionStateStep.java:32)
at org.gradle.internal.execution.steps.RemoveUntrackedExecutionStateStep.execute(RemoveUntrackedExecutionStateStep.java:21)
at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsStartedStep.execute(MarkSnapshottingInputsStartedStep.java:38)
at org.gradle.internal.execution.steps.LoadPreviousExecutionStateStep.execute(LoadPreviousExecutionStateStep.java:36)
at org.gradle.internal.execution.steps.LoadPreviousExecutionStateStep.execute(LoadPreviousExecutionStateStep.java:23)
at org.gradle.internal.execution.steps.CleanupStaleOutputsStep.execute(CleanupStaleOutputsStep.java:75)
at org.gradle.internal.execution.steps.CleanupStaleOutputsStep.execute(CleanupStaleOutputsStep.java:41)
at org.gradle.internal.execution.steps.AssignWorkspaceStep.lambda$execute$0(AssignWorkspaceStep.java:32)
at org.gradle.api.internal.tasks.execution.TaskExecution$4.withWorkspace(TaskExecution.java:293)
at org.gradle.internal.execution.steps.AssignWorkspaceStep.execute(AssignWorkspaceStep.java:30)
at org.gradle.internal.execution.steps.AssignWorkspaceStep.execute(AssignWorkspaceStep.java:21)
at org.gradle.internal.execution.steps.IdentityCacheStep.execute(IdentityCacheStep.java:37)
at org.gradle.internal.execution.steps.IdentityCacheStep.execute(IdentityCacheStep.java:27)
at org.gradle.internal.execution.steps.IdentifyStep.execute(IdentifyStep.java:47)
at org.gradle.internal.execution.steps.IdentifyStep.execute(IdentifyStep.java:34)
at org.gradle.internal.execution.impl.DefaultExecutionEngine$1.execute(DefaultExecutionEngine.java:64)
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeIfValid(ExecuteActionsTaskExecuter.java:146)
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:135)
at org.gradle.api.internal.tasks.execution.FinalizePropertiesTaskExecuter.execute(FinalizePropertiesTaskExecuter.java:46)
at org.gradle.api.internal.tasks.execution.ResolveTaskExecutionModeExecuter.execute(ResolveTaskExecutionModeExecuter.java:51)
at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:57)
at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:74)
at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:36)
at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.executeTask(EventFiringTaskExecuter.java:77)
at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:55)
at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:52)
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:199)
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:73)
at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter.execute(EventFiringTaskExecuter.java:52)
at org.gradle.execution.plan.LocalTaskNodeExecutor.execute(LocalTaskNodeExecutor.java:42)
at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:337)
at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:324)
at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:317)
at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:303)
at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.execute(DefaultPlanExecutor.java:463)
at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.run(DefaultPlanExecutor.java:380)
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
at org.gradle.internal.concurrent.AbstractManagedExecutor$1.run(AbstractManagedExecutor.java:47)


BUILD FAILED in 32s
958 actionable tasks: 957 executed, 1 up-to-date
```
</details>

## Reason

いろいろ調べた結果、[BuildConfig generated class is not visible to KSP](https://issuetracker.google.com/issues/301244513)に辿り着きました…。

> User trying to adopt the KSP version of Dagger & Hilt are running into missing classes issues because the BuildConfig generated class is not an input to KSP. They report the issue to Dagger because it worked fine in KAPT but Dagger does not control the wiring between the BuildConfig generating task and annotation processing tasks.

そういえばエラーが吐かれたモジュールでBuildConfig使ってますね。

該当のモジュールの`build.gradle.kts`ファイルは以下のような感じです。

```kotlin
android {
    namespace = "hogehoge"

    buildFeatures {
        buildConfig = true
    }

    defaultConfig {
        buildConfigField(
            type = "String",
            name = "BASE_URL",
            value = "\"https://fugafuga.net\"",
        )
    }
}

dependencies {
    // 省略
}
```
エラーでも`Reason: Task ':core:api:kspDevelopDebugKotlin' uses this output of task ':core:api:generateDevelopDebugBuildConfig' without declaring an explicit or implicit dependency. This can lead to incorrect results being produced, depending on what order the tasks are executed.`と言っているので、どうやら原因はこれっぽいですね。

## Solution

該当モジュールの`build.gradle.kts`ファイルに[スニペット](https://issuetracker.google.com/issues/301244513#comment6)をありがたくコピペさせていただきました🙏

```kotlin
androidComponents {
    onVariants(selector().all()) { variant ->
        afterEvaluate {
            // This is a workaround for https://issuetracker.google.com/301244513 which depends on internal
            // implementations of the android gradle plugin and the ksp gradle plugin which might change in the future
            // in an unpredictable way.
            val variantNameCapitalized = variant.name.uppercaseFirstChar()
            val buildConfigTaskName = "generate${variantNameCapitalized}BuildConfig"
            val buildConfigTask = this.project.tasks.named(buildConfigTaskName)
                .get() as com.android.build.gradle.tasks.GenerateBuildConfig

            project.tasks.getByName("ksp${variantNameCapitalized}Kotlin") {
                (this as org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompileTool<*>).setSource(
                    buildConfigTask.sourceOutputDir,
                )
            }
        }
    }
}
```

スニペットを追記したらビルドが通るようになりました🙌

gradleを雰囲気で使っていてよくわかってないので解決に時間がかかってしまいました…。

いつか公式でサポートされるかもしれないですが、いったん応急処置ということで🙏🩹