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