使用Clang LibTooling的最小源码,这是一种非常常见的方式:
#include "pch.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Tooling/CommonOptionsParser.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/Support/CommandLine.h"
#include "clang/Driver/Options.h"
#include "clang/AST/AST.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Frontend/ASTConsumers.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Tooling/CommonOptionsParser.h"
#include "clang/Tooling/Tooling.h"
#include "clang/Rewrite/Core/Rewriter.h"
#include <iostream>
using namespace std;
using namespace clang;
using namespace clang::driver;
using namespace clang::tooling;
using namespace llvm;
class ExampleVisitor : public RecursiveASTVisitor<ExampleVisitor> {
public:
explicit ExampleVisitor(CompilerInstance *CI) {}
};
class ExampleASTConsumer : public ASTConsumer {
private:
CompilerInstance *CI;
public:
explicit ExampleASTConsumer(CompilerInstance *CI) : CI(CI) {}
virtual void HandleTranslationUnit(ASTContext &Context) {
ExampleVisitor(CI).TraverseDecl(Context.getTranslationUnitDecl());
}
};
class ExampleFrontendAction : public ASTFrontendAction {
public:
virtual std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, StringRef file) {
return std::unique_ptr<ASTConsumer>(new ExampleASTConsumer(&CI));
}
};
void run(int argc, const char **argv, llvm::cl::OptionCategory& tc) {
CommonOptionsParser op(argc, argv, tc);
ClangTool Tool(op.getCompilations(), op.getSourcePathList());
std::cout <<"getSourcePathList.size="<< op.getSourcePathList().size()<<"\n";
int result = Tool.run(newFrontendActionFactory<ExampleFrontendAction>().get());
}
int main(int argc, const char **argv) {
llvm::cl::OptionCategory tc1("c1");
llvm::cl::OptionCategory tc2("c2");
llvm::cl::OptionCategory tc3("c3");
run(argc, argv,tc1);
run(argc, argv,tc2);
run(argc, argv,tc3);
std::cin.get();
return 0;
}用于调试应用程序的参数为:
"the_only_source_file_to_scan.cpp" --这很好。
输出是(来自main()上面的"run“方法):
getSourcePathList.size=1
getSourcePathList.size=2
getSourcePathList.size=3问题是,main()调用run() 3次,上面的参数相同,它只包含一个要扫描的源文件,但每次存储在CommonOptionsParser中的要扫描的源文件列表的大小增加1(列表中的每个项目都是从argv输入的相同文件),它似乎每次都会将要扫描的源文件附加到列表中。
在每次运行中,上面的所有内容都保存在新创建的临时变量中,那么LibTooling如何以及为什么保持上次运行的状态,以及如何“重置”这些状态?
发布于 2021-09-01 02:54:14
使用FixedCompilationDatabase可以绕过这个问题,它可以在一个进程中运行多个clangTool
发布于 2021-09-04 08:35:20
上面代码使用CommonOptionsParser,它的代码在
clang\lib\Tooling\CommonOptionsParser.cpp在CommonOptionsParser::init方法中有:
static cl::list<std::string> SourcePaths(...);每次调用都会将其源代码添加到此静态变量中。因此,正是这个局部静态变量导致了对先前调用的记忆。在每次调用中,这个局部静态变量都会被cl::ParseCommandLineOptions以某种未知的方式修改,因为它根本不会传递到cl::ParseCommandLineOptions中。在SourcePaths被修改之后(即当前调用的源代码被添加到可能已经包含先前调用的源代码的SourcePaths中),它最终被复制到一个实例变量中。
顺便说一句,@AbaoZhang的提示帮助我找到了位置,实际上CommonOptionsParser::init内部使用了FixedCompilationDatabase,但不是针对上述情况。
发布于 2021-11-13 14:16:01
static cl::list<std::string> SourcePaths(...);@jw_是的,这段代码出了问题。
你可以解决这个问题。
for (auto iter = llvm::cl::AllSubCommands->OptionsMap.begin(); iter != llvm::cl::AllSubCommands->OptionsMap.end(); iter++)
{
iter->getValue()->setDefault();
}
for (auto iter = llvm::cl::AllSubCommands->PositionalOpts.begin(); iter != llvm::cl::AllSubCommands->PositionalOpts.end(); iter++)
{
(*iter)->setDefault();
}
for (auto iter = llvm::cl::AllSubCommands->SinkOpts.begin(); iter != llvm::cl::AllSubCommands->SinkOpts.end(); iter++)
{
(*iter)->setDefault();
}https://stackoverflow.com/questions/59589065
复制相似问题