]> granicus.if.org Git - clang/commitdiff
Use LLVM's plugin registry to enable registering new compilation
authorDaniel Jasper <djasper@google.com>
Fri, 24 Aug 2012 05:50:27 +0000 (05:50 +0000)
committerDaniel Jasper <djasper@google.com>
Fri, 24 Aug 2012 05:50:27 +0000 (05:50 +0000)
databases. Move JSONCompilationDatabase.h to its own files and
register it as plugin.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@162541 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Tooling/CompilationDatabase.h
include/clang/Tooling/CompilationDatabasePluginRegistry.h [new file with mode: 0644]
include/clang/Tooling/JSONCompilationDatabase.h [new file with mode: 0644]
lib/Tooling/CMakeLists.txt
lib/Tooling/CompilationDatabase.cpp
lib/Tooling/CustomCompilationDatabase.h [deleted file]
lib/Tooling/JSONCompilationDatabase.cpp [new file with mode: 0644]
unittests/AST/DeclPrinterTest.cpp
unittests/Tooling/CompilationDatabaseTest.cpp

index f78ffaed284ce4718d593a6ec03f8ae66daa3bb6..b054b85e40eda460641369b4935cc18cff139d1e 100644 (file)
 #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 (file)
index 0000000..84fcd24
--- /dev/null
@@ -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 (file)
index 0000000..2ff2ba5
--- /dev/null
@@ -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
index 750dcc88a844c253f09cc8779e2dc85a45f4144e..c656c057910fc9a7d74d993fc71120a796e8fba1 100644 (file)
@@ -4,6 +4,7 @@ add_clang_library(clangTooling
   ArgumentsAdjusters.cpp
   CommonOptionsParser.cpp
   CompilationDatabase.cpp
+  JSONCompilationDatabase.cpp
   Refactoring.cpp
   RefactoringCallbacks.cpp
   Tooling.cpp
index 3139cc21bb8da590ec9a7204c07ca86e395b08c1..6e941802dc2a2a8f392a474f74e92822d79d1556 100644 (file)
 //
 //===----------------------------------------------------------------------===//
 //
-//  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 (file)
index b375f8d..0000000
+++ /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 (file)
index 0000000..c7cc38a
--- /dev/null
@@ -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 = "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;
+}
+
+} // end namespace tooling
+} // end namespace clang
index 758b34cd4371e5e471b2f1ba491d5f1a7c91ae35..332018e384a229a188f0508fdfc15189b2a57dc5 100644 (file)
@@ -22,6 +22,7 @@
 #include "clang/AST/ASTContext.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
 #include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/SmallString.h"
 #include "gtest/gtest.h"
 
 using namespace clang;
@@ -1237,4 +1238,3 @@ TEST(DeclPrinter, TestTemplateArgumentList15) {
     "Z<sizeof...(T)> A"));
     // Should be: with semicolon, without extra space in "> >"
 }
-
index 591d48dbbd61b5a62d4fc87989e29e717c1d9897..560b931718a334fff24b63577083e4ad28a91305 100644 (file)
@@ -11,7 +11,7 @@
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/DeclGroup.h"
 #include "clang/Frontend/FrontendAction.h"
-#include "clang/Tooling/CompilationDatabase.h"
+#include "clang/Tooling/JSONCompilationDatabase.h"
 #include "clang/Tooling/Tooling.h"
 #include "gtest/gtest.h"