Newer
Older
//===--- CompilationDatabase.cpp - ----------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file contains multiple implementations for CompilationDatabases.
//
//===----------------------------------------------------------------------===//
#include "clang/Tooling/CompilationDatabase.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/YAMLParser.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/system_error.h"
namespace clang {
namespace tooling {
namespace {
/// \brief A parser for escaped strings of command line arguments.
///
/// Assumes \-escaping for quoted arguments (see the documentation of
/// unescapeCommandLine(...)).
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
class CommandLineArgumentParser {
public:
CommandLineArgumentParser(StringRef CommandLine)
: Input(CommandLine), Position(Input.begin()-1) {}
std::vector<std::string> parse() {
bool HasMoreInput = true;
while (HasMoreInput && nextNonWhitespace()) {
std::string Argument;
HasMoreInput = parseStringInto(Argument);
CommandLine.push_back(Argument);
}
return CommandLine;
}
private:
// All private methods return true if there is more input available.
bool parseStringInto(std::string &String) {
do {
if (*Position == '"') {
if (!parseQuotedStringInto(String)) return false;
} else {
if (!parseFreeStringInto(String)) return false;
}
} while (*Position != ' ');
return true;
}
bool parseQuotedStringInto(std::string &String) {
if (!next()) return false;
while (*Position != '"') {
if (!skipEscapeCharacter()) return false;
String.push_back(*Position);
if (!next()) return false;
}
return next();
}
bool parseFreeStringInto(std::string &String) {
do {
if (!skipEscapeCharacter()) return false;
String.push_back(*Position);
if (!next()) return false;
} while (*Position != ' ' && *Position != '"');
return true;
}
bool skipEscapeCharacter() {
if (*Position == '\\') {
return next();
}
return true;
}
bool nextNonWhitespace() {
do {
if (!next()) return false;
} while (*Position == ' ');
return true;
}
bool next() {
++Position;
return Position != Input.end();
}
const StringRef Input;
StringRef::iterator Position;
std::vector<std::string> CommandLine;
};
std::vector<std::string> unescapeCommandLine(
StringRef EscapedCommandLine) {
CommandLineArgumentParser parser(EscapedCommandLine);
return parser.parse();
}
} // end namespace
CompilationDatabase::~CompilationDatabase() {}
CompilationDatabase *
CompilationDatabase::loadFromDirectory(StringRef BuildDirectory,
std::string &ErrorMessage) {
llvm::SmallString<1024> JSONDatabasePath(BuildDirectory);
llvm::sys::path::append(JSONDatabasePath, "compile_commands.json");
llvm::OwningPtr<CompilationDatabase> Database(
JSONCompilationDatabase::loadFromFile(JSONDatabasePath, ErrorMessage));
if (!Database) {
return NULL;
}
return Database.take();
}
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
FixedCompilationDatabase *
FixedCompilationDatabase::loadFromCommandLine(int &Argc,
const char **Argv,
Twine Directory) {
const char **DoubleDash = std::find(Argv, Argv + Argc, StringRef("--"));
if (DoubleDash == Argv + Argc)
return NULL;
std::vector<std::string> CommandLine(DoubleDash + 1, Argv + Argc);
Argc = DoubleDash - Argv;
return new FixedCompilationDatabase(Directory, CommandLine);
}
FixedCompilationDatabase::
FixedCompilationDatabase(Twine Directory, ArrayRef<std::string> CommandLine) {
std::vector<std::string> ToolCommandLine(1, "clang-tool");
ToolCommandLine.insert(ToolCommandLine.end(),
CommandLine.begin(), CommandLine.end());
CompileCommands.push_back(CompileCommand(Directory, ToolCommandLine));
}
std::vector<CompileCommand>
FixedCompilationDatabase::getCompileCommands(StringRef FilePath) const {
std::vector<CompileCommand> Result(CompileCommands);
Result[0].CommandLine.push_back(FilePath);
return Result;
}
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
JSONCompilationDatabase *
JSONCompilationDatabase::loadFromFile(StringRef FilePath,
std::string &ErrorMessage) {
llvm::OwningPtr<llvm::MemoryBuffer> DatabaseBuffer;
llvm::error_code Result =
llvm::MemoryBuffer::getFile(FilePath, DatabaseBuffer);
if (Result != 0) {
ErrorMessage = "Error while opening JSON database: " + Result.message();
return NULL;
}
llvm::OwningPtr<JSONCompilationDatabase> Database(
new JSONCompilationDatabase(DatabaseBuffer.take()));
if (!Database->parse(ErrorMessage))
return NULL;
return Database.take();
}
JSONCompilationDatabase *
JSONCompilationDatabase::loadFromBuffer(StringRef DatabaseString,
std::string &ErrorMessage) {
llvm::OwningPtr<llvm::MemoryBuffer> DatabaseBuffer(
llvm::MemoryBuffer::getMemBuffer(DatabaseString));
llvm::OwningPtr<JSONCompilationDatabase> Database(
new JSONCompilationDatabase(DatabaseBuffer.take()));
if (!Database->parse(ErrorMessage))
return NULL;
return Database.take();
}
std::vector<CompileCommand>
JSONCompilationDatabase::getCompileCommands(StringRef FilePath) const {

NAKAMURA Takumi
committed
llvm::SmallString<128> NativeFilePath;
llvm::sys::path::native(FilePath, NativeFilePath);
llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator

NAKAMURA Takumi
committed
CommandsRefI = IndexByFile.find(NativeFilePath);
if (CommandsRefI == IndexByFile.end())
return std::vector<CompileCommand>();
const std::vector<CompileCommandRef> &CommandsRef = CommandsRefI->getValue();
std::vector<CompileCommand> Commands;
for (int I = 0, E = CommandsRef.size(); I != E; ++I) {
llvm::SmallString<8> DirectoryStorage;
llvm::SmallString<1024> CommandStorage;
Commands.push_back(CompileCommand(
// FIXME: Escape correctly:
CommandsRef[I].first->getValue(DirectoryStorage),
unescapeCommandLine(CommandsRef[I].second->getValue(CommandStorage))));
}
return Commands;
}
bool JSONCompilationDatabase::parse(std::string &ErrorMessage) {
llvm::yaml::document_iterator I = YAMLStream.begin();
if (I == YAMLStream.end()) {
ErrorMessage = "Error while parsing YAML.";
return false;
}
llvm::yaml::Node *Root = I->getRoot();
ErrorMessage = "Error while parsing YAML.";
llvm::yaml::SequenceNode *Array =
llvm::dyn_cast<llvm::yaml::SequenceNode>(Root);
if (Array == NULL) {
ErrorMessage = "Expected array.";
return false;
}
for (llvm::yaml::SequenceNode::iterator AI = Array->begin(),
AE = Array->end();
llvm::yaml::MappingNode *Object =
llvm::dyn_cast<llvm::yaml::MappingNode>(&*AI);
if (Object == NULL) {
ErrorMessage = "Expected object.";
return false;
}
llvm::yaml::ScalarNode *Directory = NULL;
llvm::yaml::ScalarNode *Command = NULL;
llvm::yaml::ScalarNode *File = NULL;
for (llvm::yaml::MappingNode::iterator KVI = Object->begin(),
KVE = Object->end();
llvm::yaml::Node *Value = (*KVI).getValue();
if (Value == NULL) {
ErrorMessage = "Expected value.";
return false;
}
llvm::yaml::ScalarNode *ValueString =
llvm::dyn_cast<llvm::yaml::ScalarNode>(Value);
if (ValueString == NULL) {
ErrorMessage = "Expected string as value.";
return false;
}
llvm::yaml::ScalarNode *KeyString =
llvm::dyn_cast<llvm::yaml::ScalarNode>((*KVI).getKey());
if (KeyString == NULL) {
ErrorMessage = "Expected strings as key.";
return false;
}
llvm::SmallString<8> KeyStorage;
if (KeyString->getValue(KeyStorage) == "directory") {
Directory = ValueString;
} else if (KeyString->getValue(KeyStorage) == "command") {
Command = ValueString;
} else if (KeyString->getValue(KeyStorage) == "file") {
File = ValueString;
ErrorMessage = ("Unknown key: \"" +
KeyString->getRawValue() + "\"").str();
if (!File) {
ErrorMessage = "Missing key: \"file\".";
return false;
}
if (!Command) {
ErrorMessage = "Missing key: \"command\".";
return false;
}
if (!Directory) {
ErrorMessage = "Missing key: \"directory\".";
return false;
}
llvm::SmallString<8> FileStorage;

NAKAMURA Takumi
committed
llvm::SmallString<128> NativeFilePath;
llvm::sys::path::native(File->getValue(FileStorage), NativeFilePath);
IndexByFile[NativeFilePath].push_back(
CompileCommandRef(Directory, Command));
}
return true;
}
} // end namespace tooling
} // end namespace clang