blob: 25284cf11f2ff66683a62939af3ac54eb6e298da [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 == '"') {
52 if (!parseQuotedStringInto(String)) return false;
53 } else {
54 if (!parseFreeStringInto(String)) return false;
55 }
56 } while (*Position != ' ');
57 return true;
58 }
59
60 bool parseQuotedStringInto(std::string &String) {
61 if (!next()) return false;
62 while (*Position != '"') {
63 if (!skipEscapeCharacter()) return false;
64 String.push_back(*Position);
65 if (!next()) return false;
66 }
67 return next();
68 }
69
70 bool parseFreeStringInto(std::string &String) {
71 do {
72 if (!skipEscapeCharacter()) return false;
73 String.push_back(*Position);
74 if (!next()) return false;
75 } while (*Position != ' ' && *Position != '"');
76 return true;
77 }
78
79 bool skipEscapeCharacter() {
80 if (*Position == '\\') {
81 return next();
82 }
83 return true;
84 }
85
86 bool nextNonWhitespace() {
87 do {
88 if (!next()) return false;
89 } while (*Position == ' ');
90 return true;
91 }
92
93 bool next() {
94 ++Position;
95 return Position != Input.end();
96 }
97
98 const StringRef Input;
99 StringRef::iterator Position;
100 std::vector<std::string> CommandLine;
101};
102
103std::vector<std::string> unescapeCommandLine(
104 StringRef EscapedCommandLine) {
105 CommandLineArgumentParser parser(EscapedCommandLine);
106 return parser.parse();
107}
108
109} // end namespace
110
111class JSONCompilationDatabasePlugin : public CompilationDatabasePlugin {
112 virtual CompilationDatabase *loadFromDirectory(
113 StringRef Directory, std::string &ErrorMessage) {
114 llvm::SmallString<1024> JSONDatabasePath(Directory);
115 llvm::sys::path::append(JSONDatabasePath, "compile_commands.json");
116 llvm::OwningPtr<CompilationDatabase> Database(
117 JSONCompilationDatabase::loadFromFile(JSONDatabasePath, ErrorMessage));
118 if (!Database)
119 return NULL;
120 return Database.take();
121 }
122};
123
124// Register the JSONCompilationDatabasePlugin with the
125// CompilationDatabasePluginRegistry using this statically initialized variable.
126static CompilationDatabasePluginRegistry::Add<JSONCompilationDatabasePlugin>
127X("json-compilation-database", "Reads JSON formatted compilation databases");
128
129// This anchor is used to force the linker to link in the generated object file
130// and thus register the JSONCompilationDatabasePlugin.
NAKAMURA Takumi916978a2012-08-24 10:39:28 +0000131volatile int JSONAnchorSource = 0;
Daniel Jasper7fd90b02012-08-24 05:50:27 +0000132
133JSONCompilationDatabase *
134JSONCompilationDatabase::loadFromFile(StringRef FilePath,
135 std::string &ErrorMessage) {
136 llvm::OwningPtr<llvm::MemoryBuffer> DatabaseBuffer;
137 llvm::error_code Result =
138 llvm::MemoryBuffer::getFile(FilePath, DatabaseBuffer);
139 if (Result != 0) {
140 ErrorMessage = "Error while opening JSON database: " + Result.message();
141 return NULL;
142 }
143 llvm::OwningPtr<JSONCompilationDatabase> Database(
144 new JSONCompilationDatabase(DatabaseBuffer.take()));
145 if (!Database->parse(ErrorMessage))
146 return NULL;
147 return Database.take();
148}
149
150JSONCompilationDatabase *
151JSONCompilationDatabase::loadFromBuffer(StringRef DatabaseString,
152 std::string &ErrorMessage) {
153 llvm::OwningPtr<llvm::MemoryBuffer> DatabaseBuffer(
154 llvm::MemoryBuffer::getMemBuffer(DatabaseString));
155 llvm::OwningPtr<JSONCompilationDatabase> Database(
156 new JSONCompilationDatabase(DatabaseBuffer.take()));
157 if (!Database->parse(ErrorMessage))
158 return NULL;
159 return Database.take();
160}
161
162std::vector<CompileCommand>
163JSONCompilationDatabase::getCompileCommands(StringRef FilePath) const {
164 llvm::SmallString<128> NativeFilePath;
165 llvm::sys::path::native(FilePath, NativeFilePath);
Daniel Jasperd3420c92012-10-08 16:08:15 +0000166 std::vector<StringRef> PossibleMatches;
167 std::string Error;
168 llvm::raw_string_ostream ES(Error);
169 StringRef Match = MatchTrie.findEquivalent(NativeFilePath.str(), ES);
170 if (Match.empty()) {
171 if (Error.empty())
172 Error = "No match found.";
173 llvm::outs() << Error << "\n";
174 return std::vector<CompileCommand>();
175 }
Daniel Jasper7fd90b02012-08-24 05:50:27 +0000176 llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator
Daniel Jasperd3420c92012-10-08 16:08:15 +0000177 CommandsRefI = IndexByFile.find(Match);
Daniel Jasper7fd90b02012-08-24 05:50:27 +0000178 if (CommandsRefI == IndexByFile.end())
179 return std::vector<CompileCommand>();
Daniel Jasper7fd90b02012-08-24 05:50:27 +0000180 std::vector<CompileCommand> Commands;
Argyrios Kyrtzidis7e96bfb2012-12-04 07:26:44 +0000181 getCommands(CommandsRefI->getValue(), Commands);
Daniel Jasper7fd90b02012-08-24 05:50:27 +0000182 return Commands;
183}
184
185std::vector<std::string>
186JSONCompilationDatabase::getAllFiles() const {
187 std::vector<std::string> Result;
188
189 llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator
190 CommandsRefI = IndexByFile.begin();
191 const llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator
192 CommandsRefEnd = IndexByFile.end();
193 for (; CommandsRefI != CommandsRefEnd; ++CommandsRefI) {
194 Result.push_back(CommandsRefI->first().str());
195 }
196
197 return Result;
198}
199
Argyrios Kyrtzidis7e96bfb2012-12-04 07:26:44 +0000200std::vector<CompileCommand>
201JSONCompilationDatabase::getAllCompileCommands() const {
202 std::vector<CompileCommand> Commands;
203 for (llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator
204 CommandsRefI = IndexByFile.begin(), CommandsRefEnd = IndexByFile.end();
205 CommandsRefI != CommandsRefEnd; ++CommandsRefI) {
206 getCommands(CommandsRefI->getValue(), Commands);
207 }
208 return Commands;
209}
210
211void JSONCompilationDatabase::getCommands(
212 ArrayRef<CompileCommandRef> CommandsRef,
213 std::vector<CompileCommand> &Commands) const {
214 for (int I = 0, E = CommandsRef.size(); I != E; ++I) {
215 llvm::SmallString<8> DirectoryStorage;
216 llvm::SmallString<1024> CommandStorage;
217 Commands.push_back(CompileCommand(
218 // FIXME: Escape correctly:
219 CommandsRef[I].first->getValue(DirectoryStorage),
220 unescapeCommandLine(CommandsRef[I].second->getValue(CommandStorage))));
221 }
222}
223
Daniel Jasper7fd90b02012-08-24 05:50:27 +0000224bool JSONCompilationDatabase::parse(std::string &ErrorMessage) {
225 llvm::yaml::document_iterator I = YAMLStream.begin();
226 if (I == YAMLStream.end()) {
227 ErrorMessage = "Error while parsing YAML.";
228 return false;
229 }
230 llvm::yaml::Node *Root = I->getRoot();
231 if (Root == NULL) {
232 ErrorMessage = "Error while parsing YAML.";
233 return false;
234 }
235 llvm::yaml::SequenceNode *Array =
236 llvm::dyn_cast<llvm::yaml::SequenceNode>(Root);
237 if (Array == NULL) {
238 ErrorMessage = "Expected array.";
239 return false;
240 }
241 for (llvm::yaml::SequenceNode::iterator AI = Array->begin(),
242 AE = Array->end();
243 AI != AE; ++AI) {
244 llvm::yaml::MappingNode *Object =
245 llvm::dyn_cast<llvm::yaml::MappingNode>(&*AI);
246 if (Object == NULL) {
247 ErrorMessage = "Expected object.";
248 return false;
249 }
250 llvm::yaml::ScalarNode *Directory = NULL;
251 llvm::yaml::ScalarNode *Command = NULL;
252 llvm::yaml::ScalarNode *File = NULL;
253 for (llvm::yaml::MappingNode::iterator KVI = Object->begin(),
254 KVE = Object->end();
255 KVI != KVE; ++KVI) {
256 llvm::yaml::Node *Value = (*KVI).getValue();
257 if (Value == NULL) {
258 ErrorMessage = "Expected value.";
259 return false;
260 }
261 llvm::yaml::ScalarNode *ValueString =
262 llvm::dyn_cast<llvm::yaml::ScalarNode>(Value);
263 if (ValueString == NULL) {
264 ErrorMessage = "Expected string as value.";
265 return false;
266 }
267 llvm::yaml::ScalarNode *KeyString =
268 llvm::dyn_cast<llvm::yaml::ScalarNode>((*KVI).getKey());
269 if (KeyString == NULL) {
270 ErrorMessage = "Expected strings as key.";
271 return false;
272 }
273 llvm::SmallString<8> KeyStorage;
274 if (KeyString->getValue(KeyStorage) == "directory") {
275 Directory = ValueString;
276 } else if (KeyString->getValue(KeyStorage) == "command") {
277 Command = ValueString;
278 } else if (KeyString->getValue(KeyStorage) == "file") {
279 File = ValueString;
280 } else {
281 ErrorMessage = ("Unknown key: \"" +
282 KeyString->getRawValue() + "\"").str();
283 return false;
284 }
285 }
286 if (!File) {
287 ErrorMessage = "Missing key: \"file\".";
288 return false;
289 }
290 if (!Command) {
291 ErrorMessage = "Missing key: \"command\".";
292 return false;
293 }
294 if (!Directory) {
295 ErrorMessage = "Missing key: \"directory\".";
296 return false;
297 }
298 llvm::SmallString<8> FileStorage;
Daniel Jasperd3420c92012-10-08 16:08:15 +0000299 StringRef FileName = File->getValue(FileStorage);
Daniel Jasper7fd90b02012-08-24 05:50:27 +0000300 llvm::SmallString<128> NativeFilePath;
Daniel Jasperd3420c92012-10-08 16:08:15 +0000301 if (llvm::sys::path::is_relative(FileName)) {
302 llvm::SmallString<8> DirectoryStorage;
303 llvm::SmallString<128> AbsolutePath(
304 Directory->getValue(DirectoryStorage));
305 llvm::sys::path::append(AbsolutePath, FileName);
306 llvm::sys::path::native(AbsolutePath.str(), NativeFilePath);
307 } else {
308 llvm::sys::path::native(FileName, NativeFilePath);
309 }
Daniel Jasper7fd90b02012-08-24 05:50:27 +0000310 IndexByFile[NativeFilePath].push_back(
Daniel Jasperd3420c92012-10-08 16:08:15 +0000311 CompileCommandRef(Directory, Command));
312 MatchTrie.insert(NativeFilePath.str());
Daniel Jasper7fd90b02012-08-24 05:50:27 +0000313 }
314 return true;
315}
316
317} // end namespace tooling
318} // end namespace clang