blob: 8e911123bc800e45cb68c24d1cea9f64223f1d84 [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
Manuel Klimek8fa2fb82012-07-10 13:10:51 +0000125CompilationDatabase *
126CompilationDatabase::autoDetectFromSource(StringRef SourceFile,
127 std::string &ErrorMessage) {
128 llvm::SmallString<1024> AbsolutePath(getAbsolutePath(SourceFile));
129 StringRef Directory = llvm::sys::path::parent_path(AbsolutePath);
130 while (!Directory.empty()) {
131 std::string LoadErrorMessage;
132 if (CompilationDatabase *DB = loadFromDirectory(Directory,
133 LoadErrorMessage))
134 return DB;
135 Directory = llvm::sys::path::parent_path(Directory);
136 }
137 ErrorMessage = ("Could not auto-detect compilation database for file \"" +
138 SourceFile + "\"").str();
139 return NULL;
140}
141
Manuel Klimek30318e62012-04-18 07:41:50 +0000142FixedCompilationDatabase *
143FixedCompilationDatabase::loadFromCommandLine(int &Argc,
144 const char **Argv,
145 Twine Directory) {
146 const char **DoubleDash = std::find(Argv, Argv + Argc, StringRef("--"));
147 if (DoubleDash == Argv + Argc)
148 return NULL;
149 std::vector<std::string> CommandLine(DoubleDash + 1, Argv + Argc);
150 Argc = DoubleDash - Argv;
151 return new FixedCompilationDatabase(Directory, CommandLine);
152}
153
154FixedCompilationDatabase::
155FixedCompilationDatabase(Twine Directory, ArrayRef<std::string> CommandLine) {
156 std::vector<std::string> ToolCommandLine(1, "clang-tool");
157 ToolCommandLine.insert(ToolCommandLine.end(),
158 CommandLine.begin(), CommandLine.end());
159 CompileCommands.push_back(CompileCommand(Directory, ToolCommandLine));
160}
161
162std::vector<CompileCommand>
163FixedCompilationDatabase::getCompileCommands(StringRef FilePath) const {
164 std::vector<CompileCommand> Result(CompileCommands);
165 Result[0].CommandLine.push_back(FilePath);
166 return Result;
167}
168
Manuel Klimekcb971c62012-04-04 12:07:46 +0000169JSONCompilationDatabase *
170JSONCompilationDatabase::loadFromFile(StringRef FilePath,
171 std::string &ErrorMessage) {
172 llvm::OwningPtr<llvm::MemoryBuffer> DatabaseBuffer;
173 llvm::error_code Result =
174 llvm::MemoryBuffer::getFile(FilePath, DatabaseBuffer);
175 if (Result != 0) {
176 ErrorMessage = "Error while opening JSON database: " + Result.message();
177 return NULL;
178 }
179 llvm::OwningPtr<JSONCompilationDatabase> Database(
180 new JSONCompilationDatabase(DatabaseBuffer.take()));
181 if (!Database->parse(ErrorMessage))
182 return NULL;
183 return Database.take();
184}
185
186JSONCompilationDatabase *
187JSONCompilationDatabase::loadFromBuffer(StringRef DatabaseString,
188 std::string &ErrorMessage) {
189 llvm::OwningPtr<llvm::MemoryBuffer> DatabaseBuffer(
190 llvm::MemoryBuffer::getMemBuffer(DatabaseString));
191 llvm::OwningPtr<JSONCompilationDatabase> Database(
192 new JSONCompilationDatabase(DatabaseBuffer.take()));
193 if (!Database->parse(ErrorMessage))
194 return NULL;
195 return Database.take();
196}
197
198std::vector<CompileCommand>
199JSONCompilationDatabase::getCompileCommands(StringRef FilePath) const {
NAKAMURA Takumi62d198c2012-05-23 22:24:20 +0000200 llvm::SmallString<128> NativeFilePath;
201 llvm::sys::path::native(FilePath, NativeFilePath);
Manuel Klimekcb971c62012-04-04 12:07:46 +0000202 llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator
NAKAMURA Takumi62d198c2012-05-23 22:24:20 +0000203 CommandsRefI = IndexByFile.find(NativeFilePath);
Manuel Klimekcb971c62012-04-04 12:07:46 +0000204 if (CommandsRefI == IndexByFile.end())
205 return std::vector<CompileCommand>();
206 const std::vector<CompileCommandRef> &CommandsRef = CommandsRefI->getValue();
207 std::vector<CompileCommand> Commands;
208 for (int I = 0, E = CommandsRef.size(); I != E; ++I) {
Manuel Klimekc661f142012-04-17 16:54:26 +0000209 llvm::SmallString<8> DirectoryStorage;
210 llvm::SmallString<1024> CommandStorage;
Manuel Klimekcb971c62012-04-04 12:07:46 +0000211 Commands.push_back(CompileCommand(
212 // FIXME: Escape correctly:
Manuel Klimekc661f142012-04-17 16:54:26 +0000213 CommandsRef[I].first->getValue(DirectoryStorage),
214 unescapeCommandLine(CommandsRef[I].second->getValue(CommandStorage))));
Manuel Klimekcb971c62012-04-04 12:07:46 +0000215 }
216 return Commands;
217}
218
219bool JSONCompilationDatabase::parse(std::string &ErrorMessage) {
Manuel Klimekc661f142012-04-17 16:54:26 +0000220 llvm::yaml::document_iterator I = YAMLStream.begin();
221 if (I == YAMLStream.end()) {
222 ErrorMessage = "Error while parsing YAML.";
Manuel Klimekcb971c62012-04-04 12:07:46 +0000223 return false;
224 }
Manuel Klimekc661f142012-04-17 16:54:26 +0000225 llvm::yaml::Node *Root = I->getRoot();
226 if (Root == NULL) {
227 ErrorMessage = "Error while parsing YAML.";
228 return false;
229 }
230 llvm::yaml::SequenceNode *Array =
231 llvm::dyn_cast<llvm::yaml::SequenceNode>(Root);
Manuel Klimekcb971c62012-04-04 12:07:46 +0000232 if (Array == NULL) {
233 ErrorMessage = "Expected array.";
234 return false;
235 }
Manuel Klimekc661f142012-04-17 16:54:26 +0000236 for (llvm::yaml::SequenceNode::iterator AI = Array->begin(),
237 AE = Array->end();
Manuel Klimekcb971c62012-04-04 12:07:46 +0000238 AI != AE; ++AI) {
Manuel Klimekc661f142012-04-17 16:54:26 +0000239 llvm::yaml::MappingNode *Object =
240 llvm::dyn_cast<llvm::yaml::MappingNode>(&*AI);
Manuel Klimekcb971c62012-04-04 12:07:46 +0000241 if (Object == NULL) {
242 ErrorMessage = "Expected object.";
243 return false;
244 }
Manuel Klimek1a8d6862012-05-15 11:46:07 +0000245 llvm::yaml::ScalarNode *Directory = NULL;
246 llvm::yaml::ScalarNode *Command = NULL;
247 llvm::yaml::ScalarNode *File = NULL;
Manuel Klimekc661f142012-04-17 16:54:26 +0000248 for (llvm::yaml::MappingNode::iterator KVI = Object->begin(),
249 KVE = Object->end();
Manuel Klimekcb971c62012-04-04 12:07:46 +0000250 KVI != KVE; ++KVI) {
Manuel Klimekc661f142012-04-17 16:54:26 +0000251 llvm::yaml::Node *Value = (*KVI).getValue();
Manuel Klimekcb971c62012-04-04 12:07:46 +0000252 if (Value == NULL) {
253 ErrorMessage = "Expected value.";
254 return false;
255 }
Manuel Klimekc661f142012-04-17 16:54:26 +0000256 llvm::yaml::ScalarNode *ValueString =
257 llvm::dyn_cast<llvm::yaml::ScalarNode>(Value);
Manuel Klimekcb971c62012-04-04 12:07:46 +0000258 if (ValueString == NULL) {
259 ErrorMessage = "Expected string as value.";
260 return false;
261 }
Manuel Klimekc661f142012-04-17 16:54:26 +0000262 llvm::yaml::ScalarNode *KeyString =
263 llvm::dyn_cast<llvm::yaml::ScalarNode>((*KVI).getKey());
Manuel Klimek1a8d6862012-05-15 11:46:07 +0000264 if (KeyString == NULL) {
265 ErrorMessage = "Expected strings as key.";
266 return false;
267 }
Manuel Klimekc661f142012-04-17 16:54:26 +0000268 llvm::SmallString<8> KeyStorage;
269 if (KeyString->getValue(KeyStorage) == "directory") {
270 Directory = ValueString;
271 } else if (KeyString->getValue(KeyStorage) == "command") {
272 Command = ValueString;
273 } else if (KeyString->getValue(KeyStorage) == "file") {
Manuel Klimek1a8d6862012-05-15 11:46:07 +0000274 File = ValueString;
Manuel Klimekcb971c62012-04-04 12:07:46 +0000275 } else {
Manuel Klimekc661f142012-04-17 16:54:26 +0000276 ErrorMessage = ("Unknown key: \"" +
277 KeyString->getRawValue() + "\"").str();
Manuel Klimekcb971c62012-04-04 12:07:46 +0000278 return false;
279 }
280 }
Manuel Klimek1a8d6862012-05-15 11:46:07 +0000281 if (!File) {
282 ErrorMessage = "Missing key: \"file\".";
283 return false;
284 }
285 if (!Command) {
286 ErrorMessage = "Missing key: \"command\".";
287 return false;
288 }
289 if (!Directory) {
290 ErrorMessage = "Missing key: \"directory\".";
291 return false;
292 }
293 llvm::SmallString<8> FileStorage;
NAKAMURA Takumi62d198c2012-05-23 22:24:20 +0000294 llvm::SmallString<128> NativeFilePath;
295 llvm::sys::path::native(File->getValue(FileStorage), NativeFilePath);
296 IndexByFile[NativeFilePath].push_back(
Manuel Klimekc661f142012-04-17 16:54:26 +0000297 CompileCommandRef(Directory, Command));
Manuel Klimekcb971c62012-04-04 12:07:46 +0000298 }
299 return true;
300}
301
302} // end namespace tooling
303} // end namespace clang