123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323 |
- //===--- JSONCompilationDatabase.cpp - ------------------------------------===//
- //
- // The LLVM Compiler Infrastructure
- //
- // This file is distributed under the University of Illinois Open Source
- // License. See LICENSE.TXT for details.
- //
- //===----------------------------------------------------------------------===//
- //
- // This file contains the implementation of the JSONCompilationDatabase.
- //
- //===----------------------------------------------------------------------===//
- #include "clang/Tooling/JSONCompilationDatabase.h"
- #include "clang/Tooling/CompilationDatabase.h"
- #include "clang/Tooling/CompilationDatabasePluginRegistry.h"
- #include "clang/Tooling/Tooling.h"
- #include "llvm/ADT/SmallString.h"
- #include "llvm/Support/Path.h"
- #include "llvm/Support/system_error.h"
- namespace clang {
- namespace tooling {
- namespace {
- /// \brief A parser for escaped strings of command line arguments.
- ///
- /// Assumes \-escaping for quoted arguments (see the documentation of
- /// unescapeCommandLine(...)).
- class CommandLineArgumentParser {
- public:
- CommandLineArgumentParser(StringRef CommandLine)
- : Input(CommandLine), Position(Input.begin()-1) {}
- std::vector<std::string> parse() {
- bool HasMoreInput = true;
- while (HasMoreInput && nextNonWhitespace()) {
- std::string Argument;
- HasMoreInput = parseStringInto(Argument);
- CommandLine.push_back(Argument);
- }
- return CommandLine;
- }
- private:
- // All private methods return true if there is more input available.
- bool parseStringInto(std::string &String) {
- do {
- if (*Position == '"') {
- if (!parseDoubleQuotedStringInto(String)) return false;
- } else if (*Position == '\'') {
- if (!parseSingleQuotedStringInto(String)) return false;
- } else {
- if (!parseFreeStringInto(String)) return false;
- }
- } while (*Position != ' ');
- return true;
- }
- bool parseDoubleQuotedStringInto(std::string &String) {
- if (!next()) return false;
- while (*Position != '"') {
- if (!skipEscapeCharacter()) return false;
- String.push_back(*Position);
- if (!next()) return false;
- }
- return next();
- }
- bool parseSingleQuotedStringInto(std::string &String) {
- if (!next()) return false;
- while (*Position != '\'') {
- String.push_back(*Position);
- if (!next()) return false;
- }
- return next();
- }
- bool parseFreeStringInto(std::string &String) {
- do {
- if (!skipEscapeCharacter()) return false;
- String.push_back(*Position);
- if (!next()) return false;
- } while (*Position != ' ' && *Position != '"' && *Position != '\'');
- return true;
- }
- bool skipEscapeCharacter() {
- if (*Position == '\\') {
- return next();
- }
- return true;
- }
- bool nextNonWhitespace() {
- do {
- if (!next()) return false;
- } while (*Position == ' ');
- return true;
- }
- bool next() {
- ++Position;
- return Position != Input.end();
- }
- const StringRef Input;
- StringRef::iterator Position;
- std::vector<std::string> CommandLine;
- };
- std::vector<std::string> unescapeCommandLine(
- StringRef EscapedCommandLine) {
- CommandLineArgumentParser parser(EscapedCommandLine);
- return parser.parse();
- }
- class JSONCompilationDatabasePlugin : public CompilationDatabasePlugin {
- virtual CompilationDatabase *loadFromDirectory(
- StringRef Directory, std::string &ErrorMessage) {
- SmallString<1024> JSONDatabasePath(Directory);
- llvm::sys::path::append(JSONDatabasePath, "compile_commands.json");
- OwningPtr<CompilationDatabase> Database(
- JSONCompilationDatabase::loadFromFile(JSONDatabasePath, ErrorMessage));
- if (!Database)
- return NULL;
- return Database.take();
- }
- };
- } // end namespace
- // Register the JSONCompilationDatabasePlugin with the
- // CompilationDatabasePluginRegistry using this statically initialized variable.
- static CompilationDatabasePluginRegistry::Add<JSONCompilationDatabasePlugin>
- X("json-compilation-database", "Reads JSON formatted compilation databases");
- // This anchor is used to force the linker to link in the generated object file
- // and thus register the JSONCompilationDatabasePlugin.
- volatile int JSONAnchorSource = 0;
- JSONCompilationDatabase *
- JSONCompilationDatabase::loadFromFile(StringRef FilePath,
- std::string &ErrorMessage) {
- OwningPtr<llvm::MemoryBuffer> DatabaseBuffer;
- llvm::error_code Result =
- llvm::MemoryBuffer::getFile(FilePath, DatabaseBuffer);
- if (Result != 0) {
- ErrorMessage = "Error while opening JSON database: " + Result.message();
- return NULL;
- }
- OwningPtr<JSONCompilationDatabase> Database(
- new JSONCompilationDatabase(DatabaseBuffer.take()));
- if (!Database->parse(ErrorMessage))
- return NULL;
- return Database.take();
- }
- JSONCompilationDatabase *
- JSONCompilationDatabase::loadFromBuffer(StringRef DatabaseString,
- std::string &ErrorMessage) {
- OwningPtr<llvm::MemoryBuffer> DatabaseBuffer(
- llvm::MemoryBuffer::getMemBuffer(DatabaseString));
- OwningPtr<JSONCompilationDatabase> Database(
- new JSONCompilationDatabase(DatabaseBuffer.take()));
- if (!Database->parse(ErrorMessage))
- return NULL;
- return Database.take();
- }
- std::vector<CompileCommand>
- JSONCompilationDatabase::getCompileCommands(StringRef FilePath) const {
- SmallString<128> NativeFilePath;
- llvm::sys::path::native(FilePath, NativeFilePath);
- std::string Error;
- llvm::raw_string_ostream ES(Error);
- StringRef Match = MatchTrie.findEquivalent(NativeFilePath.str(), ES);
- if (Match.empty())
- return std::vector<CompileCommand>();
- llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator
- CommandsRefI = IndexByFile.find(Match);
- if (CommandsRefI == IndexByFile.end())
- return std::vector<CompileCommand>();
- std::vector<CompileCommand> Commands;
- getCommands(CommandsRefI->getValue(), Commands);
- return Commands;
- }
- std::vector<std::string>
- JSONCompilationDatabase::getAllFiles() const {
- std::vector<std::string> Result;
- llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator
- CommandsRefI = IndexByFile.begin();
- const llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator
- CommandsRefEnd = IndexByFile.end();
- for (; CommandsRefI != CommandsRefEnd; ++CommandsRefI) {
- Result.push_back(CommandsRefI->first().str());
- }
- return Result;
- }
- std::vector<CompileCommand>
- JSONCompilationDatabase::getAllCompileCommands() const {
- std::vector<CompileCommand> Commands;
- for (llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator
- CommandsRefI = IndexByFile.begin(), CommandsRefEnd = IndexByFile.end();
- CommandsRefI != CommandsRefEnd; ++CommandsRefI) {
- getCommands(CommandsRefI->getValue(), Commands);
- }
- return Commands;
- }
- void JSONCompilationDatabase::getCommands(
- ArrayRef<CompileCommandRef> CommandsRef,
- std::vector<CompileCommand> &Commands) const {
- for (int I = 0, E = CommandsRef.size(); I != E; ++I) {
- SmallString<8> DirectoryStorage;
- SmallString<1024> CommandStorage;
- Commands.push_back(CompileCommand(
- // FIXME: Escape correctly:
- CommandsRef[I].first->getValue(DirectoryStorage),
- unescapeCommandLine(CommandsRef[I].second->getValue(CommandStorage))));
- }
- }
- bool JSONCompilationDatabase::parse(std::string &ErrorMessage) {
- llvm::yaml::document_iterator I = YAMLStream.begin();
- if (I == YAMLStream.end()) {
- ErrorMessage = "Error while parsing YAML.";
- return false;
- }
- llvm::yaml::Node *Root = I->getRoot();
- if (Root == NULL) {
- ErrorMessage = "Error while parsing YAML.";
- return false;
- }
- llvm::yaml::SequenceNode *Array = dyn_cast<llvm::yaml::SequenceNode>(Root);
- if (Array == NULL) {
- ErrorMessage = "Expected array.";
- return false;
- }
- for (llvm::yaml::SequenceNode::iterator AI = Array->begin(),
- AE = Array->end();
- AI != AE; ++AI) {
- llvm::yaml::MappingNode *Object = dyn_cast<llvm::yaml::MappingNode>(&*AI);
- if (Object == NULL) {
- ErrorMessage = "Expected object.";
- return false;
- }
- llvm::yaml::ScalarNode *Directory = NULL;
- llvm::yaml::ScalarNode *Command = NULL;
- llvm::yaml::ScalarNode *File = NULL;
- for (llvm::yaml::MappingNode::iterator KVI = Object->begin(),
- KVE = Object->end();
- KVI != KVE; ++KVI) {
- llvm::yaml::Node *Value = (*KVI).getValue();
- if (Value == NULL) {
- ErrorMessage = "Expected value.";
- return false;
- }
- llvm::yaml::ScalarNode *ValueString =
- dyn_cast<llvm::yaml::ScalarNode>(Value);
- if (ValueString == NULL) {
- ErrorMessage = "Expected string as value.";
- return false;
- }
- llvm::yaml::ScalarNode *KeyString =
- dyn_cast<llvm::yaml::ScalarNode>((*KVI).getKey());
- if (KeyString == NULL) {
- ErrorMessage = "Expected strings as key.";
- return false;
- }
- SmallString<8> KeyStorage;
- if (KeyString->getValue(KeyStorage) == "directory") {
- Directory = ValueString;
- } else if (KeyString->getValue(KeyStorage) == "command") {
- Command = ValueString;
- } else if (KeyString->getValue(KeyStorage) == "file") {
- File = ValueString;
- } else {
- ErrorMessage = ("Unknown key: \"" +
- KeyString->getRawValue() + "\"").str();
- return false;
- }
- }
- if (!File) {
- ErrorMessage = "Missing key: \"file\".";
- return false;
- }
- if (!Command) {
- ErrorMessage = "Missing key: \"command\".";
- return false;
- }
- if (!Directory) {
- ErrorMessage = "Missing key: \"directory\".";
- return false;
- }
- SmallString<8> FileStorage;
- StringRef FileName = File->getValue(FileStorage);
- SmallString<128> NativeFilePath;
- if (llvm::sys::path::is_relative(FileName)) {
- SmallString<8> DirectoryStorage;
- SmallString<128> AbsolutePath(
- Directory->getValue(DirectoryStorage));
- llvm::sys::path::append(AbsolutePath, FileName);
- llvm::sys::path::native(AbsolutePath.str(), NativeFilePath);
- } else {
- llvm::sys::path::native(FileName, NativeFilePath);
- }
- IndexByFile[NativeFilePath].push_back(
- CompileCommandRef(Directory, Command));
- MatchTrie.insert(NativeFilePath.str());
- }
- return true;
- }
- } // end namespace tooling
- } // end namespace clang
|