kelbon
1
I’m using clang tooling library and I want to do the simplest thing possible - take a source code and preprocess it.
std::string preprocess(std::string code);
After several hours of different attempts and searching the Internet, my code looks like this:
std::string preprocess(std::string code) {
namespace ct = clang::tooling;
llvm::vfs::InMemoryFileSystem fs;
fs.addFile("A.cpp", time(0), llvm::MemoryBuffer::getMemBuffer(code));
fs.addFile("B.cpp", time(0), llvm::MemoryBuffer::getMemBuffer(""));
ct::FixedCompilationDatabase db("", {});
ct::ClangTool tool(db
, {"A.cpp"}
, std::make_shared<clang::PCHContainerOperations>()
, {&fs});
auto add_args = [](const ct::CommandLineArguments& _args,
llvm::StringRef Filename) -> ct::CommandLineArguments {
auto args = _args;
args.push_back("-std=c++20");
args.push_back("-P");
args.push_back("-E");
args.push_back("-o");
args.push_back("B.cpp");
return args;
};
tool.appendArgumentsAdjuster(add_args);
auto factory = ct::newFrontendActionFactory<clang::PreprocessOnlyAction>();
int result = tool.run(&*factory);
if (result != 0)
return {};
llvm::ErrorOr buf = fs.getBufferForFile("B.cpp");
if (!buf)
return {};
return std::string(buf->get()->getBuffer());
}
And its not working. No errors, but no output too. How even to check what compiler invocation sees in A.cpp?
kelbon
2
I had to study the source code inside the clang, in the end the implementation looks like this:
#include <clang/Tooling/Tooling.h>
#include <clang/Frontend/CompilerInstance.h>
// its a copy from clang/Frontend/FrontendActions.h/cpp, but:
// * with output stream customization
// * without modules support (i dont need it)
// * without binary mode, i dont care about \r\n and \n on different systems
struct print_preprocessed_action : clang::PreprocessorFrontendAction {
llvm::raw_ostream& out;
explicit print_preprocessed_action(llvm::raw_ostream& out [[clang::lifetimebound]]) : out(out) {}
bool hasPCHSupport() const override { return false; }
void ExecuteAction() override {
auto& CI = getCompilerInstance();
clang::DoPrintPreprocessedInput(CI.getPreprocessor(), &out, CI.getPreprocessorOutputOpts());
}
};
struct printer_factory : clang::tooling::FrontendActionFactory {
llvm::raw_ostream& out;
explicit printer_factory(llvm::raw_ostream& out [[clang::lifetimebound]]) : out(out) {}
std::unique_ptr<clang::FrontendAction> create() override {
return std::unique_ptr<clang::FrontendAction>(new print_preprocessed_action(out));
}
};
bool preprocess(std::string_view code, llvm::raw_ostream& out) {
// annoying asserts prevent stack creation in any case, even valid cases
llvm::IntrusiveRefCntPtr mfs(new llvm::vfs::InMemoryFileSystem);
mfs->addFile("A.cpp", 0, llvm::MemoryBuffer::getMemBuffer(code));
printer_factory factory(out);
clang::tooling::ToolInvocation invocation(
{"PP-tool", "-std=c++20", "-E", "-P", "A.cpp"}, &factory,
new clang::FileManager(clang::FileSystemOptions(), std::move(mfs)),
std::make_shared<clang::PCHContainerOperations>());
return invocation.run();
}
And usage:
std::string result;
llvm::raw_string_ostream out(result);
preprocess(R"(
#define merge_all_expand2(a, b) a ## b
#define merge_all_expand(a, b) merge_all_expand2(a, b)
#define concat_all(head, ...) merge_all_expand(head, __VA_OPT__(__THIS_MACRO__(__VA_ARGS__)))
0: concat_all(aa, bb, cc)
1: [concat_all()])",
out);
std::cout << result;