由于国内的网络限制,我们需要借助镜像下载LLVM的代码,这边推荐使用清华的镜像:https://mirror.tuna.tsinghua.edu.cn/help/llvm/
一、准备工作:
1.1 下载llvm项目
git clone https://mirrors.tuna.tsinghua.edu.cn/git/llvm/llvm.git
1.2 在llvm的 tools 目录下下载 Clang
cd llvm/tools
git clone https://mirrors.tuna.tsinghua.edu.cn/git/llvm/clang.git
1.3 在llvm的 projects 目录下下载 compiler-rt、libcxx、libcxxabi
cd ../projects
git clone https://mirrors.tuna.tsinghua.edu.cn/git/llvm/compiler-rt.g it
git clone https://mirrors.tuna.tsinghua.edu.cn/git/llvm/libcxx.git git clone https://mirrors.tuna.tsinghua.edu.cn/git/llvm/libcxxabi.git
1.4 在Clang的 tools 下安装 ertra 工具
cd ../tools/clang/tools
git clone https://mirrors.tuna.tsinghua.edu.cn/git/llvm/clang-tools-e xtra.git
二、编译LLVM工程
2.1 安装 Cmake
brew install cmake
2.2 cmake编译生成Xcode项目
mkdir build_xcode
cd build_xcode
cmake -G Xcode ../llvm
2.3 打开生成的工程,并选择 Manually Manage Schemes
2.4 我们添加 clang 和 libclang,并一一编译
三、创建Clang插件
3.1 在 llvm/tools/clang/tools
目录下新建插件 CHJPlugin
3.2 修改 llvm/tools/clang/tools
目录下的 CMakeLists.text 文件,新增 add_clang_subdirectory(CHJPlugin)
3.3 在 CHJPlugin 目录下新建一个名为 CHJPlugin.cpp
的文件和 CMakeLists.text
的文件,并在 CMakeLists.text 中写上
add_llvm_library( CHJPlugin MODULE BUILDTREE_ONLY
CHJPlugin.cpp
)
CHJPlugin目录下添加文件.png
3.4 接下来就是利用 cmake 重新生成 Xcode 项目,在 build_xcode 中 cmake -G Xcode ../llvm
四、一个简单插件 CHJPlugin.cpp 的实现:
#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 CHJPlugin {
//Cosumer :专门解析我们语法树中的节点
class CHJCosumer : public ASTConsumer {
public:
//解析完一个顶级的声明就会来这里执行!
bool HandleTopLevelDecl(DeclGroupRef D) {
cout<<"正在解析..."<<endl;
return true;
}
//解析完整个文件后调用
void HandleTranslationUnit(ASTContext &Ctx) {
cout<<"文件解析完毕!"<<endl;
}
};
//继承PluginASTAction实现我们自定义的Action
class CHJASTAction: public PluginASTAction{
public:
//直接 return 一个 true 就可以,不用管
bool ParseArgs(const CompilerInstance &CI,
const std::vector<std::string> &arg){
return true;
}
//自定义返回一个CHJCosumer
unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
StringRef InFile){
return unique_ptr<CHJCosumer>(new CHJCosumer);
}
};
}
//注册插件
static FrontendPluginRegistry::Add<CHJPlugin::CHJASTAction>
X("CHJPlugin", "This is the description of the plugin");
自定义插件编码流程:
- 注册插件
FrontendPluginRegistry::Add
- 自定义一个 Action
PluginASTAction
- 实现
ParseArgs
- 创建一个 Cosumer(
CreateASTConsumer
) - 自定义 Cosumer内容(
class CHJCosumer : public * ASTConsumer
) - 解析顶级节点
HandleTopLevelDecl
- 解析完实现
HandleTranslationUnit
将要被编译的.m:
int sum(int a);
int a;
int sum(int a) {
int b = 10;
return 10 + b;
}
int sum2(int a,int b) {
int c = 10;
return a + b + 10;
}
注意:这里面只有 4 个顶级(最外面的)节点
终端打印:
xxxx/clang(自己编译的clang文件路径) -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator13.1.sdk(SDK地址) -Xclang -load -Xclang xxx/CHJPlugin.dylib(自己的插件.dylib地址) -Xclang -add-plugin -Xclang CHJPlugin(插件名) -c xxxx.m(源码路径)
正在解析...
正在解析...
正在解析...
正在解析...
文件解析完毕!
格式为:
自己编译的clang文件路径
-isysrootSDK地址
-Xclang -load -Xclang自己的插件.dylib地址
-Xclang -add-plugin -Xclang插件名
-c源码路径
注意点:当两个-Xclang
出现的时候,是为了把处于中间的指令传给link
。如:-Xclang -load -Xclang
是为了把load
传给link
。
小结:
-
Cosumer
:专门解析我们语法树中的节点 -
HandleTopLevelDecl
:解析语法树中的 顶级 节点 -
HandleTranslationUnit
:解析完整个文件后执行 -
AST
:代表语法树
网友评论