You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 04:20:31