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