Upgrade to Pro — share decks privately, control downloads, hide ads and more …

KSPを使ってコード生成

 KSPを使ってコード生成

Takuji Nishibayashi

December 05, 2023
Tweet

More Decks by Takuji Nishibayashi

Other Decks in Technology

Transcript

  1. build.gradle.kts // ... dependencies { // KSP のAPI implementation("com.google.devtools.ksp:symbol-processing-api:1.9.21-1.0.15") //

    KotlinPoet implementation("com.squareup:kotlinpoet:1.15.3") // KotlinPoet のKSP 用拡張 implementation("com.squareup:kotlinpoet-ksp:1.15.3") } 15
  2. SymbolProcessor class ExampleSymbolProcessor( private val codeGenerator: CodeGenerator, private val logger:

    KSPLogger ) : SymbolProcessor { override fun process(resolver: Resolver): List<KSAnnotated> { resolver .getSymbolsWithAnnotation(SimpleGeneration::class.qualifiedName!!) .filterIsInstance<KSClassDeclaration>() .forEach { it.accept(SimpleGenerationVisitor(codeGenerator, logger), Unit) } return emptyList() } } 16
  3. Visitor class SimpleGenerationVisitor( private val codeGenerator: CodeGenerator, private val logger:

    KSPLogger ) : KSVisitorVoid() { override fun visitClassDeclaration(classDeclaration: KSClassDeclaration, data: Unit) { if (classDeclaration.classKind != ClassKind.INTERFACE) { logger.error("Only interface allowed", classDeclaration) return } val packageName = classDeclaration.packageName.asString() val className = ClassName(packageName, "Abstract" + classDeclaration.simpleName.asString()) val typeSpec = TypeSpec.classBuilder(className) .addModifiers(KModifier.ABSTRACT) .addSuperinterface(classDeclaration.toClassName()) FileSpec.builder(packageName, className.simpleName) .addType(typeSpec.build()) .build() .writeTo( codeGenerator, Dependencies( aggregating = false, classDeclaration.containingFile!! ) ) } } 17
  4. SymbolProcessorProvider class ExampleSymbolProcessorProvider : SymbolProcessorProvider { override fun create(environment: SymbolProcessorEnvironment):

    SymbolProcessor { return ExampleSymbolProcessor(environment.codeGenerator, environment.logger) } } 18
  5. テストコード val source = SourceFile.kotlin( "ExampleClass.kt", """ package jp.takuji31.kotlinfest2022.compiler import

    jp.takuji31.kotlinfest2022.compiler.annotation.SimpleGeneration @SimpleGeneration interface SimpleInterface { fun printHelloWorld() } """.trimIndent() ) 31
  6. テストコード val compilation = KotlinCompilation().apply { sources = listOf(source) inheritClassPath

    = true symbolProcessorProviders = listOf(ExampleSymbolProcessorProvider()) kspWithCompilation = true } val result = compilation.compile() assertThat(result.exitCode) .isEqualTo(KotlinCompilation.ExitCode.OK) 32