Kotlin Compiler Plugins & KSP: Building Code Generation Tools
Large Kotlin applications often contain repetitive code for serialization, dependency injection, adapters, database mappings, and API models.
Code generation can automate this work while keeping generated code consistent.
This tutorial introduces Kotlin Symbol Processing (KSP), explains its relationship with compiler plugins, and demonstrates the architecture of an annotation-driven generator.
KSP vs Compiler Plugins
KSP is designed for inspecting Kotlin symbols and generating source code.
Compiler plugins operate deeper in the Kotlin compilation pipeline and can perform transformations at compiler levels.
Use KSP for:
- Annotation processing
- Source generation
- Symbol inspection
- Generated adapters
- Generated registrations
Use compiler plugins when deeper compiler or IR transformations are required.
Example Annotation
Create an annotation:
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.SOURCE)
annotation class AutoMapper
A developer can then write:
@AutoMapper
data class User(
val id: Long,
val name: String
)
The processor can generate mapping code automatically.
Processor Structure
A typical processor project:
processor/
├── AutoMapperProcessor.kt
├── AutoMapperProcessorProvider.kt
└── resources/
app/
└── models/
Processor Provider
A provider creates the processor:
class AutoMapperProcessorProvider :
SymbolProcessorProvider {
override fun create(
environment: SymbolProcessorEnvironment
): SymbolProcessor {
return AutoMapperProcessor(
environment.codeGenerator,
environment.logger
)
}
}
Processing Symbols
A processor can find declarations carrying the annotation:
class AutoMapperProcessor(
private val codeGenerator: CodeGenerator,
private val logger: KSPLogger
) : SymbolProcessor {
override fun process(
resolver: Resolver
): List<KSAnnotated> {
val symbols = resolver.getSymbolsWithAnnotation(
AutoMapper::class.qualifiedName!!
)
symbols
.filterIsInstance<KSClassDeclaration>()
.forEach { generateMapper(it) }
return emptyList()
}
}
Inspecting Properties
A class declaration exposes its properties:
val properties = declaration.getAllProperties()
The processor can inspect property names, types, annotations, visibility, and nullability.
Generating Code
KSP provides CodeGenerator for writing source files.
For larger generators, KotlinPoet can simplify safe Kotlin source generation.
Generated output might look like:
object UserMapper {
fun map(user: User): UserDto {
return UserDto(
id = user.id,
name = user.name
)
}
}
Incremental Processing
Large builds benefit from incremental processing.
A good processor should:
- Have predictable inputs
- Generate predictable outputs
- Avoid unnecessary file-system scanning
- Clearly define dependencies
This helps prevent unrelated source changes from triggering expensive regeneration.
Diagnostics
Give developers useful errors:
logger.error(
"AutoMapper can only be applied to classes",
declaration
)
Good diagnostics are essential for custom build tools.
Generated Code Quality
Generated source should be readable.
Prefer:
generated/
└── mapper/
├── UserMapper.kt
└── OrderMapper.kt
over massive generated files containing unrelated code.
Compiler Plugins
A compiler plugin becomes appropriate when the requirement involves deeper transformations such as:
- Kotlin IR
- Compiler-generated behavior
- Language-level extensions
- Bytecode-related transformations
Compiler plugins are more complex and can be more sensitive to compiler version changes.
Testing
Test processors with small source examples:
Input Kotlin
↓
KSP Processor
↓
Generated Kotlin
↓
Compilation
Verify both generated source and successful compilation.
Design Principles
A good code-generation tool should be:
- Deterministic
- Fast
- Incremental
- Testable
- Version-aware
- Independent of runtime business logic
Conclusion
KSP is an excellent choice for advanced Kotlin developers who need source generation without implementing a full compiler transformation.
When deeper compiler behavior is required, compiler plugins provide additional power, but they also introduce greater complexity and maintenance requirements.
Useful Links
SDK Flutter: https://github.com/v-modal/vmodal_sdk_flutter
SDK Android: https://github.com/v-modal/vmodal_sdk_android
Discord: https://discord.gg/K72z28KUx
Top comments (0)