-
Notifications
You must be signed in to change notification settings - Fork 283
KSP multiple round preview guide
To achieve multiple round processing in KSP, a change to compiler is needed, given the timeline of compiler release, it will be in stable release of 1.4.30. Therefore, we are providing this guide so you can try out multiple round for your project before stable compiler release. In this guide, we will go through how to apply the multiple round ready compiler and the multiple round version of KSP to your project.
Due to this being a preview version and pending merging into master, incremental processing is not supported in this version.
- Add
kotlin-dev
maven repository to your project
repositories {
...
maven("https://dl.bintray.com/kotlin/kotlin-dev")
}
- Apply
1.4.30-M2-104
version of kotlin compiler to your project
plugins {
kotlin("jvm") version "1.4.30-M2-104" apply false
}
- This is intended for those who prefer to build KSP locally
- Check out
multiple-round branch
from ksp github repository - Build it to local maven repository with
./gradlew publishToMavenLocal
- Change KSP dependencies to be
-
implementation("com.google.devtools.ksp:symbol-processing-api:1.4.30-M2-104-multiple-round-preview-20201223")
- If you are building KSP locally
- You can check the locally built KSP version number at
~/.m2/repository/com/google/devtools/ksp/symbol-processing
, it is always sticking to your build date. - Add
mavenLocal()
to your processors and workload module's repository configuration, as well assettings.gradle
file
- You can check the locally built KSP version number at
- If you are building KSP locally
-
repositories {
...
mavenLocal()
...
}
- Change your processor's
process()
function to return a list of deferred symbolsList<KSAnnotated>
- Use
KSAnnotated.validate()
to filter invalid symbols to be deferred to next round. - Sample code to defer invalid symbols with validation check.
override fun process(resolver: Resolver): List<KSAnnotated> {
val symbols = resolver.getSymbolsWithAnnotation("com.example.annotation.Builder")
val ret = symbols.filter { !it.validate() }
symbols
.filter { it is KSClassDeclaration && it.validate() }
.map { it.accept(BuilderVisitor(), Unit) }
return ret
}
After applying multiple round processing, the termination condition will be until no new files are generated. If, when the termination condition is met, there are still deferred symbols not processed, KSP will log an error message for every processor with unprocessed deferred symbols.
Both newly generated files and existing files are accessible via Resolver
, there are 2 APIs provided for accessing files, Resolver.getAllFiles()
and Resolver.getNewFiles
. getAllFiles
will return a combined list of both existing files and newly generated files, while getNewFiles
only returns newly generated files.
To avoid unnecessary reprocessing of symbols, getSymbolsAnnotatedWith
will only return symbols found in newly generated files
Processor instance will only be created once, which means you can store information into the processor to be used for later rounds.
All KSP symbols will not be reusable cross rounds, due to the resolution result can potentially change based on what was generated last round. However, since KSP does not allow modifying existing code, information such as the string value for a name of a symbol should still be reusable. To summarize, processors can keep information from previous round on their own, but need to bear in mind that they might be invalid in future rounds.
Default validation logic provided by KSP checks all reachable symbols inside the enclosing scope of the symbol that is being checked. This might not suit your use case. You can reference KSValidateVisitor
and write your own validation logic by providing a custom predicate
lambda which is used by KSValidateVisitor
to filter out symbols that need to be checked.