如何告诉 clang AST 工具在哪里可以找到 stddef.h?

问题描述 投票:0回答:1

我有一个基本的 clang AST 访问者(在 chatGPT 的帮助下构建)

#include <iostream>
#include <llvm/Support/CommandLine.h>
#include <clang/AST/ASTConsumer.h>
#include <clang/ASTMatchers/ASTMatchers.h>
#include <clang/ASTMatchers/ASTMatchFinder.h>
#include <clang/AST/RecordLayout.h>
#include <clang/AST/RecursiveASTVisitor.h>
#include <clang/Frontend/CompilerInstance.h>
#include <clang/Frontend/FrontendAction.h>
#include <clang/Frontend/FrontendActions.h>
#include <clang/Rewrite/Frontend/FrontendActions.h>
#include <clang/Tooling/CommonOptionsParser.h>
#include <clang/Tooling/Tooling.h>

using namespace clang;
using namespace clang::ast_matchers;
using namespace clang::tooling;

static llvm::cl::OptionCategory MyToolCategory("my-tool options");

class OffsetOfVisitor : public RecursiveASTVisitor<OffsetOfVisitor> {
public:
    explicit OffsetOfVisitor(ASTContext *Context)
        : Context(Context) {}

    bool VisitFieldDecl(FieldDecl *FD) {
        const RecordDecl *Parent = FD->getParent();

        std::string FieldName = FD->getNameAsString();
        if (FieldName.empty()) {
            return true;
        }

        std::string ParentName;
        if (const TypedefNameDecl *TND = Parent->getTypedefNameForAnonDecl()) {
            ParentName = TND->getNameAsString();
        }

        if (ParentName.empty()) {
            return true;
        }

        llvm::outs() << ParentName << ' ' << FieldName << "\n";

        return true;
    }

private:
    ASTContext *Context;
};

class OffsetOfConsumer : public ASTConsumer {
public:
    explicit OffsetOfConsumer(ASTContext *Context)
        : Visitor(Context) {}

    void HandleTranslationUnit(ASTContext &Context) override {
        Visitor.TraverseDecl(Context.getTranslationUnitDecl());
    }

private:
    OffsetOfVisitor Visitor;
};

class OffsetOfAction : public ASTFrontendAction {
public:
    std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, StringRef File) override {
        return std::make_unique<OffsetOfConsumer>(&CI.getASTContext());
    }
};

int main(int argc, const char **argv) {
    auto ExpectedParser = CommonOptionsParser::create(argc, argv, MyToolCategory);
    if (!ExpectedParser) {
        return 1;
    }

    clang::tooling::CommonOptionsParser &OptionsParser = ExpectedParser.get();
    clang::tooling::ClangTool Tool(OptionsParser.getCompilations(), OptionsParser.getSourcePathList());

    int result = Tool.run(newFrontendActionFactory<OffsetOfAction>().get());

    return result;
}

// vim: et ts=4 sw=4

但是当我运行该工具时,它无法找到一些依赖的包含文件:

# ../visitor t2.cpp
In file included from /home/pjoot/vv/test/t2.cpp:1:
/usr/include/stdio.h:33:10: fatal error: 'stddef.h' file not found
   33 | #include <stddef.h>
      |          ^~~~~~~~~~
__fsid_t __val
__mbstate_t __count
__mbstate_t __value
1 error generated.
Error while processing /home/pjoot/vv/test/t2.cpp.

如果我运行 clang 预处理器,我会看到 stddef.h 在 clang 特定路径中找到:

# 28 "/usr/include/stdio.h" 2 3 4

extern "C" {

# 1 "/usr/bin/../lib/clang/17/include/stddef.h" 1 3 4

因此我可以通过使用附加包含路径调用该工具来解决此故障,如下所示:

../visitor t2.cpp -- -I /usr/bin/../lib/clang/17/include

两个问题:

  1. 如何以编程方式添加此附加 -I 路径到工具,以便每次调用都不必传递它?
  2. 更好:有没有办法让 clang AST 访问者自动使用 clang 本身会使用的所有相同的包含路径?

编辑:

斯科特的答案有效。 我添加了一个 make 规则来将我的工具和所有依赖的头文件复制到适当的目录结构中:

> make install
rm -rf tool
mkdir -p tool/bin tool/lib/clang/17
install visitor tool/bin/
cp -a /usr/bin/../lib/clang/17/include tool/lib/clang/17/include

> find tool -name stddef.h
tool/lib/clang/17/include/stddef.h

> cd test

> ../tool/bin/visitor t2.cpp
__fsid_t __val
__mbstate_t __count
__mbstate_t __value

我想我也可以使用符号链接来避免复制 220 个文件。 在我的 makefile 中对内部 clang 路径进行硬编码有点难看,但我可以忍受。

clang abstract-syntax-tree
1个回答
1
投票

虽然措辞完全不同,但问题 Bash 调用的 Clang 和 ClangTool 之间有什么区别? 解决了相同的根本问题,并且其 answer(由我写)是相关的。

我不确定这是否应该被视为重复的问题(部分原因是另一个问题的标题有些误导性),所以现在我只引用答案的关键部分:

解析 C++ 需要的不仅仅是一个可以读取 C++ 语法的程序。它还需要某些逻辑上属于编译器而不是 C 库的头文件; stddef.h 是前者之一,而(比如说)stdio.h 是后者之一。如果您使用 LibTooling 创建解析 C++ 的可执行文件,那么如果没有编译器标头,它是不完整的,就像缺少艺术资源的视频游戏可执行文件一样。

并且:

正确的方法是让你的工具有一个“安装”步骤或类似的步骤,将 Clang 编译器头和可执行文件复制到后者运行的位置。您需要复制 lib/clang/$(version)/include 中的每个文件。

链接的问题后来被编辑以添加一个

CMakelists.txt
片段来进行复制,尽管我还没有测试它。

一旦正确的标头与可执行文件一起安装,它将能够在解析过程中找到并使用它们。

链接的答案(以及其他几个类似的 Q+As 它链接到的答案)还建议了使用

clang
编译器头而不复制它们的方法,但这充其量只是在工具开发过程中有用的一种 hack。

© www.soinside.com 2019 - 2024. All rights reserved.