元程序的开发和调试
本文为《深入实践 kotlin 元编程》读书笔记,想要深入了解 kotlin 元编程的同学可以阅读原书。
使用 kotlin-compile-testing 编写单元测试
元编程的逻辑通常较为抽象,我们可以通过单元测试将各种边界用例记录下来,并在功能迭代之时不断对元程序进行验证,为元程序提供可靠性保证。同时,我们也能在运行单元测试时通 过断点调试、日志输出的方式对编译器内部的结构和逻辑进行深入分析,以加深对编译器内部细节的认识和理解,降低开发难度和成本。
- 编译器的调用和调试
- 检查 KAPT 的输出
- 添加对 KSP 的支持
- 运行编译后的程序
- 打印变换之后的 IR
- 多模块编译
在实际项目当中集成
在元编程项目的开发过程中,采用单元测试可以应对绝大多数的调试和测试场景。
不过,在实际项目当中进行集成也是比较重要的开发和调试手段。有些时候我们希望能够快速结合实际项目完成一些想法的验证,直接集成开发甚至似乎是一个更好的办法。
工程的组织形式
在本地开发时,为了实现集成调试,通常我们最容易想到的做法就是先将元程序发布到 Maven 的本地仓库,然后在业务项目引入。这种做法足够简单,但对于需要频繁修改元程序进 行功能验证的场景又显得比较笨重。
为了应对这样的开发场景,Gradle 提供了一套混合编译(CompositeBuild)机制,它使得我们无需修改业务代码当中的依赖配置即可实现与依赖项目的源码依赖。
单步调试 Kotlin 编译器
集成开发的时候,通常我们会通过调试 Gradle 进程的方式来调试整个编译流程。调试 Gradle 时我们可以对 Gradle 自身和 Gradle 插件进行单步调试。
在 IntelliJ IDEA 当中调试 Gradle 非常简单,我们只需要在相应的源码当中打好断点,然后选择好合适的 Gradle 任务,点击调试运行按钮即可。
Java 编译器也运行在 Gradle 的进程当中,因此我们也可以在 Java 编译器的源码当中打断点对 Java 编译器进行单步调试
不过,默认情况下我们不能直接对 Kotlin 编译器进行单步调试,因为 Kotlin 编译器默认运行在一个独立的进程当中。不过,我们只需要将 Kotlin 编译器的运行策略配置为 In process,问题就可以得到解决。
Kotlin 编译器的日志输出
在默认的 Kotlin 编译器运行策略下,除了无法单步调试以外,通过 println(...) 打印到标准输出流的内容也不会出现在 Gradle 的运行输出当中。
这其实也不难理解,默认情况下 println(...) 会将内容打印到 Kotlin 编译器守护进程的标准输出流,我们自然无法在 Gradle 的运行输出当中看到它们。在将 Kotlin 的编译 器运行策略修改为 In process 之后,问题也就可以得到暂时的解决。
不过,修改 Kotlin 编译器运行策略的方法只适用于开发调试时。当我们将符号处理器或者编译器插件发布之后,Kotlin 编译器的运行策略由接入方决定,如果我们希望输出一些日志 方便接入方查看符号处理器或者编译器插件的运行状态,就需要使用对应的日志收集器了。
本章小结
本章介绍了如何为元程序编写单元测试以及如何对元程序进行调试。元编程的场景通常较为抽象,掌握便捷的途径对程序的结果进行有效的验证,往往可以产生事半功倍的效果。