annotate clang/lib/Tooling/JSONCompilationDatabase.cpp @ 176:de4ac79aef9d

...
author Shinji KONO <kono@ie.u-ryukyu.ac.jp>
date Mon, 25 May 2020 17:13:11 +0900
parents 1d019706d866
children 2e18cbf3894f
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
150
anatofuz
parents:
diff changeset
1 //===- JSONCompilationDatabase.cpp ----------------------------------------===//
anatofuz
parents:
diff changeset
2 //
anatofuz
parents:
diff changeset
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
anatofuz
parents:
diff changeset
4 // See https://llvm.org/LICENSE.txt for license information.
anatofuz
parents:
diff changeset
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
anatofuz
parents:
diff changeset
6 //
anatofuz
parents:
diff changeset
7 //===----------------------------------------------------------------------===//
anatofuz
parents:
diff changeset
8 //
anatofuz
parents:
diff changeset
9 // This file contains the implementation of the JSONCompilationDatabase.
anatofuz
parents:
diff changeset
10 //
anatofuz
parents:
diff changeset
11 //===----------------------------------------------------------------------===//
anatofuz
parents:
diff changeset
12
anatofuz
parents:
diff changeset
13 #include "clang/Tooling/JSONCompilationDatabase.h"
anatofuz
parents:
diff changeset
14 #include "clang/Basic/LLVM.h"
anatofuz
parents:
diff changeset
15 #include "clang/Tooling/CompilationDatabase.h"
anatofuz
parents:
diff changeset
16 #include "clang/Tooling/CompilationDatabasePluginRegistry.h"
anatofuz
parents:
diff changeset
17 #include "clang/Tooling/Tooling.h"
anatofuz
parents:
diff changeset
18 #include "llvm/ADT/Optional.h"
anatofuz
parents:
diff changeset
19 #include "llvm/ADT/STLExtras.h"
anatofuz
parents:
diff changeset
20 #include "llvm/ADT/SmallString.h"
anatofuz
parents:
diff changeset
21 #include "llvm/ADT/SmallVector.h"
anatofuz
parents:
diff changeset
22 #include "llvm/ADT/StringRef.h"
anatofuz
parents:
diff changeset
23 #include "llvm/ADT/Triple.h"
anatofuz
parents:
diff changeset
24 #include "llvm/Support/Allocator.h"
anatofuz
parents:
diff changeset
25 #include "llvm/Support/Casting.h"
anatofuz
parents:
diff changeset
26 #include "llvm/Support/CommandLine.h"
anatofuz
parents:
diff changeset
27 #include "llvm/Support/ErrorOr.h"
anatofuz
parents:
diff changeset
28 #include "llvm/Support/Host.h"
anatofuz
parents:
diff changeset
29 #include "llvm/Support/MemoryBuffer.h"
anatofuz
parents:
diff changeset
30 #include "llvm/Support/Path.h"
anatofuz
parents:
diff changeset
31 #include "llvm/Support/StringSaver.h"
anatofuz
parents:
diff changeset
32 #include "llvm/Support/VirtualFileSystem.h"
anatofuz
parents:
diff changeset
33 #include "llvm/Support/YAMLParser.h"
anatofuz
parents:
diff changeset
34 #include "llvm/Support/raw_ostream.h"
anatofuz
parents:
diff changeset
35 #include <cassert>
anatofuz
parents:
diff changeset
36 #include <memory>
anatofuz
parents:
diff changeset
37 #include <string>
anatofuz
parents:
diff changeset
38 #include <system_error>
anatofuz
parents:
diff changeset
39 #include <tuple>
anatofuz
parents:
diff changeset
40 #include <utility>
anatofuz
parents:
diff changeset
41 #include <vector>
anatofuz
parents:
diff changeset
42
anatofuz
parents:
diff changeset
43 using namespace clang;
anatofuz
parents:
diff changeset
44 using namespace tooling;
anatofuz
parents:
diff changeset
45
anatofuz
parents:
diff changeset
46 namespace {
anatofuz
parents:
diff changeset
47
anatofuz
parents:
diff changeset
48 /// A parser for escaped strings of command line arguments.
anatofuz
parents:
diff changeset
49 ///
anatofuz
parents:
diff changeset
50 /// Assumes \-escaping for quoted arguments (see the documentation of
anatofuz
parents:
diff changeset
51 /// unescapeCommandLine(...)).
anatofuz
parents:
diff changeset
52 class CommandLineArgumentParser {
anatofuz
parents:
diff changeset
53 public:
anatofuz
parents:
diff changeset
54 CommandLineArgumentParser(StringRef CommandLine)
anatofuz
parents:
diff changeset
55 : Input(CommandLine), Position(Input.begin()-1) {}
anatofuz
parents:
diff changeset
56
anatofuz
parents:
diff changeset
57 std::vector<std::string> parse() {
anatofuz
parents:
diff changeset
58 bool HasMoreInput = true;
anatofuz
parents:
diff changeset
59 while (HasMoreInput && nextNonWhitespace()) {
anatofuz
parents:
diff changeset
60 std::string Argument;
anatofuz
parents:
diff changeset
61 HasMoreInput = parseStringInto(Argument);
anatofuz
parents:
diff changeset
62 CommandLine.push_back(Argument);
anatofuz
parents:
diff changeset
63 }
anatofuz
parents:
diff changeset
64 return CommandLine;
anatofuz
parents:
diff changeset
65 }
anatofuz
parents:
diff changeset
66
anatofuz
parents:
diff changeset
67 private:
anatofuz
parents:
diff changeset
68 // All private methods return true if there is more input available.
anatofuz
parents:
diff changeset
69
anatofuz
parents:
diff changeset
70 bool parseStringInto(std::string &String) {
anatofuz
parents:
diff changeset
71 do {
anatofuz
parents:
diff changeset
72 if (*Position == '"') {
anatofuz
parents:
diff changeset
73 if (!parseDoubleQuotedStringInto(String)) return false;
anatofuz
parents:
diff changeset
74 } else if (*Position == '\'') {
anatofuz
parents:
diff changeset
75 if (!parseSingleQuotedStringInto(String)) return false;
anatofuz
parents:
diff changeset
76 } else {
anatofuz
parents:
diff changeset
77 if (!parseFreeStringInto(String)) return false;
anatofuz
parents:
diff changeset
78 }
anatofuz
parents:
diff changeset
79 } while (*Position != ' ');
anatofuz
parents:
diff changeset
80 return true;
anatofuz
parents:
diff changeset
81 }
anatofuz
parents:
diff changeset
82
anatofuz
parents:
diff changeset
83 bool parseDoubleQuotedStringInto(std::string &String) {
anatofuz
parents:
diff changeset
84 if (!next()) return false;
anatofuz
parents:
diff changeset
85 while (*Position != '"') {
anatofuz
parents:
diff changeset
86 if (!skipEscapeCharacter()) return false;
anatofuz
parents:
diff changeset
87 String.push_back(*Position);
anatofuz
parents:
diff changeset
88 if (!next()) return false;
anatofuz
parents:
diff changeset
89 }
anatofuz
parents:
diff changeset
90 return next();
anatofuz
parents:
diff changeset
91 }
anatofuz
parents:
diff changeset
92
anatofuz
parents:
diff changeset
93 bool parseSingleQuotedStringInto(std::string &String) {
anatofuz
parents:
diff changeset
94 if (!next()) return false;
anatofuz
parents:
diff changeset
95 while (*Position != '\'') {
anatofuz
parents:
diff changeset
96 String.push_back(*Position);
anatofuz
parents:
diff changeset
97 if (!next()) return false;
anatofuz
parents:
diff changeset
98 }
anatofuz
parents:
diff changeset
99 return next();
anatofuz
parents:
diff changeset
100 }
anatofuz
parents:
diff changeset
101
anatofuz
parents:
diff changeset
102 bool parseFreeStringInto(std::string &String) {
anatofuz
parents:
diff changeset
103 do {
anatofuz
parents:
diff changeset
104 if (!skipEscapeCharacter()) return false;
anatofuz
parents:
diff changeset
105 String.push_back(*Position);
anatofuz
parents:
diff changeset
106 if (!next()) return false;
anatofuz
parents:
diff changeset
107 } while (*Position != ' ' && *Position != '"' && *Position != '\'');
anatofuz
parents:
diff changeset
108 return true;
anatofuz
parents:
diff changeset
109 }
anatofuz
parents:
diff changeset
110
anatofuz
parents:
diff changeset
111 bool skipEscapeCharacter() {
anatofuz
parents:
diff changeset
112 if (*Position == '\\') {
anatofuz
parents:
diff changeset
113 return next();
anatofuz
parents:
diff changeset
114 }
anatofuz
parents:
diff changeset
115 return true;
anatofuz
parents:
diff changeset
116 }
anatofuz
parents:
diff changeset
117
anatofuz
parents:
diff changeset
118 bool nextNonWhitespace() {
anatofuz
parents:
diff changeset
119 do {
anatofuz
parents:
diff changeset
120 if (!next()) return false;
anatofuz
parents:
diff changeset
121 } while (*Position == ' ');
anatofuz
parents:
diff changeset
122 return true;
anatofuz
parents:
diff changeset
123 }
anatofuz
parents:
diff changeset
124
anatofuz
parents:
diff changeset
125 bool next() {
anatofuz
parents:
diff changeset
126 ++Position;
anatofuz
parents:
diff changeset
127 return Position != Input.end();
anatofuz
parents:
diff changeset
128 }
anatofuz
parents:
diff changeset
129
anatofuz
parents:
diff changeset
130 const StringRef Input;
anatofuz
parents:
diff changeset
131 StringRef::iterator Position;
anatofuz
parents:
diff changeset
132 std::vector<std::string> CommandLine;
anatofuz
parents:
diff changeset
133 };
anatofuz
parents:
diff changeset
134
anatofuz
parents:
diff changeset
135 std::vector<std::string> unescapeCommandLine(JSONCommandLineSyntax Syntax,
anatofuz
parents:
diff changeset
136 StringRef EscapedCommandLine) {
anatofuz
parents:
diff changeset
137 if (Syntax == JSONCommandLineSyntax::AutoDetect) {
anatofuz
parents:
diff changeset
138 Syntax = JSONCommandLineSyntax::Gnu;
anatofuz
parents:
diff changeset
139 llvm::Triple Triple(llvm::sys::getProcessTriple());
anatofuz
parents:
diff changeset
140 if (Triple.getOS() == llvm::Triple::OSType::Win32) {
anatofuz
parents:
diff changeset
141 // Assume Windows command line parsing on Win32 unless the triple
anatofuz
parents:
diff changeset
142 // explicitly tells us otherwise.
anatofuz
parents:
diff changeset
143 if (!Triple.hasEnvironment() ||
anatofuz
parents:
diff changeset
144 Triple.getEnvironment() == llvm::Triple::EnvironmentType::MSVC)
anatofuz
parents:
diff changeset
145 Syntax = JSONCommandLineSyntax::Windows;
anatofuz
parents:
diff changeset
146 }
anatofuz
parents:
diff changeset
147 }
anatofuz
parents:
diff changeset
148
anatofuz
parents:
diff changeset
149 if (Syntax == JSONCommandLineSyntax::Windows) {
anatofuz
parents:
diff changeset
150 llvm::BumpPtrAllocator Alloc;
anatofuz
parents:
diff changeset
151 llvm::StringSaver Saver(Alloc);
anatofuz
parents:
diff changeset
152 llvm::SmallVector<const char *, 64> T;
anatofuz
parents:
diff changeset
153 llvm::cl::TokenizeWindowsCommandLine(EscapedCommandLine, Saver, T);
anatofuz
parents:
diff changeset
154 std::vector<std::string> Result(T.begin(), T.end());
anatofuz
parents:
diff changeset
155 return Result;
anatofuz
parents:
diff changeset
156 }
anatofuz
parents:
diff changeset
157 assert(Syntax == JSONCommandLineSyntax::Gnu);
anatofuz
parents:
diff changeset
158 CommandLineArgumentParser parser(EscapedCommandLine);
anatofuz
parents:
diff changeset
159 return parser.parse();
anatofuz
parents:
diff changeset
160 }
anatofuz
parents:
diff changeset
161
anatofuz
parents:
diff changeset
162 // This plugin locates a nearby compile_command.json file, and also infers
anatofuz
parents:
diff changeset
163 // compile commands for files not present in the database.
anatofuz
parents:
diff changeset
164 class JSONCompilationDatabasePlugin : public CompilationDatabasePlugin {
anatofuz
parents:
diff changeset
165 std::unique_ptr<CompilationDatabase>
anatofuz
parents:
diff changeset
166 loadFromDirectory(StringRef Directory, std::string &ErrorMessage) override {
anatofuz
parents:
diff changeset
167 SmallString<1024> JSONDatabasePath(Directory);
anatofuz
parents:
diff changeset
168 llvm::sys::path::append(JSONDatabasePath, "compile_commands.json");
anatofuz
parents:
diff changeset
169 auto Base = JSONCompilationDatabase::loadFromFile(
anatofuz
parents:
diff changeset
170 JSONDatabasePath, ErrorMessage, JSONCommandLineSyntax::AutoDetect);
anatofuz
parents:
diff changeset
171 return Base ? inferTargetAndDriverMode(
anatofuz
parents:
diff changeset
172 inferMissingCompileCommands(expandResponseFiles(
anatofuz
parents:
diff changeset
173 std::move(Base), llvm::vfs::getRealFileSystem())))
anatofuz
parents:
diff changeset
174 : nullptr;
anatofuz
parents:
diff changeset
175 }
anatofuz
parents:
diff changeset
176 };
anatofuz
parents:
diff changeset
177
anatofuz
parents:
diff changeset
178 } // namespace
anatofuz
parents:
diff changeset
179
anatofuz
parents:
diff changeset
180 // Register the JSONCompilationDatabasePlugin with the
anatofuz
parents:
diff changeset
181 // CompilationDatabasePluginRegistry using this statically initialized variable.
anatofuz
parents:
diff changeset
182 static CompilationDatabasePluginRegistry::Add<JSONCompilationDatabasePlugin>
anatofuz
parents:
diff changeset
183 X("json-compilation-database", "Reads JSON formatted compilation databases");
anatofuz
parents:
diff changeset
184
anatofuz
parents:
diff changeset
185 namespace clang {
anatofuz
parents:
diff changeset
186 namespace tooling {
anatofuz
parents:
diff changeset
187
anatofuz
parents:
diff changeset
188 // This anchor is used to force the linker to link in the generated object file
anatofuz
parents:
diff changeset
189 // and thus register the JSONCompilationDatabasePlugin.
anatofuz
parents:
diff changeset
190 volatile int JSONAnchorSource = 0;
anatofuz
parents:
diff changeset
191
anatofuz
parents:
diff changeset
192 } // namespace tooling
anatofuz
parents:
diff changeset
193 } // namespace clang
anatofuz
parents:
diff changeset
194
anatofuz
parents:
diff changeset
195 std::unique_ptr<JSONCompilationDatabase>
anatofuz
parents:
diff changeset
196 JSONCompilationDatabase::loadFromFile(StringRef FilePath,
anatofuz
parents:
diff changeset
197 std::string &ErrorMessage,
anatofuz
parents:
diff changeset
198 JSONCommandLineSyntax Syntax) {
anatofuz
parents:
diff changeset
199 // Don't mmap: if we're a long-lived process, the build system may overwrite.
anatofuz
parents:
diff changeset
200 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> DatabaseBuffer =
anatofuz
parents:
diff changeset
201 llvm::MemoryBuffer::getFile(FilePath, /*FileSize=*/-1,
anatofuz
parents:
diff changeset
202 /*RequiresNullTerminator=*/true,
anatofuz
parents:
diff changeset
203 /*IsVolatile=*/true);
anatofuz
parents:
diff changeset
204 if (std::error_code Result = DatabaseBuffer.getError()) {
anatofuz
parents:
diff changeset
205 ErrorMessage = "Error while opening JSON database: " + Result.message();
anatofuz
parents:
diff changeset
206 return nullptr;
anatofuz
parents:
diff changeset
207 }
anatofuz
parents:
diff changeset
208 std::unique_ptr<JSONCompilationDatabase> Database(
anatofuz
parents:
diff changeset
209 new JSONCompilationDatabase(std::move(*DatabaseBuffer), Syntax));
anatofuz
parents:
diff changeset
210 if (!Database->parse(ErrorMessage))
anatofuz
parents:
diff changeset
211 return nullptr;
anatofuz
parents:
diff changeset
212 return Database;
anatofuz
parents:
diff changeset
213 }
anatofuz
parents:
diff changeset
214
anatofuz
parents:
diff changeset
215 std::unique_ptr<JSONCompilationDatabase>
anatofuz
parents:
diff changeset
216 JSONCompilationDatabase::loadFromBuffer(StringRef DatabaseString,
anatofuz
parents:
diff changeset
217 std::string &ErrorMessage,
anatofuz
parents:
diff changeset
218 JSONCommandLineSyntax Syntax) {
anatofuz
parents:
diff changeset
219 std::unique_ptr<llvm::MemoryBuffer> DatabaseBuffer(
anatofuz
parents:
diff changeset
220 llvm::MemoryBuffer::getMemBuffer(DatabaseString));
anatofuz
parents:
diff changeset
221 std::unique_ptr<JSONCompilationDatabase> Database(
anatofuz
parents:
diff changeset
222 new JSONCompilationDatabase(std::move(DatabaseBuffer), Syntax));
anatofuz
parents:
diff changeset
223 if (!Database->parse(ErrorMessage))
anatofuz
parents:
diff changeset
224 return nullptr;
anatofuz
parents:
diff changeset
225 return Database;
anatofuz
parents:
diff changeset
226 }
anatofuz
parents:
diff changeset
227
anatofuz
parents:
diff changeset
228 std::vector<CompileCommand>
anatofuz
parents:
diff changeset
229 JSONCompilationDatabase::getCompileCommands(StringRef FilePath) const {
anatofuz
parents:
diff changeset
230 SmallString<128> NativeFilePath;
anatofuz
parents:
diff changeset
231 llvm::sys::path::native(FilePath, NativeFilePath);
anatofuz
parents:
diff changeset
232
anatofuz
parents:
diff changeset
233 std::string Error;
anatofuz
parents:
diff changeset
234 llvm::raw_string_ostream ES(Error);
anatofuz
parents:
diff changeset
235 StringRef Match = MatchTrie.findEquivalent(NativeFilePath, ES);
anatofuz
parents:
diff changeset
236 if (Match.empty())
anatofuz
parents:
diff changeset
237 return {};
anatofuz
parents:
diff changeset
238 const auto CommandsRefI = IndexByFile.find(Match);
anatofuz
parents:
diff changeset
239 if (CommandsRefI == IndexByFile.end())
anatofuz
parents:
diff changeset
240 return {};
anatofuz
parents:
diff changeset
241 std::vector<CompileCommand> Commands;
anatofuz
parents:
diff changeset
242 getCommands(CommandsRefI->getValue(), Commands);
anatofuz
parents:
diff changeset
243 return Commands;
anatofuz
parents:
diff changeset
244 }
anatofuz
parents:
diff changeset
245
anatofuz
parents:
diff changeset
246 std::vector<std::string>
anatofuz
parents:
diff changeset
247 JSONCompilationDatabase::getAllFiles() const {
anatofuz
parents:
diff changeset
248 std::vector<std::string> Result;
anatofuz
parents:
diff changeset
249 for (const auto &CommandRef : IndexByFile)
anatofuz
parents:
diff changeset
250 Result.push_back(CommandRef.first().str());
anatofuz
parents:
diff changeset
251 return Result;
anatofuz
parents:
diff changeset
252 }
anatofuz
parents:
diff changeset
253
anatofuz
parents:
diff changeset
254 std::vector<CompileCommand>
anatofuz
parents:
diff changeset
255 JSONCompilationDatabase::getAllCompileCommands() const {
anatofuz
parents:
diff changeset
256 std::vector<CompileCommand> Commands;
anatofuz
parents:
diff changeset
257 getCommands(AllCommands, Commands);
anatofuz
parents:
diff changeset
258 return Commands;
anatofuz
parents:
diff changeset
259 }
anatofuz
parents:
diff changeset
260
anatofuz
parents:
diff changeset
261 static llvm::StringRef stripExecutableExtension(llvm::StringRef Name) {
anatofuz
parents:
diff changeset
262 Name.consume_back(".exe");
anatofuz
parents:
diff changeset
263 return Name;
anatofuz
parents:
diff changeset
264 }
anatofuz
parents:
diff changeset
265
anatofuz
parents:
diff changeset
266 // There are compiler-wrappers (ccache, distcc, gomacc) that take the "real"
anatofuz
parents:
diff changeset
267 // compiler as an argument, e.g. distcc gcc -O3 foo.c.
anatofuz
parents:
diff changeset
268 // These end up in compile_commands.json when people set CC="distcc gcc".
anatofuz
parents:
diff changeset
269 // Clang's driver doesn't understand this, so we need to unwrap.
anatofuz
parents:
diff changeset
270 static bool unwrapCommand(std::vector<std::string> &Args) {
anatofuz
parents:
diff changeset
271 if (Args.size() < 2)
anatofuz
parents:
diff changeset
272 return false;
anatofuz
parents:
diff changeset
273 StringRef Wrapper =
anatofuz
parents:
diff changeset
274 stripExecutableExtension(llvm::sys::path::filename(Args.front()));
anatofuz
parents:
diff changeset
275 if (Wrapper == "distcc" || Wrapper == "gomacc" || Wrapper == "ccache") {
anatofuz
parents:
diff changeset
276 // Most of these wrappers support being invoked 3 ways:
anatofuz
parents:
diff changeset
277 // `distcc g++ file.c` This is the mode we're trying to match.
anatofuz
parents:
diff changeset
278 // We need to drop `distcc`.
anatofuz
parents:
diff changeset
279 // `distcc file.c` This acts like compiler is cc or similar.
anatofuz
parents:
diff changeset
280 // Clang's driver can handle this, no change needed.
anatofuz
parents:
diff changeset
281 // `g++ file.c` g++ is a symlink to distcc.
anatofuz
parents:
diff changeset
282 // We don't even notice this case, and all is well.
anatofuz
parents:
diff changeset
283 //
anatofuz
parents:
diff changeset
284 // We need to distinguish between the first and second case.
anatofuz
parents:
diff changeset
285 // The wrappers themselves don't take flags, so Args[1] is a compiler flag,
anatofuz
parents:
diff changeset
286 // an input file, or a compiler. Inputs have extensions, compilers don't.
anatofuz
parents:
diff changeset
287 bool HasCompiler =
anatofuz
parents:
diff changeset
288 (Args[1][0] != '-') &&
anatofuz
parents:
diff changeset
289 !llvm::sys::path::has_extension(stripExecutableExtension(Args[1]));
anatofuz
parents:
diff changeset
290 if (HasCompiler) {
anatofuz
parents:
diff changeset
291 Args.erase(Args.begin());
anatofuz
parents:
diff changeset
292 return true;
anatofuz
parents:
diff changeset
293 }
anatofuz
parents:
diff changeset
294 // If !HasCompiler, wrappers act like GCC. Fine: so do we.
anatofuz
parents:
diff changeset
295 }
anatofuz
parents:
diff changeset
296 return false;
anatofuz
parents:
diff changeset
297 }
anatofuz
parents:
diff changeset
298
anatofuz
parents:
diff changeset
299 static std::vector<std::string>
anatofuz
parents:
diff changeset
300 nodeToCommandLine(JSONCommandLineSyntax Syntax,
anatofuz
parents:
diff changeset
301 const std::vector<llvm::yaml::ScalarNode *> &Nodes) {
anatofuz
parents:
diff changeset
302 SmallString<1024> Storage;
anatofuz
parents:
diff changeset
303 std::vector<std::string> Arguments;
anatofuz
parents:
diff changeset
304 if (Nodes.size() == 1)
anatofuz
parents:
diff changeset
305 Arguments = unescapeCommandLine(Syntax, Nodes[0]->getValue(Storage));
anatofuz
parents:
diff changeset
306 else
anatofuz
parents:
diff changeset
307 for (const auto *Node : Nodes)
anatofuz
parents:
diff changeset
308 Arguments.push_back(std::string(Node->getValue(Storage)));
anatofuz
parents:
diff changeset
309 // There may be multiple wrappers: using distcc and ccache together is common.
anatofuz
parents:
diff changeset
310 while (unwrapCommand(Arguments))
anatofuz
parents:
diff changeset
311 ;
anatofuz
parents:
diff changeset
312 return Arguments;
anatofuz
parents:
diff changeset
313 }
anatofuz
parents:
diff changeset
314
anatofuz
parents:
diff changeset
315 void JSONCompilationDatabase::getCommands(
anatofuz
parents:
diff changeset
316 ArrayRef<CompileCommandRef> CommandsRef,
anatofuz
parents:
diff changeset
317 std::vector<CompileCommand> &Commands) const {
anatofuz
parents:
diff changeset
318 for (const auto &CommandRef : CommandsRef) {
anatofuz
parents:
diff changeset
319 SmallString<8> DirectoryStorage;
anatofuz
parents:
diff changeset
320 SmallString<32> FilenameStorage;
anatofuz
parents:
diff changeset
321 SmallString<32> OutputStorage;
anatofuz
parents:
diff changeset
322 auto Output = std::get<3>(CommandRef);
anatofuz
parents:
diff changeset
323 Commands.emplace_back(
anatofuz
parents:
diff changeset
324 std::get<0>(CommandRef)->getValue(DirectoryStorage),
anatofuz
parents:
diff changeset
325 std::get<1>(CommandRef)->getValue(FilenameStorage),
anatofuz
parents:
diff changeset
326 nodeToCommandLine(Syntax, std::get<2>(CommandRef)),
anatofuz
parents:
diff changeset
327 Output ? Output->getValue(OutputStorage) : "");
anatofuz
parents:
diff changeset
328 }
anatofuz
parents:
diff changeset
329 }
anatofuz
parents:
diff changeset
330
anatofuz
parents:
diff changeset
331 bool JSONCompilationDatabase::parse(std::string &ErrorMessage) {
anatofuz
parents:
diff changeset
332 llvm::yaml::document_iterator I = YAMLStream.begin();
anatofuz
parents:
diff changeset
333 if (I == YAMLStream.end()) {
anatofuz
parents:
diff changeset
334 ErrorMessage = "Error while parsing YAML.";
anatofuz
parents:
diff changeset
335 return false;
anatofuz
parents:
diff changeset
336 }
anatofuz
parents:
diff changeset
337 llvm::yaml::Node *Root = I->getRoot();
anatofuz
parents:
diff changeset
338 if (!Root) {
anatofuz
parents:
diff changeset
339 ErrorMessage = "Error while parsing YAML.";
anatofuz
parents:
diff changeset
340 return false;
anatofuz
parents:
diff changeset
341 }
anatofuz
parents:
diff changeset
342 auto *Array = dyn_cast<llvm::yaml::SequenceNode>(Root);
anatofuz
parents:
diff changeset
343 if (!Array) {
anatofuz
parents:
diff changeset
344 ErrorMessage = "Expected array.";
anatofuz
parents:
diff changeset
345 return false;
anatofuz
parents:
diff changeset
346 }
anatofuz
parents:
diff changeset
347 for (auto &NextObject : *Array) {
anatofuz
parents:
diff changeset
348 auto *Object = dyn_cast<llvm::yaml::MappingNode>(&NextObject);
anatofuz
parents:
diff changeset
349 if (!Object) {
anatofuz
parents:
diff changeset
350 ErrorMessage = "Expected object.";
anatofuz
parents:
diff changeset
351 return false;
anatofuz
parents:
diff changeset
352 }
anatofuz
parents:
diff changeset
353 llvm::yaml::ScalarNode *Directory = nullptr;
anatofuz
parents:
diff changeset
354 llvm::Optional<std::vector<llvm::yaml::ScalarNode *>> Command;
anatofuz
parents:
diff changeset
355 llvm::yaml::ScalarNode *File = nullptr;
anatofuz
parents:
diff changeset
356 llvm::yaml::ScalarNode *Output = nullptr;
anatofuz
parents:
diff changeset
357 for (auto& NextKeyValue : *Object) {
anatofuz
parents:
diff changeset
358 auto *KeyString = dyn_cast<llvm::yaml::ScalarNode>(NextKeyValue.getKey());
anatofuz
parents:
diff changeset
359 if (!KeyString) {
anatofuz
parents:
diff changeset
360 ErrorMessage = "Expected strings as key.";
anatofuz
parents:
diff changeset
361 return false;
anatofuz
parents:
diff changeset
362 }
anatofuz
parents:
diff changeset
363 SmallString<10> KeyStorage;
anatofuz
parents:
diff changeset
364 StringRef KeyValue = KeyString->getValue(KeyStorage);
anatofuz
parents:
diff changeset
365 llvm::yaml::Node *Value = NextKeyValue.getValue();
anatofuz
parents:
diff changeset
366 if (!Value) {
anatofuz
parents:
diff changeset
367 ErrorMessage = "Expected value.";
anatofuz
parents:
diff changeset
368 return false;
anatofuz
parents:
diff changeset
369 }
anatofuz
parents:
diff changeset
370 auto *ValueString = dyn_cast<llvm::yaml::ScalarNode>(Value);
anatofuz
parents:
diff changeset
371 auto *SequenceString = dyn_cast<llvm::yaml::SequenceNode>(Value);
anatofuz
parents:
diff changeset
372 if (KeyValue == "arguments" && !SequenceString) {
anatofuz
parents:
diff changeset
373 ErrorMessage = "Expected sequence as value.";
anatofuz
parents:
diff changeset
374 return false;
anatofuz
parents:
diff changeset
375 } else if (KeyValue != "arguments" && !ValueString) {
anatofuz
parents:
diff changeset
376 ErrorMessage = "Expected string as value.";
anatofuz
parents:
diff changeset
377 return false;
anatofuz
parents:
diff changeset
378 }
anatofuz
parents:
diff changeset
379 if (KeyValue == "directory") {
anatofuz
parents:
diff changeset
380 Directory = ValueString;
anatofuz
parents:
diff changeset
381 } else if (KeyValue == "arguments") {
anatofuz
parents:
diff changeset
382 Command = std::vector<llvm::yaml::ScalarNode *>();
anatofuz
parents:
diff changeset
383 for (auto &Argument : *SequenceString) {
anatofuz
parents:
diff changeset
384 auto *Scalar = dyn_cast<llvm::yaml::ScalarNode>(&Argument);
anatofuz
parents:
diff changeset
385 if (!Scalar) {
anatofuz
parents:
diff changeset
386 ErrorMessage = "Only strings are allowed in 'arguments'.";
anatofuz
parents:
diff changeset
387 return false;
anatofuz
parents:
diff changeset
388 }
anatofuz
parents:
diff changeset
389 Command->push_back(Scalar);
anatofuz
parents:
diff changeset
390 }
anatofuz
parents:
diff changeset
391 } else if (KeyValue == "command") {
anatofuz
parents:
diff changeset
392 if (!Command)
anatofuz
parents:
diff changeset
393 Command = std::vector<llvm::yaml::ScalarNode *>(1, ValueString);
anatofuz
parents:
diff changeset
394 } else if (KeyValue == "file") {
anatofuz
parents:
diff changeset
395 File = ValueString;
anatofuz
parents:
diff changeset
396 } else if (KeyValue == "output") {
anatofuz
parents:
diff changeset
397 Output = ValueString;
anatofuz
parents:
diff changeset
398 } else {
anatofuz
parents:
diff changeset
399 ErrorMessage = ("Unknown key: \"" +
anatofuz
parents:
diff changeset
400 KeyString->getRawValue() + "\"").str();
anatofuz
parents:
diff changeset
401 return false;
anatofuz
parents:
diff changeset
402 }
anatofuz
parents:
diff changeset
403 }
anatofuz
parents:
diff changeset
404 if (!File) {
anatofuz
parents:
diff changeset
405 ErrorMessage = "Missing key: \"file\".";
anatofuz
parents:
diff changeset
406 return false;
anatofuz
parents:
diff changeset
407 }
anatofuz
parents:
diff changeset
408 if (!Command) {
anatofuz
parents:
diff changeset
409 ErrorMessage = "Missing key: \"command\" or \"arguments\".";
anatofuz
parents:
diff changeset
410 return false;
anatofuz
parents:
diff changeset
411 }
anatofuz
parents:
diff changeset
412 if (!Directory) {
anatofuz
parents:
diff changeset
413 ErrorMessage = "Missing key: \"directory\".";
anatofuz
parents:
diff changeset
414 return false;
anatofuz
parents:
diff changeset
415 }
anatofuz
parents:
diff changeset
416 SmallString<8> FileStorage;
anatofuz
parents:
diff changeset
417 StringRef FileName = File->getValue(FileStorage);
anatofuz
parents:
diff changeset
418 SmallString<128> NativeFilePath;
anatofuz
parents:
diff changeset
419 if (llvm::sys::path::is_relative(FileName)) {
anatofuz
parents:
diff changeset
420 SmallString<8> DirectoryStorage;
anatofuz
parents:
diff changeset
421 SmallString<128> AbsolutePath(
anatofuz
parents:
diff changeset
422 Directory->getValue(DirectoryStorage));
anatofuz
parents:
diff changeset
423 llvm::sys::path::append(AbsolutePath, FileName);
anatofuz
parents:
diff changeset
424 llvm::sys::path::remove_dots(AbsolutePath, /*remove_dot_dot=*/ true);
anatofuz
parents:
diff changeset
425 llvm::sys::path::native(AbsolutePath, NativeFilePath);
anatofuz
parents:
diff changeset
426 } else {
anatofuz
parents:
diff changeset
427 llvm::sys::path::native(FileName, NativeFilePath);
anatofuz
parents:
diff changeset
428 }
anatofuz
parents:
diff changeset
429 auto Cmd = CompileCommandRef(Directory, File, *Command, Output);
anatofuz
parents:
diff changeset
430 IndexByFile[NativeFilePath].push_back(Cmd);
anatofuz
parents:
diff changeset
431 AllCommands.push_back(Cmd);
anatofuz
parents:
diff changeset
432 MatchTrie.insert(NativeFilePath);
anatofuz
parents:
diff changeset
433 }
anatofuz
parents:
diff changeset
434 return true;
anatofuz
parents:
diff changeset
435 }