Clang LibTooling官方给出的教程中给出了直接在LLVM/Clang代码目录下进行工具开发的示例,但这样对于代码管理不甚方便,为此,尝试独立于LLVM代码树开发(即Out-of-Tree) 省流:在编译Clang时,添加CMake选项:-DLLVM_ENABLE_RTTI=ON官方Tutorial分析
官方给出的LibTooling Tutorial大体上已经满足了开发环境的配置 官方Tutorial:https://clang.llvm.org/docs/LibASTMatchersTutorial.html如果使用官方的配置方法,尝试在其他路径下撰写项目,仅在编译时再链接LLVM/Clang库则会出现如下报错:- user@debian:~/my-tool/build$ make
- [ 50%] Linking CXX executable my_tool
- /usr/bin/ld: CMakeFiles/my_tool.dir/src/MyTool.cpp.o:(.data.rel.ro._ZTIZN5clang7tooling24newFrontendActionFactoryINS_12ast_matchers11MatchFinderEEESt10unique_ptrINS0_21FrontendActionFactoryESt14default_deleteIS5_EEPT_PNS0_19SourceFileCallbacksEEN28FrontendActionFactoryAdapter22ConsumerFactoryAdaptorE[_ZTIZN5clang7tooling24newFrontendActionFactoryINS_12ast_matchers11MatchFinderEEESt10unique_ptrINS0_21FrontendActionFactoryESt14default_deleteIS5_EEPT_PNS0_19SourceFileCallbacksEEN28FrontendActionFactoryAdapter22ConsumerFactoryAdaptorE]+0x10): undefined reference to `typeinfo for clang::ASTFrontendAction'
- /usr/bin/ld: CMakeFiles/my_tool.dir/src/MyTool.cpp.o:(.data.rel.ro._ZTIZN5clang7tooling24newFrontendActionFactoryINS_12ast_matchers11MatchFinderEEESt10unique_ptrINS0_21FrontendActionFactoryESt14default_deleteIS5_EEPT_PNS0_19SourceFileCallbacksEE28FrontendActionFactoryAdapter[_ZTIZN5clang7tooling24newFrontendActionFactoryINS_12ast_matchers11MatchFinderEEESt10unique_ptrINS0_21FrontendActionFactoryESt14default_deleteIS5_EEPT_PNS0_19SourceFileCallbacksEE28FrontendActionFactoryAdapter]+0x10): undefined reference to `typeinfo for clang::tooling::FrontendActionFactory'
- /usr/bin/ld: CMakeFiles/my_tool.dir/src/MyTool.cpp.o:(.data.rel.ro._ZTI15FunctionPrinter[_ZTI15FunctionPrinter]+0x10): undefined reference to `typeinfo for clang::ast_matchers::MatchFinder::MatchCallback'
- collect2: error: ld returned 1 exit status
- make[2]: *** [CMakeFiles/my_tool.dir/build.make:156: my_tool] Error 1
- make[1]: *** [CMakeFiles/Makefile2:351: CMakeFiles/my_tool.dir/all] Error 2
- make: *** [Makefile:91: all] Error 2
复制代码 报错分析:这是一个非常典型的C++编译选项不匹配导致的链接错误: undefined reference to 'typeinfo for ...' 意味着代码和链接的库之间 RTTI (Run-Time Type Information) 设置不一致(from Gemini2.5 pro) Out-of-Tree开发环境配置
下面我们梳理Out-of-Tree开发环境配置 Step 0: Obtaining Clang
(与官方Tutorial相同) As Clang is part of the LLVM project, you’ll need to download LLVM’s source code first. Both Clang and LLVM are in the same git repository, under different directories. For further information, see the getting started guide.- mkdir ~/clang-llvm && cd ~/clang-llvmgit clone https://github.com/llvm/llvm-project.git
复制代码 Next, you need to obtain the CMake build system and Ninja build tool.- cd ~/clang-llvm
- git clone https://github.com/martine/ninja.git
- cd ninja
- git checkout release
- ./configure.py --bootstrap
- sudo cp ninja /usr/bin/
- cd ~/clang-llvm
- git clone https://gitlab.kitware.com/cmake/cmake.git
- cd cmake
- ./bootstrap
- make
- sudo make install
复制代码 Step 1: 编译构建Clang
(与官方不同,out-of-tree环境) - cd ~/clang-llvm
- mkdir build && cd build
- cmake -G Ninja ../llvm-project/llvm \
- -DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra" \
- -DCMAKE_BUILD_TYPE=Release \
- -DLLVM_BUILD_TESTS=ON \
- -DCMAKE_INSTALL_PREFIX=/usr/local \ #安装及库文件路径
- -DLLVM_ENABLE_RTTI=ON # out-of-tree关键选项
- ninja
- ninja check # Test LLVM only.
- ninja clang-test # Test Clang only.
复制代码 最后将编译好的clang及其库文件安装至系统简单验证:- # 确认 libclangTooling.a 已被安装到指定位置
- ls /usr/local/lib/libclangTooling.a
- # 输出:/usr/local/lib/libclangTooling.a
复制代码 至此,基础out-of-tree环境基础完成 创建独立的 Out-of-Tree LibTooling 项目
- my-tool/
- ├── build
- ├── CMakeLists.txt #项目CMakeLists
- ├── src
- │ └── MyTool.cpp #项目源代码
- └── test_samples #测试用例目录
- └── test_function_name.c
复制代码可直接复制使用,注意修改地方已经标明 - cmake_minimum_required(VERSION 3.14)
- project(MyTool)
- set(CMAKE_CXX_STANDARD 17)
- find_package(LLVM REQUIRED CONFIG)
- find_package(Clang REQUIRED CONFIG)
- # 添加头文件路径
- include_directories(${LLVM_INCLUDE_DIRS})
- include_directories(${CLANG_INCLUDE_DIRS})
- link_directories(${LLVM_LIBRARY_DIRS})
- # 定义可执行文件
- # ==========注意修改此处1==========
- add_executable(my_tool
- src/MyTool.cpp
- )
- # ==========注意修改此处2==========
- # 链接 Clang 库
- target_link_libraries(my_tool
- PRIVATE
- clangTooling
- clangAST
- clangASTMatchers
- clangBasic
- clangFrontend
- clangSerialization
- clangDriver
- LLVMSupport
- )
复制代码 [code]#include "clang/ASTMatchers/ASTMatchers.h"#include "clang/ASTMatchers/ASTMatchFinder.h"#include "clang/Tooling/CommonOptionsParser.h"#include "clang/Tooling/Tooling.h"#include "llvm/Support/CommandLine.h"// 使用 namespaces 简化代码using namespace clang;using namespace clang::ast_matchers;using namespace clang::tooling;using namespace llvm;// 定义一个 Matcher 来查找所有函数定义(不仅仅是声明)// .bind("func") 将匹配到的节点绑定到名字 "func" 上,方便后续在回调中获取DeclarationMatcher FunctionMatcher = functionDecl(isDefinition()).bind("func");// 定义一个回调类,当 Matcher 找到匹配项时,它的 run 方法会被调用class FunctionPrinter : public MatchFinder::MatchCallback {public: // 重写 run 方法 virtual void run(const MatchFinder::MatchResult &Result) { // 从匹配结果中获取绑定的 "func" 节点 // 并将其转换为 FunctionDecl (函数声明) 类型 if (const FunctionDecl *FD = Result.Nodes.getNodeAs("func")) { // 获取函数名 StringRef FuncName = FD->getName(); // 获取源码位置信息 SourceManager &SM = *Result.SourceManager; SourceLocation FuncLocation = FD->getLocation(); // 使用 llvm: uts()(一个线程安全的cout)打印结果 outs() |