Part 01、了解Clang
众所周知,编译器一般分为前端和后端,编译器前端主要负责预处理、词法分析、语法分析、语法检查、生成中间代码等与底层计算机架构无关的工作。
后端以中间代码为输入,首先进行架构无关的代码优化,之后针对不同的机器架构生成不同的机器码,进行汇编链接。
Clang在iOS代码编译中主要用于C/C++、Objective-C的前端编译工作,Clang属于llvm编译链的一部分,是llvm的前端编译器。我们可以通过Clang开放出来的API接口对源码进行自定义处理,如静态代码检查、编译流程控制、代码查找提示补全等功能。Clang工具针对的对象正是AST——语法分析的结果,抽象语法树(abstract syntax tree)。
一个AST的简单例子:
Clang工具可以遍历读取AST上的每一个节点,并对节点对应的代码进行查询、修改操作。Clang插件更能够直接集成进iOS的编译流程中,控制输出自定义的编译告警、错误,控制编译流程。
Part 02、Clang工具的选择
Clang大体包含三种不同的工具,libClang、Clang插件和libTooling。Clang插件和libTooling代码类似,关于AST的所有信息都通过ASTContext上下文返回,并且对AST有完全的控制权。而libClang不同,它通过封装好的稳定高层C API进行访问,利用Cursor和Token递归遍历,不能对AST进行完全控制。三者的优缺点如下:
1. libClanglibClang是针对Clang的稳定高层C语言封装,当你无需对AST进行完全控制时,libClang是使用最简单最合适的工具,应该首先考虑使用。其次,它只能作为独立工具使用,不能嵌入当前项目的编译流程。
- 优点:可以使用XCode或Python进行集成开发,拥有稳定的高层API,使用简单;
- 缺点:不能全量控制AST,不能嵌入编译流程。
2. Clang插件Clang插件允许在代码的编译流程中额外插入一些操作,比如在编译的过程中打印特殊字符或者警告,甚至中断编译。
- 优点:能够嵌入编译流程开启或中断编译,打印自定义告警和错误,并对AST进行全量控制;
- 缺点:代码编写复杂,集成Clang插件会降低原本的编译速度。
3. libToolinglibTooling是一个基于C的用于编写独立工具的Clang工具,这点类似于libClang,但是它只能用C编写并且功能更强大,对AST能够全量控制。
- 优点:对工程文件全量的处理,对AST的全量控制;
- 缺点:不能嵌入编译流程,对Clang的升级较为敏感,API不稳定。
这三个工具,从上到下,兼容性越来越差,对Clang升级变化越来越敏感,使用越来越复杂,但是功能越来越强大。
Part 03、Clang的具体应用
在和家亲中Clang的应用正在初步展开,我们使用libClang遍历项目中的每一个源文件,找到项目中所有关于图片名称的字符串描述,图片名称往往以固定字符串的形式出现,从而判断在我们的工程中,哪些图片已经被使用而哪些已经没有在用了,进行包的大小优化。
我们使用Clang插件对已在工程中定义的却没有在工程中使用的类、方法进行告警提示。方法是:首先利用Clang插件的VisitObjCInterfaceDecl和VisitObjCMethodDecl方法找出工程中所有的类定义和方法定义,再利用VisitObjCMessageExpr和VisitObjCSelectorExpr找到所有的消息发送,在iOS中方法的调用是通过消息发送的形式进行的,对于那些没有在消息发送列表中出现的类和方法,我们认为这些类和方法未被使用,从而直接在编译的时候进行告警提示。将插件在编译器中集成即可使用。