1 //===--- JSONCompilationDatabase.cpp - ------------------------------------===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // This file contains the implementation of the JSONCompilationDatabase.
11 //
12 //===----------------------------------------------------------------------===//
13
14 #include "clang/Tooling/JSONCompilationDatabase.h"
15 #include "clang/Tooling/CompilationDatabase.h"
16 #include "clang/Tooling/CompilationDatabasePluginRegistry.h"
17 #include "clang/Tooling/Tooling.h"
18 #include "llvm/ADT/SmallString.h"
19 #include "llvm/Support/Path.h"
20 #include <system_error>
21
22 namespace clang {
23 namespace tooling {
24
25 namespace {
26
27 /// \brief A parser for escaped strings of command line arguments.
28 ///
29 /// Assumes \-escaping for quoted arguments (see the documentation of
30 /// unescapeCommandLine(...)).
31 class CommandLineArgumentParser {
32 public:
CommandLineArgumentParser(StringRef CommandLine)33 CommandLineArgumentParser(StringRef CommandLine)
34 : Input(CommandLine), Position(Input.begin()-1) {}
35
parse()36 std::vector<std::string> parse() {
37 bool HasMoreInput = true;
38 while (HasMoreInput && nextNonWhitespace()) {
39 std::string Argument;
40 HasMoreInput = parseStringInto(Argument);
41 CommandLine.push_back(Argument);
42 }
43 return CommandLine;
44 }
45
46 private:
47 // All private methods return true if there is more input available.
48
parseStringInto(std::string & String)49 bool parseStringInto(std::string &String) {
50 do {
51 if (*Position == '"') {
52 if (!parseDoubleQuotedStringInto(String)) return false;
53 } else if (*Position == '\'') {
54 if (!parseSingleQuotedStringInto(String)) return false;
55 } else {
56 if (!parseFreeStringInto(String)) return false;
57 }
58 } while (*Position != ' ');
59 return true;
60 }
61
parseDoubleQuotedStringInto(std::string & String)62 bool parseDoubleQuotedStringInto(std::string &String) {
63 if (!next()) return false;
64 while (*Position != '"') {
65 if (!skipEscapeCharacter()) return false;
66 String.push_back(*Position);
67 if (!next()) return false;
68 }
69 return next();
70 }
71
parseSingleQuotedStringInto(std::string & String)72 bool parseSingleQuotedStringInto(std::string &String) {
73 if (!next()) return false;
74 while (*Position != '\'') {
75 String.push_back(*Position);
76 if (!next()) return false;
77 }
78 return next();
79 }
80
parseFreeStringInto(std::string & String)81 bool parseFreeStringInto(std::string &String) {
82 do {
83 if (!skipEscapeCharacter()) return false;
84 String.push_back(*Position);
85 if (!next()) return false;
86 } while (*Position != ' ' && *Position != '"' && *Position != '\'');
87 return true;
88 }
89
skipEscapeCharacter()90 bool skipEscapeCharacter() {
91 if (*Position == '\\') {
92 return next();
93 }
94 return true;
95 }
96
nextNonWhitespace()97 bool nextNonWhitespace() {
98 do {
99 if (!next()) return false;
100 } while (*Position == ' ');
101 return true;
102 }
103
next()104 bool next() {
105 ++Position;
106 return Position != Input.end();
107 }
108
109 const StringRef Input;
110 StringRef::iterator Position;
111 std::vector<std::string> CommandLine;
112 };
113
unescapeCommandLine(StringRef EscapedCommandLine)114 std::vector<std::string> unescapeCommandLine(
115 StringRef EscapedCommandLine) {
116 CommandLineArgumentParser parser(EscapedCommandLine);
117 return parser.parse();
118 }
119
120 class JSONCompilationDatabasePlugin : public CompilationDatabasePlugin {
loadFromDirectory(StringRef Directory,std::string & ErrorMessage)121 CompilationDatabase *loadFromDirectory(StringRef Directory,
122 std::string &ErrorMessage) override {
123 SmallString<1024> JSONDatabasePath(Directory);
124 llvm::sys::path::append(JSONDatabasePath, "compile_commands.json");
125 std::unique_ptr<CompilationDatabase> Database(
126 JSONCompilationDatabase::loadFromFile(JSONDatabasePath, ErrorMessage));
127 if (!Database)
128 return nullptr;
129 return Database.release();
130 }
131 };
132
133 } // end namespace
134
135 // Register the JSONCompilationDatabasePlugin with the
136 // CompilationDatabasePluginRegistry using this statically initialized variable.
137 static CompilationDatabasePluginRegistry::Add<JSONCompilationDatabasePlugin>
138 X("json-compilation-database", "Reads JSON formatted compilation databases");
139
140 // This anchor is used to force the linker to link in the generated object file
141 // and thus register the JSONCompilationDatabasePlugin.
142 volatile int JSONAnchorSource = 0;
143
144 JSONCompilationDatabase *
loadFromFile(StringRef FilePath,std::string & ErrorMessage)145 JSONCompilationDatabase::loadFromFile(StringRef FilePath,
146 std::string &ErrorMessage) {
147 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> DatabaseBuffer =
148 llvm::MemoryBuffer::getFile(FilePath);
149 if (std::error_code Result = DatabaseBuffer.getError()) {
150 ErrorMessage = "Error while opening JSON database: " + Result.message();
151 return nullptr;
152 }
153 std::unique_ptr<JSONCompilationDatabase> Database(
154 new JSONCompilationDatabase(DatabaseBuffer->release()));
155 if (!Database->parse(ErrorMessage))
156 return nullptr;
157 return Database.release();
158 }
159
160 JSONCompilationDatabase *
loadFromBuffer(StringRef DatabaseString,std::string & ErrorMessage)161 JSONCompilationDatabase::loadFromBuffer(StringRef DatabaseString,
162 std::string &ErrorMessage) {
163 std::unique_ptr<llvm::MemoryBuffer> DatabaseBuffer(
164 llvm::MemoryBuffer::getMemBuffer(DatabaseString));
165 std::unique_ptr<JSONCompilationDatabase> Database(
166 new JSONCompilationDatabase(DatabaseBuffer.release()));
167 if (!Database->parse(ErrorMessage))
168 return nullptr;
169 return Database.release();
170 }
171
172 std::vector<CompileCommand>
getCompileCommands(StringRef FilePath) const173 JSONCompilationDatabase::getCompileCommands(StringRef FilePath) const {
174 SmallString<128> NativeFilePath;
175 llvm::sys::path::native(FilePath, NativeFilePath);
176
177 std::string Error;
178 llvm::raw_string_ostream ES(Error);
179 StringRef Match = MatchTrie.findEquivalent(NativeFilePath.str(), ES);
180 if (Match.empty())
181 return std::vector<CompileCommand>();
182 llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator
183 CommandsRefI = IndexByFile.find(Match);
184 if (CommandsRefI == IndexByFile.end())
185 return std::vector<CompileCommand>();
186 std::vector<CompileCommand> Commands;
187 getCommands(CommandsRefI->getValue(), Commands);
188 return Commands;
189 }
190
191 std::vector<std::string>
getAllFiles() const192 JSONCompilationDatabase::getAllFiles() const {
193 std::vector<std::string> Result;
194
195 llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator
196 CommandsRefI = IndexByFile.begin();
197 const llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator
198 CommandsRefEnd = IndexByFile.end();
199 for (; CommandsRefI != CommandsRefEnd; ++CommandsRefI) {
200 Result.push_back(CommandsRefI->first().str());
201 }
202
203 return Result;
204 }
205
206 std::vector<CompileCommand>
getAllCompileCommands() const207 JSONCompilationDatabase::getAllCompileCommands() const {
208 std::vector<CompileCommand> Commands;
209 for (llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator
210 CommandsRefI = IndexByFile.begin(), CommandsRefEnd = IndexByFile.end();
211 CommandsRefI != CommandsRefEnd; ++CommandsRefI) {
212 getCommands(CommandsRefI->getValue(), Commands);
213 }
214 return Commands;
215 }
216
getCommands(ArrayRef<CompileCommandRef> CommandsRef,std::vector<CompileCommand> & Commands) const217 void JSONCompilationDatabase::getCommands(
218 ArrayRef<CompileCommandRef> CommandsRef,
219 std::vector<CompileCommand> &Commands) const {
220 for (int I = 0, E = CommandsRef.size(); I != E; ++I) {
221 SmallString<8> DirectoryStorage;
222 SmallString<1024> CommandStorage;
223 Commands.push_back(CompileCommand(
224 // FIXME: Escape correctly:
225 CommandsRef[I].first->getValue(DirectoryStorage),
226 unescapeCommandLine(CommandsRef[I].second->getValue(CommandStorage))));
227 }
228 }
229
parse(std::string & ErrorMessage)230 bool JSONCompilationDatabase::parse(std::string &ErrorMessage) {
231 llvm::yaml::document_iterator I = YAMLStream.begin();
232 if (I == YAMLStream.end()) {
233 ErrorMessage = "Error while parsing YAML.";
234 return false;
235 }
236 llvm::yaml::Node *Root = I->getRoot();
237 if (!Root) {
238 ErrorMessage = "Error while parsing YAML.";
239 return false;
240 }
241 llvm::yaml::SequenceNode *Array = dyn_cast<llvm::yaml::SequenceNode>(Root);
242 if (!Array) {
243 ErrorMessage = "Expected array.";
244 return false;
245 }
246 for (llvm::yaml::SequenceNode::iterator AI = Array->begin(),
247 AE = Array->end();
248 AI != AE; ++AI) {
249 llvm::yaml::MappingNode *Object = dyn_cast<llvm::yaml::MappingNode>(&*AI);
250 if (!Object) {
251 ErrorMessage = "Expected object.";
252 return false;
253 }
254 llvm::yaml::ScalarNode *Directory = nullptr;
255 llvm::yaml::ScalarNode *Command = nullptr;
256 llvm::yaml::ScalarNode *File = nullptr;
257 for (llvm::yaml::MappingNode::iterator KVI = Object->begin(),
258 KVE = Object->end();
259 KVI != KVE; ++KVI) {
260 llvm::yaml::Node *Value = (*KVI).getValue();
261 if (!Value) {
262 ErrorMessage = "Expected value.";
263 return false;
264 }
265 llvm::yaml::ScalarNode *ValueString =
266 dyn_cast<llvm::yaml::ScalarNode>(Value);
267 if (!ValueString) {
268 ErrorMessage = "Expected string as value.";
269 return false;
270 }
271 llvm::yaml::ScalarNode *KeyString =
272 dyn_cast<llvm::yaml::ScalarNode>((*KVI).getKey());
273 if (!KeyString) {
274 ErrorMessage = "Expected strings as key.";
275 return false;
276 }
277 SmallString<8> KeyStorage;
278 if (KeyString->getValue(KeyStorage) == "directory") {
279 Directory = ValueString;
280 } else if (KeyString->getValue(KeyStorage) == "command") {
281 Command = ValueString;
282 } else if (KeyString->getValue(KeyStorage) == "file") {
283 File = ValueString;
284 } else {
285 ErrorMessage = ("Unknown key: \"" +
286 KeyString->getRawValue() + "\"").str();
287 return false;
288 }
289 }
290 if (!File) {
291 ErrorMessage = "Missing key: \"file\".";
292 return false;
293 }
294 if (!Command) {
295 ErrorMessage = "Missing key: \"command\".";
296 return false;
297 }
298 if (!Directory) {
299 ErrorMessage = "Missing key: \"directory\".";
300 return false;
301 }
302 SmallString<8> FileStorage;
303 StringRef FileName = File->getValue(FileStorage);
304 SmallString<128> NativeFilePath;
305 if (llvm::sys::path::is_relative(FileName)) {
306 SmallString<8> DirectoryStorage;
307 SmallString<128> AbsolutePath(
308 Directory->getValue(DirectoryStorage));
309 llvm::sys::path::append(AbsolutePath, FileName);
310 llvm::sys::path::native(AbsolutePath.str(), NativeFilePath);
311 } else {
312 llvm::sys::path::native(FileName, NativeFilePath);
313 }
314 IndexByFile[NativeFilePath].push_back(
315 CompileCommandRef(Directory, Command));
316 MatchTrie.insert(NativeFilePath.str());
317 }
318 return true;
319 }
320
321 } // end namespace tooling
322 } // end namespace clang
323