当前位置: 首页 > news >正文

LLVM/Clang Out-of-Tree开发

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及其库文件安装至系统
sudo ninja install
简单验证:
# 确认 libclangTooling.a 已被安装到指定位置
ls /usr/local/lib/libclangTooling.a
# 输出:/usr/local/lib/libclangTooling.a
至此,基础out-of-tree环境基础完成
 

创建独立的 Out-of-Tree LibTooling 项目

  1. 项目结构
my-tool/
├── build         
├── CMakeLists.txt  #项目CMakeLists
├── src
│   └── MyTool.cpp  #项目源代码
└── test_samples    #测试用例目录└── test_function_name.c
  1. CMakeLists.txt
可直接复制使用,注意修改地方已经标明
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_toolPRIVATEclangToolingclangASTclangASTMatchersclangBasicclangFrontendclangSerializationclangDriverLLVMSupport
)
  1. 撰写最小化的工具代码 (MyTool.cpp)
#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<FunctionDecl>("func")) {// 获取函数名StringRef FuncName = FD->getName();// 获取源码位置信息SourceManager &SM = *Result.SourceManager;SourceLocation FuncLocation = FD->getLocation();// 使用 llvm::outs()(一个线程安全的cout)打印结果outs() << "Found function: " << FuncName << " at "<< FuncLocation.printToString(SM) << "\n";}}
};// 为我们的工具设置命令行选项分类
static cl::OptionCategory MyToolCategory("find-functions options");int main(int argc, const char **argv) {// 使用 CommonOptionsParser 解析命令行参数,它会自动处理 -- 和 compile_commands.jsonauto ExpectedParser = CommonOptionsParser::create(argc, argv, MyToolCategory);if (!ExpectedParser) {errs() << ExpectedParser.takeError();return 1;}CommonOptionsParser &OptionsParser = ExpectedParser.get();ClangTool Tool(OptionsParser.getCompilations(), OptionsParser.getSourcePathList());// 创建我们的回调实例FunctionPrinter Printer;MatchFinder Finder;// 将 Matcher 和回调实例注册到 MatchFinder 中Finder.addMatcher(FunctionMatcher, &Printer);// 运行 ClangTool,它会解析AST并触发我们的回调return Tool.run(newFrontendActionFactory(&Finder).get());
}
  1. 测试用例(test_function_name.c)
void helper_function() {// 函数体为空,无任何外部调用
}int add(int a, int b) {return a + b;
}int main(int argc, char **argv) {// 声明一个变量并调用内部函数int result = add(5, 3);helper_function();// (void)result; return 0;
}// 一个只有声明没有定义的函数 
void forward_declared_function();
  1. 编译与运行
编译:
cd my-tool/build
cmake ..
make
运行工具:
 ./my_tool ../test_samples/test_function_name.c  --
应当出现如下结果:
liu@debian:~/code/my-tool/build$ ./my_tool ../test_samples/test_function_name.c  --
Found function: helper_function at /home/liu/code/my-tool/build/../test_samples/test_function_name.c:3:6
Found function: add at /home/liu/code/my-tool/build/../test_samples/test_function_name.c:7:5
Found function: main at /home/liu/code/my-tool/build/../test_samples/test_function_name.c:11:5

附录:

debug方法

  • make VERBOSE=1: 查看CMake执行的完整、真实的编译和链接命令,检查是否有多余的编译标志(如-fno-rtti)。
  • llvm-config --cxxflags --libs: 查看已安装的LLVM环境推荐外部应用使用的编译和链接参数。
  • find / -name "libclangTooling.a": 在整个系统中查找文件,确认其真实位置和名称。
http://www.hskmm.com/?act=detail&tid=10774

相关文章:

  • 基于LlamaIndex的相似性搜索
  • 第二周预习报告(AI)
  • 编写代码时遇到的checkstyle问题归纳
  • .netcore的Lucene.Net基础应用
  • 关于1200模拟量输入滤波的问题
  • 在Ubuntu 16.04上安装openjdk-6/7/8-jdk的步骤
  • yoloV8
  • 插座(SOCKET)
  • kettle从入门到精通 第108课 ETL之kettle 国产麒麟系统安装kettle教程
  • 部署 Squid 代理服务
  • k8s--etcd - 详解
  • HBase 的自带命令行工具 hbase shell 的基本使用
  • 市场交易反心理特征之一:太过完美而不敢买入
  • 3peak DCDC转换芯片选型列表
  • 重塑公司绩效管理的 6 种方法
  • 详细介绍:从“下山”到AI引擎:全面理解梯度下降(上)
  • flask下的MySQL增删改查
  • tips图解数组名与指针的关系
  • mysql查看数据库大小,可以通过查询系统表来实现
  • TPP21206替代MPQ8633,12A电流同步降压DCDC
  • 组件重新装载时 useSWR 会发起请求
  • kettle插件-kettle数据清洗插件,轻松处理脏数据
  • 中二
  • web应用程详解part1
  • Seedream 4.0 简直绝了!
  • 财务管理NPV与IRR投资分析在IT行业案例
  • 优化sigmoid
  • mysql查询死锁,mysql查询死锁方法
  • 【IEEE出版、已连续5届稳定快速EI检索】第六届计算机工程与智能控制学术会议(ICCEIC 2025)
  • 软工第二次作业之个人项目——论文查重