Posted by Ting-Yuan Huang – Software program Engineer, and Jiaxiang Chen – Software program Engineer
The Kotlin Image Processing (KSP) software gives a high-level API for doing meta-programming in Kotlin. Many instruments have been constructed on KSP, enabling Kotlin code to be generated at compile time. For instance, Jetpack Room makes use of KSP to generate code for accessing the database, based mostly on an interface offered by the developer, like:
@Dao interface UserDao { @Question("SELECT * FROM consumer") enjoyable getAll(): Record<Consumer> }
KSP gives the API to the Kotlin code in order that Room on this case can generate the precise implementation of that interface. Whereas KSP has grow to be a core basis for meta-programing in Kotlin, its present implementation has some gaps which we’re aiming to resolve with a brand new KSP2 structure. This weblog particulars these architectural adjustments and the influence for plugins constructed on KSP.
As well as, KSP2 has preview help for:
- The brand new Kotlin compiler (code-named K2)
- A brand new standalone supply generator that gives extra flexibility and options than the present Kotlin compiler plugin
After getting suggestions on the brand new structure and persevering with to handle gaps we are going to work in direction of releasing KSP 2.0 the place these adjustments would be the default.
Enabling the KSP2 Preview
The brand new preview adjustments may be enabled in KSP 1.0.14 or newer utilizing a flag in gradle.properties:
Be aware: You may have to enlarge the heap dimension of the Gradle daemon now that KSP and processors run within the Gradle daemon as a substitute of the Kotlin compiler’s daemon (which has bigger default heap dimension), e.g. org.gradle.jvmargs=-Xmx4096M -XX:MaxMetaspaceSize=1024m
KSP2 and K2
Internally KSP2 makes use of the Beta Kotlin K2 compiler (which would be the default compiler in Kotlin 2.0). You should utilize KSP2 earlier than switching your Kotlin compiler to K2 (through the languageVersion setting) however if you wish to use K2 for compiling your code, take a look at: Attempt the K2 compiler in your Android initiatives.
Standalone Supply Generator
KSP1 is applied as a Kotlin 1.x compiler plugin. Working KSP requires operating the compiler and specifying KSP and its plugin choices. In Gradle, KSP’s duties are personalized compilation duties, which dispatch actual work to KotlinCompileDaemon by default. This makes debugging and testing considerably troublesome, as a result of KotlinCompileDaemon runs in its personal course of, exterior of Gradle.
In KSP2, the implementation may be regarded as a library with a important entry level. Construct programs and instruments can name KSP with this entry level, with out organising the compiler. This makes it very simple to name KSP programmatically and may be very helpful particularly for debugging and testing. With KSP2 you’ll be able to set breakpoints in KSP processors with out having to carry out every other / irregular setup duties to allow debugging.
All the pieces turns into a lot simpler as a result of KSP2 now controls its lifecycle and may be known as as a standalone program or programmatically, like:
val kspConfig = KSPJvmConfig.Builder().apply { // All configurations occur right here. }.construct() val exitCode = KotlinSymbolProcessing(kspConfig, listOfProcessors, kspLoggerImpl).execute()
KSP2 API Conduct Modifications
With the brand new implementation, additionally it is an important alternative to introduce some refinements within the API habits in order that builders constructing on KSP will probably be extra productive, have higher debuggability and error restoration. For instance, when resolving Map<String, NonExistentType>, KSP1 merely returns an error kind. In KSP2, Map<String, ErrorType> will probably be returned as a substitute. Here’s a checklist of the present API habits adjustments we plan on making in KSP2:
- Resolve implicit kind from perform name: val error = mutableMapOf<String, NonExistType>()
KSP1: The entire kind will probably be an error kind attributable to failed kind decision
KSP2: It can efficiently resolve the container kind, and for the non-existent kind within the kind argument, it is going to accurately report errors on the particular kind argument.
- Unbounded kind parameter
KSP1: No bounds
KSP2: An higher certain of Any? is all the time inserted for consistency
- Resolving references to kind aliases in perform sorts and annotations
KSP1: Expanded to the underlying, non-alias kind
KSP2: Not expanded, like makes use of elsewhere.
- Totally certified names
KSP1: Constructors have FQN if the constructor is from supply, however not if the constructor is from a library.
KSP2: Constructors shouldn’t have FQN
- Sort arguments of internal sorts
KSP1: Internal sorts has arguments from outer sorts
KSP2: Internal sorts has no arguments from outer sorts
- Sort arguments of star projections
KSP1: Star projections have kind arguments which might be expanded to the efficient variances based on the declaration websites.
KSP2: No growth. Star projections have nulls of their kind arguments.
- Variance of Java Array
KSP1: Java Array has a invariant higher certain
KSP2: Java Array has a covariant higher certain
- Enum entries
KSP1: An enum entry has its personal subtype with a supertype of the enum class (incorrect habits from language perspective)
KSP2: An enum entry’s kind is the kind of the enclosing enum class
- Multi-override situation
For instance
interface GrandBaseInterface1 { enjoyable foo(): Unit } interface GrandBaseInterface2 { enjoyable foo(): Unit } interface BaseInterface1 : GrandBaseInterface1 { } interface BaseInterface2 : GrandBaseInterface2 { } class OverrideOrder1 : BaseInterface1, GrandBaseInterface2 { override enjoyable foo() = TODO() } class OverrideOrder2 : BaseInterface2, GrandBaseInterface1 { override enjoyable foo() = TODO() }
KSP1:
Discover overridden symbols in BFS order, first tremendous kind discovered on direct tremendous kind checklist that incorporates overridden image is returned
For the instance, KSP will say OverrideOrder1.foo() overrides GrandBaseInterface2.foo() and OverrideOrder2.foo() overrides GrandBaseInterface1.foo()KSP2:
DFS order, first tremendous kind discovered overridden symbols (with recursive tremendous kind search for) in direct tremendous kind checklist is returned.
For the instance, KSP will say OverrideOrder1.foo() overrides GrandBaseInterface1.foo() and OverrideOrder2.foo() overrides GrandBaseInterface2.foo()
- Java modifier
KSP1: Transient/risky fields are last by default
KSP2: Transient/risky fields are open by default
- Sort annotations
KSP1: Sort annotations on a sort argument is simply mirrored on the sort argument image
KSP2: Sort annotations on a sort argument now current within the resolved kind as nicely
- vararg parameters
KSP1: Thought of an Array kind
KSP2: Not thought-about an Array kind
- Synthesized members of Enums
KSP1: values and valueOf are lacking if the enum is outlined in Kotlin sources
KSP2: values and valueOf are all the time current
- Synthesized members of information lessons
KSP1: componentN and copy are lacking if the information class is outlined in Kotlin sources
KSP2: componentN and copy are all the time current
New Multiplatform Processing Scheme
On the subject of the processing scheme, i.e. what sources are processed when, the precept of KSP is to be according to the construct’s current compilation scheme. In different phrases, what the compiler sees is what processors see, plus the supply code that’s generated by processors.
In KSP1’s present compilation scheme, frequent / shared supply units are processed and compiled a number of occasions, with every goal. For instance, commonMain is processed and compiled 3 occasions within the following mission structure. With the ability to course of all of the sources from dependencies is handy with one exception: Processors don’t see the sources generated from commonMain when processing jvmMain and jsMain. All the pieces have to be re-processed and that may be inefficient.
duties |
inputs |
outputs |
kspKotlinCommonMainMetadata |
commonMain |
generatedCommon |
kspKotlinJvm |
commonMain, jvmMain |
generatedCommonJvm |
kspKotlinJs |
commonMain, jsMain |
generatedCommonJs |
compileKotlinCommonMainMetadata |
commonaMain, generatedCommon |
frequent.klib |
compileKotlinJvm |
commonMain, jvmMain, generatedCommonJvm |
app.jar |
compileKotlinJs |
commonMain, jsMain, generatedCommonJs |
important.js |
In KSP2, we plan so as to add an experimental mode that tries to align to how supply units are compiled in K2 higher. All sources may be processed solely as soon as with the obtainable new processing scheme:
duties |
inputs |
outputs |
Resolvable however not obtainable in getAllFiles / getSymbolsWithAnnotation |
kspKotlinCommonMainMetadata |
commonMain |
generatedCommon |
|
kspKotlinJvm |
jvmMain |
generatedJvm |
commonMain, generatedCommon |
kspKotlinJs |
jsMain |
generatedJs |
commonaMain, generatedCommon |
compileKotlinCommonMainMetadata |
commonaMain, generatedCommon |
frequent.klib |
|
compileKotlinJvm |
commonMain, jvmMain, generatedCommon, generatedJvm |
app.jar |
|
compileKotlinJs |
commonMain, jsMain, generatedCommon, generatedJs |
important.js |
Please observe that Kotlin 2.0 remains to be in beta and the compilation mannequin is topic to alter. Please tell us how this works for you and provides us suggestions.
KSP2 Preview Suggestions
KSP2 is in preview however there’s nonetheless extra work to be achieved earlier than a secure launch. We hope these new options will in the end enable you to be extra productive when utilizing KSP! Please present us along with your suggestions so we will make these enhancements superior as they progress in direction of being secure.