CompilationDatabase.cpp revision c661f1467a84a0a3a83a396b067188b647844ee9
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/YAMLParser.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 escaped strings of command line arguments.
26///
27/// Assumes \-escaping for quoted arguments (see the documentation of
28/// unescapeCommandLine(...)).
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    return Position != Input.end();
94  }
95
96  const StringRef Input;
97  StringRef::iterator Position;
98  std::vector<std::string> CommandLine;
99};
100
101std::vector<std::string> unescapeCommandLine(
102    StringRef EscapedCommandLine) {
103  CommandLineArgumentParser parser(EscapedCommandLine);
104  return parser.parse();
105}
106
107} // end namespace
108
109CompilationDatabase::~CompilationDatabase() {}
110
111CompilationDatabase *
112CompilationDatabase::loadFromDirectory(StringRef BuildDirectory,
113                                       std::string &ErrorMessage) {
114  llvm::SmallString<1024> JSONDatabasePath(BuildDirectory);
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  }
121  return Database.take();
122}
123
124JSONCompilationDatabase *
125JSONCompilationDatabase::loadFromFile(StringRef FilePath,
126                                      std::string &ErrorMessage) {
127  llvm::OwningPtr<llvm::MemoryBuffer> DatabaseBuffer;
128  llvm::error_code Result =
129    llvm::MemoryBuffer::getFile(FilePath, DatabaseBuffer);
130  if (Result != 0) {
131    ErrorMessage = "Error while opening JSON database: " + Result.message();
132    return NULL;
133  }
134  llvm::OwningPtr<JSONCompilationDatabase> Database(
135    new JSONCompilationDatabase(DatabaseBuffer.take()));
136  if (!Database->parse(ErrorMessage))
137    return NULL;
138  return Database.take();
139}
140
141JSONCompilationDatabase *
142JSONCompilationDatabase::loadFromBuffer(StringRef DatabaseString,
143                                        std::string &ErrorMessage) {
144  llvm::OwningPtr<llvm::MemoryBuffer> DatabaseBuffer(
145      llvm::MemoryBuffer::getMemBuffer(DatabaseString));
146  llvm::OwningPtr<JSONCompilationDatabase> Database(
147    new JSONCompilationDatabase(DatabaseBuffer.take()));
148  if (!Database->parse(ErrorMessage))
149    return NULL;
150  return Database.take();
151}
152
153std::vector<CompileCommand>
154JSONCompilationDatabase::getCompileCommands(StringRef FilePath) const {
155  llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator
156    CommandsRefI = IndexByFile.find(FilePath);
157  if (CommandsRefI == IndexByFile.end())
158    return std::vector<CompileCommand>();
159  const std::vector<CompileCommandRef> &CommandsRef = CommandsRefI->getValue();
160  std::vector<CompileCommand> Commands;
161  for (int I = 0, E = CommandsRef.size(); I != E; ++I) {
162    llvm::SmallString<8> DirectoryStorage;
163    llvm::SmallString<1024> CommandStorage;
164    Commands.push_back(CompileCommand(
165      // FIXME: Escape correctly:
166      CommandsRef[I].first->getValue(DirectoryStorage),
167      unescapeCommandLine(CommandsRef[I].second->getValue(CommandStorage))));
168  }
169  return Commands;
170}
171
172bool JSONCompilationDatabase::parse(std::string &ErrorMessage) {
173  llvm::yaml::document_iterator I = YAMLStream.begin();
174  if (I == YAMLStream.end()) {
175    ErrorMessage = "Error while parsing YAML.";
176    return false;
177  }
178  llvm::yaml::Node *Root = I->getRoot();
179  if (Root == NULL) {
180    ErrorMessage = "Error while parsing YAML.";
181    return false;
182  }
183  llvm::yaml::SequenceNode *Array =
184    llvm::dyn_cast<llvm::yaml::SequenceNode>(Root);
185  if (Array == NULL) {
186    ErrorMessage = "Expected array.";
187    return false;
188  }
189  for (llvm::yaml::SequenceNode::iterator AI = Array->begin(),
190                                          AE = Array->end();
191       AI != AE; ++AI) {
192    llvm::yaml::MappingNode *Object =
193      llvm::dyn_cast<llvm::yaml::MappingNode>(&*AI);
194    if (Object == NULL) {
195      ErrorMessage = "Expected object.";
196      return false;
197    }
198    llvm::yaml::ScalarNode *Directory;
199    llvm::yaml::ScalarNode *Command;
200    llvm::SmallString<8> FileStorage;
201    llvm::StringRef File;
202    for (llvm::yaml::MappingNode::iterator KVI = Object->begin(),
203                                           KVE = Object->end();
204         KVI != KVE; ++KVI) {
205      llvm::yaml::Node *Value = (*KVI).getValue();
206      if (Value == NULL) {
207        ErrorMessage = "Expected value.";
208        return false;
209      }
210      llvm::yaml::ScalarNode *ValueString =
211        llvm::dyn_cast<llvm::yaml::ScalarNode>(Value);
212      if (ValueString == NULL) {
213        ErrorMessage = "Expected string as value.";
214        return false;
215      }
216      llvm::yaml::ScalarNode *KeyString =
217        llvm::dyn_cast<llvm::yaml::ScalarNode>((*KVI).getKey());
218      llvm::SmallString<8> KeyStorage;
219      if (KeyString->getValue(KeyStorage) == "directory") {
220        Directory = ValueString;
221      } else if (KeyString->getValue(KeyStorage) == "command") {
222        Command = ValueString;
223      } else if (KeyString->getValue(KeyStorage) == "file") {
224        File = ValueString->getValue(FileStorage);
225      } else {
226        ErrorMessage = ("Unknown key: \"" +
227                        KeyString->getRawValue() + "\"").str();
228        return false;
229      }
230    }
231    IndexByFile[File].push_back(
232      CompileCommandRef(Directory, Command));
233  }
234  return true;
235}
236
237} // end namespace tooling
238} // end namespace clang
239
240