clang API Documentation

JSONCompilationDatabase.cpp
Go to the documentation of this file.
00001 //===--- JSONCompilationDatabase.cpp - ------------------------------------===//
00002 //
00003 //                     The LLVM Compiler Infrastructure
00004 //
00005 // This file is distributed under the University of Illinois Open Source
00006 // License. See LICENSE.TXT for details.
00007 //
00008 //===----------------------------------------------------------------------===//
00009 //
00010 //  This file contains the implementation of the JSONCompilationDatabase.
00011 //
00012 //===----------------------------------------------------------------------===//
00013 
00014 #include "clang/Tooling/JSONCompilationDatabase.h"
00015 #include "clang/Tooling/CompilationDatabase.h"
00016 #include "clang/Tooling/CompilationDatabasePluginRegistry.h"
00017 #include "clang/Tooling/Tooling.h"
00018 #include "llvm/ADT/SmallString.h"
00019 #include "llvm/Support/Path.h"
00020 #include <system_error>
00021 
00022 namespace clang {
00023 namespace tooling {
00024 
00025 namespace {
00026 
00027 /// \brief A parser for escaped strings of command line arguments.
00028 ///
00029 /// Assumes \-escaping for quoted arguments (see the documentation of
00030 /// unescapeCommandLine(...)).
00031 class CommandLineArgumentParser {
00032  public:
00033   CommandLineArgumentParser(StringRef CommandLine)
00034       : Input(CommandLine), Position(Input.begin()-1) {}
00035 
00036   std::vector<std::string> parse() {
00037     bool HasMoreInput = true;
00038     while (HasMoreInput && nextNonWhitespace()) {
00039       std::string Argument;
00040       HasMoreInput = parseStringInto(Argument);
00041       CommandLine.push_back(Argument);
00042     }
00043     return CommandLine;
00044   }
00045 
00046  private:
00047   // All private methods return true if there is more input available.
00048 
00049   bool parseStringInto(std::string &String) {
00050     do {
00051       if (*Position == '"') {
00052         if (!parseDoubleQuotedStringInto(String)) return false;
00053       } else if (*Position == '\'') {
00054         if (!parseSingleQuotedStringInto(String)) return false;
00055       } else {
00056         if (!parseFreeStringInto(String)) return false;
00057       }
00058     } while (*Position != ' ');
00059     return true;
00060   }
00061 
00062   bool parseDoubleQuotedStringInto(std::string &String) {
00063     if (!next()) return false;
00064     while (*Position != '"') {
00065       if (!skipEscapeCharacter()) return false;
00066       String.push_back(*Position);
00067       if (!next()) return false;
00068     }
00069     return next();
00070   }
00071 
00072   bool parseSingleQuotedStringInto(std::string &String) {
00073     if (!next()) return false;
00074     while (*Position != '\'') {
00075       String.push_back(*Position);
00076       if (!next()) return false;
00077     }
00078     return next();
00079   }
00080 
00081   bool parseFreeStringInto(std::string &String) {
00082     do {
00083       if (!skipEscapeCharacter()) return false;
00084       String.push_back(*Position);
00085       if (!next()) return false;
00086     } while (*Position != ' ' && *Position != '"' && *Position != '\'');
00087     return true;
00088   }
00089 
00090   bool skipEscapeCharacter() {
00091     if (*Position == '\\') {
00092       return next();
00093     }
00094     return true;
00095   }
00096 
00097   bool nextNonWhitespace() {
00098     do {
00099       if (!next()) return false;
00100     } while (*Position == ' ');
00101     return true;
00102   }
00103 
00104   bool next() {
00105     ++Position;
00106     return Position != Input.end();
00107   }
00108 
00109   const StringRef Input;
00110   StringRef::iterator Position;
00111   std::vector<std::string> CommandLine;
00112 };
00113 
00114 std::vector<std::string> unescapeCommandLine(
00115     StringRef EscapedCommandLine) {
00116   CommandLineArgumentParser parser(EscapedCommandLine);
00117   return parser.parse();
00118 }
00119 
00120 class JSONCompilationDatabasePlugin : public CompilationDatabasePlugin {
00121   std::unique_ptr<CompilationDatabase>
00122   loadFromDirectory(StringRef Directory, std::string &ErrorMessage) override {
00123     SmallString<1024> JSONDatabasePath(Directory);
00124     llvm::sys::path::append(JSONDatabasePath, "compile_commands.json");
00125     std::unique_ptr<CompilationDatabase> Database(
00126         JSONCompilationDatabase::loadFromFile(JSONDatabasePath, ErrorMessage));
00127     if (!Database)
00128       return nullptr;
00129     return Database;
00130   }
00131 };
00132 
00133 } // end namespace
00134 
00135 // Register the JSONCompilationDatabasePlugin with the
00136 // CompilationDatabasePluginRegistry using this statically initialized variable.
00137 static CompilationDatabasePluginRegistry::Add<JSONCompilationDatabasePlugin>
00138 X("json-compilation-database", "Reads JSON formatted compilation databases");
00139 
00140 // This anchor is used to force the linker to link in the generated object file
00141 // and thus register the JSONCompilationDatabasePlugin.
00142 volatile int JSONAnchorSource = 0;
00143 
00144 std::unique_ptr<JSONCompilationDatabase>
00145 JSONCompilationDatabase::loadFromFile(StringRef FilePath,
00146                                       std::string &ErrorMessage) {
00147   llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> DatabaseBuffer =
00148       llvm::MemoryBuffer::getFile(FilePath);
00149   if (std::error_code Result = DatabaseBuffer.getError()) {
00150     ErrorMessage = "Error while opening JSON database: " + Result.message();
00151     return nullptr;
00152   }
00153   std::unique_ptr<JSONCompilationDatabase> Database(
00154       new JSONCompilationDatabase(std::move(*DatabaseBuffer)));
00155   if (!Database->parse(ErrorMessage))
00156     return nullptr;
00157   return Database;
00158 }
00159 
00160 std::unique_ptr<JSONCompilationDatabase>
00161 JSONCompilationDatabase::loadFromBuffer(StringRef DatabaseString,
00162                                         std::string &ErrorMessage) {
00163   std::unique_ptr<llvm::MemoryBuffer> DatabaseBuffer(
00164       llvm::MemoryBuffer::getMemBuffer(DatabaseString));
00165   std::unique_ptr<JSONCompilationDatabase> Database(
00166       new JSONCompilationDatabase(std::move(DatabaseBuffer)));
00167   if (!Database->parse(ErrorMessage))
00168     return nullptr;
00169   return Database;
00170 }
00171 
00172 std::vector<CompileCommand>
00173 JSONCompilationDatabase::getCompileCommands(StringRef FilePath) const {
00174   SmallString<128> NativeFilePath;
00175   llvm::sys::path::native(FilePath, NativeFilePath);
00176 
00177   std::string Error;
00178   llvm::raw_string_ostream ES(Error);
00179   StringRef Match = MatchTrie.findEquivalent(NativeFilePath.str(), ES);
00180   if (Match.empty())
00181     return std::vector<CompileCommand>();
00182   llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator
00183     CommandsRefI = IndexByFile.find(Match);
00184   if (CommandsRefI == IndexByFile.end())
00185     return std::vector<CompileCommand>();
00186   std::vector<CompileCommand> Commands;
00187   getCommands(CommandsRefI->getValue(), Commands);
00188   return Commands;
00189 }
00190 
00191 std::vector<std::string>
00192 JSONCompilationDatabase::getAllFiles() const {
00193   std::vector<std::string> Result;
00194 
00195   llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator
00196     CommandsRefI = IndexByFile.begin();
00197   const llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator
00198     CommandsRefEnd = IndexByFile.end();
00199   for (; CommandsRefI != CommandsRefEnd; ++CommandsRefI) {
00200     Result.push_back(CommandsRefI->first().str());
00201   }
00202 
00203   return Result;
00204 }
00205 
00206 std::vector<CompileCommand>
00207 JSONCompilationDatabase::getAllCompileCommands() const {
00208   std::vector<CompileCommand> Commands;
00209   for (llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator
00210         CommandsRefI = IndexByFile.begin(), CommandsRefEnd = IndexByFile.end();
00211       CommandsRefI != CommandsRefEnd; ++CommandsRefI) {
00212     getCommands(CommandsRefI->getValue(), Commands);
00213   }
00214   return Commands;
00215 }
00216 
00217 void JSONCompilationDatabase::getCommands(
00218                                   ArrayRef<CompileCommandRef> CommandsRef,
00219                                   std::vector<CompileCommand> &Commands) const {
00220   for (int I = 0, E = CommandsRef.size(); I != E; ++I) {
00221     SmallString<8> DirectoryStorage;
00222     SmallString<1024> CommandStorage;
00223     Commands.push_back(CompileCommand(
00224       // FIXME: Escape correctly:
00225       CommandsRef[I].first->getValue(DirectoryStorage),
00226       unescapeCommandLine(CommandsRef[I].second->getValue(CommandStorage))));
00227   }
00228 }
00229 
00230 bool JSONCompilationDatabase::parse(std::string &ErrorMessage) {
00231   llvm::yaml::document_iterator I = YAMLStream.begin();
00232   if (I == YAMLStream.end()) {
00233     ErrorMessage = "Error while parsing YAML.";
00234     return false;
00235   }
00236   llvm::yaml::Node *Root = I->getRoot();
00237   if (!Root) {
00238     ErrorMessage = "Error while parsing YAML.";
00239     return false;
00240   }
00241   llvm::yaml::SequenceNode *Array = dyn_cast<llvm::yaml::SequenceNode>(Root);
00242   if (!Array) {
00243     ErrorMessage = "Expected array.";
00244     return false;
00245   }
00246   for (llvm::yaml::SequenceNode::iterator AI = Array->begin(),
00247                                           AE = Array->end();
00248        AI != AE; ++AI) {
00249     llvm::yaml::MappingNode *Object = dyn_cast<llvm::yaml::MappingNode>(&*AI);
00250     if (!Object) {
00251       ErrorMessage = "Expected object.";
00252       return false;
00253     }
00254     llvm::yaml::ScalarNode *Directory = nullptr;
00255     llvm::yaml::ScalarNode *Command = nullptr;
00256     llvm::yaml::ScalarNode *File = nullptr;
00257     for (llvm::yaml::MappingNode::iterator KVI = Object->begin(),
00258                                            KVE = Object->end();
00259          KVI != KVE; ++KVI) {
00260       llvm::yaml::Node *Value = (*KVI).getValue();
00261       if (!Value) {
00262         ErrorMessage = "Expected value.";
00263         return false;
00264       }
00265       llvm::yaml::ScalarNode *ValueString =
00266           dyn_cast<llvm::yaml::ScalarNode>(Value);
00267       if (!ValueString) {
00268         ErrorMessage = "Expected string as value.";
00269         return false;
00270       }
00271       llvm::yaml::ScalarNode *KeyString =
00272           dyn_cast<llvm::yaml::ScalarNode>((*KVI).getKey());
00273       if (!KeyString) {
00274         ErrorMessage = "Expected strings as key.";
00275         return false;
00276       }
00277       SmallString<8> KeyStorage;
00278       if (KeyString->getValue(KeyStorage) == "directory") {
00279         Directory = ValueString;
00280       } else if (KeyString->getValue(KeyStorage) == "command") {
00281         Command = ValueString;
00282       } else if (KeyString->getValue(KeyStorage) == "file") {
00283         File = ValueString;
00284       } else {
00285         ErrorMessage = ("Unknown key: \"" +
00286                         KeyString->getRawValue() + "\"").str();
00287         return false;
00288       }
00289     }
00290     if (!File) {
00291       ErrorMessage = "Missing key: \"file\".";
00292       return false;
00293     }
00294     if (!Command) {
00295       ErrorMessage = "Missing key: \"command\".";
00296       return false;
00297     }
00298     if (!Directory) {
00299       ErrorMessage = "Missing key: \"directory\".";
00300       return false;
00301     }
00302     SmallString<8> FileStorage;
00303     StringRef FileName = File->getValue(FileStorage);
00304     SmallString<128> NativeFilePath;
00305     if (llvm::sys::path::is_relative(FileName)) {
00306       SmallString<8> DirectoryStorage;
00307       SmallString<128> AbsolutePath(
00308           Directory->getValue(DirectoryStorage));
00309       llvm::sys::path::append(AbsolutePath, FileName);
00310       llvm::sys::path::native(AbsolutePath.str(), NativeFilePath);
00311     } else {
00312       llvm::sys::path::native(FileName, NativeFilePath);
00313     }
00314     IndexByFile[NativeFilePath].push_back(
00315         CompileCommandRef(Directory, Command));
00316     MatchTrie.insert(NativeFilePath.str());
00317   }
00318   return true;
00319 }
00320 
00321 } // end namespace tooling
00322 } // end namespace clang