iOS前端編譯器擴展——Clang
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中方法的調用是通過消息發送的形式進行的,對于那些沒有在消息發送列表中出現的類和方法,我們認為這些類和方法未被使用,從而直接在編譯的時候進行告警提示。將插件在編譯器中集成即可使用。