JSONCompilationDatabase.cpp revision 296417
1243791Sdim//===--- JSONCompilationDatabase.cpp - ------------------------------------===//
2243791Sdim//
3243791Sdim//                     The LLVM Compiler Infrastructure
4243791Sdim//
5243791Sdim// This file is distributed under the University of Illinois Open Source
6243791Sdim// License. See LICENSE.TXT for details.
7243791Sdim//
8243791Sdim//===----------------------------------------------------------------------===//
9243791Sdim//
10243791Sdim//  This file contains the implementation of the JSONCompilationDatabase.
11243791Sdim//
12243791Sdim//===----------------------------------------------------------------------===//
13243791Sdim
14243791Sdim#include "clang/Tooling/JSONCompilationDatabase.h"
15243791Sdim#include "clang/Tooling/CompilationDatabase.h"
16243791Sdim#include "clang/Tooling/CompilationDatabasePluginRegistry.h"
17243791Sdim#include "clang/Tooling/Tooling.h"
18243791Sdim#include "llvm/ADT/SmallString.h"
19243791Sdim#include "llvm/Support/Path.h"
20276479Sdim#include <system_error>
21243791Sdim
22243791Sdimnamespace clang {
23243791Sdimnamespace tooling {
24243791Sdim
25243791Sdimnamespace {
26243791Sdim
27243791Sdim/// \brief A parser for escaped strings of command line arguments.
28243791Sdim///
29243791Sdim/// Assumes \-escaping for quoted arguments (see the documentation of
30243791Sdim/// unescapeCommandLine(...)).
31243791Sdimclass CommandLineArgumentParser {
32243791Sdim public:
33243791Sdim  CommandLineArgumentParser(StringRef CommandLine)
34243791Sdim      : Input(CommandLine), Position(Input.begin()-1) {}
35243791Sdim
36243791Sdim  std::vector<std::string> parse() {
37243791Sdim    bool HasMoreInput = true;
38243791Sdim    while (HasMoreInput && nextNonWhitespace()) {
39243791Sdim      std::string Argument;
40243791Sdim      HasMoreInput = parseStringInto(Argument);
41243791Sdim      CommandLine.push_back(Argument);
42243791Sdim    }
43243791Sdim    return CommandLine;
44243791Sdim  }
45243791Sdim
46243791Sdim private:
47243791Sdim  // All private methods return true if there is more input available.
48243791Sdim
49243791Sdim  bool parseStringInto(std::string &String) {
50243791Sdim    do {
51243791Sdim      if (*Position == '"') {
52249423Sdim        if (!parseDoubleQuotedStringInto(String)) return false;
53249423Sdim      } else if (*Position == '\'') {
54249423Sdim        if (!parseSingleQuotedStringInto(String)) return false;
55243791Sdim      } else {
56243791Sdim        if (!parseFreeStringInto(String)) return false;
57243791Sdim      }
58243791Sdim    } while (*Position != ' ');
59243791Sdim    return true;
60243791Sdim  }
61243791Sdim
62249423Sdim  bool parseDoubleQuotedStringInto(std::string &String) {
63243791Sdim    if (!next()) return false;
64243791Sdim    while (*Position != '"') {
65243791Sdim      if (!skipEscapeCharacter()) return false;
66243791Sdim      String.push_back(*Position);
67243791Sdim      if (!next()) return false;
68243791Sdim    }
69243791Sdim    return next();
70243791Sdim  }
71243791Sdim
72249423Sdim  bool parseSingleQuotedStringInto(std::string &String) {
73249423Sdim    if (!next()) return false;
74249423Sdim    while (*Position != '\'') {
75249423Sdim      String.push_back(*Position);
76249423Sdim      if (!next()) return false;
77249423Sdim    }
78249423Sdim    return next();
79249423Sdim  }
80249423Sdim
81243791Sdim  bool parseFreeStringInto(std::string &String) {
82243791Sdim    do {
83243791Sdim      if (!skipEscapeCharacter()) return false;
84243791Sdim      String.push_back(*Position);
85243791Sdim      if (!next()) return false;
86249423Sdim    } while (*Position != ' ' && *Position != '"' && *Position != '\'');
87243791Sdim    return true;
88243791Sdim  }
89243791Sdim
90243791Sdim  bool skipEscapeCharacter() {
91243791Sdim    if (*Position == '\\') {
92243791Sdim      return next();
93243791Sdim    }
94243791Sdim    return true;
95243791Sdim  }
96243791Sdim
97243791Sdim  bool nextNonWhitespace() {
98243791Sdim    do {
99243791Sdim      if (!next()) return false;
100243791Sdim    } while (*Position == ' ');
101243791Sdim    return true;
102243791Sdim  }
103243791Sdim
104243791Sdim  bool next() {
105243791Sdim    ++Position;
106243791Sdim    return Position != Input.end();
107243791Sdim  }
108243791Sdim
109243791Sdim  const StringRef Input;
110243791Sdim  StringRef::iterator Position;
111243791Sdim  std::vector<std::string> CommandLine;
112243791Sdim};
113243791Sdim
114243791Sdimstd::vector<std::string> unescapeCommandLine(
115243791Sdim    StringRef EscapedCommandLine) {
116243791Sdim  CommandLineArgumentParser parser(EscapedCommandLine);
117243791Sdim  return parser.parse();
118243791Sdim}
119243791Sdim
120243791Sdimclass JSONCompilationDatabasePlugin : public CompilationDatabasePlugin {
121280031Sdim  std::unique_ptr<CompilationDatabase>
122280031Sdim  loadFromDirectory(StringRef Directory, std::string &ErrorMessage) override {
123249423Sdim    SmallString<1024> JSONDatabasePath(Directory);
124243791Sdim    llvm::sys::path::append(JSONDatabasePath, "compile_commands.json");
125276479Sdim    std::unique_ptr<CompilationDatabase> Database(
126243791Sdim        JSONCompilationDatabase::loadFromFile(JSONDatabasePath, ErrorMessage));
127243791Sdim    if (!Database)
128276479Sdim      return nullptr;
129280031Sdim    return Database;
130243791Sdim  }
131243791Sdim};
132243791Sdim
133261991Sdim} // end namespace
134261991Sdim
135243791Sdim// Register the JSONCompilationDatabasePlugin with the
136243791Sdim// CompilationDatabasePluginRegistry using this statically initialized variable.
137243791Sdimstatic CompilationDatabasePluginRegistry::Add<JSONCompilationDatabasePlugin>
138243791SdimX("json-compilation-database", "Reads JSON formatted compilation databases");
139243791Sdim
140243791Sdim// This anchor is used to force the linker to link in the generated object file
141243791Sdim// and thus register the JSONCompilationDatabasePlugin.
142243791Sdimvolatile int JSONAnchorSource = 0;
143243791Sdim
144280031Sdimstd::unique_ptr<JSONCompilationDatabase>
145243791SdimJSONCompilationDatabase::loadFromFile(StringRef FilePath,
146243791Sdim                                      std::string &ErrorMessage) {
147276479Sdim  llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> DatabaseBuffer =
148276479Sdim      llvm::MemoryBuffer::getFile(FilePath);
149276479Sdim  if (std::error_code Result = DatabaseBuffer.getError()) {
150243791Sdim    ErrorMessage = "Error while opening JSON database: " + Result.message();
151276479Sdim    return nullptr;
152243791Sdim  }
153276479Sdim  std::unique_ptr<JSONCompilationDatabase> Database(
154280031Sdim      new JSONCompilationDatabase(std::move(*DatabaseBuffer)));
155243791Sdim  if (!Database->parse(ErrorMessage))
156276479Sdim    return nullptr;
157280031Sdim  return Database;
158243791Sdim}
159243791Sdim
160280031Sdimstd::unique_ptr<JSONCompilationDatabase>
161243791SdimJSONCompilationDatabase::loadFromBuffer(StringRef DatabaseString,
162243791Sdim                                        std::string &ErrorMessage) {
163276479Sdim  std::unique_ptr<llvm::MemoryBuffer> DatabaseBuffer(
164243791Sdim      llvm::MemoryBuffer::getMemBuffer(DatabaseString));
165276479Sdim  std::unique_ptr<JSONCompilationDatabase> Database(
166280031Sdim      new JSONCompilationDatabase(std::move(DatabaseBuffer)));
167243791Sdim  if (!Database->parse(ErrorMessage))
168276479Sdim    return nullptr;
169280031Sdim  return Database;
170243791Sdim}
171243791Sdim
172243791Sdimstd::vector<CompileCommand>
173243791SdimJSONCompilationDatabase::getCompileCommands(StringRef FilePath) const {
174249423Sdim  SmallString<128> NativeFilePath;
175243791Sdim  llvm::sys::path::native(FilePath, NativeFilePath);
176276479Sdim
177243791Sdim  std::string Error;
178243791Sdim  llvm::raw_string_ostream ES(Error);
179288943Sdim  StringRef Match = MatchTrie.findEquivalent(NativeFilePath, ES);
180249423Sdim  if (Match.empty())
181243791Sdim    return std::vector<CompileCommand>();
182243791Sdim  llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator
183243791Sdim    CommandsRefI = IndexByFile.find(Match);
184243791Sdim  if (CommandsRefI == IndexByFile.end())
185243791Sdim    return std::vector<CompileCommand>();
186243791Sdim  std::vector<CompileCommand> Commands;
187249423Sdim  getCommands(CommandsRefI->getValue(), Commands);
188243791Sdim  return Commands;
189243791Sdim}
190243791Sdim
191243791Sdimstd::vector<std::string>
192243791SdimJSONCompilationDatabase::getAllFiles() const {
193243791Sdim  std::vector<std::string> Result;
194243791Sdim
195243791Sdim  llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator
196243791Sdim    CommandsRefI = IndexByFile.begin();
197243791Sdim  const llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator
198243791Sdim    CommandsRefEnd = IndexByFile.end();
199243791Sdim  for (; CommandsRefI != CommandsRefEnd; ++CommandsRefI) {
200243791Sdim    Result.push_back(CommandsRefI->first().str());
201243791Sdim  }
202243791Sdim
203243791Sdim  return Result;
204243791Sdim}
205243791Sdim
206249423Sdimstd::vector<CompileCommand>
207249423SdimJSONCompilationDatabase::getAllCompileCommands() const {
208249423Sdim  std::vector<CompileCommand> Commands;
209296417Sdim  getCommands(AllCommands, Commands);
210249423Sdim  return Commands;
211249423Sdim}
212249423Sdim
213296417Sdimstatic std::vector<std::string>
214296417SdimnodeToCommandLine(const std::vector<llvm::yaml::ScalarNode *> &Nodes) {
215296417Sdim  SmallString<1024> Storage;
216296417Sdim  if (Nodes.size() == 1) {
217296417Sdim    return unescapeCommandLine(Nodes[0]->getValue(Storage));
218296417Sdim  }
219296417Sdim  std::vector<std::string> Arguments;
220296417Sdim  for (auto *Node : Nodes) {
221296417Sdim    Arguments.push_back(Node->getValue(Storage));
222296417Sdim  }
223296417Sdim  return Arguments;
224296417Sdim}
225296417Sdim
226249423Sdimvoid JSONCompilationDatabase::getCommands(
227296417Sdim    ArrayRef<CompileCommandRef> CommandsRef,
228296417Sdim    std::vector<CompileCommand> &Commands) const {
229249423Sdim  for (int I = 0, E = CommandsRef.size(); I != E; ++I) {
230249423Sdim    SmallString<8> DirectoryStorage;
231296417Sdim    SmallString<32> FilenameStorage;
232288943Sdim    Commands.emplace_back(
233296417Sdim      std::get<0>(CommandsRef[I])->getValue(DirectoryStorage),
234296417Sdim      std::get<1>(CommandsRef[I])->getValue(FilenameStorage),
235296417Sdim      nodeToCommandLine(std::get<2>(CommandsRef[I])));
236249423Sdim  }
237249423Sdim}
238249423Sdim
239243791Sdimbool JSONCompilationDatabase::parse(std::string &ErrorMessage) {
240243791Sdim  llvm::yaml::document_iterator I = YAMLStream.begin();
241243791Sdim  if (I == YAMLStream.end()) {
242243791Sdim    ErrorMessage = "Error while parsing YAML.";
243243791Sdim    return false;
244243791Sdim  }
245243791Sdim  llvm::yaml::Node *Root = I->getRoot();
246276479Sdim  if (!Root) {
247243791Sdim    ErrorMessage = "Error while parsing YAML.";
248243791Sdim    return false;
249243791Sdim  }
250249423Sdim  llvm::yaml::SequenceNode *Array = dyn_cast<llvm::yaml::SequenceNode>(Root);
251276479Sdim  if (!Array) {
252243791Sdim    ErrorMessage = "Expected array.";
253243791Sdim    return false;
254243791Sdim  }
255296417Sdim  for (auto& NextObject : *Array) {
256296417Sdim    llvm::yaml::MappingNode *Object = dyn_cast<llvm::yaml::MappingNode>(&NextObject);
257276479Sdim    if (!Object) {
258243791Sdim      ErrorMessage = "Expected object.";
259243791Sdim      return false;
260243791Sdim    }
261276479Sdim    llvm::yaml::ScalarNode *Directory = nullptr;
262296417Sdim    llvm::Optional<std::vector<llvm::yaml::ScalarNode *>> Command;
263276479Sdim    llvm::yaml::ScalarNode *File = nullptr;
264296417Sdim    for (auto& NextKeyValue : *Object) {
265296417Sdim      llvm::yaml::ScalarNode *KeyString =
266296417Sdim          dyn_cast<llvm::yaml::ScalarNode>(NextKeyValue.getKey());
267296417Sdim      if (!KeyString) {
268296417Sdim        ErrorMessage = "Expected strings as key.";
269296417Sdim        return false;
270296417Sdim      }
271296417Sdim      SmallString<10> KeyStorage;
272296417Sdim      StringRef KeyValue = KeyString->getValue(KeyStorage);
273296417Sdim      llvm::yaml::Node *Value = NextKeyValue.getValue();
274276479Sdim      if (!Value) {
275243791Sdim        ErrorMessage = "Expected value.";
276243791Sdim        return false;
277243791Sdim      }
278243791Sdim      llvm::yaml::ScalarNode *ValueString =
279249423Sdim          dyn_cast<llvm::yaml::ScalarNode>(Value);
280296417Sdim      llvm::yaml::SequenceNode *SequenceString =
281296417Sdim          dyn_cast<llvm::yaml::SequenceNode>(Value);
282296417Sdim      if (KeyValue == "arguments" && !SequenceString) {
283296417Sdim        ErrorMessage = "Expected sequence as value.";
284296417Sdim        return false;
285296417Sdim      } else if (KeyValue != "arguments" && !ValueString) {
286243791Sdim        ErrorMessage = "Expected string as value.";
287243791Sdim        return false;
288243791Sdim      }
289296417Sdim      if (KeyValue == "directory") {
290243791Sdim        Directory = ValueString;
291296417Sdim      } else if (KeyValue == "arguments") {
292296417Sdim        Command = std::vector<llvm::yaml::ScalarNode *>();
293296417Sdim        for (auto &Argument : *SequenceString) {
294296417Sdim          auto Scalar = dyn_cast<llvm::yaml::ScalarNode>(&Argument);
295296417Sdim          if (!Scalar) {
296296417Sdim            ErrorMessage = "Only strings are allowed in 'arguments'.";
297296417Sdim            return false;
298296417Sdim          }
299296417Sdim          Command->push_back(Scalar);
300296417Sdim        }
301296417Sdim      } else if (KeyValue == "command") {
302296417Sdim        if (!Command)
303296417Sdim          Command = std::vector<llvm::yaml::ScalarNode *>(1, ValueString);
304296417Sdim      } else if (KeyValue == "file") {
305243791Sdim        File = ValueString;
306243791Sdim      } else {
307243791Sdim        ErrorMessage = ("Unknown key: \"" +
308243791Sdim                        KeyString->getRawValue() + "\"").str();
309243791Sdim        return false;
310243791Sdim      }
311243791Sdim    }
312243791Sdim    if (!File) {
313243791Sdim      ErrorMessage = "Missing key: \"file\".";
314243791Sdim      return false;
315243791Sdim    }
316243791Sdim    if (!Command) {
317296417Sdim      ErrorMessage = "Missing key: \"command\" or \"arguments\".";
318243791Sdim      return false;
319243791Sdim    }
320243791Sdim    if (!Directory) {
321243791Sdim      ErrorMessage = "Missing key: \"directory\".";
322243791Sdim      return false;
323243791Sdim    }
324249423Sdim    SmallString<8> FileStorage;
325243791Sdim    StringRef FileName = File->getValue(FileStorage);
326249423Sdim    SmallString<128> NativeFilePath;
327243791Sdim    if (llvm::sys::path::is_relative(FileName)) {
328249423Sdim      SmallString<8> DirectoryStorage;
329249423Sdim      SmallString<128> AbsolutePath(
330243791Sdim          Directory->getValue(DirectoryStorage));
331243791Sdim      llvm::sys::path::append(AbsolutePath, FileName);
332288943Sdim      llvm::sys::path::native(AbsolutePath, NativeFilePath);
333243791Sdim    } else {
334243791Sdim      llvm::sys::path::native(FileName, NativeFilePath);
335243791Sdim    }
336296417Sdim    auto Cmd = CompileCommandRef(Directory, File, *Command);
337296417Sdim    IndexByFile[NativeFilePath].push_back(Cmd);
338296417Sdim    AllCommands.push_back(Cmd);
339288943Sdim    MatchTrie.insert(NativeFilePath);
340243791Sdim  }
341243791Sdim  return true;
342243791Sdim}
343243791Sdim
344243791Sdim} // end namespace tooling
345243791Sdim} // end namespace clang
346