首先说,基于clang进行工具开发适合干什么事情,现在llvm的编译器前端是完全采用的clang,clang会把源代码转换为AST,然后再交给llvm进行各种代码优化和最终的代码生成,所以所有需要基于源码的工具都可以基于clang进行开发,可以节省很多成本,毕竟访问AST比自己做源码解析简单很多,因为clang的设计就是基于模块的,所以本身就支持很多二次开发和插件开发,上手并没有那么难。常见的工具有静态分析相关工具,代码转换等等.
llvm官方针对何时采用何种方式已经有非常详细的描述,文档地址为:clang工具官方介绍
这个文档我也看了好几次了,每次要用的时候又看一遍,这次总结一下,免得老是重复劳动,真是老了记忆力不行了…
开发基于clang的工具一共有三种方式:
- LibClang
- Clang Plugins
- LibTooling
LibClang
LibClang有一个非常稳定,高级的接口,适合干一些对源码细节不是那么在意的事情。文档中大意是,如果你在怀疑自己是否该使用LibClang的方式进行工具开发的时候,那不妨先用LibClang的方式尝试一下,除非你有非常明确的不能使用LibClang的理由。当前基于LibClang的工具有Xcode,Clang Python bindings。我记得Xcode的自动补全就完全是采用LibClang提供的Api完成的。
好处大致列一下:
- 可以用Python开发,其他两种方式都必须基于C++进行开发,毕竟写C++不是那么愉快的事情
- 接口稳定,其他两种方式随着Clang的升级,api可能发生改变
- api抽象比较高级,采用cursor的方式访问AST,不需要关心一些AST很细节的部分
LibClang的方式我还是有发言权的,之前使用python bindings做过一个代码翻译工具,确实如官方文档所说,这里获得得AST是比较抽象的,很多细节会缺失,对于做代码翻译这种需要很多细节的工具其实不是很合适的。
Clang Plugins
Clang Plugins让你可以在编译过程中对AST做处理,这个plugin是在clang运行的时候,以动态库的形式被加载到内存中,比较容易和编译环境结合起来。主要的使用例子有针对性的对代码做一些warning之类的提示,甚至可以新增一个编译步骤。
使用场景大致如下:
- 当编译文件之间的依赖改变时,需要重新开始。我理解是当你改变AST中的内容时,有时候需要触发一下重新编译。
- 希望插件有中断某次编译的能力
- 拥有对ASY完全的控制权
不合适的场景:
- 希望制作一个独立于clang的工具,因为这种方式是作为一个clang的动态库加载的,必须在clang的环境中运行
- want full control on how Clang is set up, including mapping of in-memory virtual files——这句没太懂什么意思,我理解就是因为插件的控制范围有限吧
- 不能针对几个单独的文件使用(整个这句话不是很理解得清楚,大概就是这个意思,但为什么我也没太想明白)
感觉这种插件方式确实不太适合做一些独立运行的工具,更适合做一些每次编译都要带在里面的功能,比如检查一些特定的错误之类的.
LibTooling
LibTooling同样也是采用c++语言进行开发,但可以独立于编译过程称为单独的工具,可以只针对单个文件进行run,但是官方文档表明该种方式的api不是很稳定,随着clang升级可能会改变。
适合场景:
- 针对单个文件生效
- 对AST的完全控制
- 和Clang Plugins复用代码
不适合的场景基本上就是以上的反面。
现在已经出了一系列基于clang开发的工具,被统一叫做Clang tools,页面下方有一个列表。
我最近准备撸一个代码混淆工具,网上貌似很多人用念茜提供的一个工具地址,我觉得还是有点原始,所以打算基于clang做一个自己的混淆工具,目前看来,libclang和libtooling都是比较适合我的,犹豫的原因是c++毕竟不熟。工具本身用到clang的部分倒不是很麻烦,如果为了做一个简单的工具在c++遇到很多坑想想是一件很不划算的事情。等我具体实施的时候再补上最终选定的心里路程,顺便附上我自己制作的教程吧.