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

Learn step by step code generation with KSP

ThawZinToe
November 06, 2023

Learn step by step code generation with KSP

Learn what annotation is and primary usage and a step-by-step guide for how to generate integer to enum code with KSP (Kotlin symbol Processor)

ThawZinToe

November 06, 2023
Tweet

More Decks by ThawZinToe

Other Decks in Technology

Transcript

  1. Kotlin Symbol Processing is a tool that allows developer to

    write custom code generators for Kotlin Kotlin Poet is a popular library that helps in generating Kotlin code programmatically. What is KSP? What is Kotlin Poet In this slide, we will learn how to set up KSP with Kotlin Poet for code generation in an Android project. Where’re we going? Introduction to KSP KSP #
  2. Headline goes here KSP # 01 Familiar with Android Annotation

    02 Processor, Symbols & Visitor in KSP (Interfaces) 03 Dependency Structure for KSP 04 Deep Dive Into Code
  3. 5 Annotation in Android development are code elements marked with

    "@" that associate metadata with other code elements. They help convey information and can be used for automatic source code generation through annotation processing. This can reduce boilerplate code, and enhances code functionality and readability. Annotation Annotation #
  4. Entry point of KSP Analyze and dynamically code generate SymbolProcessor

    Provider SymbolProcessor Symbol Custom KSVisitor for SymbolProcessor Visitor Processor, Symbols & Visitor in KSP KSP #
  5. SymbolProcessorProvider Processor in KSP # interface used by plugins to

    integrate into Kotlin Symbol Processing It contains current information about current compilation session and has communication with Kotlin Compiler Called by Kotlin Symbol Processing to create the processor
  6. SymbolProcessor Processor in KSP # Main component for symbol processing

    in KSP such as class , field, property and annotation symbol analyze and manipulate these symbols during compilation process provides information about the symbol being processed, such as its name, kind (class, function, property, etc.), annotations, modifiers, etc
  7. KSVisitor Visitor in KSP # Main component for symbol processing

    in KSP such as class , field, property and annotation symbol analyze and manipulate these symbols during compilation process provides information about the symbol being processed, such as its name, kind (class, function, property, etc.), annotations, modifiers, etc
  8. • Annotation: This holds your annotation class. • Processor: This

    contains KSP code generation logic. • App: This is the Android app that consumes the generated files. Three Modules KSP #
  9. How to create modules Create new two modules 1. annotation

    2. processor in enumKSP module as shown in picture below
  10. Add KSP plugin Kotlin DSLG plugins { id("com.google.devtools.ksp") version "1.8.10-1.0.9"

    apply false } plugins { id("com.google.devtools.ksp") } build.gradle.kts
  11. Add KSP Symbol Processing API in build.gradle.kts in processor module

    implementation( "com.google.devtools.ksp:symbol-processing-api:latest-version" ) implementation("com.squareup:kotlinpoet-ksp:1.12.0") Add Kotlin Poet for code generation in build.gradle.kts in processor module Add KSP plugin
  12. Generate User class into Enum code data class User( @Enum(enumConstants

    = [ "Male", "Female", "Other" ]) val genderType: Int = 1 )
  13. Use case for SymbolProcessor SymbolProcessor # Using Kotlin reflect to

    get Sequence<KSAnnotated> from Enum Annotation filter valid KSAnnotated Symbols Sequence Accept visitor from KSNode
  14. process method in SymbolProcessor override fun process(resolver: Resolver): List<KSAnnotated> {

    // Using Kotlin reflect to get class from GenerateEnum Annotation (User) val symbols = resolver.getSymbols(GenerateEnum::class) val validatedSymbols = symbols.filter { it.validate() }.toList() val visitor = EnumGenerateVisitor(logger, codeGenerator) validatedSymbols.forEach { symbol -> symbol.accept(visitor, Unit) } return emptyList() }
  15. The Purpose of using KSVisitor Purpose of KSP # Code

    Analysis: better to find potential errors Code Generation : better code generation such as class or functions Code Refactoring : better refactoring and renaming code
  16. Use case for Visitor Visitor # Get Information from declared

    Annotation Generate code with KotlinPoet Library File Write in auto generated code (ksp folder)
  17. class EnumGenerateVisitorFromProperty( private val logger: KSPLogger, private val codeGenerator: CodeGenerator,

    ) : KSVisitorVoid() { private val enumClass = Enum::class.simpleName override fun visitPropertyDeclaration(property: KSPropertyDeclaration,...) { // code generate StringBuilder or Kotlin Poet Library } }
  18. Get information from declared annotation KSP # Find Annotation @Enum

    Listing Arguments from parameters of @Enum annotation ( Male, Female, Other) Now we think logic and generate code that we want to change
  19. val enumAnnotation = property.annotations.find { it.shortName.asString() == enumClass } //

    Get parameters (Male, Female, Other) from declare Enum annotation val listArguments = enumAnnotation?.arguments?.find { arg -> arg.name?.asString() == enumConstants }?.value as List<*> "enumConstants" "Enum::class.simpleName"
  20. Code generation with KotlinPoet KotlinPoet # Code Analysis: better to

    find potential errors Code Generation : better code generation such as class or functions Code Refactoring : better refactoring and renaming code
  21. Generate Enum class with private constructor “type” val type =

    FunSpec.constructorBuilder() .addParameter("type", Int::class) .build() val enumClass = TypeSpec.enumBuilder(propertyName) .primaryConstructor(type) .addProperty( PropertySpec.builder("type", Int::class) .initializer("type") .addModifiers(KModifier.PRIVATE) .build(), )
  22. Generate Enum constants “MALE(0)”,“FEMALE(1)”,“OTHER(2)” listArguments.forEachIndexed { index, value -> val

    enumConstantValue = TypeSpec .anonymousClassBuilder() .addSuperclassConstructorParameter("%L", index) .build() addEnumConstant( value.toString().uppercase(), enumConstantValue, ).build() }
  23. File write in generate file ksp val fileSpec = FileSpec.builder(packageName,

    propertyName).apply { addType(enumClass) }.build() fileSpec.writeTo(codeGenerator, false) "Enum::class.simpleName"