]> CyberLeo.Net >> Repos - FreeBSD/stable/9.git/blob - contrib/llvm/tools/clang/lib/Tooling/CompilationDatabase.cpp
MFC r234353:
[FreeBSD/stable/9.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 "llvm/ADT/SmallString.h"
16 #include "llvm/Support/YAMLParser.h"
17 #include "llvm/Support/Path.h"
18 #include "llvm/Support/system_error.h"
19
20 namespace clang {
21 namespace tooling {
22
23 namespace {
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(...)).
29 class 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
101 std::vector<std::string> unescapeCommandLine(
102     StringRef EscapedCommandLine) {
103   CommandLineArgumentParser parser(EscapedCommandLine);
104   return parser.parse();
105 }
106
107 } // end namespace
108
109 CompilationDatabase::~CompilationDatabase() {}
110
111 CompilationDatabase *
112 CompilationDatabase::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
124 FixedCompilationDatabase *
125 FixedCompilationDatabase::loadFromCommandLine(int &Argc,
126                                               const char **Argv,
127                                               Twine Directory) {
128   const char **DoubleDash = std::find(Argv, Argv + Argc, StringRef("--"));
129   if (DoubleDash == Argv + Argc)
130     return NULL;
131   std::vector<std::string> CommandLine(DoubleDash + 1, Argv + Argc);
132   Argc = DoubleDash - Argv;
133   return new FixedCompilationDatabase(Directory, CommandLine);
134 }
135
136 FixedCompilationDatabase::
137 FixedCompilationDatabase(Twine Directory, ArrayRef<std::string> CommandLine) {
138   std::vector<std::string> ToolCommandLine(1, "clang-tool");
139   ToolCommandLine.insert(ToolCommandLine.end(),
140                          CommandLine.begin(), CommandLine.end());
141   CompileCommands.push_back(CompileCommand(Directory, ToolCommandLine));
142 }
143
144 std::vector<CompileCommand>
145 FixedCompilationDatabase::getCompileCommands(StringRef FilePath) const {
146   std::vector<CompileCommand> Result(CompileCommands);
147   Result[0].CommandLine.push_back(FilePath);
148   return Result;
149 }
150
151 JSONCompilationDatabase *
152 JSONCompilationDatabase::loadFromFile(StringRef FilePath,
153                                       std::string &ErrorMessage) {
154   llvm::OwningPtr<llvm::MemoryBuffer> DatabaseBuffer;
155   llvm::error_code Result =
156     llvm::MemoryBuffer::getFile(FilePath, DatabaseBuffer);
157   if (Result != 0) {
158     ErrorMessage = "Error while opening JSON database: " + Result.message();
159     return NULL;
160   }
161   llvm::OwningPtr<JSONCompilationDatabase> Database(
162     new JSONCompilationDatabase(DatabaseBuffer.take()));
163   if (!Database->parse(ErrorMessage))
164     return NULL;
165   return Database.take();
166 }
167
168 JSONCompilationDatabase *
169 JSONCompilationDatabase::loadFromBuffer(StringRef DatabaseString,
170                                         std::string &ErrorMessage) {
171   llvm::OwningPtr<llvm::MemoryBuffer> DatabaseBuffer(
172       llvm::MemoryBuffer::getMemBuffer(DatabaseString));
173   llvm::OwningPtr<JSONCompilationDatabase> Database(
174     new JSONCompilationDatabase(DatabaseBuffer.take()));
175   if (!Database->parse(ErrorMessage))
176     return NULL;
177   return Database.take();
178 }
179
180 std::vector<CompileCommand>
181 JSONCompilationDatabase::getCompileCommands(StringRef FilePath) const {
182   llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator
183     CommandsRefI = IndexByFile.find(FilePath);
184   if (CommandsRefI == IndexByFile.end())
185     return std::vector<CompileCommand>();
186   const std::vector<CompileCommandRef> &CommandsRef = CommandsRefI->getValue();
187   std::vector<CompileCommand> Commands;
188   for (int I = 0, E = CommandsRef.size(); I != E; ++I) {
189     llvm::SmallString<8> DirectoryStorage;
190     llvm::SmallString<1024> CommandStorage;
191     Commands.push_back(CompileCommand(
192       // FIXME: Escape correctly:
193       CommandsRef[I].first->getValue(DirectoryStorage),
194       unescapeCommandLine(CommandsRef[I].second->getValue(CommandStorage))));
195   }
196   return Commands;
197 }
198
199 bool JSONCompilationDatabase::parse(std::string &ErrorMessage) {
200   llvm::yaml::document_iterator I = YAMLStream.begin();
201   if (I == YAMLStream.end()) {
202     ErrorMessage = "Error while parsing YAML.";
203     return false;
204   }
205   llvm::yaml::Node *Root = I->getRoot();
206   if (Root == NULL) {
207     ErrorMessage = "Error while parsing YAML.";
208     return false;
209   }
210   llvm::yaml::SequenceNode *Array =
211     llvm::dyn_cast<llvm::yaml::SequenceNode>(Root);
212   if (Array == NULL) {
213     ErrorMessage = "Expected array.";
214     return false;
215   }
216   for (llvm::yaml::SequenceNode::iterator AI = Array->begin(),
217                                           AE = Array->end();
218        AI != AE; ++AI) {
219     llvm::yaml::MappingNode *Object =
220       llvm::dyn_cast<llvm::yaml::MappingNode>(&*AI);
221     if (Object == NULL) {
222       ErrorMessage = "Expected object.";
223       return false;
224     }
225     llvm::yaml::ScalarNode *Directory;
226     llvm::yaml::ScalarNode *Command;
227     llvm::SmallString<8> FileStorage;
228     llvm::StringRef File;
229     for (llvm::yaml::MappingNode::iterator KVI = Object->begin(),
230                                            KVE = Object->end();
231          KVI != KVE; ++KVI) {
232       llvm::yaml::Node *Value = (*KVI).getValue();
233       if (Value == NULL) {
234         ErrorMessage = "Expected value.";
235         return false;
236       }
237       llvm::yaml::ScalarNode *ValueString =
238         llvm::dyn_cast<llvm::yaml::ScalarNode>(Value);
239       if (ValueString == NULL) {
240         ErrorMessage = "Expected string as value.";
241         return false;
242       }
243       llvm::yaml::ScalarNode *KeyString =
244         llvm::dyn_cast<llvm::yaml::ScalarNode>((*KVI).getKey());
245       llvm::SmallString<8> KeyStorage;
246       if (KeyString->getValue(KeyStorage) == "directory") {
247         Directory = ValueString;
248       } else if (KeyString->getValue(KeyStorage) == "command") {
249         Command = ValueString;
250       } else if (KeyString->getValue(KeyStorage) == "file") {
251         File = ValueString->getValue(FileStorage);
252       } else {
253         ErrorMessage = ("Unknown key: \"" +
254                         KeyString->getRawValue() + "\"").str();
255         return false;
256       }
257     }
258     IndexByFile[File].push_back(
259       CompileCommandRef(Directory, Command));
260   }
261   return true;
262 }
263
264 } // end namespace tooling
265 } // end namespace clang
266