【kapt】 처리 대상 클래스의 패키지를 지정하여 파일을 내보내는 【KotlinPoet】

13476 단어 KotlinPoetKotlinkapt
kapt + KotlinPoet 에서 처리 대상 클래스의 패키지를 지정하여 파일을 내보냅니다.
하는 방법은 몇 가지 있습니다만, 이 기사에서는 ClassName 로부터 패키지명·클래스명의 정보를 취득해, ProcessingEnvironment 로부터 취득한 filer 를 지정해 내보내는 방법을 소개합니다.

버전은 다음과 같습니다.
  • Kotlin : 1.4.31
  • KotlinPoet : 1.7.2
  • AutoCommon : 0.11
  • AutoService : 1.0-rc7

  • 샘플 코드



    어노테이션을 붙인 클래스에 대해서, 그 코드와 같은 패키지에 hello from ${クラス名} (와)과 프린트 하는 코드를 생성하는 샘플입니다.
    특히 중요한 것은 generateHelloWorld 함수입니다.

    이하, 이쪽의 샘플 코드를 이용해 해설해 갑니다.
    import com.google.auto.common.BasicAnnotationProcessor
    import com.google.auto.service.AutoService
    import com.google.common.collect.SetMultimap
    import com.mapk.annotations.TargetAnnotation
    import com.squareup.kotlinpoet.*
    import javax.annotation.processing.Filer
    import javax.annotation.processing.Processor
    import javax.lang.model.SourceVersion
    import javax.lang.model.element.Element
    import javax.lang.model.element.ElementKind
    import javax.lang.model.element.TypeElement
    
    // 処理対象アノテーション
    @Target(AnnotationTarget.CLASS)
    @Retention(AnnotationRetention.RUNTIME)
    @MustBeDocumented
    annotation class TargetAnnotation
    
    @AutoService(Processor::class)
    class Processor : BasicAnnotationProcessor() {
        override fun getSupportedSourceVersion(): SourceVersion = SourceVersion.latestSupported()
    
        override fun getSupportedOptions(): Set<String> = setOf()
    
        override fun initSteps(): Iterable<ProcessingStep> {
            return listOf(TempProcessingStep(processingEnv.filer))
        }
    }
    
    class TempProcessingStep(private val filer: Filer) : BasicAnnotationProcessor.ProcessingStep {
        override fun annotations() = setOf(TargetAnnotation::class.java)
    
        override fun process(elementsByAnnotation: SetMultimap<Class<out Annotation>, Element>): Set<Element> {
            annotations()
                .flatMap { elementsByAnnotation[it] }
                .forEach {
                    // 簡単のためクラスに付与する想定
                    if (it.kind == ElementKind.CLASS) generateHelloWorld(it as TypeElement)
                }
            return emptySet()
        }
    
        // ハローワールドを出力する関数
        private fun generateHelloWorld(element: TypeElement) {
            val className = element.asType().asTypeName() as ClassName
    
            val funSpec = FunSpec.builder("helloWorld").apply {
                addStatement("""println("hello from ${className.simpleName}")""")
            }.build()
    
            val file = FileSpec.builder(className.packageName, "HelloFrom${className.simpleName}")
                .addFunction(funSpec)
                .build()
    
            file.writeTo(System.out) // デバッグ用にSystem.outにも出力
            file.writeTo(filer) // ファイルへ出力
        }
    }
    

    ClassName 얻기



    처리 대상의 ElementTypeElement 이면, element.asType().asTypeName() 로 취득한 TypeNameClassName 에 캐스트 할 수 있습니다.

    ClassName 취득부
    private fun generateHelloWorld(element: TypeElement) {
        val className = element.asType().asTypeName() as ClassName
    

    ClassElement 로부터는 패키지명이나 클래스명을 간단하게 취득할 수 있습니다.

    패키지명・파일명 설정부
    val file = FileSpec.builder(className.packageName, "HelloFrom${className.simpleName}")
    

    보충


    TypeMirror.asTypeName() 는 현재 Deprecated 되고 있어 , 「 kotlinpoet-metadata 를 사용해」라고 하는 취지의 경고가 나옵니다만 , 방법을 모르기 때문에 이 기사에서는 무시하고 있습니다.
    이 문제는 아마 KotlinJava 로 표현이 다른 형( Int 이나 String )에 대해서 일어나는 것으로, 이번 취급하는 범위에서는 문제 없다고 생각하고 있습니다.

    출력 대상 지정



    출력처는 ProcessingEnvironment 로부터 취득한 Filer 를 지정합니다.ProcessingEnvironmentAbstractProcessor ( BasicAnnotationProcessor 의 부모 클래스)를 상속하는 경우 processingEnv 필드에서 가져올 수 있습니다.

    파일에 내보내기
    file.writeTo(filer) // ファイルへ出力
    

    이것을 지정했을 경우, 이용측에서 특별히 지정이 없으면 ${モジュールのroot}/build/generated/source/kapt/main/${指定したパッケージへのパス} 가 출력처가 됩니다.

    실행 결과


    com.mapk.test 라는 패키지에 다음과 같은 처리 대상을 준비해 실행한 결과를 나타냅니다.
    package com.mapk.test
    
    import com.mapk.annotations.TargetAnnotation
    
    @TargetAnnotation
    object TestTarget
    
    fun main() { helloWorld() }
    

    빌드 로그


    file.writeTo(System.out) 에 의해, 이하와 같이 결과를 확인할 수 있습니다.
    # 略
    
    > Task :test:processResources NO-SOURCE
    
    > Task :test:kaptKotlin
    package com.mapk.test
    
    import kotlin.Unit
    
    public fun helloWorld(): Unit {
      println("hello from TestTarget")
    }
    
    > Task :test:compileKotlin
    
    # 略
    

    생성된 코드



    다음과 같이 코드가 생성됩니다.
    제대로 패키지도 지정되어 있는 것을 알 수 있습니다.



    생성된 코드
    package com.mapk.test
    
    import kotlin.Unit
    
    public fun helloWorld(): Unit {
      println("hello from TestTarget")
    }
    

    참고로 한 내용


  • square/moshi: A modern JSON library for Kotlin and Java.
  • Egorand/kotlin-pizza-factory: A simple example of annotation processing with Kotlin's kapt and Square's kotlinpoet.
  • 좋은 웹페이지 즐겨찾기