blob: bba71f23674a4cf00673309785e9190076c19a6e [file] [log] [blame]
Daniel Jasper7fd90b02012-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 Jasper7fd90b02012-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"
20#include "llvm/Support/system_error.h"
21
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 Collingbourneb88d9482013-03-02 06:00:16 +000052 if (!parseDoubleQuotedStringInto(String)) return false;
53 } else if (*Position == '\'') {
54 if (!parseSingleQuotedStringInto(String)) return false;
Daniel Jasper7fd90b02012-08-24 05:50:27 +000055 } else {
56 if (!parseFreeStringInto(String)) return false;
57 }
58 } while (*Position != ' ');
59 return true;
60 }
61
Peter Collingbourneb88d9482013-03-02 06:00:16 +000062 bool parseDoubleQuotedStringInto(std::string &String) {
Daniel Jasper7fd90b02012-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 Collingbourneb88d9482013-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 Jasper7fd90b02012-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 Collingbourneb88d9482013-03-02 06:00:16 +000086 } while (*Position != ' ' && *Position != '"' && *Position != '\'');
Daniel Jasper7fd90b02012-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 Jasper7fd90b02012-08-24 05:50:27 +0000120class JSONCompilationDatabasePlugin : public CompilationDatabasePlugin {
Stephen Hines651f13c2014-04-23 16:59:28 -0700121 CompilationDatabase *loadFromDirectory(StringRef Directory,
122 std::string &ErrorMessage) override {
Dmitri Gribenkocfa88f82013-01-12 19:30:44 +0000123 SmallString<1024> JSONDatabasePath(Directory);
Daniel Jasper7fd90b02012-08-24 05:50:27 +0000124 llvm::sys::path::append(JSONDatabasePath, "compile_commands.json");
Stephen Hines651f13c2014-04-23 16:59:28 -0700125 std::unique_ptr<CompilationDatabase> Database(
Daniel Jasper7fd90b02012-08-24 05:50:27 +0000126 JSONCompilationDatabase::loadFromFile(JSONDatabasePath, ErrorMessage));
127 if (!Database)
Stephen Hines6bcf27b2014-05-29 04:14:42 -0700128 return nullptr;
Stephen Hines651f13c2014-04-23 16:59:28 -0700129 return Database.release();
Daniel Jasper7fd90b02012-08-24 05:50:27 +0000130 }
131};
132
Craig Topperbc540252013-07-01 06:34:58 +0000133} // end namespace
134
Daniel Jasper7fd90b02012-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 Takumi916978a2012-08-24 10:39:28 +0000142volatile int JSONAnchorSource = 0;
Daniel Jasper7fd90b02012-08-24 05:50:27 +0000143
144JSONCompilationDatabase *
145JSONCompilationDatabase::loadFromFile(StringRef FilePath,
146 std::string &ErrorMessage) {
Stephen Hines651f13c2014-04-23 16:59:28 -0700147 std::unique_ptr<llvm::MemoryBuffer> DatabaseBuffer;
Daniel Jasper7fd90b02012-08-24 05:50:27 +0000148 llvm::error_code Result =
149 llvm::MemoryBuffer::getFile(FilePath, DatabaseBuffer);
Stephen Hines6bcf27b2014-05-29 04:14:42 -0700150 if (Result != nullptr) {
Daniel Jasper7fd90b02012-08-24 05:50:27 +0000151 ErrorMessage = "Error while opening JSON database: " + Result.message();
Stephen Hines6bcf27b2014-05-29 04:14:42 -0700152 return nullptr;
Daniel Jasper7fd90b02012-08-24 05:50:27 +0000153 }
Stephen Hines651f13c2014-04-23 16:59:28 -0700154 std::unique_ptr<JSONCompilationDatabase> Database(
155 new JSONCompilationDatabase(DatabaseBuffer.release()));
Daniel Jasper7fd90b02012-08-24 05:50:27 +0000156 if (!Database->parse(ErrorMessage))
Stephen Hines6bcf27b2014-05-29 04:14:42 -0700157 return nullptr;
Stephen Hines651f13c2014-04-23 16:59:28 -0700158 return Database.release();
Daniel Jasper7fd90b02012-08-24 05:50:27 +0000159}
160
161JSONCompilationDatabase *
162JSONCompilationDatabase::loadFromBuffer(StringRef DatabaseString,
163 std::string &ErrorMessage) {
Stephen Hines651f13c2014-04-23 16:59:28 -0700164 std::unique_ptr<llvm::MemoryBuffer> DatabaseBuffer(
Daniel Jasper7fd90b02012-08-24 05:50:27 +0000165 llvm::MemoryBuffer::getMemBuffer(DatabaseString));
Stephen Hines651f13c2014-04-23 16:59:28 -0700166 std::unique_ptr<JSONCompilationDatabase> Database(
167 new JSONCompilationDatabase(DatabaseBuffer.release()));
Daniel Jasper7fd90b02012-08-24 05:50:27 +0000168 if (!Database->parse(ErrorMessage))
Stephen Hines6bcf27b2014-05-29 04:14:42 -0700169 return nullptr;
Stephen Hines651f13c2014-04-23 16:59:28 -0700170 return Database.release();
Daniel Jasper7fd90b02012-08-24 05:50:27 +0000171}
172
173std::vector<CompileCommand>
174JSONCompilationDatabase::getCompileCommands(StringRef FilePath) const {
Dmitri Gribenkocfa88f82013-01-12 19:30:44 +0000175 SmallString<128> NativeFilePath;
Daniel Jasper7fd90b02012-08-24 05:50:27 +0000176 llvm::sys::path::native(FilePath, NativeFilePath);
Stephen Hines651f13c2014-04-23 16:59:28 -0700177
Daniel Jasperd3420c92012-10-08 16:08:15 +0000178 std::string Error;
179 llvm::raw_string_ostream ES(Error);
180 StringRef Match = MatchTrie.findEquivalent(NativeFilePath.str(), ES);
Arnaud A. de Grandmaison9cd506b2013-01-12 18:37:52 +0000181 if (Match.empty())
Daniel Jasperd3420c92012-10-08 16:08:15 +0000182 return std::vector<CompileCommand>();
Daniel Jasper7fd90b02012-08-24 05:50:27 +0000183 llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator
Daniel Jasperd3420c92012-10-08 16:08:15 +0000184 CommandsRefI = IndexByFile.find(Match);
Daniel Jasper7fd90b02012-08-24 05:50:27 +0000185 if (CommandsRefI == IndexByFile.end())
186 return std::vector<CompileCommand>();
Daniel Jasper7fd90b02012-08-24 05:50:27 +0000187 std::vector<CompileCommand> Commands;
Argyrios Kyrtzidis7e96bfb2012-12-04 07:26:44 +0000188 getCommands(CommandsRefI->getValue(), Commands);
Daniel Jasper7fd90b02012-08-24 05:50:27 +0000189 return Commands;
190}
191
192std::vector<std::string>
193JSONCompilationDatabase::getAllFiles() const {
194 std::vector<std::string> Result;
195
196 llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator
197 CommandsRefI = IndexByFile.begin();
198 const llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator
199 CommandsRefEnd = IndexByFile.end();
200 for (; CommandsRefI != CommandsRefEnd; ++CommandsRefI) {
201 Result.push_back(CommandsRefI->first().str());
202 }
203
204 return Result;
205}
206
Argyrios Kyrtzidis7e96bfb2012-12-04 07:26:44 +0000207std::vector<CompileCommand>
208JSONCompilationDatabase::getAllCompileCommands() const {
209 std::vector<CompileCommand> Commands;
210 for (llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator
211 CommandsRefI = IndexByFile.begin(), CommandsRefEnd = IndexByFile.end();
212 CommandsRefI != CommandsRefEnd; ++CommandsRefI) {
213 getCommands(CommandsRefI->getValue(), Commands);
214 }
215 return Commands;
216}
217
218void JSONCompilationDatabase::getCommands(
219 ArrayRef<CompileCommandRef> CommandsRef,
220 std::vector<CompileCommand> &Commands) const {
221 for (int I = 0, E = CommandsRef.size(); I != E; ++I) {
Dmitri Gribenkocfa88f82013-01-12 19:30:44 +0000222 SmallString<8> DirectoryStorage;
223 SmallString<1024> CommandStorage;
Argyrios Kyrtzidis7e96bfb2012-12-04 07:26:44 +0000224 Commands.push_back(CompileCommand(
225 // FIXME: Escape correctly:
226 CommandsRef[I].first->getValue(DirectoryStorage),
227 unescapeCommandLine(CommandsRef[I].second->getValue(CommandStorage))));
228 }
229}
230
Daniel Jasper7fd90b02012-08-24 05:50:27 +0000231bool JSONCompilationDatabase::parse(std::string &ErrorMessage) {
232 llvm::yaml::document_iterator I = YAMLStream.begin();
233 if (I == YAMLStream.end()) {
234 ErrorMessage = "Error while parsing YAML.";
235 return false;
236 }
237 llvm::yaml::Node *Root = I->getRoot();
Stephen Hines6bcf27b2014-05-29 04:14:42 -0700238 if (!Root) {
Daniel Jasper7fd90b02012-08-24 05:50:27 +0000239 ErrorMessage = "Error while parsing YAML.";
240 return false;
241 }
Dmitri Gribenkocfa88f82013-01-12 19:30:44 +0000242 llvm::yaml::SequenceNode *Array = dyn_cast<llvm::yaml::SequenceNode>(Root);
Stephen Hines6bcf27b2014-05-29 04:14:42 -0700243 if (!Array) {
Daniel Jasper7fd90b02012-08-24 05:50:27 +0000244 ErrorMessage = "Expected array.";
245 return false;
246 }
247 for (llvm::yaml::SequenceNode::iterator AI = Array->begin(),
248 AE = Array->end();
249 AI != AE; ++AI) {
Dmitri Gribenkocfa88f82013-01-12 19:30:44 +0000250 llvm::yaml::MappingNode *Object = dyn_cast<llvm::yaml::MappingNode>(&*AI);
Stephen Hines6bcf27b2014-05-29 04:14:42 -0700251 if (!Object) {
Daniel Jasper7fd90b02012-08-24 05:50:27 +0000252 ErrorMessage = "Expected object.";
253 return false;
254 }
Stephen Hines6bcf27b2014-05-29 04:14:42 -0700255 llvm::yaml::ScalarNode *Directory = nullptr;
256 llvm::yaml::ScalarNode *Command = nullptr;
257 llvm::yaml::ScalarNode *File = nullptr;
Daniel Jasper7fd90b02012-08-24 05:50:27 +0000258 for (llvm::yaml::MappingNode::iterator KVI = Object->begin(),
259 KVE = Object->end();
260 KVI != KVE; ++KVI) {
261 llvm::yaml::Node *Value = (*KVI).getValue();
Stephen Hines6bcf27b2014-05-29 04:14:42 -0700262 if (!Value) {
Daniel Jasper7fd90b02012-08-24 05:50:27 +0000263 ErrorMessage = "Expected value.";
264 return false;
265 }
266 llvm::yaml::ScalarNode *ValueString =
Dmitri Gribenkocfa88f82013-01-12 19:30:44 +0000267 dyn_cast<llvm::yaml::ScalarNode>(Value);
Stephen Hines6bcf27b2014-05-29 04:14:42 -0700268 if (!ValueString) {
Daniel Jasper7fd90b02012-08-24 05:50:27 +0000269 ErrorMessage = "Expected string as value.";
270 return false;
271 }
272 llvm::yaml::ScalarNode *KeyString =
Dmitri Gribenkocfa88f82013-01-12 19:30:44 +0000273 dyn_cast<llvm::yaml::ScalarNode>((*KVI).getKey());
Stephen Hines6bcf27b2014-05-29 04:14:42 -0700274 if (!KeyString) {
Daniel Jasper7fd90b02012-08-24 05:50:27 +0000275 ErrorMessage = "Expected strings as key.";
276 return false;
277 }
Dmitri Gribenkocfa88f82013-01-12 19:30:44 +0000278 SmallString<8> KeyStorage;
Daniel Jasper7fd90b02012-08-24 05:50:27 +0000279 if (KeyString->getValue(KeyStorage) == "directory") {
280 Directory = ValueString;
281 } else if (KeyString->getValue(KeyStorage) == "command") {
282 Command = ValueString;
283 } else if (KeyString->getValue(KeyStorage) == "file") {
284 File = ValueString;
285 } else {
286 ErrorMessage = ("Unknown key: \"" +
287 KeyString->getRawValue() + "\"").str();
288 return false;
289 }
290 }
291 if (!File) {
292 ErrorMessage = "Missing key: \"file\".";
293 return false;
294 }
295 if (!Command) {
296 ErrorMessage = "Missing key: \"command\".";
297 return false;
298 }
299 if (!Directory) {
300 ErrorMessage = "Missing key: \"directory\".";
301 return false;
302 }
Dmitri Gribenkocfa88f82013-01-12 19:30:44 +0000303 SmallString<8> FileStorage;
Daniel Jasperd3420c92012-10-08 16:08:15 +0000304 StringRef FileName = File->getValue(FileStorage);
Dmitri Gribenkocfa88f82013-01-12 19:30:44 +0000305 SmallString<128> NativeFilePath;
Daniel Jasperd3420c92012-10-08 16:08:15 +0000306 if (llvm::sys::path::is_relative(FileName)) {
Dmitri Gribenkocfa88f82013-01-12 19:30:44 +0000307 SmallString<8> DirectoryStorage;
308 SmallString<128> AbsolutePath(
Daniel Jasperd3420c92012-10-08 16:08:15 +0000309 Directory->getValue(DirectoryStorage));
310 llvm::sys::path::append(AbsolutePath, FileName);
311 llvm::sys::path::native(AbsolutePath.str(), NativeFilePath);
312 } else {
313 llvm::sys::path::native(FileName, NativeFilePath);
314 }
Daniel Jasper7fd90b02012-08-24 05:50:27 +0000315 IndexByFile[NativeFilePath].push_back(
Daniel Jasperd3420c92012-10-08 16:08:15 +0000316 CompileCommandRef(Directory, Command));
317 MatchTrie.insert(NativeFilePath.str());
Daniel Jasper7fd90b02012-08-24 05:50:27 +0000318 }
319 return true;
320}
321
322} // end namespace tooling
323} // end namespace clang