四、LLVM 源码下载
- 4.1、下载LLVM 和 下载clang
- 创建一个文件夹 LLVM_ALL ,其他的名字也可以
- 先下载 LLVM 到 LLVM_ALL 文件夹下 ,大小 648.2 M,仅供参考
git clone https://github.com/llvm-mirror/llvm.git
- 再下载clang,大小 240.6 M,仅供参考,放到
/LLVM_ALL /llvm/tools
目录下
// clang 放到 /LLVM_ALL /llvm/tools 目录下 cd llvm/tools // 下载 clang git clone https://github.com/llvm-mirror/clang.git
- 4.2、源码编译
- 安装
cmake
和ninja
(先安装brew,https://brew.sh/)
brew install cmake brew install ninja
提示:如果 ninja 如果安装失败,可以直接从github获取release版放入【
/usr/local/bin
】中
在 llvm
源码同级目录下新建一个【llvm_build】目录(最终会在【llvm_build】目录下生成【build.ninja】)
cd llvm_build
cmake -G Ninja ../llvm -DCMAKE_INSTALL_PREFIX=llvm的安装路径
提示:更多cmake相关选项,可以参考: https://llvm.org/docs/CMake.html
- 依次执行编译、安装指令
- 编译完毕后, 【llvm_build】目录大概 21.05 G(仅供参考)
ninja
- 安装完毕后,安装目录大概 11.92 G(仅供参考)
ninja install
- 4.3、如果不想按照
4.2
的编译方式,也可以生成Xcode项目再进行编译,但是速度很慢(可能需要1个多小时)
- 在 llvm 同级目录下新建一个【llvm_xcode】目录
cd llvm_xcode cmake -G Xcode ../llvm
五、应用与实践
- 5.1、libclang、libTooling
官方参考:https://clang.llvm.org/docs/Tooling.html
应用:语法树分析、语言转换等 - 5.2、 Clang插件开发
官方参考
https://clang.llvm.org/docs/ClangPlugins.html
https://clang.llvm.org/docs/ExternalClangExamples.html
https://clang.llvm.org/docs/RAVFrontendAction.html
应用:代码检查(命名规范、代码规范)等 - 5.3、Pass开发
官方参考:https://llvm.org/docs/WritingAnLLVMPass.html
应用:代码优化、代码混淆等 - 5.4、开发新的编程语言
https://llvm-tutorial-cn.readthedocs.io/en/latest/index.html
https://kaleidoscope-llvm-tutorial-zh-cn.readthedocs.io/zh_CN/latest/
六、clang插件开发
- 6.1、clang插件开发1 – 插件目录
- 在
clang/tools
源码目录下新建一个插件目录,假设叫做mj-plugin
在 clang/tools/CMakeLists.txt
最后加入内容: add_clang_subdirectory(mj-plugin)
,小括号里是 插件目录名
6.2、插件必要文件
- 在
mj-plugin
下创建MJPlugin.cpp
文件,插件使用 C++ 编写
touch MJPlugin.cpp
- 在
mj-plugin
里面也要有一份CMakeLists.txt
文件,里面写清插件需要加载哪些 C++ 代码,文件内容如下
add_llvm_loadable_module(MJPlugin MJPlugin.cpp)
提示:如果多个 C++ 文件,可以如下
6.3、clang插件开发3 – 编写插件源码
在 llvm 同级目录下新建一个【llvm_xcode】目录
cd llvm_xcode cmake -G Xcode ../llvm
找到我们的插件文件进行开发,如下
具体的源码
#include <iostream> #include "clang/AST/AST.h" #include "clang/AST/ASTConsumer.h" #include "clang/ASTMatchers/ASTMatchers.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendPluginRegistry.h" using namespace clang; using namespace std; using namespace llvm; using namespace clang::ast_matchers; namespace MJPlugin { class MJHandler : public MatchFinder::MatchCallback { private: CompilerInstance &ci; public: MJHandler(CompilerInstance &ci) :ci(ci) {} void run(const MatchFinder::MatchResult &Result) { if (const ObjCInterfaceDecl *decl = Result.Nodes.getNodeAs<ObjCInterfaceDecl>("ObjCInterfaceDecl")) { size_t pos = decl->getName().find('_'); if (pos != StringRef::npos) { DiagnosticsEngine &D = ci.getDiagnostics(); SourceLocation loc = decl->getLocation().getLocWithOffset(pos); D.Report(loc, D.getCustomDiagID(DiagnosticsEngine::Error, "M了个J:类名中不能带有下划线")); } } } }; class MJASTConsumer: public ASTConsumer { private: MatchFinder matcher; MJHandler handler; public: MJASTConsumer(CompilerInstance &ci) :handler(ci) { matcher.addMatcher(objcInterfaceDecl().bind("ObjCInterfaceDecl"), &handler); } void HandleTranslationUnit(ASTContext &context) { matcher.matchAST(context); } }; class MJASTAction: public PluginASTAction { public: unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &ci, StringRef iFile) { return unique_ptr<MJASTConsumer> (new MJASTConsumer(ci)); } bool ParseArgs(const CompilerInstance &ci, const vector<string> &args) { return true; } }; } // 注册插件 // 左边是插件的名称,右边是插件的描述,X 可以随便写,官方使用的是 X static FrontendPluginRegistry::Add<MJPlugin::MJAction> X("MJPlugin", "The MJPlugin is my first clang-plugin.");
- 6.4、clang插件开发4 – 编译插件
- 利用cmake生成的Xcode项目来编译插件(第一次编写完插件,需要利用cmake重新生成一下Xcode项目)
- 插件源代码在【Sources/Loadable modules】目录下可以找到,这样就可以直接在Xcode里编写插件代码
- 选择MJPlugin这个target进行编译,编译完会生成一个动态库文件
6.5、clang插件开发5 – 加载插件
- 在Xcode项目中指定加载插件动态库:
Build Settings
>OTHER_CFLAGS
-Xclang -load -Xclang 动态库路径 -Xclang -add-plugin -Xclang 插件名称
6.6、clang插件开发6 – Hack Xcode (Xcode 破解 由于制作Xcode插件)
- 首先要对Xcode进行Hack,才能修改默认的编译器
- 下载【XcodeHacking.zip】,解压,修改
HackedClang.xcplugin/Contents/Resources/HackedClang.xcspec
】的内容,设
置一下自己编译好的clang的路径
- 然后在XcodeHacking目录下进行命令行,将XcodeHacking的内容剪切到Xcode内部
// 命令一 sudo mv HackedClang.xcplugin `xcode-select-print�path`/../PlugIns/Xcode3Core.ideplugin/Contents/SharedSupport/Developer/Library/Xcode/Plug-ins // 命令二 sudo mv HackedBuildSystem.xcspec `xcode-select-print�path`/Platforms/iPhoneSimulator.platform/Developer/Library/Xcode/Specifications
- 6.7、clang插件开发7 – 修改Xcode的编译器,修改位置如下,使用我们自己编译的 clang
6.8、clang插件开发8 – 编译项目
编译项目后,会在编译日志看到MJPlugin插件的打印信息(如果插件更新了,最好先Clean一下项目)
6.9、clang插件开发9 – 更多
想要实现更复杂的插件功能,就需要利用clang的API针对语法树(AST)进行相应的分析和处理