CompilationDatabase.cpp revision 234287
1//===--- CompilationDatabase.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 multiple implementations for CompilationDatabases.
11//
12//===----------------------------------------------------------------------===//
13
14#include "clang/Tooling/CompilationDatabase.h"
15#include "llvm/ADT/SmallString.h"
16#include "llvm/Support/JSONParser.h"
17#include "llvm/Support/Path.h"
18#include "llvm/Support/system_error.h"
19
20namespace clang {
21namespace tooling {
22
23namespace {
24
25/// \brief A parser for JSON escaped strings of command line arguments.
26///
27/// Assumes \-escaping for quoted arguments (see the documentation of
28/// unescapeJSONCommandLine(...)).
29class CommandLineArgumentParser {
30 public:
31  CommandLineArgumentParser(StringRef CommandLine)
32      : Input(CommandLine), Position(Input.begin()-1) {}
33
34  std::vector<std::string> parse() {
35    bool HasMoreInput = true;
36    while (HasMoreInput && nextNonWhitespace()) {
37      std::string Argument;
38      HasMoreInput = parseStringInto(Argument);
39      CommandLine.push_back(Argument);
40    }
41    return CommandLine;
42  }
43
44 private:
45  // All private methods return true if there is more input available.
46
47  bool parseStringInto(std::string &String) {
48    do {
49      if (*Position == '"') {
50        if (!parseQuotedStringInto(String)) return false;
51      } else {
52        if (!parseFreeStringInto(String)) return false;
53      }
54    } while (*Position != ' ');
55    return true;
56  }
57
58  bool parseQuotedStringInto(std::string &String) {
59    if (!next()) return false;
60    while (*Position != '"') {
61      if (!skipEscapeCharacter()) return false;
62      String.push_back(*Position);
63      if (!next()) return false;
64    }
65    return next();
66  }
67
68  bool parseFreeStringInto(std::string &String) {
69    do {
70      if (!skipEscapeCharacter()) return false;
71      String.push_back(*Position);
72      if (!next()) return false;
73    } while (*Position != ' ' && *Position != '"');
74    return true;
75  }
76
77  bool skipEscapeCharacter() {
78    if (*Position == '\\') {
79      return next();
80    }
81    return true;
82  }
83
84  bool nextNonWhitespace() {
85    do {
86      if (!next()) return false;
87    } while (*Position == ' ');
88    return true;
89  }
90
91  bool next() {
92    ++Position;
93    if (Position == Input.end()) return false;
94    // Remove the JSON escaping first. This is done unconditionally.
95    if (*Position == '\\') ++Position;
96    return Position != Input.end();
97  }
98
99  const StringRef Input;
100  StringRef::iterator Position;
101  std::vector<std::string> CommandLine;
102};
103
104std::vector<std::string> unescapeJSONCommandLine(
105    StringRef JSONEscapedCommandLine) {
106  CommandLineArgumentParser parser(JSONEscapedCommandLine);
107  return parser.parse();
108}
109
110} // end namespace
111
112CompilationDatabase::~CompilationDatabase() {}
113
114CompilationDatabase *
115CompilationDatabase::loadFromDirectory(StringRef BuildDirectory,
116                                       std::string &ErrorMessage) {
117  llvm::SmallString<1024> JSONDatabasePath(BuildDirectory);
118  llvm::sys::path::append(JSONDatabasePath, "compile_commands.json");
119  llvm::OwningPtr<CompilationDatabase> Database(
120    JSONCompilationDatabase::loadFromFile(JSONDatabasePath, ErrorMessage));
121  if (!Database) {
122    return NULL;
123  }
124  return Database.take();
125}
126
127JSONCompilationDatabase *
128JSONCompilationDatabase::loadFromFile(StringRef FilePath,
129                                      std::string &ErrorMessage) {
130  llvm::OwningPtr<llvm::MemoryBuffer> DatabaseBuffer;
131  llvm::error_code Result =
132    llvm::MemoryBuffer::getFile(FilePath, DatabaseBuffer);
133  if (Result != 0) {
134    ErrorMessage = "Error while opening JSON database: " + Result.message();
135    return NULL;
136  }
137  llvm::OwningPtr<JSONCompilationDatabase> Database(
138    new JSONCompilationDatabase(DatabaseBuffer.take()));
139  if (!Database->parse(ErrorMessage))
140    return NULL;
141  return Database.take();
142}
143
144JSONCompilationDatabase *
145JSONCompilationDatabase::loadFromBuffer(StringRef DatabaseString,
146                                        std::string &ErrorMessage) {
147  llvm::OwningPtr<llvm::MemoryBuffer> DatabaseBuffer(
148      llvm::MemoryBuffer::getMemBuffer(DatabaseString));
149  llvm::OwningPtr<JSONCompilationDatabase> Database(
150    new JSONCompilationDatabase(DatabaseBuffer.take()));
151  if (!Database->parse(ErrorMessage))
152    return NULL;
153  return Database.take();
154}
155
156std::vector<CompileCommand>
157JSONCompilationDatabase::getCompileCommands(StringRef FilePath) const {
158  llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator
159    CommandsRefI = IndexByFile.find(FilePath);
160  if (CommandsRefI == IndexByFile.end())
161    return std::vector<CompileCommand>();
162  const std::vector<CompileCommandRef> &CommandsRef = CommandsRefI->getValue();
163  std::vector<CompileCommand> Commands;
164  for (int I = 0, E = CommandsRef.size(); I != E; ++I) {
165    Commands.push_back(CompileCommand(
166      // FIXME: Escape correctly:
167      CommandsRef[I].first,
168      unescapeJSONCommandLine(CommandsRef[I].second)));
169  }
170  return Commands;
171}
172
173bool JSONCompilationDatabase::parse(std::string &ErrorMessage) {
174  llvm::SourceMgr SM;
175  llvm::JSONParser Parser(Database->getBuffer(), &SM);
176  llvm::JSONValue *Root = Parser.parseRoot();
177  if (Root == NULL) {
178    ErrorMessage = "Error while parsing JSON.";
179    return false;
180  }
181  llvm::JSONArray *Array = dyn_cast<llvm::JSONArray>(Root);
182  if (Array == NULL) {
183    ErrorMessage = "Expected array.";
184    return false;
185  }
186  for (llvm::JSONArray::const_iterator AI = Array->begin(), AE = Array->end();
187       AI != AE; ++AI) {
188    const llvm::JSONObject *Object = dyn_cast<llvm::JSONObject>(*AI);
189    if (Object == NULL) {
190      ErrorMessage = "Expected object.";
191      return false;
192    }
193    StringRef EntryDirectory;
194    StringRef EntryFile;
195    StringRef EntryCommand;
196    for (llvm::JSONObject::const_iterator KVI = Object->begin(),
197                                          KVE = Object->end();
198         KVI != KVE; ++KVI) {
199      const llvm::JSONValue *Value = (*KVI)->Value;
200      if (Value == NULL) {
201        ErrorMessage = "Expected value.";
202        return false;
203      }
204      const llvm::JSONString *ValueString =
205        dyn_cast<llvm::JSONString>(Value);
206      if (ValueString == NULL) {
207        ErrorMessage = "Expected string as value.";
208        return false;
209      }
210      if ((*KVI)->Key->getRawText() == "directory") {
211        EntryDirectory = ValueString->getRawText();
212      } else if ((*KVI)->Key->getRawText() == "file") {
213        EntryFile = ValueString->getRawText();
214      } else if ((*KVI)->Key->getRawText() == "command") {
215        EntryCommand = ValueString->getRawText();
216      } else {
217        ErrorMessage = (Twine("Unknown key: \"") +
218                        (*KVI)->Key->getRawText() + "\"").str();
219        return false;
220      }
221    }
222    IndexByFile[EntryFile].push_back(
223      CompileCommandRef(EntryDirectory, EntryCommand));
224  }
225  return true;
226}
227
228} // end namespace tooling
229} // end namespace clang
230
231