CompilationDatabase.cpp revision 068040ede2b2e6449d84273cc16e62fcd5e7e541
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 "clang/Tooling/Tooling.h"
16#include "llvm/ADT/SmallString.h"
17#include "llvm/Support/YAMLParser.h"
18#include "llvm/Support/Path.h"
19#include "llvm/Support/system_error.h"
20
21#ifdef USE_CUSTOM_COMPILATION_DATABASE
22#include "CustomCompilationDatabase.h"
23#endif
24
25namespace clang {
26namespace tooling {
27
28namespace {
29
30/// \brief A parser for escaped strings of command line arguments.
31///
32/// Assumes \-escaping for quoted arguments (see the documentation of
33/// unescapeCommandLine(...)).
34class CommandLineArgumentParser {
35 public:
36  CommandLineArgumentParser(StringRef CommandLine)
37      : Input(CommandLine), Position(Input.begin()-1) {}
38
39  std::vector<std::string> parse() {
40    bool HasMoreInput = true;
41    while (HasMoreInput && nextNonWhitespace()) {
42      std::string Argument;
43      HasMoreInput = parseStringInto(Argument);
44      CommandLine.push_back(Argument);
45    }
46    return CommandLine;
47  }
48
49 private:
50  // All private methods return true if there is more input available.
51
52  bool parseStringInto(std::string &String) {
53    do {
54      if (*Position == '"') {
55        if (!parseQuotedStringInto(String)) return false;
56      } else {
57        if (!parseFreeStringInto(String)) return false;
58      }
59    } while (*Position != ' ');
60    return true;
61  }
62
63  bool parseQuotedStringInto(std::string &String) {
64    if (!next()) return false;
65    while (*Position != '"') {
66      if (!skipEscapeCharacter()) return false;
67      String.push_back(*Position);
68      if (!next()) return false;
69    }
70    return next();
71  }
72
73  bool parseFreeStringInto(std::string &String) {
74    do {
75      if (!skipEscapeCharacter()) return false;
76      String.push_back(*Position);
77      if (!next()) return false;
78    } while (*Position != ' ' && *Position != '"');
79    return true;
80  }
81
82  bool skipEscapeCharacter() {
83    if (*Position == '\\') {
84      return next();
85    }
86    return true;
87  }
88
89  bool nextNonWhitespace() {
90    do {
91      if (!next()) return false;
92    } while (*Position == ' ');
93    return true;
94  }
95
96  bool next() {
97    ++Position;
98    return Position != Input.end();
99  }
100
101  const StringRef Input;
102  StringRef::iterator Position;
103  std::vector<std::string> CommandLine;
104};
105
106std::vector<std::string> unescapeCommandLine(
107    StringRef EscapedCommandLine) {
108  CommandLineArgumentParser parser(EscapedCommandLine);
109  return parser.parse();
110}
111
112} // end namespace
113
114CompilationDatabase::~CompilationDatabase() {}
115
116CompilationDatabase *
117CompilationDatabase::loadFromDirectory(StringRef BuildDirectory,
118                                       std::string &ErrorMessage) {
119  llvm::SmallString<1024> JSONDatabasePath(BuildDirectory);
120  llvm::sys::path::append(JSONDatabasePath, "compile_commands.json");
121  llvm::OwningPtr<CompilationDatabase> Database(
122    JSONCompilationDatabase::loadFromFile(JSONDatabasePath, ErrorMessage));
123  if (!Database) {
124    return NULL;
125  }
126  return Database.take();
127}
128
129static CompilationDatabase *
130findCompilationDatabaseFromDirectory(StringRef Directory) {
131#ifdef USE_CUSTOM_COMPILATION_DATABASE
132  if (CompilationDatabase *DB =
133      ::findCompilationDatabaseForDirectory(Directory))
134    return DB;
135#endif
136  while (!Directory.empty()) {
137    std::string LoadErrorMessage;
138
139    if (CompilationDatabase *DB =
140           CompilationDatabase::loadFromDirectory(Directory, LoadErrorMessage))
141      return DB;
142
143    Directory = llvm::sys::path::parent_path(Directory);
144  }
145  return NULL;
146}
147
148CompilationDatabase *
149CompilationDatabase::autoDetectFromSource(StringRef SourceFile,
150                                          std::string &ErrorMessage) {
151  llvm::SmallString<1024> AbsolutePath(getAbsolutePath(SourceFile));
152  StringRef Directory = llvm::sys::path::parent_path(AbsolutePath);
153
154  CompilationDatabase *DB = findCompilationDatabaseFromDirectory(Directory);
155
156  if (!DB)
157    ErrorMessage = ("Could not auto-detect compilation database for file \"" +
158                   SourceFile + "\"").str();
159  return DB;
160}
161
162CompilationDatabase *
163CompilationDatabase::autoDetectFromDirectory(StringRef SourceDir,
164                                             std::string &ErrorMessage) {
165  llvm::SmallString<1024> AbsolutePath(getAbsolutePath(SourceDir));
166
167  CompilationDatabase *DB = findCompilationDatabaseFromDirectory(AbsolutePath);
168
169  if (!DB)
170    ErrorMessage = ("Could not auto-detect compilation database from directory \"" +
171                   SourceDir + "\"").str();
172  return DB;
173}
174
175FixedCompilationDatabase *
176FixedCompilationDatabase::loadFromCommandLine(int &Argc,
177                                              const char **Argv,
178                                              Twine Directory) {
179  const char **DoubleDash = std::find(Argv, Argv + Argc, StringRef("--"));
180  if (DoubleDash == Argv + Argc)
181    return NULL;
182  std::vector<std::string> CommandLine(DoubleDash + 1, Argv + Argc);
183  Argc = DoubleDash - Argv;
184  return new FixedCompilationDatabase(Directory, CommandLine);
185}
186
187FixedCompilationDatabase::
188FixedCompilationDatabase(Twine Directory, ArrayRef<std::string> CommandLine) {
189  std::vector<std::string> ToolCommandLine(1, "clang-tool");
190  ToolCommandLine.insert(ToolCommandLine.end(),
191                         CommandLine.begin(), CommandLine.end());
192  CompileCommands.push_back(CompileCommand(Directory, ToolCommandLine));
193}
194
195std::vector<CompileCommand>
196FixedCompilationDatabase::getCompileCommands(StringRef FilePath) const {
197  std::vector<CompileCommand> Result(CompileCommands);
198  Result[0].CommandLine.push_back(FilePath);
199  return Result;
200}
201
202JSONCompilationDatabase *
203JSONCompilationDatabase::loadFromFile(StringRef FilePath,
204                                      std::string &ErrorMessage) {
205  llvm::OwningPtr<llvm::MemoryBuffer> DatabaseBuffer;
206  llvm::error_code Result =
207    llvm::MemoryBuffer::getFile(FilePath, DatabaseBuffer);
208  if (Result != 0) {
209    ErrorMessage = "Error while opening JSON database: " + Result.message();
210    return NULL;
211  }
212  llvm::OwningPtr<JSONCompilationDatabase> Database(
213    new JSONCompilationDatabase(DatabaseBuffer.take()));
214  if (!Database->parse(ErrorMessage))
215    return NULL;
216  return Database.take();
217}
218
219JSONCompilationDatabase *
220JSONCompilationDatabase::loadFromBuffer(StringRef DatabaseString,
221                                        std::string &ErrorMessage) {
222  llvm::OwningPtr<llvm::MemoryBuffer> DatabaseBuffer(
223      llvm::MemoryBuffer::getMemBuffer(DatabaseString));
224  llvm::OwningPtr<JSONCompilationDatabase> Database(
225    new JSONCompilationDatabase(DatabaseBuffer.take()));
226  if (!Database->parse(ErrorMessage))
227    return NULL;
228  return Database.take();
229}
230
231std::vector<CompileCommand>
232JSONCompilationDatabase::getCompileCommands(StringRef FilePath) const {
233  llvm::SmallString<128> NativeFilePath;
234  llvm::sys::path::native(FilePath, NativeFilePath);
235  llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator
236    CommandsRefI = IndexByFile.find(NativeFilePath);
237  if (CommandsRefI == IndexByFile.end())
238    return std::vector<CompileCommand>();
239  const std::vector<CompileCommandRef> &CommandsRef = CommandsRefI->getValue();
240  std::vector<CompileCommand> Commands;
241  for (int I = 0, E = CommandsRef.size(); I != E; ++I) {
242    llvm::SmallString<8> DirectoryStorage;
243    llvm::SmallString<1024> CommandStorage;
244    Commands.push_back(CompileCommand(
245      // FIXME: Escape correctly:
246      CommandsRef[I].first->getValue(DirectoryStorage),
247      unescapeCommandLine(CommandsRef[I].second->getValue(CommandStorage))));
248  }
249  return Commands;
250}
251
252bool JSONCompilationDatabase::parse(std::string &ErrorMessage) {
253  llvm::yaml::document_iterator I = YAMLStream.begin();
254  if (I == YAMLStream.end()) {
255    ErrorMessage = "Error while parsing YAML.";
256    return false;
257  }
258  llvm::yaml::Node *Root = I->getRoot();
259  if (Root == NULL) {
260    ErrorMessage = "Error while parsing YAML.";
261    return false;
262  }
263  llvm::yaml::SequenceNode *Array =
264    llvm::dyn_cast<llvm::yaml::SequenceNode>(Root);
265  if (Array == NULL) {
266    ErrorMessage = "Expected array.";
267    return false;
268  }
269  for (llvm::yaml::SequenceNode::iterator AI = Array->begin(),
270                                          AE = Array->end();
271       AI != AE; ++AI) {
272    llvm::yaml::MappingNode *Object =
273      llvm::dyn_cast<llvm::yaml::MappingNode>(&*AI);
274    if (Object == NULL) {
275      ErrorMessage = "Expected object.";
276      return false;
277    }
278    llvm::yaml::ScalarNode *Directory = NULL;
279    llvm::yaml::ScalarNode *Command = NULL;
280    llvm::yaml::ScalarNode *File = NULL;
281    for (llvm::yaml::MappingNode::iterator KVI = Object->begin(),
282                                           KVE = Object->end();
283         KVI != KVE; ++KVI) {
284      llvm::yaml::Node *Value = (*KVI).getValue();
285      if (Value == NULL) {
286        ErrorMessage = "Expected value.";
287        return false;
288      }
289      llvm::yaml::ScalarNode *ValueString =
290        llvm::dyn_cast<llvm::yaml::ScalarNode>(Value);
291      if (ValueString == NULL) {
292        ErrorMessage = "Expected string as value.";
293        return false;
294      }
295      llvm::yaml::ScalarNode *KeyString =
296        llvm::dyn_cast<llvm::yaml::ScalarNode>((*KVI).getKey());
297      if (KeyString == NULL) {
298        ErrorMessage = "Expected strings as key.";
299        return false;
300      }
301      llvm::SmallString<8> KeyStorage;
302      if (KeyString->getValue(KeyStorage) == "directory") {
303        Directory = ValueString;
304      } else if (KeyString->getValue(KeyStorage) == "command") {
305        Command = ValueString;
306      } else if (KeyString->getValue(KeyStorage) == "file") {
307        File = ValueString;
308      } else {
309        ErrorMessage = ("Unknown key: \"" +
310                        KeyString->getRawValue() + "\"").str();
311        return false;
312      }
313    }
314    if (!File) {
315      ErrorMessage = "Missing key: \"file\".";
316      return false;
317    }
318    if (!Command) {
319      ErrorMessage = "Missing key: \"command\".";
320      return false;
321    }
322    if (!Directory) {
323      ErrorMessage = "Missing key: \"directory\".";
324      return false;
325    }
326    llvm::SmallString<8> FileStorage;
327    llvm::SmallString<128> NativeFilePath;
328    llvm::sys::path::native(File->getValue(FileStorage), NativeFilePath);
329    IndexByFile[NativeFilePath].push_back(
330      CompileCommandRef(Directory, Command));
331  }
332  return true;
333}
334
335} // end namespace tooling
336} // end namespace clang
337