I have built a custom tool (based on libtooling) for transforming source code. I used clang's own tutorial and managed to run my own custom FrontendAction. I now need to parse the compile flags that were provided to the tool (on the command line) to customise the transformation. However no matter what I do the CompilationDatabase seems to always return an empty list of compile commands.
This is the sample code:
#include "clang/Tooling/CommonOptionsParser.h"
#include "clang/Tooling/Tooling.h"
static cl::OptionCategory MyToolCategory("CustomTool");
static cl::extrahelp MoreHelp("\nMore help text...");
int main(int argc, const char **argv)
{
CommonOptionsParser op(argc, argv, MyToolCategory); // Parse the command-line arguments
CompilationDatabase &Compilations = op.getCompilations();
for (const auto &cmd: Compilations.getAllCompileCommands()) { //<---- this list is always empty!!!
std::cout << "filename: " << cmd.Filename;
// Do stuff with compile flags
}
ClangTool Tool(Compilations, op.getSourcePathList()); // Create a new Clang Tool instance (a LibTooling environment)
return Tool.run(newFrontendActionFactory<MyASTFrontendAction>().get()); // Run custom Frontendaction
}
This is how I invoke the tool:
./custom-tool sample.c -- -I/some/include/path -std=gnu11
I want to be able to get the command line flags -I/some/include/path
and -std=gnu11
.
Your compilation database (compile_commands.json) is either empty or non-existent. All compile commands are technically all of the commands in the database and, thus, empty.
Clang creates an ad-hoc wrapper around the CompilationDatabase
object that adds those options to whatever file this CompilationDatabase
has been asked about, even when this file is not in the database itself, and even when it doesn't exist.
Instead of iterating compilation commands, you can iterate over the source list and ask (aka getCompileCommands
) the database to give the commands for that particular file.
#include "clang/Tooling/CommonOptionsParser.h"
using namespace clang;
using namespace tooling;
using namespace llvm;
static cl::OptionCategory MyToolCategory("CustomTool");
static cl::extrahelp MoreHelp("\nMore help text...");
void print(const std::vector<CompileCommand> &Commands) {
if (Commands.empty()) {
return;
}
for (auto opt : Commands[0].CommandLine) {
llvm::errs() << "\t" << opt << "\n";
}
}
int main(int argc, const char **argv) {
CommonOptionsParser OP(argc, argv,
MyToolCategory);
CompilationDatabase &Compilations = OP.getCompilations();
for (const auto &File :
OP.getSourcePathList()) {
llvm::errs() << "filename: " << File << "\n";
llvm::errs() << "opts:\n";
auto Commands = Compilations.getCompileCommands(File);
print(Commands);
}
auto CommandsForFakeFile = Compilations.getCompileCommands("baloney.c");
llvm::errs() << "fake filename: baloney.c\n";
print(CommandsForFakeFile);
return 0;
}
The code above generates the following output:
> ./myTool test.cpp -- -I/some/include/path -std=gnu11
filename: test.cpp
opts:
$PATH_TO_MYTOOL_EXEC/clang-tool
-I/some/include/path
-std=gnu11
test.cpp
fake filename: baloney.c
$PATH_TO_MYTOOL_EXEC/clang-tool
-I/some/include/path
-std=gnu11
baloney.c
I hope this information will be useful! Happy hacking with Clang!