]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/llvm/tools/clang/lib/Tooling/CompilationDatabase.cpp
Update dialog to 20120706: includes minor useability enhancements and
[FreeBSD/FreeBSD.git] / contrib / llvm / tools / clang / lib / Tooling / CompilationDatabase.cpp
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
25 namespace clang {
26 namespace tooling {
27
28 namespace {
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(...)).
34 class 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
106 std::vector<std::string> unescapeCommandLine(
107     StringRef EscapedCommandLine) {
108   CommandLineArgumentParser parser(EscapedCommandLine);
109   return parser.parse();
110 }
111
112 } // end namespace
113
114 CompilationDatabase::~CompilationDatabase() {}
115
116 CompilationDatabase *
117 CompilationDatabase::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
129 static CompilationDatabase *
130 findCompilationDatabaseFromDirectory(StringRef Directory) {
131 #ifdef USE_CUSTOM_COMPILATION_DATABASE
132   if (CompilationDatabase *DB =
133       ::clang::tooling::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
148 CompilationDatabase *
149 CompilationDatabase::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
162 CompilationDatabase *
163 CompilationDatabase::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
175 FixedCompilationDatabase *
176 FixedCompilationDatabase::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
187 FixedCompilationDatabase::
188 FixedCompilationDatabase(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
195 std::vector<CompileCommand>
196 FixedCompilationDatabase::getCompileCommands(StringRef FilePath) const {
197   std::vector<CompileCommand> Result(CompileCommands);
198   Result[0].CommandLine.push_back(FilePath);
199   return Result;
200 }
201
202 std::vector<std::string>
203 FixedCompilationDatabase::getAllFiles() const {
204   return std::vector<std::string>();
205 }
206
207 JSONCompilationDatabase *
208 JSONCompilationDatabase::loadFromFile(StringRef FilePath,
209                                       std::string &ErrorMessage) {
210   llvm::OwningPtr<llvm::MemoryBuffer> DatabaseBuffer;
211   llvm::error_code Result =
212     llvm::MemoryBuffer::getFile(FilePath, DatabaseBuffer);
213   if (Result != 0) {
214     ErrorMessage = "Error while opening JSON database: " + Result.message();
215     return NULL;
216   }
217   llvm::OwningPtr<JSONCompilationDatabase> Database(
218     new JSONCompilationDatabase(DatabaseBuffer.take()));
219   if (!Database->parse(ErrorMessage))
220     return NULL;
221   return Database.take();
222 }
223
224 JSONCompilationDatabase *
225 JSONCompilationDatabase::loadFromBuffer(StringRef DatabaseString,
226                                         std::string &ErrorMessage) {
227   llvm::OwningPtr<llvm::MemoryBuffer> DatabaseBuffer(
228       llvm::MemoryBuffer::getMemBuffer(DatabaseString));
229   llvm::OwningPtr<JSONCompilationDatabase> Database(
230     new JSONCompilationDatabase(DatabaseBuffer.take()));
231   if (!Database->parse(ErrorMessage))
232     return NULL;
233   return Database.take();
234 }
235
236 std::vector<CompileCommand>
237 JSONCompilationDatabase::getCompileCommands(StringRef FilePath) const {
238   llvm::SmallString<128> NativeFilePath;
239   llvm::sys::path::native(FilePath, NativeFilePath);
240   llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator
241     CommandsRefI = IndexByFile.find(NativeFilePath);
242   if (CommandsRefI == IndexByFile.end())
243     return std::vector<CompileCommand>();
244   const std::vector<CompileCommandRef> &CommandsRef = CommandsRefI->getValue();
245   std::vector<CompileCommand> Commands;
246   for (int I = 0, E = CommandsRef.size(); I != E; ++I) {
247     llvm::SmallString<8> DirectoryStorage;
248     llvm::SmallString<1024> CommandStorage;
249     Commands.push_back(CompileCommand(
250       // FIXME: Escape correctly:
251       CommandsRef[I].first->getValue(DirectoryStorage),
252       unescapeCommandLine(CommandsRef[I].second->getValue(CommandStorage))));
253   }
254   return Commands;
255 }
256
257 std::vector<std::string>
258 JSONCompilationDatabase::getAllFiles() const {
259   std::vector<std::string> Result;
260
261   llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator
262     CommandsRefI = IndexByFile.begin();
263   const llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator
264     CommandsRefEnd = IndexByFile.end();
265   for (; CommandsRefI != CommandsRefEnd; ++CommandsRefI) {
266     Result.push_back(CommandsRefI->first().str());
267   }
268
269   return Result;
270 }
271
272 bool JSONCompilationDatabase::parse(std::string &ErrorMessage) {
273   llvm::yaml::document_iterator I = YAMLStream.begin();
274   if (I == YAMLStream.end()) {
275     ErrorMessage = "Error while parsing YAML.";
276     return false;
277   }
278   llvm::yaml::Node *Root = I->getRoot();
279   if (Root == NULL) {
280     ErrorMessage = "Error while parsing YAML.";
281     return false;
282   }
283   llvm::yaml::SequenceNode *Array =
284     llvm::dyn_cast<llvm::yaml::SequenceNode>(Root);
285   if (Array == NULL) {
286     ErrorMessage = "Expected array.";
287     return false;
288   }
289   for (llvm::yaml::SequenceNode::iterator AI = Array->begin(),
290                                           AE = Array->end();
291        AI != AE; ++AI) {
292     llvm::yaml::MappingNode *Object =
293       llvm::dyn_cast<llvm::yaml::MappingNode>(&*AI);
294     if (Object == NULL) {
295       ErrorMessage = "Expected object.";
296       return false;
297     }
298     llvm::yaml::ScalarNode *Directory = NULL;
299     llvm::yaml::ScalarNode *Command = NULL;
300     llvm::yaml::ScalarNode *File = NULL;
301     for (llvm::yaml::MappingNode::iterator KVI = Object->begin(),
302                                            KVE = Object->end();
303          KVI != KVE; ++KVI) {
304       llvm::yaml::Node *Value = (*KVI).getValue();
305       if (Value == NULL) {
306         ErrorMessage = "Expected value.";
307         return false;
308       }
309       llvm::yaml::ScalarNode *ValueString =
310         llvm::dyn_cast<llvm::yaml::ScalarNode>(Value);
311       if (ValueString == NULL) {
312         ErrorMessage = "Expected string as value.";
313         return false;
314       }
315       llvm::yaml::ScalarNode *KeyString =
316         llvm::dyn_cast<llvm::yaml::ScalarNode>((*KVI).getKey());
317       if (KeyString == NULL) {
318         ErrorMessage = "Expected strings as key.";
319         return false;
320       }
321       llvm::SmallString<8> KeyStorage;
322       if (KeyString->getValue(KeyStorage) == "directory") {
323         Directory = ValueString;
324       } else if (KeyString->getValue(KeyStorage) == "command") {
325         Command = ValueString;
326       } else if (KeyString->getValue(KeyStorage) == "file") {
327         File = ValueString;
328       } else {
329         ErrorMessage = ("Unknown key: \"" +
330                         KeyString->getRawValue() + "\"").str();
331         return false;
332       }
333     }
334     if (!File) {
335       ErrorMessage = "Missing key: \"file\".";
336       return false;
337     }
338     if (!Command) {
339       ErrorMessage = "Missing key: \"command\".";
340       return false;
341     }
342     if (!Directory) {
343       ErrorMessage = "Missing key: \"directory\".";
344       return false;
345     }
346     llvm::SmallString<8> FileStorage;
347     llvm::SmallString<128> NativeFilePath;
348     llvm::sys::path::native(File->getValue(FileStorage), NativeFilePath);
349     IndexByFile[NativeFilePath].push_back(
350       CompileCommandRef(Directory, Command));
351   }
352   return true;
353 }
354
355 } // end namespace tooling
356 } // end namespace clang