1 //===--- JSONCompilationDatabase.cpp - ------------------------------------===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // This file contains the implementation of the JSONCompilationDatabase.
12 //===----------------------------------------------------------------------===//
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 "llvm/Support/system_error.h"
27 /// \brief A parser for escaped strings of command line arguments.
29 /// Assumes \-escaping for quoted arguments (see the documentation of
30 /// unescapeCommandLine(...)).
31 class CommandLineArgumentParser {
33 CommandLineArgumentParser(StringRef CommandLine)
34 : Input(CommandLine), Position(Input.begin()-1) {}
36 std::vector<std::string> parse() {
37 bool HasMoreInput = true;
38 while (HasMoreInput && nextNonWhitespace()) {
40 HasMoreInput = parseStringInto(Argument);
41 CommandLine.push_back(Argument);
47 // All private methods return true if there is more input available.
49 bool parseStringInto(std::string &String) {
51 if (*Position == '"') {
52 if (!parseDoubleQuotedStringInto(String)) return false;
53 } else if (*Position == '\'') {
54 if (!parseSingleQuotedStringInto(String)) return false;
56 if (!parseFreeStringInto(String)) return false;
58 } while (*Position != ' ');
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;
72 bool parseSingleQuotedStringInto(std::string &String) {
73 if (!next()) return false;
74 while (*Position != '\'') {
75 String.push_back(*Position);
76 if (!next()) return false;
81 bool parseFreeStringInto(std::string &String) {
83 if (!skipEscapeCharacter()) return false;
84 String.push_back(*Position);
85 if (!next()) return false;
86 } while (*Position != ' ' && *Position != '"' && *Position != '\'');
90 bool skipEscapeCharacter() {
91 if (*Position == '\\') {
97 bool nextNonWhitespace() {
99 if (!next()) return false;
100 } while (*Position == ' ');
106 return Position != Input.end();
109 const StringRef Input;
110 StringRef::iterator Position;
111 std::vector<std::string> CommandLine;
114 std::vector<std::string> unescapeCommandLine(
115 StringRef EscapedCommandLine) {
116 CommandLineArgumentParser parser(EscapedCommandLine);
117 return parser.parse();
122 class JSONCompilationDatabasePlugin : public CompilationDatabasePlugin {
123 virtual CompilationDatabase *loadFromDirectory(
124 StringRef Directory, std::string &ErrorMessage) {
125 SmallString<1024> JSONDatabasePath(Directory);
126 llvm::sys::path::append(JSONDatabasePath, "compile_commands.json");
127 OwningPtr<CompilationDatabase> Database(
128 JSONCompilationDatabase::loadFromFile(JSONDatabasePath, ErrorMessage));
131 return Database.take();
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");
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;
144 JSONCompilationDatabase *
145 JSONCompilationDatabase::loadFromFile(StringRef FilePath,
146 std::string &ErrorMessage) {
147 OwningPtr<llvm::MemoryBuffer> DatabaseBuffer;
148 llvm::error_code Result =
149 llvm::MemoryBuffer::getFile(FilePath, DatabaseBuffer);
151 ErrorMessage = "Error while opening JSON database: " + Result.message();
154 OwningPtr<JSONCompilationDatabase> Database(
155 new JSONCompilationDatabase(DatabaseBuffer.take()));
156 if (!Database->parse(ErrorMessage))
158 return Database.take();
161 JSONCompilationDatabase *
162 JSONCompilationDatabase::loadFromBuffer(StringRef DatabaseString,
163 std::string &ErrorMessage) {
164 OwningPtr<llvm::MemoryBuffer> DatabaseBuffer(
165 llvm::MemoryBuffer::getMemBuffer(DatabaseString));
166 OwningPtr<JSONCompilationDatabase> Database(
167 new JSONCompilationDatabase(DatabaseBuffer.take()));
168 if (!Database->parse(ErrorMessage))
170 return Database.take();
173 std::vector<CompileCommand>
174 JSONCompilationDatabase::getCompileCommands(StringRef FilePath) const {
175 SmallString<128> NativeFilePath;
176 llvm::sys::path::native(FilePath, NativeFilePath);
177 std::vector<StringRef> PossibleMatches;
179 llvm::raw_string_ostream ES(Error);
180 StringRef Match = MatchTrie.findEquivalent(NativeFilePath.str(), ES);
182 return std::vector<CompileCommand>();
183 llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator
184 CommandsRefI = IndexByFile.find(Match);
185 if (CommandsRefI == IndexByFile.end())
186 return std::vector<CompileCommand>();
187 std::vector<CompileCommand> Commands;
188 getCommands(CommandsRefI->getValue(), Commands);
192 std::vector<std::string>
193 JSONCompilationDatabase::getAllFiles() const {
194 std::vector<std::string> Result;
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());
207 std::vector<CompileCommand>
208 JSONCompilationDatabase::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);
218 void JSONCompilationDatabase::getCommands(
219 ArrayRef<CompileCommandRef> CommandsRef,
220 std::vector<CompileCommand> &Commands) const {
221 for (int I = 0, E = CommandsRef.size(); I != E; ++I) {
222 SmallString<8> DirectoryStorage;
223 SmallString<1024> CommandStorage;
224 Commands.push_back(CompileCommand(
225 // FIXME: Escape correctly:
226 CommandsRef[I].first->getValue(DirectoryStorage),
227 unescapeCommandLine(CommandsRef[I].second->getValue(CommandStorage))));
231 bool JSONCompilationDatabase::parse(std::string &ErrorMessage) {
232 llvm::yaml::document_iterator I = YAMLStream.begin();
233 if (I == YAMLStream.end()) {
234 ErrorMessage = "Error while parsing YAML.";
237 llvm::yaml::Node *Root = I->getRoot();
239 ErrorMessage = "Error while parsing YAML.";
242 llvm::yaml::SequenceNode *Array = dyn_cast<llvm::yaml::SequenceNode>(Root);
244 ErrorMessage = "Expected array.";
247 for (llvm::yaml::SequenceNode::iterator AI = Array->begin(),
250 llvm::yaml::MappingNode *Object = dyn_cast<llvm::yaml::MappingNode>(&*AI);
251 if (Object == NULL) {
252 ErrorMessage = "Expected object.";
255 llvm::yaml::ScalarNode *Directory = NULL;
256 llvm::yaml::ScalarNode *Command = NULL;
257 llvm::yaml::ScalarNode *File = NULL;
258 for (llvm::yaml::MappingNode::iterator KVI = Object->begin(),
261 llvm::yaml::Node *Value = (*KVI).getValue();
263 ErrorMessage = "Expected value.";
266 llvm::yaml::ScalarNode *ValueString =
267 dyn_cast<llvm::yaml::ScalarNode>(Value);
268 if (ValueString == NULL) {
269 ErrorMessage = "Expected string as value.";
272 llvm::yaml::ScalarNode *KeyString =
273 dyn_cast<llvm::yaml::ScalarNode>((*KVI).getKey());
274 if (KeyString == NULL) {
275 ErrorMessage = "Expected strings as key.";
278 SmallString<8> KeyStorage;
279 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") {
286 ErrorMessage = ("Unknown key: \"" +
287 KeyString->getRawValue() + "\"").str();
292 ErrorMessage = "Missing key: \"file\".";
296 ErrorMessage = "Missing key: \"command\".";
300 ErrorMessage = "Missing key: \"directory\".";
303 SmallString<8> FileStorage;
304 StringRef FileName = File->getValue(FileStorage);
305 SmallString<128> NativeFilePath;
306 if (llvm::sys::path::is_relative(FileName)) {
307 SmallString<8> DirectoryStorage;
308 SmallString<128> AbsolutePath(
309 Directory->getValue(DirectoryStorage));
310 llvm::sys::path::append(AbsolutePath, FileName);
311 llvm::sys::path::native(AbsolutePath.str(), NativeFilePath);
313 llvm::sys::path::native(FileName, NativeFilePath);
315 IndexByFile[NativeFilePath].push_back(
316 CompileCommandRef(Directory, Command));
317 MatchTrie.insert(NativeFilePath.str());
322 } // end namespace tooling
323 } // end namespace clang