一榕堰、編譯器
性能優(yōu)化:啟動優(yōu)化竖慧、界面優(yōu)化、架構優(yōu)化
編譯型語言:OC(編譯器是clang)逆屡、C(編譯器可以直接執(zhí)行嗎圾旨,不可以,編譯出來的是一個可執(zhí)行文件魏蔗,然后再執(zhí)行可執(zhí)行文件)
解釋型語言:Python(Python的解釋器是python砍的,解釋器可以直接執(zhí)行)
解釋器和編譯器就干一件事,把高級語言的代碼翻譯成計算機能夠讀懂的可執(zhí)行文件莺治、能夠讀懂的0廓鞠、1的組合
架構:arm64、arm32 (硬件不一樣谣旁,導致架構不一樣床佳,導致指令集不一樣)
二、LLVM
LLVM概述
LLVM是架構編譯器(compiler)的框架系統(tǒng)榄审,以C++編寫而成砌们,用于優(yōu)化以任意程序語言編寫的程序的編譯時間(compile-time)、鏈接時間(link-time)瘟判、運行時間(run-time)以及空閑時間(idle-time)怨绣,對開發(fā)者保持開放,并兼容已有腳本拷获。
LLVM計劃啟動于2000年篮撑,最初由美國UIUC大學的Chris Lattner博士主持開展。
2006年Chris Lattner加盟Apple Inc.并致力于LLVM在Apple開發(fā)體系中的應用匆瓜。
Apple也是LLVM計劃的主要資助者赢笨。
目前LLVM已經(jīng)被蘋果IOS開發(fā)工具未蝌、Xilinx Vivado、Facebook茧妒、Google等各大公司采用萧吠。
傳統(tǒng)編譯器設計
編譯器前段(Frontend)
編譯器前段的任務是解析源代碼。它會進行:詞法分析桐筏,語法分析纸型,語義分析,檢查源代碼是否存在錯誤梅忌,然后構建抽象語法樹(Abstract Syntax Tree狰腌,AST),LLVM的前段還會生成中間代碼(intermediate representation牧氮,IR)
優(yōu)化器(Optimizer)
優(yōu)化器負責進行各種優(yōu)化琼腔。改善代碼的運行時間,例如消除冗余計算等踱葛。
后端(Backend)/代碼生成器(CodeGenerator)
將代碼映射到目標指令集丹莲。生成機器語言,并且進行機器相關的代碼優(yōu)化尸诽。
iOS的編譯器架構
Objective C/C/C++使用的編譯器前端是Clang甥材,Swift是Swift,后端都是LLVM性含。
LLVM的設計
當編譯器決定支持多種源語言或多種硬件架構時擂达,LLVM最重要的地方就來了。其他的編譯器如GCC胶滋,它方法非常成功板鬓,但由于它是作為整體應用程序設計的,因此它們的用途受到了很大的限制究恤。
LLVM設計的最重要方面是俭令,使用通用的代碼表示形式(IR),它是用來在編譯器中表示代碼的形式部宿。所以LLVM可以為任何編程語言獨立編寫前端抄腔,并且可以為任意硬件架構獨立編寫后端。
Clang
Clang是LLVM項目中的一個子項目理张。它是基于LLVM架構的輕量級編譯器赫蛇,誕生之初是為了替代GCC,提供更快的編譯速度雾叭。它是負責編譯C悟耘、C++堪唐、Objective-C語言的編譯器酥夭,它屬于整個LLVM架構中的芹血,編譯器前端遮精。對于開發(fā)者來說,研究Clang可以給我們帶來很多好處旺嬉。
編譯流程
通過命令可以打印源代碼的編譯階段
.m(源碼) --> .ll(IR) --> .bc --> .s(匯編) --> .o(目標文件)(Mach-O 64-bit
object
x86_64) --> 可執(zhí)行文件( Mach-O 64-bitexecutable
x86_64)(黑黑的框框管行、.out)
Mach-O文件是.o的集合
目標文件 鏈接(linker) 生成可執(zhí)行文件
clang -ccc-print-phases main.m
0: input, "main.m", objective-c
1: preprocessor, {0}, objective-c-cpp-output
2: compiler, {1}, ir
3: backend, {2}, assembler
4: assembler, {3}, object
5: linker, {4}, image
6: bind-arch, "x86_64", {5}, image
0:輸入文件:找到源文件
1:預處理階段:這個過程處理包括宏的替換,頭文件的導入邪媳。
2:編譯階段:進行詞法分析捐顷、語法分析、檢測語法是否正確雨效,最終生成IR套菜。
3:后端:這里LLVM會通過一個一個的Pass去優(yōu)化,每個Pass做一些事情设易,最終生成匯編代碼。
4:生成目標文件蛹头。
5:鏈接:鏈接需要的動態(tài)庫和靜態(tài)庫顿肺,生成可執(zhí)行文件。
6:通過不同的架構渣蜗,生成對應的可執(zhí)行文件屠尊。
預處理階段
執(zhí)行如下命令
clang -E main.m
clang -E main.m >> main2.m //重定向到main2.m
執(zhí)行完畢可以看到頭文件的導入和宏的替換。
編譯階段
詞法分析
預處理完成后就會進行詞法分析耕拷。這里會把代碼切成一個個Token讼昆,比如大小括號,等于號還有字符串等骚烧。
clang -fmodules -fsyntax-only -Xclang -dump-tokens main.m
annot_module_include '#import <stdio.h>
#define C 30
typedef int DYZ_INT_64;
int main(int argc, const char * argv[]) {
@autoreleasepool {
int a = 10;' Loc=<main.m:9:1>
typedef 'typedef' [StartOfLine] Loc=<main.m:12:1>
int 'int' [LeadingSpace] Loc=<main.m:12:9>
identifier 'DYZ_INT_64' [LeadingSpace] Loc=<main.m:12:13>
semi ';' Loc=<main.m:12:23>
int 'int' [StartOfLine] Loc=<main.m:14:1>
identifier 'main' [LeadingSpace] Loc=<main.m:14:5>
l_paren '(' Loc=<main.m:14:9>
int 'int' Loc=<main.m:14:10>
identifier 'argc' [LeadingSpace] Loc=<main.m:14:14>
comma ',' Loc=<main.m:14:18>
const 'const' [LeadingSpace] Loc=<main.m:14:20>
char 'char' [LeadingSpace] Loc=<main.m:14:26>
star '*' [LeadingSpace] Loc=<main.m:14:31>
identifier 'argv' [LeadingSpace] Loc=<main.m:14:33>
l_square '[' Loc=<main.m:14:37>
r_square ']' Loc=<main.m:14:38>
r_paren ')' Loc=<main.m:14:39>
l_brace '{' [LeadingSpace] Loc=<main.m:14:41>
at '@' [StartOfLine] [LeadingSpace] Loc=<main.m:15:5>
identifier 'autoreleasepool' Loc=<main.m:15:6>
l_brace '{' [LeadingSpace] Loc=<main.m:15:22>
int 'int' [StartOfLine] [LeadingSpace] Loc=<main.m:16:9>
identifier 'a' [LeadingSpace] Loc=<main.m:16:13>
equal '=' [LeadingSpace] Loc=<main.m:16:15>
numeric_constant '10' [LeadingSpace] Loc=<main.m:16:17>
semi ';' Loc=<main.m:16:19>
identifier 'DYZ_INT_64' [StartOfLine] [LeadingSpace] Loc=<main.m:17:9>
identifier 'b' [LeadingSpace] Loc=<main.m:17:20>
equal '=' [LeadingSpace] Loc=<main.m:17:22>
numeric_constant '20' [LeadingSpace] Loc=<main.m:17:24>
semi ';' Loc=<main.m:17:26>
identifier 'printf' [StartOfLine] [LeadingSpace] Loc=<main.m:18:9>
l_paren '(' Loc=<main.m:18:15>
string_literal '"%d\n"' Loc=<main.m:18:16>
comma ',' Loc=<main.m:18:22>
identifier 'a' Loc=<main.m:18:23>
plus '+' [LeadingSpace] Loc=<main.m:18:25>
identifier 'b' [LeadingSpace] Loc=<main.m:18:27>
plus '+' [LeadingSpace] Loc=<main.m:18:29>
numeric_constant '30' [LeadingSpace] Loc=<main.m:18:31 <Spelling=main.m:11:11>>
r_paren ')' Loc=<main.m:18:32>
semi ';' Loc=<main.m:18:33>
r_brace '}' [StartOfLine] [LeadingSpace] Loc=<main.m:19:5>
return 'return' [StartOfLine] [LeadingSpace] Loc=<main.m:20:5>
numeric_constant '0' [LeadingSpace] Loc=<main.m:20:12>
semi ';' Loc=<main.m:20:13>
r_brace '}' [StartOfLine] Loc=<main.m:21:1>
eof '' Loc=<main.m:21:2>
語法分析
詞法分析完成之后就是語法分析浸赫,它的任務是驗證語法是否正確。在詞法分析的基礎上將單詞序列組合成各類語法短語赃绊,如“程序”既峡,“語句”,“表達式”等等碧查,然后將所有節(jié)點組成抽象語法樹(Abstract Syntax Tree, AST)运敢。語法分析程序判斷源程序在結(jié)構上是否正確。
clang -fmodules -fsyntax-only -Xclang -ast-dump main.m
TranslationUnitDecl 0x7fe55c01b608 <<invalid sloc>> <invalid sloc> <undeserialized declarations>
|-TypedefDecl 0x7fe55c01bea0 <<invalid sloc>> <invalid sloc> implicit __int128_t '__int128'
| `-BuiltinType 0x7fe55c01bba0 '__int128'
|-TypedefDecl 0x7fe55c01bf10 <<invalid sloc>> <invalid sloc> implicit __uint128_t 'unsigned __int128'
| `-BuiltinType 0x7fe55c01bbc0 'unsigned __int128'
|-TypedefDecl 0x7fe55c01bfb0 <<invalid sloc>> <invalid sloc> implicit SEL 'SEL *'
| `-PointerType 0x7fe55c01bf70 'SEL *'
| `-BuiltinType 0x7fe55c01be00 'SEL'
|-TypedefDecl 0x7fe55c01c098 <<invalid sloc>> <invalid sloc> implicit id 'id'
| `-ObjCObjectPointerType 0x7fe55c01c040 'id'
| `-ObjCObjectType 0x7fe55c01c010 'id'
|-TypedefDecl 0x7fe55c01c178 <<invalid sloc>> <invalid sloc> implicit Class 'Class'
| `-ObjCObjectPointerType 0x7fe55c01c120 'Class'
| `-ObjCObjectType 0x7fe55c01c0f0 'Class'
|-ObjCInterfaceDecl 0x7fe55c01c1d0 <<invalid sloc>> <invalid sloc> implicit Protocol
|-TypedefDecl 0x7fe55c01c548 <<invalid sloc>> <invalid sloc> implicit __NSConstantString 'struct __NSConstantString_tag'
| `-RecordType 0x7fe55c01c340 'struct __NSConstantString_tag'
| `-Record 0x7fe55c01c2a0 '__NSConstantString_tag'
|-TypedefDecl 0x7fe55c058c00 <<invalid sloc>> <invalid sloc> implicit __builtin_ms_va_list 'char *'
| `-PointerType 0x7fe55c01c5a0 'char *'
| `-BuiltinType 0x7fe55c01b6a0 'char'
|-TypedefDecl 0x7fe55c058ee8 <<invalid sloc>> <invalid sloc> implicit __builtin_va_list 'struct __va_list_tag [1]'
| `-ConstantArrayType 0x7fe55c058e90 'struct __va_list_tag [1]' 1
| `-RecordType 0x7fe55c058cf0 'struct __va_list_tag'
| `-Record 0x7fe55c058c58 '__va_list_tag'
|-ImportDecl 0x7fe55c059710 <main.m:9:1> col:1 implicit Darwin.C.stdio
|-TypedefDecl 0x7fe55c059768 <line:12:1, col:13> col:13 referenced DYZ_INT_64 'int'
| `-BuiltinType 0x7fe55c01b700 'int'
`-FunctionDecl 0x7fe55c059a40 <line:14:1, line:21:1> line:14:5 main 'int (int, const char **)'
|-ParmVarDecl 0x7fe55c0597d8 <col:10, col:14> col:14 argc 'int'
|-ParmVarDecl 0x7fe55c0598f0 <col:20, col:38> col:33 argv 'const char **':'const char **'
`-CompoundStmt 0x7fe55c94eb80 <col:41, line:21:1>
|-ObjCAutoreleasePoolStmt 0x7fe55c94eb38 <line:15:5, line:19:5>
| `-CompoundStmt 0x7fe55c94eb10 <line:15:22, line:19:5>
| |-DeclStmt 0x7fe55c94e420 <line:16:9, col:19>
| | `-VarDecl 0x7fe55c059b90 <col:9, col:17> col:13 used a 'int' cinit
| | `-IntegerLiteral 0x7fe55c94e400 <col:17> 'int' 10
| |-DeclStmt 0x7fe55c94e8d8 <line:17:9, col:26>
| | `-VarDecl 0x7fe55c94e470 <col:9, col:24> col:20 used b 'DYZ_INT_64':'int' cinit
| | `-IntegerLiteral 0x7fe55c94e4d8 <col:24> 'int' 20
| `-CallExpr 0x7fe55c94eab0 <line:18:9, col:32> 'int'
| |-ImplicitCastExpr 0x7fe55c94ea98 <col:9> 'int (*)(const char *, ...)' <FunctionToPointerDecay>
| | `-DeclRefExpr 0x7fe55c94e8f0 <col:9> 'int (const char *, ...)' Function 0x7fe55c94e500 'printf' 'int (const char *, ...)'
| |-ImplicitCastExpr 0x7fe55c94eaf8 <col:16> 'const char *' <NoOp>
| | `-ImplicitCastExpr 0x7fe55c94eae0 <col:16> 'char *' <ArrayToPointerDecay>
| | `-StringLiteral 0x7fe55c94e948 <col:16> 'char [4]' lvalue "%d\n"
| `-BinaryOperator 0x7fe55c94ea48 <col:23, line:11:11> 'int' '+'
| |-BinaryOperator 0x7fe55c94ea08 <line:18:23, col:27> 'int' '+'
| | |-ImplicitCastExpr 0x7fe55c94e9d8 <col:23> 'int' <LValueToRValue>
| | | `-DeclRefExpr 0x7fe55c94e968 <col:23> 'int' lvalue Var 0x7fe55c059b90 'a' 'int'
| | `-ImplicitCastExpr 0x7fe55c94e9f0 <col:27> 'DYZ_INT_64':'int' <LValueToRValue>
| | `-DeclRefExpr 0x7fe55c94e9a0 <col:27> 'DYZ_INT_64':'int' lvalue Var 0x7fe55c94e470 'b' 'DYZ_INT_64':'int'
| `-IntegerLiteral 0x7fe55c94ea28 <line:11:11> 'int' 30
`-ReturnStmt 0x7fe55c94eb70 <line:20:5, col:12>
`-IntegerLiteral 0x7fe55c94eb50 <col:12> 'int' 0
如果導入頭文件找不到忠售,那么可以指定SDK
clang -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator12.2.sdk(自己SDK路徑) -fmodules -fsyntax-only -Xclang -ast-dump main.m
clang -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator12.2.sdk(自己SDK路徑) -fmodules -fsyntax-only -Xclang -ast-dump main.m
生成中間代碼IR(intermediate representation)
完成以上步驟后就開始生成中間代碼IR了传惠,代碼生成器(Code Generation)會將語法樹自頂向下遍歷逐步編譯成LLVM IR。通過下面命令可以生成.ll的文本文件稻扬,查看IR代碼卦方。
clang -S -fobjc-arc -emit-llvm main.m
Objective C代碼在這一步會進行runtime的橋接:property合成,ARC處理等
IR的基本語法
@ 全局標識
% 局部標識
alloca 開辟空間
align 內(nèi)存對齊
i32 32個bit泰佳,4個字節(jié)
store 寫入內(nèi)存
load 讀取數(shù)據(jù)
call 調(diào)用函數(shù)
ret 返回
IR的優(yōu)化
LLVM的優(yōu)化級別分別是 -O0 -O1 -O2 -O3 -Os(第一個是大寫英文字母O)
clang -Os -S -fobjc-arc -emit-llvm main.m //生成main.ll
clang -Os -S -fobjc-arc -emit-llvm main.m -o main1.ll //生成main1.ll
對應Xcode中的優(yōu)化級別選擇
bitCode
xcode7 以后開啟bitcode蘋果會做進一步的優(yōu)化愿汰。生成.bc的中間代碼困后。
我們通過優(yōu)化后的IR代碼生成.bc代碼
clang -emit-llvm -c main.ll -o main.bc
生成匯編代碼
我們通過最終的.bc或者.ll 代碼生成匯編代碼
clang -S -fobjc-arc main.bc -o main.s
clang -S -fobjc-arc main.ll -o main.s
生成匯編代碼也可以進行優(yōu)化
clang -Os -S -fobjc-arc main.m -o main.s
生成目標文件(匯編器)
目標文件的生成,是匯編以匯編代碼作為輸入衬廷,將匯編代碼轉(zhuǎn)換為機器代碼摇予,最后輸出目標文件(object file)。
clang -fmodules -c main.s -o main.o
通過nm命令吗跋,查看下main.o中的符號
$xcrun nm -nm main.o
(undefined) external _printf
0000000000000000 (__TEXT,__text) external _test
0000000000000020 (__TEXT,__text) external _main
_printf是一個是undefined external侧戴。
undefined表示在當前文件暫時找不到符號_printf
external表示這個符號是外部可以訪問的。
生成可執(zhí)行文件(鏈接)
連接器把編譯產(chǎn)生的.o文件和(.dylib .a)文件跌宛,生成一個mach-o文件(黑框框)酗宋。
clang main.o -o main
查看鏈接之后的符號
$xcrun nm -nm main
(undefined) external _printf (from libSystem)
(undefined) external dyld_stub_binder (from libSystem)
0000000100000000 (__TEXT,__text) [referenced dynamically] external __mh_execute_header
0000000100000f20 (__TEXT,__text) external _test
0000000100000f40 (__TEXT,__text) external _main
0000000100002008 (__DATA,__data) non-external __dyld_private
三、Clang插件
LLVM下載
由于國內(nèi)的網(wǎng)絡限制疆拘,我們需要借助鏡像下載LLVM的源碼
https://mirror.tuna.tsinghua.edu.cn/help/llvm/
- 下載llvm項目
git clone https://mirrors.tuna.tsinghua.edu.cn/git/llvm/llvm.git
如果網(wǎng)速不好蜕猫,就下載不下來“テ可以周末來公司下載回右。- 在LLVM的tools目錄下下載Clang
cd llvm/tools
git clone https://mirrors.tuna.tsinghua.edu.cn/git/llvm/clang.git
- 在LLVM的projects目錄下下載compiler-rt,libcxx漱挚,libcxxabi
cd ../projects
git clone https://mirrors.tuna.tsinghua.edu.cn/git/llvm/compiler-rt.git
git clone https://mirrors.tuna.tsinghua.edu.cn/git/llvm/libcxx.git
git clone https://mirrors.tuna.tsinghua.edu.cn/git/llvm/libcxxabi.git
- 在Clang的tools下安裝extra工具
cd ../tools/clang/tools
git clone https://mirrors.tuna.tsinghua.edu.cn/git/llvm/clang-tools-extra.git
LLVM編譯
由于最新的LLVM只支持cmake來編譯了翔烁,我們還需要安裝cmake。
安裝 cmake
- 查看brew是否安裝cmake如果有就跳過下面步驟
brew list
- 通過brew安裝cmake
brew install cmake
編譯 LLVM
通過 xcode 編譯 LLVM
- cmake編譯成Xcode項目
mkdir build_xcode //創(chuàng)建一個build_xcode文件夾
cd build_xcode
cmake -G Xcode ../llvm //cmake -G Xcode llvm的路徑 //編譯成Xcode項目
- 使用Xcode編譯Clang
-
自動創(chuàng)建Schemes旨涝,比較耗時蹬屹,所以一般選擇手動管理
-
自動創(chuàng)建Schemes旨涝,比較耗時蹬屹,所以一般選擇手動管理
-
編譯,選擇clang 進行編譯白华,編譯的時間會比較長慨默。編譯完成之后就可以Show in Finder,查看clang弧腥。
然后再選擇libclang編譯一下业筏。
選中任意一個文件夾 command+A 全選,然后command+鼠標左鍵是反選鸟赫,然后去掉command蒜胖,此時鍵盤的方向鍵左右是合并和展開(前提是選中)
通過 ninja 編譯LLVM
- 使用ninja進行編譯則還需要安裝ninja。使用
brew install ninja
命令即可安裝ninja抛蚤。 - 在llvm源碼根目錄下新建一個build_ninja目錄台谢,最終會在build_ninja目錄下生成build.ninja。
- 在llvm源碼根目錄下新建一個llvm_release目錄岁经,最終編譯文件會在llvm_release文件夾路徑下朋沮。
cd llvm_build
cmake -G Ninja ../llvm -DCMAKE_INSTALL_PREFIX=安裝路徑(本機為/Users/xxx/xxx/LLVM/llvm_release,注意CMAKE_INSTALL_PREFIX后面不能有空格。)
- 依次執(zhí)行編譯樊拓、安裝指令纠亚。
ninja
ninja install
創(chuàng)建插件
-
在
/llvm/tools/clang/tools
目錄下新建插件DYZPlugin
-
修改
/llvm/tools/clang/tools
目錄下的CMakeLists.txt
文件,新增add_clang_subdirectory(DYZPlugin)
筋夏。 在
DYZPlugin
目錄下新建一個名為DYZPlugin.cpp
(終端指令創(chuàng)建 touch DYZPlugin.cpp)的文件和CMakeLists.txt
的文件蒂胞。在CMakeLists.txt
中寫上
add_llvm_library( DYZPlugin MODULE BUILDTREE_ONLY
DYZPlugin.cpp
)
接下來利用
cmake
重新生成一個Xcode項目,在build_xcode
中cmake -G Xcode ../llvm
-
打開
build_xcode
項目 -
最后可以在LLVM的Xcode項目中可以看到
Loadable modules
目錄下有自己的Plugin目錄了条篷。我們可以在里面編寫插件代碼骗随。
編寫插件代碼
#include <iostream>
#include "clang/AST/AST.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Frontend/FrontendPluginRegistry.h"
using namespace clang;
using namespace std;
using namespace llvm;
using namespace clang::ast_matchers;
namespace DYZPlugin {
class DYZMatchCallback: public MatchFinder::MatchCallback {
private:
CompilerInstance &CI;
//判斷是否是自己的文件
bool isUserSourceCode(const string filename) {
if (filename.empty()) return false;
// 非Xcode中的源碼都認為是用戶源碼
if (filename.find("/Applications/Xcode.app/") == 0) return false;
return true;
}
//判斷是否應該用copy修飾。
bool isShouldUseCopy(const string typeStr) {
if (typeStr.find("NSString") != string::npos ||
typeStr.find("NSArray") != string::npos ||
typeStr.find("NSDictionary") != string::npos/*...*/) {
return true;
}
return false;
}
public:
DYZMatchCallback(CompilerInstance &CI):CI(CI){}
void run(const MatchFinder::MatchResult &Result){
//通過結(jié)果獲取到節(jié)點赴叹。
const ObjCPropertyDecl *propertyDecl = Result.Nodes.getNodeAs<ObjCPropertyDecl>("objcPropertyDecl");
//獲取文件名稱
string filename = CI.getSourceManager().getFilename(propertyDecl->getSourceRange().getBegin()).str();
if (propertyDecl && isUserSourceCode(filename)) {//如果節(jié)點有值,并且是用戶文件
//拿到屬性的類型
string typeStr = propertyDecl->getType().getAsString();
//拿到節(jié)點的描述信息
ObjCPropertyDecl::PropertyAttributeKind attrKind = propertyDecl->getPropertyAttributes();
//判斷是不是應該用Copy
if (isShouldUseCopy(typeStr) && !(attrKind & ObjCPropertyDecl::OBJC_PR_copy)) {
cout<<typeStr<<"應該用copy修飾而沒用Copy鸿染,發(fā)出警告!"<<endl;
//診斷引擎
DiagnosticsEngine &diag = CI.getDiagnostics();
//Report 報告
diag.Report(propertyDecl->getBeginLoc(),diag.getCustomDiagID(DiagnosticsEngine::Warning, "%0這個地方推薦用Copy"))<<typeStr;
}
}
}
};
//自定義的DYZConsumer
class DYZConsumer: public ASTConsumer {
private:
MatchFinder matcher;
DYZMatchCallback callback;
public:
DYZConsumer(CompilerInstance &CI):callback(CI){
//添加一個MatchFinder去匹objcPropertyDecl節(jié)點
//回調(diào)在DYZMatchCallback的run方法里面乞巧。
matcher.addMatcher(objcPropertyDecl().bind("objcPropertyDecl"),&callback);
}
// 在整個文件都解析完后被調(diào)用
void HandleTranslationUnit(ASTContext &context) {
cout<<"解析完畢了涨椒!"<<endl;
matcher.matchAST(context);
}
};
//繼承PluginASTAction實現(xiàn)我們自定義的 Action
class DYZASTAction: public PluginASTAction {
public:
bool ParseArgs(const CompilerInstance &CI,const vector<string> &arg){
return true;
}
unique_ptr <ASTConsumer> CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
return unique_ptr <DYZConsumer> (new DYZConsumer(CI));
}
};
}
//注冊插件
static FrontendPluginRegistry::Add<DYZPlugin::DYZASTAction> X("DYZPlugin", "This is the description of the plugin");
測試插件
-
自己編譯的clang文件路徑
-isysroot/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator12.2.sdk
-Xclang -load -Xclang插件(.dylib)路徑
-Xclang -add-plugin -Xclang 插件名 -c 源碼路徑 - 鏈接完成之后生成.o文件
Xcode集成插件
加載插件
打開測試項目,在Build Settings -> Other C Flags 添加如下內(nèi)容:
-Xclang -load -Xclang (.dylib)動態(tài)庫路徑 -Xclang -add-plugin -Xclang DYZPlugin
設置編譯器
-
由于Clang插件需要使用對應的版本去加載绽媒,如果版本不一致則會導致編譯錯誤蚕冬,會出現(xiàn)如下圖所示
-
在Build Settings欄目中新增兩項用戶定義的設置
-
分別是CC和CXX
CXX對應的是自己編譯的clang++的絕對路徑
CC對應的是自己編譯的clang的絕對路徑
-
接下來在Build Settings欄目中搜索index,將Enable Index-While-Building Functionality的Default改為NO些椒。