kotlin-ketchup
A project aiming to generate KMP declarations from several library versions
LICENSE
Apache 2.0. See ./LICENSE in this repository
See the ./NOTICE for more details on the third-party sources or libraries that we use
Why
Assume one does a library or a plugin that has to be compatible with several host versions. Think about an IntelliJ plugin (or maybe an Android App?)
There are may ways to set up the project. One is to have a dedicated src/kotlin
source roots per different versions. The shared code can still be in the src/main/kotlin
source set. That scenario usually have a parameter somewhere in Gradle to specify what actual version of the platform to use.
More formally, it may look like
project
\
src
+ kotlin <-- here comes the common part
+ v2021.1 <-- a specific code to include for 2021.1 version
+ v2021.2
+ v2021.3
Something similar we may have with resource folders, tests, ect.
Problem
This approach has problems
- every time there are un-included source folders
- refactorings are tricky to implement as un-included folders has to be processed
- the code in common part may assume a class declaration from the version part
These problems are not showstoppers from one hand. From the other hand, this projects aims to hack the solution with the help of Kotlin Multiplatform
Solution
We try to generate necessary expect/actual
declarations with Kotlin Multiplatform and allow having all source folders included as if there were several target directories.
The main trick in the generation is that it should only include symbols which are available in the intersection of platform libraries.
To achieve that we use ASM library to read the bytecode of the dependencies from the classpath. We are going to use KotlinPoet to generate the necessary declarations as Kotlin code.
Next is the Gradle part. This is a Gradle plugin. It intersects the classpath and attaches the generated Kotlin code to the common (with actual
s) and to each of version-specific targets. On the other hand, it could be a dedicated sub-project, which can be generated once and be ready for use.
Downsides
Parsing JVM bytecode with ASM and kotlinx-metadata requires an additional code to map its object into Kotlin code declarations back.
We have to have a duality - one approach uses ASM and JVM to recover Java declarations, the second uses Kotlin own metadata to generate the proper declarations.
Kotlin compiler parameters or package-wide nullability options has to be included and supported as well. That is harder to implement and requires effective testing.
Generated declarations would miss original javadocs/dokka annotations. It could make using the libraries harder as well. An alternative could be to parse sources instead of the compiled bytecode to enrich the generated code with annotations.
Alternative approaches
At that point it seems more practical to implement the logic as a Kotlin compiler plugin. It is hard to say how easy or if that could be easier to implement.