diff options
-rw-r--r-- | include/clang/Tooling/CompilationDatabase.h | 94 | ||||
-rw-r--r-- | include/clang/Tooling/CompilationDatabasePluginRegistry.h | 27 | ||||
-rw-r--r-- | include/clang/Tooling/JSONCompilationDatabase.h | 104 | ||||
-rw-r--r-- | lib/Tooling/CMakeLists.txt | 1 | ||||
-rw-r--r-- | lib/Tooling/CompilationDatabase.cpp | 292 | ||||
-rw-r--r-- | lib/Tooling/CustomCompilationDatabase.h | 42 | ||||
-rw-r--r-- | lib/Tooling/JSONCompilationDatabase.cpp | 283 | ||||
-rw-r--r-- | unittests/AST/DeclPrinterTest.cpp | 2 | ||||
-rw-r--r-- | unittests/Tooling/CompilationDatabaseTest.cpp | 2 |
9 files changed, 474 insertions, 373 deletions
diff --git a/include/clang/Tooling/CompilationDatabase.h b/include/clang/Tooling/CompilationDatabase.h index f78ffaed28..b054b85e40 100644 --- a/include/clang/Tooling/CompilationDatabase.h +++ b/include/clang/Tooling/CompilationDatabase.h @@ -31,12 +31,8 @@ #include "clang/Basic/LLVM.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/OwningPtr.h" -#include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" -#include "llvm/Support/MemoryBuffer.h" -#include "llvm/Support/SourceMgr.h" -#include "llvm/Support/YAMLParser.h" #include <string> #include <vector> @@ -111,6 +107,27 @@ public: virtual std::vector<std::string> getAllFiles() const = 0; }; +/// \brief Interface for compilation database plugins. +/// +/// A compilation database plugin allows the user to register custom compilation +/// databases that are picked up as compilation database if the corresponding +/// library is linked in. To register a plugin, declare a static variable like: +/// +/// \code +/// static CompilationDatabasePluginRegistry::Add<MyDatabasePlugin> +/// X("my-compilation-database", "Reads my own compilation database"); +/// \endcode +class CompilationDatabasePlugin { +public: + virtual ~CompilationDatabasePlugin(); + + /// \brief Loads a compilation database from a build directory. + /// + /// \see CompilationDatabase::loadFromDirectory(). + virtual CompilationDatabase *loadFromDirectory(StringRef Directory, + std::string &ErrorMessage) = 0; +}; + /// \brief A compilation database that returns a single compile command line. /// /// Useful when we want a tool to behave more like a compiler invocation. @@ -169,75 +186,6 @@ private: std::vector<CompileCommand> CompileCommands; }; -/// \brief A JSON based compilation database. -/// -/// JSON compilation database files must contain a list of JSON objects which -/// provide the command lines in the attributes 'directory', 'command' and -/// 'file': -/// [ -/// { "directory": "<working directory of the compile>", -/// "command": "<compile command line>", -/// "file": "<path to source file>" -/// }, -/// ... -/// ] -/// Each object entry defines one compile action. The specified file is -/// considered to be the main source file for the translation unit. -/// -/// JSON compilation databases can for example be generated in CMake projects -/// by setting the flag -DCMAKE_EXPORT_COMPILE_COMMANDS. -class JSONCompilationDatabase : public CompilationDatabase { -public: - /// \brief Loads a JSON compilation database from the specified file. - /// - /// Returns NULL and sets ErrorMessage if the database could not be - /// loaded from the given file. - static JSONCompilationDatabase *loadFromFile(StringRef FilePath, - std::string &ErrorMessage); - - /// \brief Loads a JSON compilation database from a data buffer. - /// - /// Returns NULL and sets ErrorMessage if the database could not be loaded. - static JSONCompilationDatabase *loadFromBuffer(StringRef DatabaseString, - std::string &ErrorMessage); - - /// \brief Returns all compile comamnds in which the specified file was - /// compiled. - /// - /// FIXME: Currently FilePath must be an absolute path inside the - /// source directory which does not have symlinks resolved. - virtual std::vector<CompileCommand> getCompileCommands( - StringRef FilePath) const; - - /// \brief Returns the list of all files available in the compilation database. - /// - /// These are the 'file' entries of the JSON objects. - virtual std::vector<std::string> getAllFiles() const; - -private: - /// \brief Constructs a JSON compilation database on a memory buffer. - JSONCompilationDatabase(llvm::MemoryBuffer *Database) - : Database(Database), YAMLStream(Database->getBuffer(), SM) {} - - /// \brief Parses the database file and creates the index. - /// - /// Returns whether parsing succeeded. Sets ErrorMessage if parsing - /// failed. - bool parse(std::string &ErrorMessage); - - // Tuple (directory, commandline) where 'commandline' pointing to the - // corresponding nodes in the YAML stream. - typedef std::pair<llvm::yaml::ScalarNode*, - llvm::yaml::ScalarNode*> CompileCommandRef; - - // Maps file paths to the compile command lines for that file. - llvm::StringMap< std::vector<CompileCommandRef> > IndexByFile; - - llvm::OwningPtr<llvm::MemoryBuffer> Database; - llvm::SourceMgr SM; - llvm::yaml::Stream YAMLStream; -}; - } // end namespace tooling } // end namespace clang diff --git a/include/clang/Tooling/CompilationDatabasePluginRegistry.h b/include/clang/Tooling/CompilationDatabasePluginRegistry.h new file mode 100644 index 0000000000..84fcd24b4a --- /dev/null +++ b/include/clang/Tooling/CompilationDatabasePluginRegistry.h @@ -0,0 +1,27 @@ +//===--- CompilationDatabasePluginRegistry.h - ------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_COMPILATION_DATABASE_PLUGIN_REGISTRY_H +#define LLVM_CLANG_TOOLING_COMPILATION_DATABASE_PLUGIN_REGISTRY_H + +#include "clang/Tooling/CompilationDatabase.h" +#include "llvm/Support/Registry.h" + +namespace clang { +namespace tooling { + +class CompilationDatabasePlugin; + +typedef llvm::Registry<CompilationDatabasePlugin> + CompilationDatabasePluginRegistry; + +} // end namespace tooling +} // end namespace clang + +#endif // LLVM_CLANG_TOOLING_COMPILATION_DATABASE_PLUGIN_REGISTRY_H diff --git a/include/clang/Tooling/JSONCompilationDatabase.h b/include/clang/Tooling/JSONCompilationDatabase.h new file mode 100644 index 0000000000..2ff2ba5a11 --- /dev/null +++ b/include/clang/Tooling/JSONCompilationDatabase.h @@ -0,0 +1,104 @@ +//===--- JSONCompilationDatabase.h - ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// The JSONCompilationDatabase finds compilation databases supplied as a file +// 'compile_commands.json'. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_JSON_COMPILATION_DATABASE_H +#define LLVM_CLANG_TOOLING_JSON_COMPILATION_DATABASE_H + +#include "clang/Basic/LLVM.h" +#include "clang/Tooling/CompilationDatabase.h" +#include "llvm/ADT/OwningPtr.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/YAMLParser.h" +#include <string> +#include <vector> + +namespace clang { +namespace tooling { + +/// \brief A JSON based compilation database. +/// +/// JSON compilation database files must contain a list of JSON objects which +/// provide the command lines in the attributes 'directory', 'command' and +/// 'file': +/// [ +/// { "directory": "<working directory of the compile>", +/// "command": "<compile command line>", +/// "file": "<path to source file>" +/// }, +/// ... +/// ] +/// Each object entry defines one compile action. The specified file is +/// considered to be the main source file for the translation unit. +/// +/// JSON compilation databases can for example be generated in CMake projects +/// by setting the flag -DCMAKE_EXPORT_COMPILE_COMMANDS. +class JSONCompilationDatabase : public CompilationDatabase { +public: + /// \brief Loads a JSON compilation database from the specified file. + /// + /// Returns NULL and sets ErrorMessage if the database could not be + /// loaded from the given file. + static JSONCompilationDatabase *loadFromFile(StringRef FilePath, + std::string &ErrorMessage); + + /// \brief Loads a JSON compilation database from a data buffer. + /// + /// Returns NULL and sets ErrorMessage if the database could not be loaded. + static JSONCompilationDatabase *loadFromBuffer(StringRef DatabaseString, + std::string &ErrorMessage); + + /// \brief Returns all compile comamnds in which the specified file was + /// compiled. + /// + /// FIXME: Currently FilePath must be an absolute path inside the + /// source directory which does not have symlinks resolved. + virtual std::vector<CompileCommand> getCompileCommands( + StringRef FilePath) const; + + /// \brief Returns the list of all files available in the compilation database. + /// + /// These are the 'file' entries of the JSON objects. + virtual std::vector<std::string> getAllFiles() const; + +private: + /// \brief Constructs a JSON compilation database on a memory buffer. + JSONCompilationDatabase(llvm::MemoryBuffer *Database) + : Database(Database), YAMLStream(Database->getBuffer(), SM) {} + + /// \brief Parses the database file and creates the index. + /// + /// Returns whether parsing succeeded. Sets ErrorMessage if parsing + /// failed. + bool parse(std::string &ErrorMessage); + + // Tuple (directory, commandline) where 'commandline' pointing to the + // corresponding nodes in the YAML stream. + typedef std::pair<llvm::yaml::ScalarNode*, + llvm::yaml::ScalarNode*> CompileCommandRef; + + // Maps file paths to the compile command lines for that file. + llvm::StringMap< std::vector<CompileCommandRef> > IndexByFile; + + llvm::OwningPtr<llvm::MemoryBuffer> Database; + llvm::SourceMgr SM; + llvm::yaml::Stream YAMLStream; +}; + +} // end namespace tooling +} // end namespace clang + +#endif // LLVM_CLANG_TOOLING_JSON_COMPILATION_DATABASE_H diff --git a/lib/Tooling/CMakeLists.txt b/lib/Tooling/CMakeLists.txt index 750dcc88a8..c656c05791 100644 --- a/lib/Tooling/CMakeLists.txt +++ b/lib/Tooling/CMakeLists.txt @@ -4,6 +4,7 @@ add_clang_library(clangTooling ArgumentsAdjusters.cpp CommonOptionsParser.cpp CompilationDatabase.cpp + JSONCompilationDatabase.cpp Refactoring.cpp RefactoringCallbacks.cpp Tooling.cpp diff --git a/lib/Tooling/CompilationDatabase.cpp b/lib/Tooling/CompilationDatabase.cpp index 3139cc21bb..6e941802dc 100644 --- a/lib/Tooling/CompilationDatabase.cpp +++ b/lib/Tooling/CompilationDatabase.cpp @@ -7,141 +7,60 @@ // //===----------------------------------------------------------------------===// // -// This file contains multiple implementations for CompilationDatabases. +// This file contains implementations of the CompilationDatabase base class +// and the FixedCompilationDatabase. // //===----------------------------------------------------------------------===// +#include <sstream> #include "clang/Tooling/CompilationDatabase.h" +#include "clang/Tooling/CompilationDatabasePluginRegistry.h" #include "clang/Tooling/Tooling.h" #include "llvm/ADT/SmallString.h" -#include "llvm/Support/YAMLParser.h" #include "llvm/Support/Path.h" #include "llvm/Support/system_error.h" -#ifdef USE_CUSTOM_COMPILATION_DATABASE -#include "CustomCompilationDatabase.h" -#endif - 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 (!parseQuotedStringInto(String)) return false; - } else { - if (!parseFreeStringInto(String)) return false; - } - } while (*Position != ' '); - return true; - } - - bool parseQuotedStringInto(std::string &String) { - if (!next()) return false; - while (*Position != '"') { - if (!skipEscapeCharacter()) return false; - 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 != '"'); - 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(); -} - -} // end namespace - CompilationDatabase::~CompilationDatabase() {} CompilationDatabase * CompilationDatabase::loadFromDirectory(StringRef BuildDirectory, std::string &ErrorMessage) { - llvm::SmallString<1024> JSONDatabasePath(BuildDirectory); - llvm::sys::path::append(JSONDatabasePath, "compile_commands.json"); - llvm::OwningPtr<CompilationDatabase> Database( - JSONCompilationDatabase::loadFromFile(JSONDatabasePath, ErrorMessage)); - if (!Database) { - return NULL; + std::stringstream ErrorStream; + for (CompilationDatabasePluginRegistry::iterator + It = CompilationDatabasePluginRegistry::begin(), + Ie = CompilationDatabasePluginRegistry::end(); + It != Ie; ++It) { + std::string DatabaseErrorMessage; + OwningPtr<CompilationDatabasePlugin> Plugin(It->instantiate()); + if (CompilationDatabase *DB = + Plugin->loadFromDirectory(BuildDirectory, DatabaseErrorMessage)) + return DB; + else + ErrorStream << It->getName() << ": " << DatabaseErrorMessage << "\n"; } - return Database.take(); + ErrorMessage = ErrorStream.str(); + return NULL; } static CompilationDatabase * -findCompilationDatabaseFromDirectory(StringRef Directory) { -#ifdef USE_CUSTOM_COMPILATION_DATABASE - if (CompilationDatabase *DB = - ::clang::tooling::findCompilationDatabaseForDirectory(Directory)) - return DB; -#endif +findCompilationDatabaseFromDirectory(StringRef Directory, + std::string &ErrorMessage) { + std::stringstream ErrorStream; while (!Directory.empty()) { std::string LoadErrorMessage; if (CompilationDatabase *DB = CompilationDatabase::loadFromDirectory(Directory, LoadErrorMessage)) return DB; + ErrorStream << "No compilation database found in " << Directory.str() + << "\n" << LoadErrorMessage; Directory = llvm::sys::path::parent_path(Directory); } + ErrorMessage = ErrorStream.str(); return NULL; } @@ -151,11 +70,12 @@ CompilationDatabase::autoDetectFromSource(StringRef SourceFile, llvm::SmallString<1024> AbsolutePath(getAbsolutePath(SourceFile)); StringRef Directory = llvm::sys::path::parent_path(AbsolutePath); - CompilationDatabase *DB = findCompilationDatabaseFromDirectory(Directory); + CompilationDatabase *DB = findCompilationDatabaseFromDirectory(Directory, + ErrorMessage); if (!DB) ErrorMessage = ("Could not auto-detect compilation database for file \"" + - SourceFile + "\"").str(); + SourceFile + "\"\n" + ErrorMessage).str(); return DB; } @@ -164,14 +84,17 @@ CompilationDatabase::autoDetectFromDirectory(StringRef SourceDir, std::string &ErrorMessage) { llvm::SmallString<1024> AbsolutePath(getAbsolutePath(SourceDir)); - CompilationDatabase *DB = findCompilationDatabaseFromDirectory(AbsolutePath); + CompilationDatabase *DB = findCompilationDatabaseFromDirectory(AbsolutePath, + ErrorMessage); if (!DB) ErrorMessage = ("Could not auto-detect compilation database from directory \"" + - SourceDir + "\"").str(); + SourceDir + "\"\n" + ErrorMessage).str(); return DB; } +CompilationDatabasePlugin::~CompilationDatabasePlugin() {} + FixedCompilationDatabase * FixedCompilationDatabase::loadFromCommandLine(int &Argc, const char **Argv, @@ -204,153 +127,10 @@ FixedCompilationDatabase::getAllFiles() const { return std::vector<std::string>(); } -JSONCompilationDatabase * -JSONCompilationDatabase::loadFromFile(StringRef FilePath, - std::string &ErrorMessage) { - llvm::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; - } - llvm::OwningPtr<JSONCompilationDatabase> Database( - new JSONCompilationDatabase(DatabaseBuffer.take())); - if (!Database->parse(ErrorMessage)) - return NULL; - return Database.take(); -} - -JSONCompilationDatabase * -JSONCompilationDatabase::loadFromBuffer(StringRef DatabaseString, - std::string &ErrorMessage) { - llvm::OwningPtr<llvm::MemoryBuffer> DatabaseBuffer( - llvm::MemoryBuffer::getMemBuffer(DatabaseString)); - llvm::OwningPtr<JSONCompilationDatabase> Database( - new JSONCompilationDatabase(DatabaseBuffer.take())); - if (!Database->parse(ErrorMessage)) - return NULL; - return Database.take(); -} - -std::vector<CompileCommand> -JSONCompilationDatabase::getCompileCommands(StringRef FilePath) const { - llvm::SmallString<128> NativeFilePath; - llvm::sys::path::native(FilePath, NativeFilePath); - llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator - CommandsRefI = IndexByFile.find(NativeFilePath); - if (CommandsRefI == IndexByFile.end()) - return std::vector<CompileCommand>(); - const std::vector<CompileCommandRef> &CommandsRef = CommandsRefI->getValue(); - std::vector<CompileCommand> Commands; - for (int I = 0, E = CommandsRef.size(); I != E; ++I) { - llvm::SmallString<8> DirectoryStorage; - llvm::SmallString<1024> CommandStorage; - Commands.push_back(CompileCommand( - // FIXME: Escape correctly: - CommandsRef[I].first->getValue(DirectoryStorage), - unescapeCommandLine(CommandsRef[I].second->getValue(CommandStorage)))); - } - 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; -} - -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 = - llvm::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 = - llvm::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 = - llvm::dyn_cast<llvm::yaml::ScalarNode>(Value); - if (ValueString == NULL) { - ErrorMessage = "Expected string as value."; - return false; - } - llvm::yaml::ScalarNode *KeyString = - llvm::dyn_cast<llvm::yaml::ScalarNode>((*KVI).getKey()); - if (KeyString == NULL) { - ErrorMessage = "Expected strings as key."; - return false; - } - llvm::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; - } - llvm::SmallString<8> FileStorage; - llvm::SmallString<128> NativeFilePath; - llvm::sys::path::native(File->getValue(FileStorage), NativeFilePath); - IndexByFile[NativeFilePath].push_back( - CompileCommandRef(Directory, Command)); - } - return true; -} +// This anchor is used to force the linker to link in the generated object file +// and thus register the JSONCompilationDatabasePlugin. +extern volatile int JSONAnchorSource; +static int JSONAnchorDest = JSONAnchorSource; } // end namespace tooling } // end namespace clang diff --git a/lib/Tooling/CustomCompilationDatabase.h b/lib/Tooling/CustomCompilationDatabase.h deleted file mode 100644 index b375f8d256..0000000000 --- a/lib/Tooling/CustomCompilationDatabase.h +++ /dev/null @@ -1,42 +0,0 @@ -//===--- CustomCompilationDatabase.h --------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file contains a hook to supply a custom \c CompilationDatabase -// implementation. -// -// The mechanism can be used by IDEs or non-public code bases to integrate with -// their build system. Currently we support statically linking in an -// implementation of \c findCompilationDatabaseForDirectory and enabling it -// with -DUSE_CUSTOM_COMPILATION_DATABASE when compiling the Tooling library. -// -// FIXME: The strategy forward is to provide a plugin system that can load -// custom compilation databases and make enabling that a build option. -// -//===----------------------------------------------------------------------===// -#ifndef LLVM_CLANG_TOOLING_CUSTOM_COMPILATION_DATABASE_H -#define LLVM_CLANG_TOOLING_CUSTOM_COMPILATION_DATABASE_H - -#include "llvm/ADT/StringRef.h" - -namespace clang { -namespace tooling { -class CompilationDatabase; - -/// \brief Returns a CompilationDatabase for the given \c Directory. -/// -/// \c Directory can be any directory within a project. This methods will -/// then try to find compilation database files in \c Directory or any of its -/// parents. If a compilation database cannot be found or loaded, returns NULL. -clang::tooling::CompilationDatabase *findCompilationDatabaseForDirectory( - llvm::StringRef Directory); - -} // namespace tooling -} // namespace clang - -#endif // LLVM_CLANG_TOOLING_CUSTOM_COMPILATION_DATABASE_H diff --git a/lib/Tooling/JSONCompilationDatabase.cpp b/lib/Tooling/JSONCompilationDatabase.cpp new file mode 100644 index 0000000000..c7cc38a31f --- /dev/null +++ b/lib/Tooling/JSONCompilationDatabase.cpp @@ -0,0 +1,283 @@ +//===--- 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 (!parseQuotedStringInto(String)) return false; + } else { + if (!parseFreeStringInto(String)) return false; + } + } while (*Position != ' '); + return true; + } + + bool parseQuotedStringInto(std::string &String) { + if (!next()) return false; + while (*Position != '"') { + if (!skipEscapeCharacter()) return false; + 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 != '"'); + 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(); +} + +} // end namespace + +class JSONCompilationDatabasePlugin : public CompilationDatabasePlugin { + virtual CompilationDatabase *loadFromDirectory( + StringRef Directory, std::string &ErrorMessage) { + llvm::SmallString<1024> JSONDatabasePath(Directory); + llvm::sys::path::append(JSONDatabasePath, "compile_commands.json"); + llvm::OwningPtr<CompilationDatabase> Database( + JSONCompilationDatabase::loadFromFile(JSONDatabasePath, ErrorMessage)); + if (!Database) + return NULL; + return Database.take(); + } +}; + +// 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. +int JSONAnchorSource = 0; + +JSONCompilationDatabase * +JSONCompilationDatabase::loadFromFile(StringRef FilePath, + std::string &ErrorMessage) { + llvm::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; + } + llvm::OwningPtr<JSONCompilationDatabase> Database( + new JSONCompilationDatabase(DatabaseBuffer.take())); + if (!Database->parse(ErrorMessage)) + return NULL; + return Database.take(); +} + +JSONCompilationDatabase * +JSONCompilationDatabase::loadFromBuffer(StringRef DatabaseString, + std::string &ErrorMessage) { + llvm::OwningPtr<llvm::MemoryBuffer> DatabaseBuffer( + llvm::MemoryBuffer::getMemBuffer(DatabaseString)); + llvm::OwningPtr<JSONCompilationDatabase> Database( + new JSONCompilationDatabase(DatabaseBuffer.take())); + if (!Database->parse(ErrorMessage)) + return NULL; + return Database.take(); +} + +std::vector<CompileCommand> +JSONCompilationDatabase::getCompileCommands(StringRef FilePath) const { + llvm::SmallString<128> NativeFilePath; + llvm::sys::path::native(FilePath, NativeFilePath); + llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator + CommandsRefI = IndexByFile.find(NativeFilePath); + if (CommandsRefI == IndexByFile.end()) + return std::vector<CompileCommand>(); + const std::vector<CompileCommandRef> &CommandsRef = CommandsRefI->getValue(); + std::vector<CompileCommand> Commands; + for (int I = 0, E = CommandsRef.size(); I != E; ++I) { + llvm::SmallString<8> DirectoryStorage; + llvm::SmallString<1024> CommandStorage; + Commands.push_back(CompileCommand( + // FIXME: Escape correctly: + CommandsRef[I].first->getValue(DirectoryStorage), + unescapeCommandLine(CommandsRef[I].second->getValue(CommandStorage)))); + } + 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; +} + +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 = + llvm::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 = + llvm::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 = + llvm::dyn_cast<llvm::yaml::ScalarNode>(Value); + if (ValueString == NULL) { + ErrorMessage = "Expected string as value."; + return false; + } + llvm::yaml::ScalarNode *KeyString = + llvm::dyn_cast<llvm::yaml::ScalarNode>((*KVI).getKey()); + if (KeyString == NULL) { + ErrorMessage = "Expected strings as key."; + return false; + } + llvm::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 = "Missi |