blob: 299fbdc149bfb2fefe538ee64c9062b3913d282b [file] [log] [blame]
Daniel Jasper6ed1f852012-08-24 05:50:27 +00001//===--- 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"
Daniel Jasper6ed1f852012-08-24 05:50:27 +000015#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"
Rafael Espindola8a8e5542014-06-12 17:19:42 +000020#include <system_error>
Daniel Jasper6ed1f852012-08-24 05:50:27 +000021
22namespace clang {
23namespace tooling {
24
25namespace {
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(...)).
31class CommandLineArgumentParser {
32 public:
33 CommandLineArgumentParser(StringRef CommandLine)
34 : Input(CommandLine), Position(Input.begin()-1) {}
35
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
49 bool parseStringInto(std::string &String) {
50 do {
51 if (*Position == '"') {
Peter Collingbournefe7a3482013-03-02 06:00:16 +000052 if (!parseDoubleQuotedStringInto(String)) return false;
53 } else if (*Position == '\'') {
54 if (!parseSingleQuotedStringInto(String)) return false;
Daniel Jasper6ed1f852012-08-24 05:50:27 +000055 } else {
56 if (!parseFreeStringInto(String)) return false;
57 }
58 } while (*Position != ' ');
59 return true;
60 }
61
Peter Collingbournefe7a3482013-03-02 06:00:16 +000062 bool parseDoubleQuotedStringInto(std::string &String) {
Daniel Jasper6ed1f852012-08-24 05:50:27 +000063 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
Peter Collingbournefe7a3482013-03-02 06:00:16 +000072 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
Daniel Jasper6ed1f852012-08-24 05:50:27 +000081 bool parseFreeStringInto(std::string &String) {
82 do {
83 if (!skipEscapeCharacter()) return false;
84 String.push_back(*Position);
85 if (!next()) return false;
Peter Collingbournefe7a3482013-03-02 06:00:16 +000086 } while (*Position != ' ' && *Position != '"' && *Position != '\'');
Daniel Jasper6ed1f852012-08-24 05:50:27 +000087 return true;
88 }
89
90 bool skipEscapeCharacter() {
91 if (*Position == '\\') {
92 return next();
93 }
94 return true;
95 }
96
97 bool nextNonWhitespace() {
98 do {
99 if (!next()) return false;
100 } while (*Position == ' ');
101 return true;
102 }
103
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
114std::vector<std::string> unescapeCommandLine(
115 StringRef EscapedCommandLine) {
116 CommandLineArgumentParser parser(EscapedCommandLine);
117 return parser.parse();
118}
119
Daniel Jasper6ed1f852012-08-24 05:50:27 +0000120class JSONCompilationDatabasePlugin : public CompilationDatabasePlugin {
David Blaikiecdba84c2014-08-08 16:06:15 +0000121 std::unique_ptr<CompilationDatabase>
122 loadFromDirectory(StringRef Directory, std::string &ErrorMessage) override {
Dmitri Gribenkof8579502013-01-12 19:30:44 +0000123 SmallString<1024> JSONDatabasePath(Directory);
Daniel Jasper6ed1f852012-08-24 05:50:27 +0000124 llvm::sys::path::append(JSONDatabasePath, "compile_commands.json");
Ahmed Charlesb8984322014-03-07 20:03:18 +0000125 std::unique_ptr<CompilationDatabase> Database(
Daniel Jasper6ed1f852012-08-24 05:50:27 +0000126 JSONCompilationDatabase::loadFromFile(JSONDatabasePath, ErrorMessage));
127 if (!Database)
Craig Topperccbc35e2014-05-20 04:51:16 +0000128 return nullptr;
David Blaikiecdba84c2014-08-08 16:06:15 +0000129 return Database;
Daniel Jasper6ed1f852012-08-24 05:50:27 +0000130 }
131};
132
Craig Topper69b62772013-07-01 06:34:58 +0000133} // end namespace
134
Daniel Jasper6ed1f852012-08-24 05:50:27 +0000135// Register the JSONCompilationDatabasePlugin with the
136// CompilationDatabasePluginRegistry using this statically initialized variable.
137static CompilationDatabasePluginRegistry::Add<JSONCompilationDatabasePlugin>
138X("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.
NAKAMURA Takumid574ac22012-08-24 10:39:28 +0000142volatile int JSONAnchorSource = 0;
Daniel Jasper6ed1f852012-08-24 05:50:27 +0000143
David Blaikiecdba84c2014-08-08 16:06:15 +0000144std::unique_ptr<JSONCompilationDatabase>
Daniel Jasper6ed1f852012-08-24 05:50:27 +0000145JSONCompilationDatabase::loadFromFile(StringRef FilePath,
146 std::string &ErrorMessage) {
Rafael Espindola2d2b4202014-07-06 17:43:24 +0000147 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> DatabaseBuffer =
148 llvm::MemoryBuffer::getFile(FilePath);
149 if (std::error_code Result = DatabaseBuffer.getError()) {
Daniel Jasper6ed1f852012-08-24 05:50:27 +0000150 ErrorMessage = "Error while opening JSON database: " + Result.message();
Craig Topperccbc35e2014-05-20 04:51:16 +0000151 return nullptr;
Daniel Jasper6ed1f852012-08-24 05:50:27 +0000152 }
Ahmed Charlesb8984322014-03-07 20:03:18 +0000153 std::unique_ptr<JSONCompilationDatabase> Database(
David Blaikieb29bb452014-08-08 22:01:06 +0000154 new JSONCompilationDatabase(std::move(*DatabaseBuffer)));
Daniel Jasper6ed1f852012-08-24 05:50:27 +0000155 if (!Database->parse(ErrorMessage))
Craig Topperccbc35e2014-05-20 04:51:16 +0000156 return nullptr;
David Blaikiecdba84c2014-08-08 16:06:15 +0000157 return Database;
Daniel Jasper6ed1f852012-08-24 05:50:27 +0000158}
159
David Blaikiecdba84c2014-08-08 16:06:15 +0000160std::unique_ptr<JSONCompilationDatabase>
Daniel Jasper6ed1f852012-08-24 05:50:27 +0000161JSONCompilationDatabase::loadFromBuffer(StringRef DatabaseString,
162 std::string &ErrorMessage) {
Ahmed Charlesb8984322014-03-07 20:03:18 +0000163 std::unique_ptr<llvm::MemoryBuffer> DatabaseBuffer(
Daniel Jasper6ed1f852012-08-24 05:50:27 +0000164 llvm::MemoryBuffer::getMemBuffer(DatabaseString));
Ahmed Charlesb8984322014-03-07 20:03:18 +0000165 std::unique_ptr<JSONCompilationDatabase> Database(
David Blaikieb29bb452014-08-08 22:01:06 +0000166 new JSONCompilationDatabase(std::move(DatabaseBuffer)));
Daniel Jasper6ed1f852012-08-24 05:50:27 +0000167 if (!Database->parse(ErrorMessage))
Craig Topperccbc35e2014-05-20 04:51:16 +0000168 return nullptr;
David Blaikiecdba84c2014-08-08 16:06:15 +0000169 return Database;
Daniel Jasper6ed1f852012-08-24 05:50:27 +0000170}
171
172std::vector<CompileCommand>
173JSONCompilationDatabase::getCompileCommands(StringRef FilePath) const {
Dmitri Gribenkof8579502013-01-12 19:30:44 +0000174 SmallString<128> NativeFilePath;
Daniel Jasper6ed1f852012-08-24 05:50:27 +0000175 llvm::sys::path::native(FilePath, NativeFilePath);
Alp Toker965f8822013-11-27 05:22:15 +0000176
Daniel Jasper26cf9c42012-10-08 16:08:15 +0000177 std::string Error;
178 llvm::raw_string_ostream ES(Error);
Yaron Keren92e1b622015-03-18 10:17:07 +0000179 StringRef Match = MatchTrie.findEquivalent(NativeFilePath, ES);
Arnaud A. de Grandmaison3128a112013-01-12 18:37:52 +0000180 if (Match.empty())
Daniel Jasper26cf9c42012-10-08 16:08:15 +0000181 return std::vector<CompileCommand>();
Daniel Jasper6ed1f852012-08-24 05:50:27 +0000182 llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator
Daniel Jasper26cf9c42012-10-08 16:08:15 +0000183 CommandsRefI = IndexByFile.find(Match);
Daniel Jasper6ed1f852012-08-24 05:50:27 +0000184 if (CommandsRefI == IndexByFile.end())
185 return std::vector<CompileCommand>();
Daniel Jasper6ed1f852012-08-24 05:50:27 +0000186 std::vector<CompileCommand> Commands;
Argyrios Kyrtzidis251ad5e2012-12-04 07:26:44 +0000187 getCommands(CommandsRefI->getValue(), Commands);
Daniel Jasper6ed1f852012-08-24 05:50:27 +0000188 return Commands;
189}
190
191std::vector<std::string>
192JSONCompilationDatabase::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
Argyrios Kyrtzidis251ad5e2012-12-04 07:26:44 +0000206std::vector<CompileCommand>
207JSONCompilationDatabase::getAllCompileCommands() const {
208 std::vector<CompileCommand> Commands;
Argyrios Kyrtzidis64f67be2015-09-22 17:22:33 +0000209 getCommands(AllCommands, Commands);
Argyrios Kyrtzidis251ad5e2012-12-04 07:26:44 +0000210 return Commands;
211}
212
Manuel Klimek3ecd8c02015-09-08 15:14:06 +0000213static std::vector<std::string>
214nodeToCommandLine(const std::vector<llvm::yaml::ScalarNode *> &Nodes) {
215 SmallString<1024> Storage;
216 if (Nodes.size() == 1) {
217 return unescapeCommandLine(Nodes[0]->getValue(Storage));
218 }
219 std::vector<std::string> Arguments;
220 for (auto *Node : Nodes) {
221 Arguments.push_back(Node->getValue(Storage));
222 }
223 return Arguments;
224}
225
Argyrios Kyrtzidis251ad5e2012-12-04 07:26:44 +0000226void JSONCompilationDatabase::getCommands(
Manuel Klimek3ecd8c02015-09-08 15:14:06 +0000227 ArrayRef<CompileCommandRef> CommandsRef,
228 std::vector<CompileCommand> &Commands) const {
Argyrios Kyrtzidis251ad5e2012-12-04 07:26:44 +0000229 for (int I = 0, E = CommandsRef.size(); I != E; ++I) {
Dmitri Gribenkof8579502013-01-12 19:30:44 +0000230 SmallString<8> DirectoryStorage;
Argyrios Kyrtzidis74bcd212015-09-11 20:43:05 +0000231 SmallString<32> FilenameStorage;
232 Commands.emplace_back(
233 std::get<0>(CommandsRef[I])->getValue(DirectoryStorage),
234 std::get<1>(CommandsRef[I])->getValue(FilenameStorage),
235 nodeToCommandLine(std::get<2>(CommandsRef[I])));
Argyrios Kyrtzidis251ad5e2012-12-04 07:26:44 +0000236 }
237}
238
Daniel Jasper6ed1f852012-08-24 05:50:27 +0000239bool JSONCompilationDatabase::parse(std::string &ErrorMessage) {
240 llvm::yaml::document_iterator I = YAMLStream.begin();
241 if (I == YAMLStream.end()) {
242 ErrorMessage = "Error while parsing YAML.";
243 return false;
244 }
245 llvm::yaml::Node *Root = I->getRoot();
Craig Topperccbc35e2014-05-20 04:51:16 +0000246 if (!Root) {
Daniel Jasper6ed1f852012-08-24 05:50:27 +0000247 ErrorMessage = "Error while parsing YAML.";
248 return false;
249 }
Dmitri Gribenkof8579502013-01-12 19:30:44 +0000250 llvm::yaml::SequenceNode *Array = dyn_cast<llvm::yaml::SequenceNode>(Root);
Craig Topperccbc35e2014-05-20 04:51:16 +0000251 if (!Array) {
Daniel Jasper6ed1f852012-08-24 05:50:27 +0000252 ErrorMessage = "Expected array.";
253 return false;
254 }
Manuel Klimek54042e72015-08-14 09:55:36 +0000255 for (auto& NextObject : *Array) {
256 llvm::yaml::MappingNode *Object = dyn_cast<llvm::yaml::MappingNode>(&NextObject);
Craig Topperccbc35e2014-05-20 04:51:16 +0000257 if (!Object) {
Daniel Jasper6ed1f852012-08-24 05:50:27 +0000258 ErrorMessage = "Expected object.";
259 return false;
260 }
Craig Topperccbc35e2014-05-20 04:51:16 +0000261 llvm::yaml::ScalarNode *Directory = nullptr;
Manuel Klimek3ecd8c02015-09-08 15:14:06 +0000262 llvm::Optional<std::vector<llvm::yaml::ScalarNode *>> Command;
Craig Topperccbc35e2014-05-20 04:51:16 +0000263 llvm::yaml::ScalarNode *File = nullptr;
Manuel Klimek54042e72015-08-14 09:55:36 +0000264 for (auto& NextKeyValue : *Object) {
265 llvm::yaml::ScalarNode *KeyString =
266 dyn_cast<llvm::yaml::ScalarNode>(NextKeyValue.getKey());
267 if (!KeyString) {
268 ErrorMessage = "Expected strings as key.";
269 return false;
270 }
271 SmallString<10> KeyStorage;
272 StringRef KeyValue = KeyString->getValue(KeyStorage);
273 llvm::yaml::Node *Value = NextKeyValue.getValue();
Craig Topperccbc35e2014-05-20 04:51:16 +0000274 if (!Value) {
Daniel Jasper6ed1f852012-08-24 05:50:27 +0000275 ErrorMessage = "Expected value.";
276 return false;
277 }
278 llvm::yaml::ScalarNode *ValueString =
Dmitri Gribenkof8579502013-01-12 19:30:44 +0000279 dyn_cast<llvm::yaml::ScalarNode>(Value);
Manuel Klimek54042e72015-08-14 09:55:36 +0000280 llvm::yaml::SequenceNode *SequenceString =
281 dyn_cast<llvm::yaml::SequenceNode>(Value);
282 if (KeyValue == "arguments" && !SequenceString) {
283 ErrorMessage = "Expected sequence as value.";
284 return false;
285 } else if (KeyValue != "arguments" && !ValueString) {
Daniel Jasper6ed1f852012-08-24 05:50:27 +0000286 ErrorMessage = "Expected string as value.";
287 return false;
288 }
Manuel Klimek54042e72015-08-14 09:55:36 +0000289 if (KeyValue == "directory") {
Daniel Jasper6ed1f852012-08-24 05:50:27 +0000290 Directory = ValueString;
Manuel Klimek54042e72015-08-14 09:55:36 +0000291 } else if (KeyValue == "arguments") {
Manuel Klimek3ecd8c02015-09-08 15:14:06 +0000292 Command = std::vector<llvm::yaml::ScalarNode *>();
293 for (auto &Argument : *SequenceString) {
294 auto Scalar = dyn_cast<llvm::yaml::ScalarNode>(&Argument);
295 if (!Scalar) {
296 ErrorMessage = "Only strings are allowed in 'arguments'.";
297 return false;
298 }
299 Command->push_back(Scalar);
Manuel Klimek54042e72015-08-14 09:55:36 +0000300 }
Manuel Klimek54042e72015-08-14 09:55:36 +0000301 } else if (KeyValue == "command") {
Manuel Klimek3ecd8c02015-09-08 15:14:06 +0000302 if (!Command)
303 Command = std::vector<llvm::yaml::ScalarNode *>(1, ValueString);
Manuel Klimek54042e72015-08-14 09:55:36 +0000304 } else if (KeyValue == "file") {
Daniel Jasper6ed1f852012-08-24 05:50:27 +0000305 File = ValueString;
306 } else {
307 ErrorMessage = ("Unknown key: \"" +
308 KeyString->getRawValue() + "\"").str();
309 return false;
310 }
311 }
312 if (!File) {
313 ErrorMessage = "Missing key: \"file\".";
314 return false;
315 }
Manuel Klimek3ecd8c02015-09-08 15:14:06 +0000316 if (!Command) {
Manuel Klimek54042e72015-08-14 09:55:36 +0000317 ErrorMessage = "Missing key: \"command\" or \"arguments\".";
Daniel Jasper6ed1f852012-08-24 05:50:27 +0000318 return false;
319 }
320 if (!Directory) {
321 ErrorMessage = "Missing key: \"directory\".";
322 return false;
323 }
Dmitri Gribenkof8579502013-01-12 19:30:44 +0000324 SmallString<8> FileStorage;
Daniel Jasper26cf9c42012-10-08 16:08:15 +0000325 StringRef FileName = File->getValue(FileStorage);
Dmitri Gribenkof8579502013-01-12 19:30:44 +0000326 SmallString<128> NativeFilePath;
Daniel Jasper26cf9c42012-10-08 16:08:15 +0000327 if (llvm::sys::path::is_relative(FileName)) {
Dmitri Gribenkof8579502013-01-12 19:30:44 +0000328 SmallString<8> DirectoryStorage;
329 SmallString<128> AbsolutePath(
Daniel Jasper26cf9c42012-10-08 16:08:15 +0000330 Directory->getValue(DirectoryStorage));
331 llvm::sys::path::append(AbsolutePath, FileName);
Yaron Keren92e1b622015-03-18 10:17:07 +0000332 llvm::sys::path::native(AbsolutePath, NativeFilePath);
Daniel Jasper26cf9c42012-10-08 16:08:15 +0000333 } else {
334 llvm::sys::path::native(FileName, NativeFilePath);
335 }
Argyrios Kyrtzidis64f67be2015-09-22 17:22:33 +0000336 auto Cmd = CompileCommandRef(Directory, File, *Command);
337 IndexByFile[NativeFilePath].push_back(Cmd);
338 AllCommands.push_back(Cmd);
Yaron Keren92e1b622015-03-18 10:17:07 +0000339 MatchTrie.insert(NativeFilePath);
Daniel Jasper6ed1f852012-08-24 05:50:27 +0000340 }
341 return true;
342}
343
344} // end namespace tooling
345} // end namespace clang