KSP2における変更
android-developers.googleblog.com
上のAndroid Developersのブログ記事にて様々な変更点が紹介されています。その中でも特にスタンドアローンに実行できる点が気になったため試してみました。
エントリーポイントのあるライブラリとして実装されているため、デバッグやテストが容易になっているらしいです。
テストを書いてみる
試しにコード生成の単体テストを書いてみます。
依存関係を追加する
まずは依存関係を追加します。
implementation("com.google.devtools.ksp:symbol-processing-aa-embeddable:<version>") // もし足りない場合は以下の依存関係も追加する implementation("com.google.devtools.ksp:symbol-processing-api:<version>") implementation("com.google.devtools.ksp:symbol-processing-common-deps:<version>")
実行したいモジュールに任意のバージョンのsymbol-processing-aa-embeddableを追加します(執筆時点の最新は2.0.0-RC2-1.0.20)。 筆者の手元の環境では、KSPLoggerとKSPJvmConfigの情報が足りなかったため、symbol-processing-apiとsymbol-processing-common-depsも追加しています。
テストを書く
Processorを書いている前提で簡単なテストコードを書いてみます。
以下が最終的なテストコードです。
class SampleTest { // 生成ファイル用のDirの生成 @JvmField @Rule val kotlinOutputDirFolder = TemporaryFolder() // その他のファイル用のDir生成 @JvmField @Rule val dummyFolder = TemporaryFolder() @Test fun sample_test() { // 生成ファイル用のFileインスタンス生成 val annotationFolder = kotlinOutputDirFolder.newFolder( "com", "sample", "annotation" ) val generatedFile = File(annotationFolder, "GeneratedFile.kt") // Processorが処理するファイルの準備 val projectFile = File(dummyFolder.root, "Project.kt") projectFile.writeText( """ package com.sample.ksp @TargetAnnotation Interface Hoge annotation class TargetAnnotation() """.trimIndent(), ) // Configの生成 val kspConfig = KSPJvmConfig.Builder().apply { kotlinOutputDir = kotlinOutputDirFolder.root javaOutputDir = dummyFolder.newFolder("java") outputBaseDir = dummyFolder.newFolder("base") resourceOutputDir = dummyFolder.newFolder("resource") cachesDir = dummyFolder.newFolder("cache") classOutputDir = dummyFolder.newFolder("class") jvmTarget = JvmTarget.DEFAULT.description moduleName = "<module_name>" sourceRoots = listOf(projectFile) javaSourceRoots = listOf() commonSourceRoots = listOf() projectBaseDir = dummyFolder.root languageVersion = "<version>" apiVersion = "<version>" }.build() val exitCode = KotlinSymbolProcessing( kspConfig, listOf(HogeProcessorProvider()), TestKSPLogger() ).execute() assertTrue( exitCode == KotlinSymbolProcessing.ExitCode.OK && generatedFile.exists() ) } } class TestKSPLogger: KSPLogger { override fun error(message: String, symbol: KSNode?) { // operation } override fun exception(e: Throwable) { // operation } override fun info(message: String, symbol: KSNode?) { // operation } override fun logging(message: String, symbol: KSNode?) { // operation } override fun warn(message: String, symbol: KSNode?) { // operation } }
順番に見ていきます。
フォルダを用意する
Configを作成する際、ファイルの読み込み先と出力先となるディレクトリを渡す必要があるようです。今回はTemporaryFolderを使用してフォルダを生成しました。
@JvmField @Rule val dummyFolder = TemporaryFolder()
テストメソッド毎にフォルダを再作成できるように、Ruleアノテーションを付けておきます。 また、Kotlinで使用する場合はJvmFieldアノテーションも必要みたいです。
読み込むファイルを用意する
SymbolProcessorの処理対象となるファイルを用意します。
val projectFile = File(dummyFolder.root, "Project.kt") projectFile.writeText( """ package com.example.ksp @TargetAnnotation Interface Hoge annotation class TargetAnnotation() """.trimIndent(), )
Configを用意する
諸々の設定情報を渡すためにKSPJvmConfigを用意する必要があります。
val kspConfig = KSPJvmConfig.Builder().apply { kotlinOutputDir = kotlinOutputDirFolder.root javaOutputDir = dummyFolder.newFolder("java") outputBaseDir = dummyFolder.newFolder("base") resourceOutputDir = dummyFolder.newFolder("resource") cachesDir = dummyFolder.newFolder("cache") classOutputDir = dummyFolder.newFolder("class") jvmTarget = JvmTarget.DEFAULT.description moduleName = "<module_name>" sourceRoots = listOf(projectFile) javaSourceRoots = listOf() commonSourceRoots = listOf() projectBaseDir = dummyFolder.root languageVersion = "<version>" apiVersion = "<version>" }.build()
先ほど作成した出力先のフォルダをkotlinOutputDirに、読み込むファイルをsourceRootsに設定します。残りはよしなに埋めておきます。
Loggerを用意する
KotlinSymbolProcessingにKSPLoggerを実装したクラスを渡す必要があります。
class TestKSPLogger: KSPLogger { override fun error(message: String, symbol: KSNode?) { // operation } override fun exception(e: Throwable) { // operation } override fun info(message: String, symbol: KSNode?) { // operation } override fun logging(message: String, symbol: KSNode?) { // operation } override fun warn(message: String, symbol: KSNode?) { // operation } }
KSPの処理を実行する
作成しておいたConfigとSymbolProcessorProvider、Loggerを渡して、executeメソッドを呼びます。今回は、返り値のExitCodeと生成ファイルを用いてassertしておきます。
val exitCode = KotlinSymbolProcessing( kspConfig, listOf(HogeProcessorProvider()), TestKSPLogger() ).execute() assertTrue( exitCode == KotlinSymbolProcessing.ExitCode.OK && generatedFile.exists() )
参考リンク
Is there any guidance or example project for how to use KSP2 kotlinlang #ksp
Prepare for K2 by ZacSweers · Pull Request #196 · ZacSweers/kotlin-compile-testing · GitHub