10b57cec5SDimitry Andric //===- JSONCompilationDatabase.cpp ----------------------------------------===// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric // 90b57cec5SDimitry Andric // This file contains the implementation of the JSONCompilationDatabase. 100b57cec5SDimitry Andric // 110b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 120b57cec5SDimitry Andric 130b57cec5SDimitry Andric #include "clang/Tooling/JSONCompilationDatabase.h" 140b57cec5SDimitry Andric #include "clang/Basic/LLVM.h" 150b57cec5SDimitry Andric #include "clang/Tooling/CompilationDatabase.h" 160b57cec5SDimitry Andric #include "clang/Tooling/CompilationDatabasePluginRegistry.h" 170b57cec5SDimitry Andric #include "clang/Tooling/Tooling.h" 180b57cec5SDimitry Andric #include "llvm/ADT/STLExtras.h" 190b57cec5SDimitry Andric #include "llvm/ADT/SmallString.h" 200b57cec5SDimitry Andric #include "llvm/ADT/SmallVector.h" 210b57cec5SDimitry Andric #include "llvm/ADT/StringRef.h" 220b57cec5SDimitry Andric #include "llvm/Support/Allocator.h" 230b57cec5SDimitry Andric #include "llvm/Support/Casting.h" 240b57cec5SDimitry Andric #include "llvm/Support/CommandLine.h" 250b57cec5SDimitry Andric #include "llvm/Support/ErrorOr.h" 260b57cec5SDimitry Andric #include "llvm/Support/MemoryBuffer.h" 270b57cec5SDimitry Andric #include "llvm/Support/Path.h" 280b57cec5SDimitry Andric #include "llvm/Support/StringSaver.h" 29480093f4SDimitry Andric #include "llvm/Support/VirtualFileSystem.h" 300b57cec5SDimitry Andric #include "llvm/Support/YAMLParser.h" 310b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h" 3206c3fb27SDimitry Andric #include "llvm/TargetParser/Host.h" 3306c3fb27SDimitry Andric #include "llvm/TargetParser/Triple.h" 340b57cec5SDimitry Andric #include <cassert> 350b57cec5SDimitry Andric #include <memory> 36bdd1243dSDimitry Andric #include <optional> 370b57cec5SDimitry Andric #include <string> 380b57cec5SDimitry Andric #include <system_error> 390b57cec5SDimitry Andric #include <tuple> 400b57cec5SDimitry Andric #include <utility> 410b57cec5SDimitry Andric #include <vector> 420b57cec5SDimitry Andric 430b57cec5SDimitry Andric using namespace clang; 440b57cec5SDimitry Andric using namespace tooling; 450b57cec5SDimitry Andric 460b57cec5SDimitry Andric namespace { 470b57cec5SDimitry Andric 480b57cec5SDimitry Andric /// A parser for escaped strings of command line arguments. 490b57cec5SDimitry Andric /// 500b57cec5SDimitry Andric /// Assumes \-escaping for quoted arguments (see the documentation of 510b57cec5SDimitry Andric /// unescapeCommandLine(...)). 520b57cec5SDimitry Andric class CommandLineArgumentParser { 530b57cec5SDimitry Andric public: 540b57cec5SDimitry Andric CommandLineArgumentParser(StringRef CommandLine) 550b57cec5SDimitry Andric : Input(CommandLine), Position(Input.begin()-1) {} 560b57cec5SDimitry Andric 570b57cec5SDimitry Andric std::vector<std::string> parse() { 580b57cec5SDimitry Andric bool HasMoreInput = true; 590b57cec5SDimitry Andric while (HasMoreInput && nextNonWhitespace()) { 600b57cec5SDimitry Andric std::string Argument; 610b57cec5SDimitry Andric HasMoreInput = parseStringInto(Argument); 620b57cec5SDimitry Andric CommandLine.push_back(Argument); 630b57cec5SDimitry Andric } 640b57cec5SDimitry Andric return CommandLine; 650b57cec5SDimitry Andric } 660b57cec5SDimitry Andric 670b57cec5SDimitry Andric private: 680b57cec5SDimitry Andric // All private methods return true if there is more input available. 690b57cec5SDimitry Andric 700b57cec5SDimitry Andric bool parseStringInto(std::string &String) { 710b57cec5SDimitry Andric do { 720b57cec5SDimitry Andric if (*Position == '"') { 730b57cec5SDimitry Andric if (!parseDoubleQuotedStringInto(String)) return false; 740b57cec5SDimitry Andric } else if (*Position == '\'') { 750b57cec5SDimitry Andric if (!parseSingleQuotedStringInto(String)) return false; 760b57cec5SDimitry Andric } else { 770b57cec5SDimitry Andric if (!parseFreeStringInto(String)) return false; 780b57cec5SDimitry Andric } 790b57cec5SDimitry Andric } while (*Position != ' '); 800b57cec5SDimitry Andric return true; 810b57cec5SDimitry Andric } 820b57cec5SDimitry Andric 830b57cec5SDimitry Andric bool parseDoubleQuotedStringInto(std::string &String) { 840b57cec5SDimitry Andric if (!next()) return false; 850b57cec5SDimitry Andric while (*Position != '"') { 860b57cec5SDimitry Andric if (!skipEscapeCharacter()) return false; 870b57cec5SDimitry Andric String.push_back(*Position); 880b57cec5SDimitry Andric if (!next()) return false; 890b57cec5SDimitry Andric } 900b57cec5SDimitry Andric return next(); 910b57cec5SDimitry Andric } 920b57cec5SDimitry Andric 930b57cec5SDimitry Andric bool parseSingleQuotedStringInto(std::string &String) { 940b57cec5SDimitry Andric if (!next()) return false; 950b57cec5SDimitry Andric while (*Position != '\'') { 960b57cec5SDimitry Andric String.push_back(*Position); 970b57cec5SDimitry Andric if (!next()) return false; 980b57cec5SDimitry Andric } 990b57cec5SDimitry Andric return next(); 1000b57cec5SDimitry Andric } 1010b57cec5SDimitry Andric 1020b57cec5SDimitry Andric bool parseFreeStringInto(std::string &String) { 1030b57cec5SDimitry Andric do { 1040b57cec5SDimitry Andric if (!skipEscapeCharacter()) return false; 1050b57cec5SDimitry Andric String.push_back(*Position); 1060b57cec5SDimitry Andric if (!next()) return false; 1070b57cec5SDimitry Andric } while (*Position != ' ' && *Position != '"' && *Position != '\''); 1080b57cec5SDimitry Andric return true; 1090b57cec5SDimitry Andric } 1100b57cec5SDimitry Andric 1110b57cec5SDimitry Andric bool skipEscapeCharacter() { 1120b57cec5SDimitry Andric if (*Position == '\\') { 1130b57cec5SDimitry Andric return next(); 1140b57cec5SDimitry Andric } 1150b57cec5SDimitry Andric return true; 1160b57cec5SDimitry Andric } 1170b57cec5SDimitry Andric 1180b57cec5SDimitry Andric bool nextNonWhitespace() { 1190b57cec5SDimitry Andric do { 1200b57cec5SDimitry Andric if (!next()) return false; 1210b57cec5SDimitry Andric } while (*Position == ' '); 1220b57cec5SDimitry Andric return true; 1230b57cec5SDimitry Andric } 1240b57cec5SDimitry Andric 1250b57cec5SDimitry Andric bool next() { 1260b57cec5SDimitry Andric ++Position; 1270b57cec5SDimitry Andric return Position != Input.end(); 1280b57cec5SDimitry Andric } 1290b57cec5SDimitry Andric 1300b57cec5SDimitry Andric const StringRef Input; 1310b57cec5SDimitry Andric StringRef::iterator Position; 1320b57cec5SDimitry Andric std::vector<std::string> CommandLine; 1330b57cec5SDimitry Andric }; 1340b57cec5SDimitry Andric 1350b57cec5SDimitry Andric std::vector<std::string> unescapeCommandLine(JSONCommandLineSyntax Syntax, 1360b57cec5SDimitry Andric StringRef EscapedCommandLine) { 1370b57cec5SDimitry Andric if (Syntax == JSONCommandLineSyntax::AutoDetect) { 138349cc55cSDimitry Andric #ifdef _WIN32 139349cc55cSDimitry Andric // Assume Windows command line parsing on Win32 1400b57cec5SDimitry Andric Syntax = JSONCommandLineSyntax::Windows; 141349cc55cSDimitry Andric #else 142349cc55cSDimitry Andric Syntax = JSONCommandLineSyntax::Gnu; 143349cc55cSDimitry Andric #endif 1440b57cec5SDimitry Andric } 1450b57cec5SDimitry Andric 1460b57cec5SDimitry Andric if (Syntax == JSONCommandLineSyntax::Windows) { 1470b57cec5SDimitry Andric llvm::BumpPtrAllocator Alloc; 1480b57cec5SDimitry Andric llvm::StringSaver Saver(Alloc); 1490b57cec5SDimitry Andric llvm::SmallVector<const char *, 64> T; 1500b57cec5SDimitry Andric llvm::cl::TokenizeWindowsCommandLine(EscapedCommandLine, Saver, T); 1510b57cec5SDimitry Andric std::vector<std::string> Result(T.begin(), T.end()); 1520b57cec5SDimitry Andric return Result; 1530b57cec5SDimitry Andric } 1540b57cec5SDimitry Andric assert(Syntax == JSONCommandLineSyntax::Gnu); 1550b57cec5SDimitry Andric CommandLineArgumentParser parser(EscapedCommandLine); 1560b57cec5SDimitry Andric return parser.parse(); 1570b57cec5SDimitry Andric } 1580b57cec5SDimitry Andric 1590b57cec5SDimitry Andric // This plugin locates a nearby compile_command.json file, and also infers 1600b57cec5SDimitry Andric // compile commands for files not present in the database. 1610b57cec5SDimitry Andric class JSONCompilationDatabasePlugin : public CompilationDatabasePlugin { 1620b57cec5SDimitry Andric std::unique_ptr<CompilationDatabase> 1630b57cec5SDimitry Andric loadFromDirectory(StringRef Directory, std::string &ErrorMessage) override { 1640b57cec5SDimitry Andric SmallString<1024> JSONDatabasePath(Directory); 1650b57cec5SDimitry Andric llvm::sys::path::append(JSONDatabasePath, "compile_commands.json"); 1660b57cec5SDimitry Andric auto Base = JSONCompilationDatabase::loadFromFile( 1670b57cec5SDimitry Andric JSONDatabasePath, ErrorMessage, JSONCommandLineSyntax::AutoDetect); 1680b57cec5SDimitry Andric return Base ? inferTargetAndDriverMode( 169480093f4SDimitry Andric inferMissingCompileCommands(expandResponseFiles( 170480093f4SDimitry Andric std::move(Base), llvm::vfs::getRealFileSystem()))) 1710b57cec5SDimitry Andric : nullptr; 1720b57cec5SDimitry Andric } 1730b57cec5SDimitry Andric }; 1740b57cec5SDimitry Andric 1750b57cec5SDimitry Andric } // namespace 1760b57cec5SDimitry Andric 1770b57cec5SDimitry Andric // Register the JSONCompilationDatabasePlugin with the 1780b57cec5SDimitry Andric // CompilationDatabasePluginRegistry using this statically initialized variable. 1790b57cec5SDimitry Andric static CompilationDatabasePluginRegistry::Add<JSONCompilationDatabasePlugin> 1800b57cec5SDimitry Andric X("json-compilation-database", "Reads JSON formatted compilation databases"); 1810b57cec5SDimitry Andric 1820b57cec5SDimitry Andric namespace clang { 1830b57cec5SDimitry Andric namespace tooling { 1840b57cec5SDimitry Andric 1850b57cec5SDimitry Andric // This anchor is used to force the linker to link in the generated object file 1860b57cec5SDimitry Andric // and thus register the JSONCompilationDatabasePlugin. 1870b57cec5SDimitry Andric volatile int JSONAnchorSource = 0; 1880b57cec5SDimitry Andric 1890b57cec5SDimitry Andric } // namespace tooling 1900b57cec5SDimitry Andric } // namespace clang 1910b57cec5SDimitry Andric 1920b57cec5SDimitry Andric std::unique_ptr<JSONCompilationDatabase> 1930b57cec5SDimitry Andric JSONCompilationDatabase::loadFromFile(StringRef FilePath, 1940b57cec5SDimitry Andric std::string &ErrorMessage, 1950b57cec5SDimitry Andric JSONCommandLineSyntax Syntax) { 1960b57cec5SDimitry Andric // Don't mmap: if we're a long-lived process, the build system may overwrite. 1970b57cec5SDimitry Andric llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> DatabaseBuffer = 198fe6060f1SDimitry Andric llvm::MemoryBuffer::getFile(FilePath, /*IsText=*/false, 1990b57cec5SDimitry Andric /*RequiresNullTerminator=*/true, 2000b57cec5SDimitry Andric /*IsVolatile=*/true); 2010b57cec5SDimitry Andric if (std::error_code Result = DatabaseBuffer.getError()) { 2020b57cec5SDimitry Andric ErrorMessage = "Error while opening JSON database: " + Result.message(); 2030b57cec5SDimitry Andric return nullptr; 2040b57cec5SDimitry Andric } 2050b57cec5SDimitry Andric std::unique_ptr<JSONCompilationDatabase> Database( 2060b57cec5SDimitry Andric new JSONCompilationDatabase(std::move(*DatabaseBuffer), Syntax)); 2070b57cec5SDimitry Andric if (!Database->parse(ErrorMessage)) 2080b57cec5SDimitry Andric return nullptr; 2090b57cec5SDimitry Andric return Database; 2100b57cec5SDimitry Andric } 2110b57cec5SDimitry Andric 2120b57cec5SDimitry Andric std::unique_ptr<JSONCompilationDatabase> 2130b57cec5SDimitry Andric JSONCompilationDatabase::loadFromBuffer(StringRef DatabaseString, 2140b57cec5SDimitry Andric std::string &ErrorMessage, 2150b57cec5SDimitry Andric JSONCommandLineSyntax Syntax) { 2160b57cec5SDimitry Andric std::unique_ptr<llvm::MemoryBuffer> DatabaseBuffer( 217e8d8bef9SDimitry Andric llvm::MemoryBuffer::getMemBufferCopy(DatabaseString)); 2180b57cec5SDimitry Andric std::unique_ptr<JSONCompilationDatabase> Database( 2190b57cec5SDimitry Andric new JSONCompilationDatabase(std::move(DatabaseBuffer), Syntax)); 2200b57cec5SDimitry Andric if (!Database->parse(ErrorMessage)) 2210b57cec5SDimitry Andric return nullptr; 2220b57cec5SDimitry Andric return Database; 2230b57cec5SDimitry Andric } 2240b57cec5SDimitry Andric 2250b57cec5SDimitry Andric std::vector<CompileCommand> 2260b57cec5SDimitry Andric JSONCompilationDatabase::getCompileCommands(StringRef FilePath) const { 2270b57cec5SDimitry Andric SmallString<128> NativeFilePath; 2280b57cec5SDimitry Andric llvm::sys::path::native(FilePath, NativeFilePath); 2290b57cec5SDimitry Andric 2300b57cec5SDimitry Andric std::string Error; 2310b57cec5SDimitry Andric llvm::raw_string_ostream ES(Error); 2320b57cec5SDimitry Andric StringRef Match = MatchTrie.findEquivalent(NativeFilePath, ES); 2330b57cec5SDimitry Andric if (Match.empty()) 2340b57cec5SDimitry Andric return {}; 2350b57cec5SDimitry Andric const auto CommandsRefI = IndexByFile.find(Match); 2360b57cec5SDimitry Andric if (CommandsRefI == IndexByFile.end()) 2370b57cec5SDimitry Andric return {}; 2380b57cec5SDimitry Andric std::vector<CompileCommand> Commands; 2390b57cec5SDimitry Andric getCommands(CommandsRefI->getValue(), Commands); 2400b57cec5SDimitry Andric return Commands; 2410b57cec5SDimitry Andric } 2420b57cec5SDimitry Andric 2430b57cec5SDimitry Andric std::vector<std::string> 2440b57cec5SDimitry Andric JSONCompilationDatabase::getAllFiles() const { 2450b57cec5SDimitry Andric std::vector<std::string> Result; 2460b57cec5SDimitry Andric for (const auto &CommandRef : IndexByFile) 2470b57cec5SDimitry Andric Result.push_back(CommandRef.first().str()); 2480b57cec5SDimitry Andric return Result; 2490b57cec5SDimitry Andric } 2500b57cec5SDimitry Andric 2510b57cec5SDimitry Andric std::vector<CompileCommand> 2520b57cec5SDimitry Andric JSONCompilationDatabase::getAllCompileCommands() const { 2530b57cec5SDimitry Andric std::vector<CompileCommand> Commands; 2540b57cec5SDimitry Andric getCommands(AllCommands, Commands); 2550b57cec5SDimitry Andric return Commands; 2560b57cec5SDimitry Andric } 2570b57cec5SDimitry Andric 2580b57cec5SDimitry Andric static llvm::StringRef stripExecutableExtension(llvm::StringRef Name) { 2590b57cec5SDimitry Andric Name.consume_back(".exe"); 2600b57cec5SDimitry Andric return Name; 2610b57cec5SDimitry Andric } 2620b57cec5SDimitry Andric 263*0fca6ea1SDimitry Andric // There are compiler-wrappers (ccache, distcc) that take the "real" 2640b57cec5SDimitry Andric // compiler as an argument, e.g. distcc gcc -O3 foo.c. 2650b57cec5SDimitry Andric // These end up in compile_commands.json when people set CC="distcc gcc". 2660b57cec5SDimitry Andric // Clang's driver doesn't understand this, so we need to unwrap. 2670b57cec5SDimitry Andric static bool unwrapCommand(std::vector<std::string> &Args) { 2680b57cec5SDimitry Andric if (Args.size() < 2) 2690b57cec5SDimitry Andric return false; 2700b57cec5SDimitry Andric StringRef Wrapper = 2710b57cec5SDimitry Andric stripExecutableExtension(llvm::sys::path::filename(Args.front())); 272*0fca6ea1SDimitry Andric if (Wrapper == "distcc" || Wrapper == "ccache" || Wrapper == "sccache") { 2730b57cec5SDimitry Andric // Most of these wrappers support being invoked 3 ways: 2740b57cec5SDimitry Andric // `distcc g++ file.c` This is the mode we're trying to match. 2750b57cec5SDimitry Andric // We need to drop `distcc`. 2760b57cec5SDimitry Andric // `distcc file.c` This acts like compiler is cc or similar. 2770b57cec5SDimitry Andric // Clang's driver can handle this, no change needed. 2780b57cec5SDimitry Andric // `g++ file.c` g++ is a symlink to distcc. 2790b57cec5SDimitry Andric // We don't even notice this case, and all is well. 2800b57cec5SDimitry Andric // 2810b57cec5SDimitry Andric // We need to distinguish between the first and second case. 2820b57cec5SDimitry Andric // The wrappers themselves don't take flags, so Args[1] is a compiler flag, 2830b57cec5SDimitry Andric // an input file, or a compiler. Inputs have extensions, compilers don't. 2840b57cec5SDimitry Andric bool HasCompiler = 2850b57cec5SDimitry Andric (Args[1][0] != '-') && 2860b57cec5SDimitry Andric !llvm::sys::path::has_extension(stripExecutableExtension(Args[1])); 2870b57cec5SDimitry Andric if (HasCompiler) { 2880b57cec5SDimitry Andric Args.erase(Args.begin()); 2890b57cec5SDimitry Andric return true; 2900b57cec5SDimitry Andric } 2910b57cec5SDimitry Andric // If !HasCompiler, wrappers act like GCC. Fine: so do we. 2920b57cec5SDimitry Andric } 2930b57cec5SDimitry Andric return false; 2940b57cec5SDimitry Andric } 2950b57cec5SDimitry Andric 2960b57cec5SDimitry Andric static std::vector<std::string> 2970b57cec5SDimitry Andric nodeToCommandLine(JSONCommandLineSyntax Syntax, 2980b57cec5SDimitry Andric const std::vector<llvm::yaml::ScalarNode *> &Nodes) { 2990b57cec5SDimitry Andric SmallString<1024> Storage; 3000b57cec5SDimitry Andric std::vector<std::string> Arguments; 3010b57cec5SDimitry Andric if (Nodes.size() == 1) 3020b57cec5SDimitry Andric Arguments = unescapeCommandLine(Syntax, Nodes[0]->getValue(Storage)); 3030b57cec5SDimitry Andric else 3040b57cec5SDimitry Andric for (const auto *Node : Nodes) 3055ffd83dbSDimitry Andric Arguments.push_back(std::string(Node->getValue(Storage))); 3060b57cec5SDimitry Andric // There may be multiple wrappers: using distcc and ccache together is common. 3070b57cec5SDimitry Andric while (unwrapCommand(Arguments)) 3080b57cec5SDimitry Andric ; 3090b57cec5SDimitry Andric return Arguments; 3100b57cec5SDimitry Andric } 3110b57cec5SDimitry Andric 3120b57cec5SDimitry Andric void JSONCompilationDatabase::getCommands( 3130b57cec5SDimitry Andric ArrayRef<CompileCommandRef> CommandsRef, 3140b57cec5SDimitry Andric std::vector<CompileCommand> &Commands) const { 3150b57cec5SDimitry Andric for (const auto &CommandRef : CommandsRef) { 3160b57cec5SDimitry Andric SmallString<8> DirectoryStorage; 3170b57cec5SDimitry Andric SmallString<32> FilenameStorage; 3180b57cec5SDimitry Andric SmallString<32> OutputStorage; 3190b57cec5SDimitry Andric auto Output = std::get<3>(CommandRef); 3200b57cec5SDimitry Andric Commands.emplace_back( 3210b57cec5SDimitry Andric std::get<0>(CommandRef)->getValue(DirectoryStorage), 3220b57cec5SDimitry Andric std::get<1>(CommandRef)->getValue(FilenameStorage), 3230b57cec5SDimitry Andric nodeToCommandLine(Syntax, std::get<2>(CommandRef)), 3240b57cec5SDimitry Andric Output ? Output->getValue(OutputStorage) : ""); 3250b57cec5SDimitry Andric } 3260b57cec5SDimitry Andric } 3270b57cec5SDimitry Andric 3280b57cec5SDimitry Andric bool JSONCompilationDatabase::parse(std::string &ErrorMessage) { 3290b57cec5SDimitry Andric llvm::yaml::document_iterator I = YAMLStream.begin(); 3300b57cec5SDimitry Andric if (I == YAMLStream.end()) { 3310b57cec5SDimitry Andric ErrorMessage = "Error while parsing YAML."; 3320b57cec5SDimitry Andric return false; 3330b57cec5SDimitry Andric } 3340b57cec5SDimitry Andric llvm::yaml::Node *Root = I->getRoot(); 3350b57cec5SDimitry Andric if (!Root) { 3360b57cec5SDimitry Andric ErrorMessage = "Error while parsing YAML."; 3370b57cec5SDimitry Andric return false; 3380b57cec5SDimitry Andric } 3390b57cec5SDimitry Andric auto *Array = dyn_cast<llvm::yaml::SequenceNode>(Root); 3400b57cec5SDimitry Andric if (!Array) { 3410b57cec5SDimitry Andric ErrorMessage = "Expected array."; 3420b57cec5SDimitry Andric return false; 3430b57cec5SDimitry Andric } 3440b57cec5SDimitry Andric for (auto &NextObject : *Array) { 3450b57cec5SDimitry Andric auto *Object = dyn_cast<llvm::yaml::MappingNode>(&NextObject); 3460b57cec5SDimitry Andric if (!Object) { 3470b57cec5SDimitry Andric ErrorMessage = "Expected object."; 3480b57cec5SDimitry Andric return false; 3490b57cec5SDimitry Andric } 3500b57cec5SDimitry Andric llvm::yaml::ScalarNode *Directory = nullptr; 351bdd1243dSDimitry Andric std::optional<std::vector<llvm::yaml::ScalarNode *>> Command; 3520b57cec5SDimitry Andric llvm::yaml::ScalarNode *File = nullptr; 3530b57cec5SDimitry Andric llvm::yaml::ScalarNode *Output = nullptr; 3540b57cec5SDimitry Andric for (auto& NextKeyValue : *Object) { 3550b57cec5SDimitry Andric auto *KeyString = dyn_cast<llvm::yaml::ScalarNode>(NextKeyValue.getKey()); 3560b57cec5SDimitry Andric if (!KeyString) { 3570b57cec5SDimitry Andric ErrorMessage = "Expected strings as key."; 3580b57cec5SDimitry Andric return false; 3590b57cec5SDimitry Andric } 3600b57cec5SDimitry Andric SmallString<10> KeyStorage; 3610b57cec5SDimitry Andric StringRef KeyValue = KeyString->getValue(KeyStorage); 3620b57cec5SDimitry Andric llvm::yaml::Node *Value = NextKeyValue.getValue(); 3630b57cec5SDimitry Andric if (!Value) { 3640b57cec5SDimitry Andric ErrorMessage = "Expected value."; 3650b57cec5SDimitry Andric return false; 3660b57cec5SDimitry Andric } 3670b57cec5SDimitry Andric auto *ValueString = dyn_cast<llvm::yaml::ScalarNode>(Value); 3680b57cec5SDimitry Andric auto *SequenceString = dyn_cast<llvm::yaml::SequenceNode>(Value); 369e8d8bef9SDimitry Andric if (KeyValue == "arguments") { 370e8d8bef9SDimitry Andric if (!SequenceString) { 3710b57cec5SDimitry Andric ErrorMessage = "Expected sequence as value."; 3720b57cec5SDimitry Andric return false; 3730b57cec5SDimitry Andric } 3740b57cec5SDimitry Andric Command = std::vector<llvm::yaml::ScalarNode *>(); 3750b57cec5SDimitry Andric for (auto &Argument : *SequenceString) { 3760b57cec5SDimitry Andric auto *Scalar = dyn_cast<llvm::yaml::ScalarNode>(&Argument); 3770b57cec5SDimitry Andric if (!Scalar) { 3780b57cec5SDimitry Andric ErrorMessage = "Only strings are allowed in 'arguments'."; 3790b57cec5SDimitry Andric return false; 3800b57cec5SDimitry Andric } 3810b57cec5SDimitry Andric Command->push_back(Scalar); 3820b57cec5SDimitry Andric } 383e8d8bef9SDimitry Andric } else { 384e8d8bef9SDimitry Andric if (!ValueString) { 385e8d8bef9SDimitry Andric ErrorMessage = "Expected string as value."; 386e8d8bef9SDimitry Andric return false; 387e8d8bef9SDimitry Andric } 388e8d8bef9SDimitry Andric if (KeyValue == "directory") { 389e8d8bef9SDimitry Andric Directory = ValueString; 3900b57cec5SDimitry Andric } else if (KeyValue == "command") { 3910b57cec5SDimitry Andric if (!Command) 3920b57cec5SDimitry Andric Command = std::vector<llvm::yaml::ScalarNode *>(1, ValueString); 3930b57cec5SDimitry Andric } else if (KeyValue == "file") { 3940b57cec5SDimitry Andric File = ValueString; 3950b57cec5SDimitry Andric } else if (KeyValue == "output") { 3960b57cec5SDimitry Andric Output = ValueString; 3970b57cec5SDimitry Andric } else { 398e8d8bef9SDimitry Andric ErrorMessage = 399e8d8bef9SDimitry Andric ("Unknown key: \"" + KeyString->getRawValue() + "\"").str(); 4000b57cec5SDimitry Andric return false; 4010b57cec5SDimitry Andric } 4020b57cec5SDimitry Andric } 403e8d8bef9SDimitry Andric } 4040b57cec5SDimitry Andric if (!File) { 4050b57cec5SDimitry Andric ErrorMessage = "Missing key: \"file\"."; 4060b57cec5SDimitry Andric return false; 4070b57cec5SDimitry Andric } 4080b57cec5SDimitry Andric if (!Command) { 4090b57cec5SDimitry Andric ErrorMessage = "Missing key: \"command\" or \"arguments\"."; 4100b57cec5SDimitry Andric return false; 4110b57cec5SDimitry Andric } 4120b57cec5SDimitry Andric if (!Directory) { 4130b57cec5SDimitry Andric ErrorMessage = "Missing key: \"directory\"."; 4140b57cec5SDimitry Andric return false; 4150b57cec5SDimitry Andric } 4160b57cec5SDimitry Andric SmallString<8> FileStorage; 4170b57cec5SDimitry Andric StringRef FileName = File->getValue(FileStorage); 4180b57cec5SDimitry Andric SmallString<128> NativeFilePath; 4190b57cec5SDimitry Andric if (llvm::sys::path::is_relative(FileName)) { 4200b57cec5SDimitry Andric SmallString<8> DirectoryStorage; 421bdd1243dSDimitry Andric SmallString<128> AbsolutePath(Directory->getValue(DirectoryStorage)); 4220b57cec5SDimitry Andric llvm::sys::path::append(AbsolutePath, FileName); 4230b57cec5SDimitry Andric llvm::sys::path::native(AbsolutePath, NativeFilePath); 4240b57cec5SDimitry Andric } else { 4250b57cec5SDimitry Andric llvm::sys::path::native(FileName, NativeFilePath); 4260b57cec5SDimitry Andric } 427bdd1243dSDimitry Andric llvm::sys::path::remove_dots(NativeFilePath, /*remove_dot_dot=*/true); 4280b57cec5SDimitry Andric auto Cmd = CompileCommandRef(Directory, File, *Command, Output); 4290b57cec5SDimitry Andric IndexByFile[NativeFilePath].push_back(Cmd); 4300b57cec5SDimitry Andric AllCommands.push_back(Cmd); 4310b57cec5SDimitry Andric MatchTrie.insert(NativeFilePath); 4320b57cec5SDimitry Andric } 4330b57cec5SDimitry Andric return true; 4340b57cec5SDimitry Andric } 435