Kotlin ksp命令行解析kotlin文件自动生成UML类图
ksp是官方提供的用来解析Kotlin文件的工具,这里我用它来实现解析Kotlin项目,自动生成UML类图的功能(如上图所示)。
网上包括外网所有的ksp介绍都是在Android Studio中使用通过gradle配置,因为我想用ksp解析任意项目,而不是集成到某个项目里面,所以我研究了一下通过命令行调用的方式。
官方教程在这个地址里面。
1. 下载kotlin编译器和ksp
#!/bin/bash
# Kotlin compiler
wget https://github.com/JetBrains/kotlin/releases/download/v1.8.0/kotlin-compiler-1.8.0.zip
unzip kotlin-compiler-1.8.0.zip
# KSP
wget https://github.com/google/ksp/releases/download/1.8.0-1.0.8/artifacts.zip
unzip artifacts.zip
2. bash命令行调用kotlinc
#!/bin/bash
KSP_PLUGIN_ID=com.google.devtools.ksp.symbol-processing
KSP_PLUGIN_OPT=plugin:$KSP_PLUGIN_ID
KSP_PLUGIN_JAR=./com/google/devtools/ksp/symbol-processing-cmdline/1.8.0-1.0.8/symbol-processing-cmdline-1.8.0-1.0.8.jar
KSP_API_JAR=./com/google/devtools/ksp/symbol-processing-api/1.8.0-1.0.8/symbol-processing-api-1.8.0-1.0.8.jar
KOTLINC=./kotlinc/bin/kotlinc
AP=/path/to/your-processor.jar
mkdir out
$KOTLINC \
-Xplugin=$KSP_PLUGIN_JAR \
-Xplugin=$KSP_API_JAR \
-Xallow-no-source-files \
-P $KSP_PLUGIN_OPT:apclasspath=$AP \
-P $KSP_PLUGIN_OPT:projectBaseDir=. \
-P $KSP_PLUGIN_OPT:classOutputDir=./out \
-P $KSP_PLUGIN_OPT:javaOutputDir=./out \
-P $KSP_PLUGIN_OPT:kotlinOutputDir=./out \
-P $KSP_PLUGIN_OPT:resourceOutputDir=./out \
-P $KSP_PLUGIN_OPT:kspOutputDir=./out \
-P $KSP_PLUGIN_OPT:cachesDir=./out \
-P $KSP_PLUGIN_OPT:incremental=false \
-P $KSP_PLUGIN_OPT:apoption=key1=value1 \
-P $KSP_PLUGIN_OPT:apoption=key2=value2 \
$*
官方教程到这一步就结束了,里面没有介绍最关键的ksp处理类的jar包生成。只知道要传一个AP=/path/to/your-processor.jar
进去。
3. ksp处理类
package cc.rome753
import com.google.devtools.ksp.*
import com.google.devtools.ksp.processing.Dependencies
import com.google.devtools.ksp.processing.Resolver
import com.google.devtools.ksp.processing.SymbolProcessor
import com.google.devtools.ksp.processing.SymbolProcessorProvider
import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
import com.google.devtools.ksp.symbol.*
class MySymbolProcessorProvider:SymbolProcessorProvider {
override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor {
return MySymbolProcessor(environment)
}
}
class MySymbolProcessor(private val environment: SymbolProcessorEnvironment): SymbolProcessor {
@OptIn(KspExperimental::class)
override fun process(resolver: Resolver): List<KSAnnotated> {
environment.logger.info("process--------------------------------")
val logger = environment.logger
val files = resolver.getAllFiles()
val allClass = mutableListOf<MyClass>()
val dictNameId = mutableMapOf<String,Int>()
files.forEach { file ->
logger.info("file: ${file.fileName}")
file.declarations.forEach { dec ->
if (dec is KSClassDeclaration) {
实际上就是一个继承SymbolProcessorProvider和一个继承SymbolProcessor的类,在SymbolProcessor的process方法里面可以遍历所有的kotlin文件和类、类的属性和方法,这样就能把这些保存到json中,供后面使用。
4. ksp处理类生成jar包
用kotlinc编译指令生成jar包
kotlinc myprocessor -cp symbol-processing-api-1.8.0-1.0.8.jar -d myprocessor.jar
但是这个生成的jar包是不能直接提供给上面的bash命令使用的,因为kotlinc编译器不知道怎么从jar包里面找MySymbolProcessorProvider这个类。
我参考了一下github上ksp示例,它是Android Studio工程的,里面有jar包的META-INF设置。方法如下:
新建一个META-INF文件夹,文件夹里面放一个文本文件:
文件名是:com.google.devtools.ksp.processing.SymbolProcessorProvider
文件内容是:cc.rome753.MySymbolProcessorProvider
用jar命令把文件夹写入jar包里
jar uvMf myprocessor.jar META-INF
这样就能运行kotlinc的bash指令了。
5. 绘制UML类图
将ksp处理类生成的类相关的json信息用vis-network绘制到网页中展示。具体参考Swift自动生成UML类图这篇教程,虽然是Swift的,但是原理是一样的。
完整代码都在github仓库
运行方法:下载或克隆仓库,进入主目录,运行命令即可
python3 runKotlin.py /YourKotlinProjectDir
6. 缺点
用ksp命令行有一个致命的问题,就是当kotlin文件稍微多点(超过100个,大部分非demo项目都超过这个数),kotlinc命令行会就产生OOM.
exception: java.lang.OutOfMemoryError: GC overhead limit exceeded
at org.jetbrains.kotlin.protobuf.CodedOutputStream.newInstance(CodedOutputStream.java:106)
at org.jetbrains.kotlin.metadata.ProtoBuf$Type.<init>(ProtoBuf.java:4838)
at org.jetbrains.kotlin.metadata.ProtoBuf$Type.<init>(ProtoBuf.java:4807)
at org.jetbrains.kotlin.metadata.ProtoBuf$Type$1.parsePartialFrom(ProtoBuf.java:4979)
at org.jetbrains.kotlin.metadata.ProtoBuf$Type$1.parsePartialFrom(ProtoBuf.java:4974)
at org.jetbrains.kotlin.protobuf.CodedInputStream.readMessage(CodedInputStream.java:495)
at org.jetbrains.kotlin.metadata.ProtoBuf$Type$Argument.<init>(ProtoBuf.java:5094)
at org.jetbrains.kotlin.metadata.ProtoBuf$Type$Argument.<init>(ProtoBuf.java:5030)
我配置了一些提升虚拟机内存的指令,但是没什么效果。
kotlinc -Dkotlin.daemon.jvm.options="-Xms2g -Xmx12g -XX:+UseG1GC -XX:G1HeapRegionSize=2048m -XX:MaxGCPauseMillis=5000 -XX:MaxPermSize=700m -XX:ReservedCodeCacheSize=480m
当然这个问题是kotlinc命令行导致的,跟ksp没有关系。我在issues里反应了,看看有没有解决方法。