CompilationDatabase.cpp revision 235633
1234287Sdim//===--- CompilationDatabase.cpp - ----------------------------------------===//
2234287Sdim//
3234287Sdim//                     The LLVM Compiler Infrastructure
4234287Sdim//
5234287Sdim// This file is distributed under the University of Illinois Open Source
6234287Sdim// License. See LICENSE.TXT for details.
7234287Sdim//
8234287Sdim//===----------------------------------------------------------------------===//
9234287Sdim//
10234287Sdim//  This file contains multiple implementations for CompilationDatabases.
11234287Sdim//
12234287Sdim//===----------------------------------------------------------------------===//
13234287Sdim
14234287Sdim#include "clang/Tooling/CompilationDatabase.h"
15234287Sdim#include "llvm/ADT/SmallString.h"
16235633Sdim#include "llvm/Support/YAMLParser.h"
17234287Sdim#include "llvm/Support/Path.h"
18234287Sdim#include "llvm/Support/system_error.h"
19234287Sdim
20234287Sdimnamespace clang {
21234287Sdimnamespace tooling {
22234287Sdim
23234287Sdimnamespace {
24234287Sdim
25235633Sdim/// \brief A parser for escaped strings of command line arguments.
26234287Sdim///
27234287Sdim/// Assumes \-escaping for quoted arguments (see the documentation of
28235633Sdim/// unescapeCommandLine(...)).
29234287Sdimclass CommandLineArgumentParser {
30234287Sdim public:
31234287Sdim  CommandLineArgumentParser(StringRef CommandLine)
32234287Sdim      : Input(CommandLine), Position(Input.begin()-1) {}
33234287Sdim
34234287Sdim  std::vector<std::string> parse() {
35234287Sdim    bool HasMoreInput = true;
36234287Sdim    while (HasMoreInput && nextNonWhitespace()) {
37234287Sdim      std::string Argument;
38234287Sdim      HasMoreInput = parseStringInto(Argument);
39234287Sdim      CommandLine.push_back(Argument);
40234287Sdim    }
41234287Sdim    return CommandLine;
42234287Sdim  }
43234287Sdim
44234287Sdim private:
45234287Sdim  // All private methods return true if there is more input available.
46234287Sdim
47234287Sdim  bool parseStringInto(std::string &String) {
48234287Sdim    do {
49234287Sdim      if (*Position == '"') {
50234287Sdim        if (!parseQuotedStringInto(String)) return false;
51234287Sdim      } else {
52234287Sdim        if (!parseFreeStringInto(String)) return false;
53234287Sdim      }
54234287Sdim    } while (*Position != ' ');
55234287Sdim    return true;
56234287Sdim  }
57234287Sdim
58234287Sdim  bool parseQuotedStringInto(std::string &String) {
59234287Sdim    if (!next()) return false;
60234287Sdim    while (*Position != '"') {
61234287Sdim      if (!skipEscapeCharacter()) return false;
62234287Sdim      String.push_back(*Position);
63234287Sdim      if (!next()) return false;
64234287Sdim    }
65234287Sdim    return next();
66234287Sdim  }
67234287Sdim
68234287Sdim  bool parseFreeStringInto(std::string &String) {
69234287Sdim    do {
70234287Sdim      if (!skipEscapeCharacter()) return false;
71234287Sdim      String.push_back(*Position);
72234287Sdim      if (!next()) return false;
73234287Sdim    } while (*Position != ' ' && *Position != '"');
74234287Sdim    return true;
75234287Sdim  }
76234287Sdim
77234287Sdim  bool skipEscapeCharacter() {
78234287Sdim    if (*Position == '\\') {
79234287Sdim      return next();
80234287Sdim    }
81234287Sdim    return true;
82234287Sdim  }
83234287Sdim
84234287Sdim  bool nextNonWhitespace() {
85234287Sdim    do {
86234287Sdim      if (!next()) return false;
87234287Sdim    } while (*Position == ' ');
88234287Sdim    return true;
89234287Sdim  }
90234287Sdim
91234287Sdim  bool next() {
92234287Sdim    ++Position;
93234287Sdim    return Position != Input.end();
94234287Sdim  }
95234287Sdim
96234287Sdim  const StringRef Input;
97234287Sdim  StringRef::iterator Position;
98234287Sdim  std::vector<std::string> CommandLine;
99234287Sdim};
100234287Sdim
101235633Sdimstd::vector<std::string> unescapeCommandLine(
102235633Sdim    StringRef EscapedCommandLine) {
103235633Sdim  CommandLineArgumentParser parser(EscapedCommandLine);
104234287Sdim  return parser.parse();
105234287Sdim}
106234287Sdim
107234287Sdim} // end namespace
108234287Sdim
109234287SdimCompilationDatabase::~CompilationDatabase() {}
110234287Sdim
111234287SdimCompilationDatabase *
112234287SdimCompilationDatabase::loadFromDirectory(StringRef BuildDirectory,
113234287Sdim                                       std::string &ErrorMessage) {
114234287Sdim  llvm::SmallString<1024> JSONDatabasePath(BuildDirectory);
115234287Sdim  llvm::sys::path::append(JSONDatabasePath, "compile_commands.json");
116234287Sdim  llvm::OwningPtr<CompilationDatabase> Database(
117234287Sdim    JSONCompilationDatabase::loadFromFile(JSONDatabasePath, ErrorMessage));
118234287Sdim  if (!Database) {
119234287Sdim    return NULL;
120234287Sdim  }
121234287Sdim  return Database.take();
122234287Sdim}
123234287Sdim
124235633SdimFixedCompilationDatabase *
125235633SdimFixedCompilationDatabase::loadFromCommandLine(int &Argc,
126235633Sdim                                              const char **Argv,
127235633Sdim                                              Twine Directory) {
128235633Sdim  const char **DoubleDash = std::find(Argv, Argv + Argc, StringRef("--"));
129235633Sdim  if (DoubleDash == Argv + Argc)
130235633Sdim    return NULL;
131235633Sdim  std::vector<std::string> CommandLine(DoubleDash + 1, Argv + Argc);
132235633Sdim  Argc = DoubleDash - Argv;
133235633Sdim  return new FixedCompilationDatabase(Directory, CommandLine);
134235633Sdim}
135235633Sdim
136235633SdimFixedCompilationDatabase::
137235633SdimFixedCompilationDatabase(Twine Directory, ArrayRef<std::string> CommandLine) {
138235633Sdim  std::vector<std::string> ToolCommandLine(1, "clang-tool");
139235633Sdim  ToolCommandLine.insert(ToolCommandLine.end(),
140235633Sdim                         CommandLine.begin(), CommandLine.end());
141235633Sdim  CompileCommands.push_back(CompileCommand(Directory, ToolCommandLine));
142235633Sdim}
143235633Sdim
144235633Sdimstd::vector<CompileCommand>
145235633SdimFixedCompilationDatabase::getCompileCommands(StringRef FilePath) const {
146235633Sdim  std::vector<CompileCommand> Result(CompileCommands);
147235633Sdim  Result[0].CommandLine.push_back(FilePath);
148235633Sdim  return Result;
149235633Sdim}
150235633Sdim
151234287SdimJSONCompilationDatabase *
152234287SdimJSONCompilationDatabase::loadFromFile(StringRef FilePath,
153234287Sdim                                      std::string &ErrorMessage) {
154234287Sdim  llvm::OwningPtr<llvm::MemoryBuffer> DatabaseBuffer;
155234287Sdim  llvm::error_code Result =
156234287Sdim    llvm::MemoryBuffer::getFile(FilePath, DatabaseBuffer);
157234287Sdim  if (Result != 0) {
158234287Sdim    ErrorMessage = "Error while opening JSON database: " + Result.message();
159234287Sdim    return NULL;
160234287Sdim  }
161234287Sdim  llvm::OwningPtr<JSONCompilationDatabase> Database(
162234287Sdim    new JSONCompilationDatabase(DatabaseBuffer.take()));
163234287Sdim  if (!Database->parse(ErrorMessage))
164234287Sdim    return NULL;
165234287Sdim  return Database.take();
166234287Sdim}
167234287Sdim
168234287SdimJSONCompilationDatabase *
169234287SdimJSONCompilationDatabase::loadFromBuffer(StringRef DatabaseString,
170234287Sdim                                        std::string &ErrorMessage) {
171234287Sdim  llvm::OwningPtr<llvm::MemoryBuffer> DatabaseBuffer(
172234287Sdim      llvm::MemoryBuffer::getMemBuffer(DatabaseString));
173234287Sdim  llvm::OwningPtr<JSONCompilationDatabase> Database(
174234287Sdim    new JSONCompilationDatabase(DatabaseBuffer.take()));
175234287Sdim  if (!Database->parse(ErrorMessage))
176234287Sdim    return NULL;
177234287Sdim  return Database.take();
178234287Sdim}
179234287Sdim
180234287Sdimstd::vector<CompileCommand>
181234287SdimJSONCompilationDatabase::getCompileCommands(StringRef FilePath) const {
182234287Sdim  llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator
183234287Sdim    CommandsRefI = IndexByFile.find(FilePath);
184234287Sdim  if (CommandsRefI == IndexByFile.end())
185234287Sdim    return std::vector<CompileCommand>();
186234287Sdim  const std::vector<CompileCommandRef> &CommandsRef = CommandsRefI->getValue();
187234287Sdim  std::vector<CompileCommand> Commands;
188234287Sdim  for (int I = 0, E = CommandsRef.size(); I != E; ++I) {
189235633Sdim    llvm::SmallString<8> DirectoryStorage;
190235633Sdim    llvm::SmallString<1024> CommandStorage;
191234287Sdim    Commands.push_back(CompileCommand(
192234287Sdim      // FIXME: Escape correctly:
193235633Sdim      CommandsRef[I].first->getValue(DirectoryStorage),
194235633Sdim      unescapeCommandLine(CommandsRef[I].second->getValue(CommandStorage))));
195234287Sdim  }
196234287Sdim  return Commands;
197234287Sdim}
198234287Sdim
199234287Sdimbool JSONCompilationDatabase::parse(std::string &ErrorMessage) {
200235633Sdim  llvm::yaml::document_iterator I = YAMLStream.begin();
201235633Sdim  if (I == YAMLStream.end()) {
202235633Sdim    ErrorMessage = "Error while parsing YAML.";
203235633Sdim    return false;
204235633Sdim  }
205235633Sdim  llvm::yaml::Node *Root = I->getRoot();
206234287Sdim  if (Root == NULL) {
207235633Sdim    ErrorMessage = "Error while parsing YAML.";
208234287Sdim    return false;
209234287Sdim  }
210235633Sdim  llvm::yaml::SequenceNode *Array =
211235633Sdim    llvm::dyn_cast<llvm::yaml::SequenceNode>(Root);
212234287Sdim  if (Array == NULL) {
213234287Sdim    ErrorMessage = "Expected array.";
214234287Sdim    return false;
215234287Sdim  }
216235633Sdim  for (llvm::yaml::SequenceNode::iterator AI = Array->begin(),
217235633Sdim                                          AE = Array->end();
218234287Sdim       AI != AE; ++AI) {
219235633Sdim    llvm::yaml::MappingNode *Object =
220235633Sdim      llvm::dyn_cast<llvm::yaml::MappingNode>(&*AI);
221234287Sdim    if (Object == NULL) {
222234287Sdim      ErrorMessage = "Expected object.";
223234287Sdim      return false;
224234287Sdim    }
225235633Sdim    llvm::yaml::ScalarNode *Directory;
226235633Sdim    llvm::yaml::ScalarNode *Command;
227235633Sdim    llvm::SmallString<8> FileStorage;
228235633Sdim    llvm::StringRef File;
229235633Sdim    for (llvm::yaml::MappingNode::iterator KVI = Object->begin(),
230235633Sdim                                           KVE = Object->end();
231234287Sdim         KVI != KVE; ++KVI) {
232235633Sdim      llvm::yaml::Node *Value = (*KVI).getValue();
233234287Sdim      if (Value == NULL) {
234234287Sdim        ErrorMessage = "Expected value.";
235234287Sdim        return false;
236234287Sdim      }
237235633Sdim      llvm::yaml::ScalarNode *ValueString =
238235633Sdim        llvm::dyn_cast<llvm::yaml::ScalarNode>(Value);
239234287Sdim      if (ValueString == NULL) {
240234287Sdim        ErrorMessage = "Expected string as value.";
241234287Sdim        return false;
242234287Sdim      }
243235633Sdim      llvm::yaml::ScalarNode *KeyString =
244235633Sdim        llvm::dyn_cast<llvm::yaml::ScalarNode>((*KVI).getKey());
245235633Sdim      llvm::SmallString<8> KeyStorage;
246235633Sdim      if (KeyString->getValue(KeyStorage) == "directory") {
247235633Sdim        Directory = ValueString;
248235633Sdim      } else if (KeyString->getValue(KeyStorage) == "command") {
249235633Sdim        Command = ValueString;
250235633Sdim      } else if (KeyString->getValue(KeyStorage) == "file") {
251235633Sdim        File = ValueString->getValue(FileStorage);
252234287Sdim      } else {
253235633Sdim        ErrorMessage = ("Unknown key: \"" +
254235633Sdim                        KeyString->getRawValue() + "\"").str();
255234287Sdim        return false;
256234287Sdim      }
257234287Sdim    }
258235633Sdim    IndexByFile[File].push_back(
259235633Sdim      CompileCommandRef(Directory, Command));
260234287Sdim  }
261234287Sdim  return true;
262234287Sdim}
263234287Sdim
264234287Sdim} // end namespace tooling
265234287Sdim} // end namespace clang
266234287Sdim
267