如何在Clang工具中调用编译器并让其重新解析修改后的源码
解决方案:在Clang中集成libtooling修改工具,无需保存源码直接编译
你的核心需求是跳过磁盘写入,让Clang直接使用Rewriter修改后的源码进行编译,关键在于替换Clang的输入源,而非让它重新读取磁盘文件。以下是几种可行的实现方式:
1. 替换输入的MemoryBuffer(最直接方案)
Clang的编译流程依赖MemoryBuffer读取源码内容,只要在解析前替换这个缓冲区的内容,后续所有编译阶段都会使用修改后的代码:
- 自定义
FrontendAction,重写BeginSourceFile方法:bool BeginSourceFileAction(CompilerInstance &CI, StringRef Filename) override { // 获取当前的SourceManager和Rewriter SourceManager &SM = CI.getSourceManager(); Rewriter TheRewriter; TheRewriter.setSourceMgr(SM, CI.getLangOpts()); // 执行你的Matchers+Rewriters修改逻辑 applyYourCustomRewrites(TheRewriter); // 提取修改后的源码字符串 std::string ModifiedCode; raw_string_ostream OS(ModifiedCode); TheRewriter.getEditBuffer(SM.getMainFileID()).write(OS); OS.flush(); // 创建新的内存缓冲区 std::unique_ptr<MemoryBuffer> NewBuffer = MemoryBuffer::getMemBuffer(ModifiedCode, Filename); // 替换原文件的内容 SM.overrideFileContents(SM.getFileEntryForID(SM.getMainFileID()), std::move(NewBuffer)); return FrontendAction::BeginSourceFileAction(CI, Filename); } - 这样Clang后续的解析、语义分析、代码生成都会基于修改后的MemoryBuffer,完全不需要写入磁盘。
2. AST修改后重新触发编译流程(针对AST后修改场景)
如果你的修改是在AST构建完成后(比如在HandleTranslationUnit中通过Matchers修改),可以重新启动一个编译实例,直接传入修改后的源码:
void HandleTranslationUnit(ASTContext &Context) override { Rewriter TheRewriter; TheRewriter.setSourceMgr(Context.getSourceManager(), Context.getLangOpts()); // 执行你的AST修改逻辑 applyYourCustomRewrites(TheRewriter); // 提取修改后的代码 std::string ModifiedCode; raw_string_ostream OS(ModifiedCode); TheRewriter.getEditBuffer(Context.getSourceManager().getMainFileID()).write(OS); OS.flush(); // 复用原编译参数,创建新的CompilerInvocation CompilerInvocation NewInvocation = Context.getCompilerInstance().getInvocation(); // 设置新的输入为修改后的内存缓冲区 NewInvocation.getFrontendOpts().Inputs.clear(); NewInvocation.getFrontendOpts().Inputs.push_back( FrontendInputFile(MemoryBuffer::getMemBuffer(ModifiedCode)->getMemBufferRef(), NewInvocation.getFrontendOpts().Inputs[0].getKind())); // 创建新的CompilerInstance并执行编译 CompilerInstance NewCI; NewCI.setInvocation(std::make_unique<CompilerInvocation>(std::move(NewInvocation))); NewCI.createDiagnostics(); if (!NewCI.hasDiagnostics()) return; std::unique_ptr<FrontendAction> Action = createDefaultFrontendAction(); if (!NewCI.ExecuteAction(*Action)) { // 处理编译错误 } }
- 注意要确保新的编译实例继承原有的所有编译参数(头文件路径、宏定义、优化等级等),避免编译环境不一致。
关键注意事项
- SourceLocation映射:修改后的代码要保证SourceLocation的正确性,避免后续诊断信息(如错误、警告)出现位置偏移。
- 避免无限循环:如果你的修改逻辑会重复触发匹配条件,需要添加标记(比如全局变量或AST节点标记),确保只执行一次修改。
- 多文件处理:如果项目包含多个源文件,需要逐个替换对应文件的MemoryBuffer,或者在编译流程的早期统一拦截所有输入文件。
内容的提问来源于stack exchange,提问作者D_D
相关产品推荐
相关产品推荐

