blob: a06343ddab6ee9994aa41ef136a3a6af5c9a21c3 [file] [log] [blame]
Manuel Klimekcb971c62012-04-04 12:07:46 +00001//===--- 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"
Manuel Klimek8fa2fb82012-07-10 13:10:51 +000015#include "clang/Tooling/Tooling.h"
Manuel Klimekcb971c62012-04-04 12:07:46 +000016#include "llvm/ADT/SmallString.h"
Manuel Klimekc661f142012-04-17 16:54:26 +000017#include "llvm/Support/YAMLParser.h"
Manuel Klimekcb971c62012-04-04 12:07:46 +000018#include "llvm/Support/Path.h"
19#include "llvm/Support/system_error.h"
20
21namespace clang {
22namespace tooling {
23
24namespace {
25
Manuel Klimekc661f142012-04-17 16:54:26 +000026/// \brief A parser for escaped strings of command line arguments.
Manuel Klimekcb971c62012-04-04 12:07:46 +000027///
28/// Assumes \-escaping for quoted arguments (see the documentation of
Manuel Klimekc661f142012-04-17 16:54:26 +000029/// unescapeCommandLine(...)).
Manuel Klimekcb971c62012-04-04 12:07:46 +000030class CommandLineArgumentParser {
31 public:
32 CommandLineArgumentParser(StringRef CommandLine)
33 : Input(CommandLine), Position(Input.begin()-1) {}
34
35 std::vector<std::string> parse() {
36 bool HasMoreInput = true;
37 while (HasMoreInput && nextNonWhitespace()) {
38 std::string Argument;
39 HasMoreInput = parseStringInto(Argument);
40 CommandLine.push_back(Argument);
41 }
42 return CommandLine;
43 }
44
45 private:
46 // All private methods return true if there is more input available.
47
48 bool parseStringInto(std::string &String) {
49 do {
50 if (*Position == '"') {
51 if (!parseQuotedStringInto(String)) return false;
52 } else {
53 if (!parseFreeStringInto(String)) return false;
54 }
55 } while (*Position != ' ');
56 return true;
57 }
58
59 bool parseQuotedStringInto(std::string &String) {
60 if (!next()) return false;
61 while (*Position != '"') {
62 if (!skipEscapeCharacter()) return false;
63 String.push_back(*Position);
64 if (!next()) return false;
65 }
66 return next();
67 }
68
69 bool parseFreeStringInto(std::string &String) {
70 do {
71 if (!skipEscapeCharacter()) return false;
72 String.push_back(*Position);
73 if (!next()) return false;
74 } while (*Position != ' ' && *Position != '"');
75 return true;
76 }
77
78 bool skipEscapeCharacter() {
79 if (*Position == '\\') {
80 return next();
81 }
82 return true;
83 }
84
85 bool nextNonWhitespace() {
86 do {
87 if (!next()) return false;
88 } while (*Position == ' ');
89 return true;
90 }
91
92 bool next() {
93 ++Position;
Manuel Klimekcb971c62012-04-04 12:07:46 +000094 return Position != Input.end();
95 }
96
97 const StringRef Input;
98 StringRef::iterator Position;
99 std::vector<std::string> CommandLine;
100};
101
Manuel Klimekc661f142012-04-17 16:54:26 +0000102std::vector<std::string> unescapeCommandLine(
103 StringRef EscapedCommandLine) {
104 CommandLineArgumentParser parser(EscapedCommandLine);
Manuel Klimekcb971c62012-04-04 12:07:46 +0000105 return parser.parse();
106}
107
108} // end namespace
109
110CompilationDatabase::~CompilationDatabase() {}
111
112CompilationDatabase *
113CompilationDatabase::loadFromDirectory(StringRef BuildDirectory,
114 std::string &ErrorMessage) {
115 llvm::SmallString<1024> JSONDatabasePath(BuildDirectory);
116 llvm::sys::path::append(JSONDatabasePath, "compile_commands.json");
117 llvm::OwningPtr<CompilationDatabase> Database(
118 JSONCompilationDatabase::loadFromFile(JSONDatabasePath, ErrorMessage));
119 if (!Database) {
120 return NULL;
121 }
122 return Database.take();
123}
124
Arnaud A. de Grandmaison4187df52012-07-10 16:56:35 +0000125static CompilationDatabase *
126findCompilationDatabaseFromDirectory(StringRef Directory) {
127 while (!Directory.empty()) {
128 std::string LoadErrorMessage;
129
130 if (CompilationDatabase *DB =
131 CompilationDatabase::loadFromDirectory(Directory, LoadErrorMessage))
132 return DB;
133
134 Directory = llvm::sys::path::parent_path(Directory);
135 }
136 return NULL;
137}
138
Manuel Klimek8fa2fb82012-07-10 13:10:51 +0000139CompilationDatabase *
140CompilationDatabase::autoDetectFromSource(StringRef SourceFile,
141 std::string &ErrorMessage) {
142 llvm::SmallString<1024> AbsolutePath(getAbsolutePath(SourceFile));
143 StringRef Directory = llvm::sys::path::parent_path(AbsolutePath);
Arnaud A. de Grandmaison4187df52012-07-10 16:56:35 +0000144
145 CompilationDatabase *DB = findCompilationDatabaseFromDirectory(Directory);
146
147 if (!DB)
148 ErrorMessage = ("Could not auto-detect compilation database for file \"" +
149 SourceFile + "\"").str();
150 return DB;
151}
152
153CompilationDatabase *
154CompilationDatabase::autoDetectFromDirectory(StringRef SourceDir,
155 std::string &ErrorMessage) {
156 llvm::SmallString<1024> AbsolutePath(getAbsolutePath(SourceDir));
157
158 CompilationDatabase *DB = findCompilationDatabaseFromDirectory(AbsolutePath);
159
160 if (!DB)
161 ErrorMessage = ("Could not auto-detect compilation database from directory \"" +
162 SourceDir + "\"").str();
163 return DB;
Manuel Klimek8fa2fb82012-07-10 13:10:51 +0000164}
165
Manuel Klimek30318e62012-04-18 07:41:50 +0000166FixedCompilationDatabase *
167FixedCompilationDatabase::loadFromCommandLine(int &Argc,
168 const char **Argv,
169 Twine Directory) {
170 const char **DoubleDash = std::find(Argv, Argv + Argc, StringRef("--"));
171 if (DoubleDash == Argv + Argc)
172 return NULL;
173 std::vector<std::string> CommandLine(DoubleDash + 1, Argv + Argc);
174 Argc = DoubleDash - Argv;
175 return new FixedCompilationDatabase(Directory, CommandLine);
176}
177
178FixedCompilationDatabase::
179FixedCompilationDatabase(Twine Directory, ArrayRef<std::string> CommandLine) {
180 std::vector<std::string> ToolCommandLine(1, "clang-tool");
181 ToolCommandLine.insert(ToolCommandLine.end(),
182 CommandLine.begin(), CommandLine.end());
183 CompileCommands.push_back(CompileCommand(Directory, ToolCommandLine));
184}
185
186std::vector<CompileCommand>
187FixedCompilationDatabase::getCompileCommands(StringRef FilePath) const {
188 std::vector<CompileCommand> Result(CompileCommands);
189 Result[0].CommandLine.push_back(FilePath);
190 return Result;
191}
192
Manuel Klimekcb971c62012-04-04 12:07:46 +0000193JSONCompilationDatabase *
194JSONCompilationDatabase::loadFromFile(StringRef FilePath,
195 std::string &ErrorMessage) {
196 llvm::OwningPtr<llvm::MemoryBuffer> DatabaseBuffer;
197 llvm::error_code Result =
198 llvm::MemoryBuffer::getFile(FilePath, DatabaseBuffer);
199 if (Result != 0) {
200 ErrorMessage = "Error while opening JSON database: " + Result.message();
201 return NULL;
202 }
203 llvm::OwningPtr<JSONCompilationDatabase> Database(
204 new JSONCompilationDatabase(DatabaseBuffer.take()));
205 if (!Database->parse(ErrorMessage))
206 return NULL;
207 return Database.take();
208}
209
210JSONCompilationDatabase *
211JSONCompilationDatabase::loadFromBuffer(StringRef DatabaseString,
212 std::string &ErrorMessage) {
213 llvm::OwningPtr<llvm::MemoryBuffer> DatabaseBuffer(
214 llvm::MemoryBuffer::getMemBuffer(DatabaseString));
215 llvm::OwningPtr<JSONCompilationDatabase> Database(
216 new JSONCompilationDatabase(DatabaseBuffer.take()));
217 if (!Database->parse(ErrorMessage))
218 return NULL;
219 return Database.take();
220}
221
222std::vector<CompileCommand>
223JSONCompilationDatabase::getCompileCommands(StringRef FilePath) const {
NAKAMURA Takumi62d198c2012-05-23 22:24:20 +0000224 llvm::SmallString<128> NativeFilePath;
225 llvm::sys::path::native(FilePath, NativeFilePath);
Manuel Klimekcb971c62012-04-04 12:07:46 +0000226 llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator
NAKAMURA Takumi62d198c2012-05-23 22:24:20 +0000227 CommandsRefI = IndexByFile.find(NativeFilePath);
Manuel Klimekcb971c62012-04-04 12:07:46 +0000228 if (CommandsRefI == IndexByFile.end())
229 return std::vector<CompileCommand>();
230 const std::vector<CompileCommandRef> &CommandsRef = CommandsRefI->getValue();
231 std::vector<CompileCommand> Commands;
232 for (int I = 0, E = CommandsRef.size(); I != E; ++I) {
Manuel Klimekc661f142012-04-17 16:54:26 +0000233 llvm::SmallString<8> DirectoryStorage;
234 llvm::SmallString<1024> CommandStorage;
Manuel Klimekcb971c62012-04-04 12:07:46 +0000235 Commands.push_back(CompileCommand(
236 // FIXME: Escape correctly:
Manuel Klimekc661f142012-04-17 16:54:26 +0000237 CommandsRef[I].first->getValue(DirectoryStorage),
238 unescapeCommandLine(CommandsRef[I].second->getValue(CommandStorage))));
Manuel Klimekcb971c62012-04-04 12:07:46 +0000239 }
240 return Commands;
241}
242
243bool JSONCompilationDatabase::parse(std::string &ErrorMessage) {
Manuel Klimekc661f142012-04-17 16:54:26 +0000244 llvm::yaml::document_iterator I = YAMLStream.begin();
245 if (I == YAMLStream.end()) {
246 ErrorMessage = "Error while parsing YAML.";
Manuel Klimekcb971c62012-04-04 12:07:46 +0000247 return false;
248 }
Manuel Klimekc661f142012-04-17 16:54:26 +0000249 llvm::yaml::Node *Root = I->getRoot();
250 if (Root == NULL) {
251 ErrorMessage = "Error while parsing YAML.";
252 return false;
253 }
254 llvm::yaml::SequenceNode *Array =
255 llvm::dyn_cast<llvm::yaml::SequenceNode>(Root);
Manuel Klimekcb971c62012-04-04 12:07:46 +0000256 if (Array == NULL) {
257 ErrorMessage = "Expected array.";
258 return false;
259 }
Manuel Klimekc661f142012-04-17 16:54:26 +0000260 for (llvm::yaml::SequenceNode::iterator AI = Array->begin(),
261 AE = Array->end();
Manuel Klimekcb971c62012-04-04 12:07:46 +0000262 AI != AE; ++AI) {
Manuel Klimekc661f142012-04-17 16:54:26 +0000263 llvm::yaml::MappingNode *Object =
264 llvm::dyn_cast<llvm::yaml::MappingNode>(&*AI);
Manuel Klimekcb971c62012-04-04 12:07:46 +0000265 if (Object == NULL) {
266 ErrorMessage = "Expected object.";
267 return false;
268 }
Manuel Klimek1a8d6862012-05-15 11:46:07 +0000269 llvm::yaml::ScalarNode *Directory = NULL;
270 llvm::yaml::ScalarNode *Command = NULL;
271 llvm::yaml::ScalarNode *File = NULL;
Manuel Klimekc661f142012-04-17 16:54:26 +0000272 for (llvm::yaml::MappingNode::iterator KVI = Object->begin(),
273 KVE = Object->end();
Manuel Klimekcb971c62012-04-04 12:07:46 +0000274 KVI != KVE; ++KVI) {
Manuel Klimekc661f142012-04-17 16:54:26 +0000275 llvm::yaml::Node *Value = (*KVI).getValue();
Manuel Klimekcb971c62012-04-04 12:07:46 +0000276 if (Value == NULL) {
277 ErrorMessage = "Expected value.";
278 return false;
279 }
Manuel Klimekc661f142012-04-17 16:54:26 +0000280 llvm::yaml::ScalarNode *ValueString =
281 llvm::dyn_cast<llvm::yaml::ScalarNode>(Value);
Manuel Klimekcb971c62012-04-04 12:07:46 +0000282 if (ValueString == NULL) {
283 ErrorMessage = "Expected string as value.";
284 return false;
285 }
Manuel Klimekc661f142012-04-17 16:54:26 +0000286 llvm::yaml::ScalarNode *KeyString =
287 llvm::dyn_cast<llvm::yaml::ScalarNode>((*KVI).getKey());
Manuel Klimek1a8d6862012-05-15 11:46:07 +0000288 if (KeyString == NULL) {
289 ErrorMessage = "Expected strings as key.";
290 return false;
291 }
Manuel Klimekc661f142012-04-17 16:54:26 +0000292 llvm::SmallString<8> KeyStorage;
293 if (KeyString->getValue(KeyStorage) == "directory") {
294 Directory = ValueString;
295 } else if (KeyString->getValue(KeyStorage) == "command") {
296 Command = ValueString;
297 } else if (KeyString->getValue(KeyStorage) == "file") {
Manuel Klimek1a8d6862012-05-15 11:46:07 +0000298 File = ValueString;
Manuel Klimekcb971c62012-04-04 12:07:46 +0000299 } else {
Manuel Klimekc661f142012-04-17 16:54:26 +0000300 ErrorMessage = ("Unknown key: \"" +
301 KeyString->getRawValue() + "\"").str();
Manuel Klimekcb971c62012-04-04 12:07:46 +0000302 return false;
303 }
304 }
Manuel Klimek1a8d6862012-05-15 11:46:07 +0000305 if (!File) {
306 ErrorMessage = "Missing key: \"file\".";
307 return false;
308 }
309 if (!Command) {
310 ErrorMessage = "Missing key: \"command\".";
311 return false;
312 }
313 if (!Directory) {
314 ErrorMessage = "Missing key: \"directory\".";
315 return false;
316 }
317 llvm::SmallString<8> FileStorage;
NAKAMURA Takumi62d198c2012-05-23 22:24:20 +0000318 llvm::SmallString<128> NativeFilePath;
319 llvm::sys::path::native(File->getValue(FileStorage), NativeFilePath);
320 IndexByFile[NativeFilePath].push_back(
Manuel Klimekc661f142012-04-17 16:54:26 +0000321 CompileCommandRef(Directory, Command));
Manuel Klimekcb971c62012-04-04 12:07:46 +0000322 }
323 return true;
324}
325
326} // end namespace tooling
327} // end namespace clang