The main goal of this change is to make using compiler plugins in Clang much
easier than the current setup. The changes to the PrintFunctionNames example and
the documentation should illustrate how much less boilerplate is required to get
plugins working.


# HG changeset patch
# User Joshua Cranmer <[email protected]>
# Date 1325721797 21600
# Node ID 1f6948a65e07550c29902fb5f60a3b7461b2deff
# Parent  c9a439f1329c017911303e6b0de50b4ef098a903
Implement a sane plugin API for clang

The main goal of this change is to make using compiler plugins in Clang much
easier than the current setup. The changes to the PrintFunctionNames example and
the documentation should illustrate how much less boilerplate is required to get
plugins working.

diff --git a/docs/ClangPlugins.rst b/docs/ClangPlugins.rst
--- a/docs/ClangPlugins.rst
+++ b/docs/ClangPlugins.rst
@@ -4,64 +4,92 @@ Clang Plugins
 
 Clang Plugins make it possible to run extra user defined actions during a
 compilation. This document will provide a basic walkthrough of how to write and
 run a Clang Plugin.
 
 Introduction
 ============
 
-Clang Plugins run FrontendActions over code. See the :doc:`FrontendAction
-tutorial <RAVFrontendAction>` on how to write a ``FrontendAction`` using the
-``RecursiveASTVisitor``. In this tutorial, we'll demonstrate how to write a
-simple clang plugin.
+Clang Plugins run a set of callbacks over code. These callbacks are set up by
+the plugin when the compiler calls a function exported by the plugin. The main
+function exported by the plugin is clang_plugin_begin_tu, which is called before
+any file is compiled. Passed in as arguments to this hook are a struct that
+contains information about how the plugin was invoked (including both
+command-line arguments and the actual filename used), the name of the file being
+compiled, a CompilerInstance object which encapsulates the options of the
+compiler itself, and a callbacks object that the plugin fills in with callbacks
+to observer or effect the compilation process.
 
-Writing a ``PluginASTAction``
-=============================
+All of the exposed callbacks can be found in the `Doxygen manual
+<../doxygen/classclang_1_1plugin_1_1TUCallbacks.html>`_. The most import of
+these callbacks is the ``ASTConsumer`` callback, which allows plugins to
+interact with the abstract syntax tree of code. See the :doc:`FrontendAction
+tutorial <RAVFrontendAction>` for how to write an ``ASTConsumer`` using the
+``RecursiveASTVisitor`` and the :doc:`Clang AST introduction
+IntroductionToTheClangAST` for general information about the AST. In this
+tutorial, we'll demonstrate how to write a simple clang plugin.
 
-The main difference from writing normal ``FrontendActions`` is that you can
-handle plugin command line options. The ``PluginASTAction`` base class declares
-a ``ParseArgs`` method which you have to implement in your plugin.
+Plugin hooks
+============
+
+The function that is exported to the compiler by plugins is
+``clang_plugin_begin_tu``, called before every translation unit is invoked.
+Information about the plugin invocation is passed in, so plugins can make us of
+arguments or find auxiliary files based on where the plugin library is located
+in the filesystem. Also passed in is the name of the file being compiled as well
+as the an instance of ``CompilerInstance``, which manages the various objects
+used in the course of compilation such as language options or the diagnostics
+engine. Also passed into this method is a callbacks object, which contains
+various hook points that a plugin can set to interact with the compiler. An
+example of this function looks like this:
 
 .. code-block:: c++
 
-  bool ParseArgs(const CompilerInstance &CI,
-                 const std::vector<std::string>& args) {
-    for (unsigned i = 0, e = args.size(); i != e; ++i) {
-      if (args[i] == "-some-arg") {
-        // Handle the command line argument.
-      }
+  void clang_plugin_on_tu(const plugin::PluginInfo &Info,
+                          llvm::StringRef FileName,
+                          const CompilerInstance &CI,
+                          plugin::TUCallbacks &Callbacks) {
+    llvm::errs() << "Invoking plugin " << Info.PluginPath << '\n';
+    for (unsigned i = 0; i < Info.Arguments.size(); i++) {
+      llvm::errs() << "Plugin argument: argument " << Info.Arguments[i].first <<
+        " is set to value " << Info.Arguments[i].second << '\n';
     }
-    return true;
+    llvm::errs() << "Found when compiling " << FileName << ":\n";
+    Callbacks.ASTCallback = new PrintFunctionsConsumer();
   }
 
-Registering a plugin
+Plugin compatibility
 ====================
 
-A plugin is loaded from a dynamic library at runtime by the compiler. To
-register a plugin in a library, use ``FrontendPluginRegistry::Add<>``:
+Clang has no guarantees about API or ABI compatibility for most of the APIs. To
+enforce that plugins cannot be used with versions of clang different from the
+one they were compiled with, a ``DECLARE_PLUGIN_COMPATIBILITY`` macro is used.
+This creates an exported global variable that clang uses to determine if the
+plugin is compatible.
 
-.. code-block:: c++
-
-  static FrontendPluginRegistry::Add<MyPlugin> X("my-plugin-name", "my plugin description");
+Although plugins are not ABI-compatible with multiple versions of clang, it
+is possible for them to remain API-compatible. By using the
+``CLANG_VERSION_MAJOR`` and ``CLANG_VERSION_MINOR`` macros, it is possible to do
+conditional compilation on the version of clang.`
 
 Putting it all together
 =======================
 
 Let's look at an example plugin that prints top-level function names.  This
 example is also checked into the clang repository; please also take a look at
 the latest `checked in version of PrintFunctionNames.cpp
 <http://llvm.org/viewvc/llvm-project/cfe/trunk/examples/PrintFunctionNames/PrintFunctionNames.cpp?view=markup>`_.
 
 .. code-block:: c++
 
-    #include "clang/Frontend/FrontendPluginRegistry.h"
+    #include "clang/Basic/Plugin.h"
+    #include "clang/Basic/Version.h"
     #include "clang/AST/ASTConsumer.h"
     #include "clang/AST/AST.h"
-    #include "clang/Frontend/CompilerInstance.h"
     #include "llvm/Support/raw_ostream.h"
     using namespace clang;
 
     namespace {
 
     class PrintFunctionsConsumer : public ASTConsumer {
     public:
       virtual bool HandleTopLevelDecl(DeclGroupRef DG) {
@@ -70,81 +98,47 @@ the latest `checked in version of PrintF
           if (const NamedDecl *ND = dyn_cast<NamedDecl>(D))
             llvm::errs() << "top-level-decl: \"" << ND->getNameAsString() << "\"\n";
         }
 
         return true;
       }
     };
 
-    class PrintFunctionNamesAction : public PluginASTAction {
-    protected:
-      ASTConsumer *CreateASTConsumer(CompilerInstance &CI, llvm::StringRef) {
-        return new PrintFunctionsConsumer();
-      }
-
-      bool ParseArgs(const CompilerInstance &CI,
-                     const std::vector<std::string>& args) {
-        for (unsigned i = 0, e = args.size(); i != e; ++i) {
-          llvm::errs() << "PrintFunctionNames arg = " << args[i] << "\n";
-
-          // Example error handling.
-          if (args[i] == "-an-error") {
-            DiagnosticsEngine &D = CI.getDiagnostics();
-            unsigned DiagID = D.getCustomDiagID(
-              DiagnosticsEngine::Error, "invalid argument '" + args[i] + "'");
-            D.Report(DiagID);
-            return false;
-          }
-        }
-        if (args.size() && args[0] == "help")
-          PrintHelp(llvm::errs());
-
-        return true;
-      }
-      void PrintHelp(llvm::raw_ostream& ros) {
-        ros << "Help for PrintFunctionNames plugin goes here\n";
-      }
-
-    };
-
     }
 
-    static FrontendPluginRegistry::Add<PrintFunctionNamesAction>
-    X("print-fns", "print function names");
+    DECLARE_PLUGIN_COMPATIBILITY();
+    
+    void clang_plugin_on_tu(const plugin::PluginInfo &Info,
+                            llvm::StringRef FileName,
+                            const CompilerInstance &CI,
+                            plugin::TUCallbacks &Callbacks) {
+      llvm::errs() << "Invoking plugin " << Info.PluginPath << '\n';
+      for (unsigned i = 0; i &lt; Info.Arguments.size(); i++) {
+        llvm::errs() << "Plugin argument: argument " << Info.Arguments[i].first <<
+          " is set to value " << Info.Arguments[i].second << '\n';
+      }
+      llvm::errs() << "Found when compiling " << FileName << ":\n";
+      Callbacks.ASTCallback = new PrintFunctionsConsumer();
+    }
 
 Running the plugin
 ==================
+ asdf*
+To run a plugin, you merely need to specify the plugin to be loaded using the
+:option:`-fplugin` command line options. Additional parameters for the plugins
+can be passed with :option:`-fplugin-arg-<plugin-name>-<arg>=<value>` or
+:option:`-fplugin-arg-<plugin-name>-<arg>`. The plugin name is the basename of
+the file minus the extension, so for a plugin whose library is ``libplugin.so``,
+the name to use would be ``libplugin``. These options do not require the use of the :option:`-cc1` or :option:`-Xclang` options to work properly.
 
-To run a plugin, the dynamic library containing the plugin registry must be
-loaded via the :option:`-load` command line option. This will load all plugins
-that are registered, and you can select the plugins to run by specifying the
-:option:`-plugin` option. Additional parameters for the plugins can be passed with
-:option:`-plugin-arg-<plugin-name>`.
-
-Note that those options must reach clang's cc1 process. There are two
-ways to do so:
-
-* Directly call the parsing process by using the :option:`-cc1` option; this
-  has the downside of not configuring the default header search paths, so
-  you'll need to specify the full system path configuration on the command
-  line.
-* Use clang as usual, but prefix all arguments to the cc1 process with
-  :option:`-Xclang`.
-
-For example, to run the ``print-function-names`` plugin over a source file in
-clang, first build the plugin, and then call clang with the plugin from the
-source tree:
+For example, to run the ``print-function-names`` plugin over a source file named``example.cpp``, first build the plugin, and then call clang with the
+plugin from the source tree:
 
 .. code-block:: console
 
   $ export BD=/path/to/build/directory
   $ (cd $BD && make PrintFunctionNames )
-  $ clang++ -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS \
-            -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -D_GNU_SOURCE \
-            -I$BD/tools/clang/include -Itools/clang/include -I$BD/include -Iinclude \
-            tools/clang/tools/clang-check/ClangCheck.cpp -fsyntax-only \
-            -Xclang -load -Xclang $BD/lib/PrintFunctionNames.so -Xclang \
-            -plugin -Xclang print-fns
+  $ clang++ -fplugin=$BD/lib/PrintFunctionsNames.so example.cpp
 
 Also see the print-function-name plugin example's
 `README <http://llvm.org/viewvc/llvm-project/cfe/trunk/examples/PrintFunctionNames/README.txt?view=markup>`_
 
diff --git a/examples/PrintFunctionNames/PrintFunctionNames.cpp b/examples/PrintFunctionNames/PrintFunctionNames.cpp
--- a/examples/PrintFunctionNames/PrintFunctionNames.cpp
+++ b/examples/PrintFunctionNames/PrintFunctionNames.cpp
@@ -7,20 +7,20 @@
 //
 //===----------------------------------------------------------------------===//
 //
 // Example clang plugin which simply prints the names of all the top-level decls
 // in the input file.
 //
 //===----------------------------------------------------------------------===//
 
-#include "clang/Frontend/FrontendPluginRegistry.h"
+#include "clang/Basic/Plugin.h"
+#include "clang/Basic/Version.h"
 #include "clang/AST/AST.h"
 #include "clang/AST/ASTConsumer.h"
-#include "clang/Frontend/CompilerInstance.h"
 #include "llvm/Support/raw_ostream.h"
 using namespace clang;
 
 namespace {
 
 class PrintFunctionsConsumer : public ASTConsumer {
 public:
   virtual bool HandleTopLevelDecl(DeclGroupRef DG) {
@@ -29,43 +29,24 @@ public:
       if (const NamedDecl *ND = dyn_cast<NamedDecl>(D))
         llvm::errs() << "top-level-decl: \"" << ND->getNameAsString() << "\"\n";
     }
 
     return true;
   }
 };
 
-class PrintFunctionNamesAction : public PluginASTAction {
-protected:
-  ASTConsumer *CreateASTConsumer(CompilerInstance &CI, llvm::StringRef) {
-    return new PrintFunctionsConsumer();
-  }
-
-  bool ParseArgs(const CompilerInstance &CI,
-                 const std::vector<std::string>& args) {
-    for (unsigned i = 0, e = args.size(); i != e; ++i) {
-      llvm::errs() << "PrintFunctionNames arg = " << args[i] << "\n";
-
-      // Example error handling.
-      if (args[i] == "-an-error") {
-        DiagnosticsEngine &D = CI.getDiagnostics();
-        unsigned DiagID = D.getCustomDiagID(
-          DiagnosticsEngine::Error, "invalid argument '" + args[i] + "'");
-        D.Report(DiagID);
-        return false;
-      }
-    }
-    if (args.size() && args[0] == "help")
-      PrintHelp(llvm::errs());
-
-    return true;
-  }
-  void PrintHelp(llvm::raw_ostream& ros) {
-    ros << "Help for PrintFunctionNames plugin goes here\n";
-  }
-
-};
-
 }
 
-static FrontendPluginRegistry::Add<PrintFunctionNamesAction>
-X("print-fns", "print function names");
+DECLARE_PLUGIN_COMPATIBILITY();
+
+void clang_plugin_on_tu(const plugin::PluginInfo &Info,
+                        llvm::StringRef FileName,
+                        const CompilerInstance &CI,
+                        plugin::TUCallbacks &Callbacks) {
+  llvm::errs() << "Invoking plugin " << Info.PluginPath << '\n';
+  for (unsigned i = 0; i < Info.Arguments.size(); i++) {
+    llvm::errs() << "Plugin argument: argument " << Info.Arguments[i].first <<
+      " is set to value " << Info.Arguments[i].second << '\n';
+  }
+  llvm::errs() << "Found when compiling " << FileName << ":\n";
+  Callbacks.ASTCallback = new PrintFunctionsConsumer();
+}
diff --git a/examples/PrintFunctionNames/PrintFunctionNames.exports b/examples/PrintFunctionNames/PrintFunctionNames.exports
--- a/examples/PrintFunctionNames/PrintFunctionNames.exports
+++ b/examples/PrintFunctionNames/PrintFunctionNames.exports
@@ -1,1 +1,1 @@
-_ZN4llvm8Registry*
+clang_plugin_*
diff --git a/examples/PrintFunctionNames/README.txt b/examples/PrintFunctionNames/README.txt
--- a/examples/PrintFunctionNames/README.txt
+++ b/examples/PrintFunctionNames/README.txt
@@ -1,16 +1,16 @@
 This is a simple example demonstrating how to use clang's facility for
 providing AST consumers using a plugin.
 
 Build the plugin by running `make` in this directory.
 
 Once the plugin is built, you can run it using:
 --
 Linux:
-$ clang -cc1 -load ../../Debug+Asserts/lib/libPrintFunctionNames.so -plugin print-fns some-input-file.c
-$ clang -cc1 -load ../../Debug+Asserts/lib/libPrintFunctionNames.so -plugin print-fns -plugin-arg-print-fns help -plugin-arg-print-fns --example-argument some-input-file.c
-$ clang -cc1 -load ../../Debug+Asserts/lib/libPrintFunctionNames.so -plugin print-fns -plugin-arg-print-fns -an-error some-input-file.c
+$ clang -fplugin=../../Debug+Asserts/lib/libPrintFunctionNames.so some-input-file.c
+$ clang -fplugin=../../Debug+Asserts/lib/libPrintFunctionNames.so -fplugin-arg-libPrintFunctionNames-example=some-input some-input-file.c
+$ clang -fplugin=../../Debug+Asserts/lib/libPrintFunctionNames.so -fplugin-arg-libPrintFunctionNames-example2 some-input-file.c
 
 Mac:
-$ clang -cc1 -load ../../Debug+Asserts/lib/libPrintFunctionNames.dylib -plugin print-fns some-input-file.c
-$ clang -cc1 -load ../../Debug+Asserts/lib/libPrintFunctionNames.dylib -plugin print-fns -plugin-arg-print-fns help -plugin-arg-print-fns --example-argument some-input-file.c
-$ clang -cc1 -load ../../Debug+Asserts/lib/libPrintFunctionNames.dylib -plugin print-fns -plugin-arg-print-fns -an-error some-input-file.c
+$ clang -fplugin=../../Debug+Asserts/lib/libPrintFunctionNames.dylib some-input-file.c
+$ clang -fplugin=../../Debug+Asserts/lib/libPrintFunctionNames.dylib -fplugin-arg-libPrintFunctionNames-example=some-input some-input-file.c
+$ clang -fplugin=../../Debug+Asserts/lib/libPrintFunctionNames.dylib -fplugin-arg-libPrintFunctionNames-example2 some-input-file.c
diff --git a/include/clang/Basic/Plugin.h b/include/clang/Basic/Plugin.h
new file mode 100644
--- /dev/null
+++ b/include/clang/Basic/Plugin.h
@@ -0,0 +1,110 @@
+//===--- Plugin.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_BASIC_PLUGIN_H
+#define LLVM_CLANG_BASIC_PLUGIN_H
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringRef.h"
+
+#include <string>
+#include <utility>
+
+namespace clang {
+
+class ASTConsumer;
+class CompilerInstance;
+class DiagnosticConsumer;
+class PPCallbacks;
+
+namespace plugin {
+
+/// A struct containing sufficient information to express ABI compatibility for
+/// plugins. It is sufficient for plugins to use the
+/// DECLARE_PLUGIN_COMPATIBILITY macro to find compatibility.
+struct Version {
+  /// The major version of clang (corresponds to CLANG_VERSION_MAJOR)
+  unsigned Major;
+  /// The minor version of clang (corresponds to CLANG_VERSION_MINOR)
+  unsigned Minor;
+};
+
+/// The following macro is used in the source code of a plugin so that the
+/// compiler can reject plugins that were compiled with different versions of
+/// clang. This macro should not be used outside of a plugin, and must be used
+/// in the global scope outside of any namespaces to guarantee correctness.
+#define DECLARE_PLUGIN_COMPATIBILITY() \
+  extern "C" const clang::plugin::Version clang_plugin_compatibility = { \
+    CLANG_VERSION_MAJOR, \
+    CLANG_VERSION_MINOR \
+  }
+
+/// A collection of all of the information about how a plugin is invoked which
+/// gets passed to every plugin callback.
+struct PluginInfo {
+  /// The set of arguments passed in via the command line to the plugin. A
+  /// plugin argument is of the form -fplugin-arg-<basename>-<name>[=<value>],
+  /// where <basename> is the basename of the plugin, minus the extension,
+  /// <name> is a generic string which is the first element in a pair on this
+  /// list and <value> is a generic string which is the second element in a pair
+  /// on this list.
+  llvm::ArrayRef<std::pair<std::string, std::string> > Arguments;
+
+  /// The path to the plugin library, which can be either absolute or relative
+  /// to the current working directory.
+  llvm::StringRef PluginPath;
+};
+
+
+/// This is a set of callbacks which should be filled in by the plugin to hook
+/// into the main compilation process.
+struct TUCallbacks {
+  TUCallbacks() :
+    ASTCallback(0),
+    PreprocessorCallback(0),
+    DiagnosticsCallback(0) {}
+
+  /// A callback that allows plugins to view (but not modify) the AST of a file.
+  ASTConsumer *ASTCallback;
+  /// A callback that allows plugins to be notified of preprocessor directives.
+  PPCallbacks *PreprocessorCallback;
+  /// A callback that allows plugins to be notified of warnings and errors.
+  DiagnosticConsumer *DiagnosticsCallback;
+};
+
+} // namespace plugin
+} // namespace clang
+
+/// \defgroup plugin Plugin Hooks
+/// The following functions are not implemented within clang itself, but are
+/// prototypes of the various functions that may be implemented by plugins.
+/// Including it in a header file allows these hooks to be documented with
+/// doxygen, and the use of extern "C" on declarations allows people who forget
+/// to define these functions with extern "C" to still have them work.
+/// \{
+extern "C" {
+/// Callback that is called before a file is compiled.
+///
+/// Before this function is called, Callbacks will have all of its pointers set
+/// to null. All non-null callbacks will be deleted by the compiler internally.
+///
+/// \arg Info      The invocation environment of the plugin.
+/// \arg FileName  The name of the translation unit being compiled.
+/// \arg CI        The compiler instance, which contains information about how
+///                the file is being compiled and other utility classes.
+/// \arg Callbacks The callbacks that this plugin will define for this file.
+extern void clang_plugin_on_tu(const clang::plugin::PluginInfo &Info,
+                               llvm::StringRef FileName,
+                               const clang::CompilerInstance &CI,
+                               clang::plugin::TUCallbacks &Callbacks);
+/// \}
+
+}
+
+#endif
diff --git a/include/clang/Basic/PluginManager.h b/include/clang/Basic/PluginManager.h
new file mode 100644
--- /dev/null
+++ b/include/clang/Basic/PluginManager.h
@@ -0,0 +1,84 @@
+//===--- PluginManager.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_PLUGINMANAGER_H
+#define LLVM_CLANG_PLUGINMANAGER_H
+
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/Plugin.h"
+
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/DynamicLibrary.h"
+
+#include <string>
+#include <utility>
+
+namespace clang {
+
+class CompilerInstance;
+class DiagnosticsEngine;
+class Preprocessor;
+
+namespace plugin {
+
+/// Contains the data needed for a single plugin.
+class Plugin {
+  /// The library that contains the plugin itself
+  llvm::sys::DynamicLibrary PluginLibrary;
+
+  /// The structure that contains all of the callbacks for the library
+  TUCallbacks Callbacks;
+
+  /// The plugin information passed into hooks
+  const PluginInfo Info;
+
+public:
+  Plugin(llvm::sys::DynamicLibrary Library, const plugin::PluginInfo &Info) :
+    PluginLibrary(Library), Callbacks(), Info(Info) {}
+  plugin::TUCallbacks &getCallbacks() { return Callbacks; }
+  const plugin::PluginInfo &getPluginInfo() const { return Info; }
+  intptr_t getPluginHook(const char *name) {
+    return (intptr_t)PluginLibrary.getAddressOfSymbol(name);
+  }
+};
+
+/// A class that manages all of the plugins loaded by the compiler.
+class PluginManager {
+  /// The list of plugins that are currently loaded.
+  SmallVector<Plugin, 4> Plugins;
+
+public:
+  /// Load the given plugin.
+  /// \param PluginFile     The path of the plugin object to be loaded.
+  /// \param PluginArgs     A list of arguments to pass to the plugin hooks.
+  /// \param ErrorMsg [out] The reason why plugin loading failed.
+  /// \return True if the plugin loaded correctly, false if there was an error.
+  bool loadPlugin(StringRef PluginFile,
+                  ArrayRef<std::pair<std::string, std::string> > PluginArgs,
+                  std::string &ErrorMsg);
+
+  /// Call the clang_plugin_on_tu hook for all loaded plugins.
+  /// \param FileName The file name of the translation unit
+  /// \param CI       The compiler instance to pass to the plugin hook.
+  void callOnTUHook(StringRef FileName, const CompilerInstance &CI);
+
+  /// Wrap the original AST consumer with those proffered by plugins.
+  ASTConsumer *wrapASTConsumer(ASTConsumer *Original);
+
+  /// Add all PPCallbacks proffered by plugins to the preprocessor object.
+  void addPPCallbacks(Preprocessor &PP);
+
+  /// Wrap the original diagnostics consumer with those proffered by plugins.
+  void addDiagnosticConsumers(DiagnosticsEngine &Diag);
+};
+
+} // namespace plugin
+} // namespace clang
+
+#endif
diff --git a/include/clang/Driver/Options.td b/include/clang/Driver/Options.td
--- a/include/clang/Driver/Options.td
+++ b/include/clang/Driver/Options.td
@@ -645,16 +645,20 @@ def fpack_struct_EQ : Joined<["-"], "fpa
   HelpText<"Specify the default maximum struct packing alignment">;
 def fpascal_strings : Flag<["-"], "fpascal-strings">, Group<f_Group>, Flags<[CC1Option]>,
   HelpText<"Recognize and construct Pascal-style string literals">;
 def fpch_preprocess : Flag<["-"], "fpch-preprocess">, Group<f_Group>;
 def fpic : Flag<["-"], "fpic">, Group<f_Group>;
 def fno_pic : Flag<["-"], "fno-pic">, Group<f_Group>;
 def fpie : Flag<["-"], "fpie">, Group<f_Group>;
 def fno_pie : Flag<["-"], "fno-pie">, Group<f_Group>;
+def fplugin_EQ : Joined<["-"], "fplugin=">, Group<f_Group>, Flags<[CC1Option]>,
+  HelpText<"Run the given plugin while compiling code">, MetaVarName<"<dsopath>">;
+def fplugin_arg_ : Joined<["-"], "fplugin-arg-">, Group<f_Group>, Flags<[CC1Option]>,
+  HelpText<"Pass an argument to a plugin">, MetaVarName<"<plugin>-<name>[=<value>]">;
 def fprofile_arcs : Flag<["-"], "fprofile-arcs">, Group<f_Group>;
 def fprofile_generate : Flag<["-"], "fprofile-generate">, Group<f_Group>;
 def framework : Separate<["-"], "framework">, Flags<[LinkerInput]>;
 def frandom_seed_EQ : Joined<["-"], "frandom-seed=">, Group<clang_ignored_f_Group>;
 def frtti : Flag<["-"], "frtti">, Group<f_Group>;
 def fsched_interblock : Flag<["-"], "fsched-interblock">, Group<clang_ignored_f_Group>;
 def fshort_enums : Flag<["-"], "fshort-enums">, Group<f_Group>, Flags<[CC1Option]>,
   HelpText<"Allocate to an enum type only as many bytes as it needs for the declared range of possible values">;
diff --git a/include/clang/Frontend/CompilerInstance.h b/include/clang/Frontend/CompilerInstance.h
--- a/include/clang/Frontend/CompilerInstance.h
+++ b/include/clang/Frontend/CompilerInstance.h
@@ -41,16 +41,19 @@ class FileEntry;
 class FileManager;
 class FrontendAction;
 class Module;
 class Preprocessor;
 class Sema;
 class SourceManager;
 class TargetInfo;
 
+namespace plugin {
+class PluginManager;
+}
 /// CompilerInstance - Helper class for managing a single instance of the Clang
 /// compiler.
 ///
 /// The CompilerInstance serves two purposes:
 ///  (1) It manages the various objects which are necessary to run the compiler,
 ///      for example the preprocessor, the target information, and the AST
 ///      context.
 ///  (2) It provides utility routines for constructing and manipulating the
@@ -132,16 +135,18 @@ class CompilerInstance : public ModuleLo
 
     OutputFile(const std::string &filename, const std::string &tempFilename,
                raw_ostream *os)
       : Filename(filename), TempFilename(tempFilename), OS(os) { }
   };
 
   /// The list of active output files.
   std::list<OutputFile> OutputFiles;
+  /// The plugin manager, initialized when initializing plugins.
+  OwningPtr<plugin::PluginManager> PluginMgr;
 
   CompilerInstance(const CompilerInstance &) LLVM_DELETED_FUNCTION;
   void operator=(const CompilerInstance &) LLVM_DELETED_FUNCTION;
 public:
   CompilerInstance();
   ~CompilerInstance();
 
   /// @name High-Level Operations
@@ -363,16 +368,26 @@ public:
   }
 
   void resetAndLeakPreprocessor() {
     PP.resetWithoutRelease();
   }
 
   /// Replace the current preprocessor.
   void setPreprocessor(Preprocessor *Value);
+  /// }
+  /// @name Plugin Manager
+  /// {
+
+  bool hasPluginManager() { return PluginMgr != 0; }
+
+  plugin::PluginManager &getPluginManager() {
+    assert(PluginMgr && "Compiler instance has no plugin manager");
+    return *PluginMgr;
+  }
 
   /// }
   /// @name ASTContext
   /// {
 
   bool hasASTContext() const { return Context != 0; }
 
   ASTContext &getASTContext() const {
@@ -649,16 +664,21 @@ public:
   ///
   /// \return True on success.
   static bool InitializeSourceManager(const FrontendInputFile &Input,
                 DiagnosticsEngine &Diags,
                 FileManager &FileMgr,
                 SourceManager &SourceMgr,
                 const FrontendOptions &Opts);
 
+  /// Loads and initializes the plugins found in the current FrontendOptions.
+  ///
+  /// \return True on success.
+  bool initializePlugins();
+
   /// }
   
   virtual ModuleLoadResult loadModule(SourceLocation ImportLoc,
                                       ModuleIdPath Path,
                                       Module::NameVisibilityKind Visibility,
                                       bool IsInclusionDirective);
 
   virtual void makeModuleVisible(Module *Mod,
diff --git a/include/clang/Frontend/FrontendOptions.h b/include/clang/Frontend/FrontendOptions.h
--- a/include/clang/Frontend/FrontendOptions.h
+++ b/include/clang/Frontend/FrontendOptions.h
@@ -191,16 +191,27 @@ public:
   std::vector<std::string> AddPluginActions;
 
   /// Args to pass to the additional plugins
   std::vector<std::vector<std::string> > AddPluginArgs;
 
   /// The list of plugins to load.
   std::vector<std::string> Plugins;
 
+  struct CompilerPlugin {
+    /// The filename containing the plugin
+    std::string Plugin;
+
+    /// Arguments to the plugin
+    SmallVector<std::pair<std::string, std::string>, 4> Args;
+  };
+
+  /// The list of compiler plugins to load.
+  std::vector<CompilerPlugin> CompilerPlugins;
+
   /// \brief The list of AST files to merge.
   std::vector<std::string> ASTMergeFiles;
 
   /// \brief A list of arguments to forward to LLVM's option processing; this
   /// should only be used for debugging and experimental features.
   std::vector<std::string> LLVMArgs;
 
   /// \brief File name of the file that will provide record layouts
diff --git a/lib/Basic/CMakeLists.txt b/lib/Basic/CMakeLists.txt
--- a/lib/Basic/CMakeLists.txt
+++ b/lib/Basic/CMakeLists.txt
@@ -7,16 +7,17 @@ add_clang_library(clangBasic
   DiagnosticIDs.cpp
   FileManager.cpp
   FileSystemStatCache.cpp
   IdentifierTable.cpp
   LangOptions.cpp
   Module.cpp
   ObjCRuntime.cpp
   OperatorPrecedence.cpp
+  PluginManager.cpp
   SourceLocation.cpp
   SourceManager.cpp
   TargetInfo.cpp
   Targets.cpp
   TokenKinds.cpp
   Version.cpp
   VersionTuple.cpp
   )
diff --git a/lib/Basic/PluginManager.cpp b/lib/Basic/PluginManager.cpp
new file mode 100644
--- /dev/null
+++ b/lib/Basic/PluginManager.cpp
@@ -0,0 +1,97 @@
+//===--- PluginManager.cpp - Utility methods for plugins ------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+//  This file implements the PluginManager interface.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/PluginManager.h"
+#include "clang/Basic/Version.h"
+#include "clang/Frontend/ChainedDiagnosticConsumer.h"
+#include "clang/Frontend/MultiplexConsumer.h"
+#include "clang/Lex/Preprocessor.h"
+
+using namespace clang;
+using namespace clang::plugin;
+
+bool
+PluginManager::loadPlugin(StringRef Path,
+                          ArrayRef<std::pair<std::string, std::string> > Args,
+                          std::string &ErrorMsg) {
+  llvm::sys::DynamicLibrary Library =
+    llvm::sys::DynamicLibrary::getPermanentLibrary(Path.data(), &ErrorMsg);
+  if (!Library.isValid()) {
+    return false;
+  }
+
+  // Library loaded, check version compatibility
+  Version *Compatibility = (Version *)
+    Library.getAddressOfSymbol("clang_plugin_compatibility");
+  if (!Compatibility) {
+    ErrorMsg = "Could not find symbol clang_plugin_compatibility";
+    return false;
+  } else if (Compatibility->Major != CLANG_VERSION_MAJOR ||
+             Compatibility->Minor != CLANG_VERSION_MINOR) {
+    ErrorMsg = "Plugin is not compatible with this version of clang";
+    return false;
+  }
+
+  // Build plugin info
+  PluginInfo Info;
+  Info.Arguments = Args;
+  Info.PluginPath = Path;
+
+  // Add the plugin to the list
+  Plugins.push_back(Plugin(Library, Info));
+  return true;
+}
+
+void PluginManager::callOnTUHook(StringRef FileName, const CompilerInstance &CI) {
+  for (SmallVector<Plugin, 4>::iterator it = Plugins.begin();
+       it != Plugins.end(); ++it) {
+    typedef void (*TransUnitTy)(const plugin::PluginInfo&, StringRef,
+                                const CompilerInstance&, plugin::TUCallbacks&);
+    TransUnitTy hook = (TransUnitTy)it->getPluginHook("clang_plugin_on_tu");
+    if (hook)
+      hook(it->getPluginInfo(), FileName, CI, it->getCallbacks());
+  }
+}
+
+ASTConsumer *PluginManager::wrapASTConsumer(ASTConsumer *Original) {
+  SmallVector<ASTConsumer*, 4> Consumers;
+  Consumers.push_back(Original);
+
+  for (SmallVector<Plugin, 4>::iterator it = Plugins.begin();
+       it != Plugins.end(); ++it)
+    if (ASTConsumer *Consumer = it->getCallbacks().ASTCallback)
+      Consumers.push_back(Consumer);
+
+  // Don't construct a multiplex consumer if we're only using the original.
+  return Consumers.size() > 1 ? new MultiplexConsumer(Consumers) : Original;
+}
+
+void PluginManager::addPPCallbacks(Preprocessor &PP) {
+  for (SmallVector<Plugin, 4>::iterator it = Plugins.begin();
+       it != Plugins.end(); ++it) {
+    if (it->getCallbacks().PreprocessorCallback)
+      PP.addPPCallbacks(it->getCallbacks().PreprocessorCallback);
+  }
+}
+
+void PluginManager::addDiagnosticConsumers(DiagnosticsEngine &Diag) {
+  DiagnosticConsumer *Consumer = Diag.takeClient();
+  for (SmallVector<Plugin, 4>::iterator it = Plugins.begin();
+       it != Plugins.end(); ++it) {
+    if (it->getCallbacks().DiagnosticsCallback)
+      Consumer = new ChainedDiagnosticConsumer(Consumer,
+        it->getCallbacks().DiagnosticsCallback);
+  }
+  Diag.setClient(Consumer);
+}
diff --git a/lib/Driver/Tools.cpp b/lib/Driver/Tools.cpp
--- a/lib/Driver/Tools.cpp
+++ b/lib/Driver/Tools.cpp
@@ -1916,16 +1916,20 @@ void Clang::ConstructJob(Compilation &C,
     CmdArgs.push_back("-w");
 
     // Add -Xanalyzer arguments when running as analyzer.
     Args.AddAllArgValues(CmdArgs, options::OPT_Xanalyzer);
   }
 
   CheckCodeGenerationOptions(D, Args);
 
+  // Pass through plugins
+  Args.AddAllArgs(CmdArgs, options::OPT_fplugin_EQ);
+  Args.AddAllArgs(CmdArgs, options::OPT_fplugin_arg_);
+
   // For the PIC and PIE flag options, this logic is different from the legacy
   // logic in very old versions of GCC, as that logic was just a bug no one had
   // ever fixed. This logic is both more rational and consistent with GCC's new
   // logic now that the bugs are fixed. The last argument relating to either
   // PIC or PIE wins, and no other argument is used. If the last argument is
   // any flavor of the '-fno-...' arguments, both PIC and PIE are disabled. Any
   // PIE option implicitly enables PIC at the same level.
   bool PIE = false;
diff --git a/lib/Frontend/CompilerInstance.cpp b/lib/Frontend/CompilerInstance.cpp
--- a/lib/Frontend/CompilerInstance.cpp
+++ b/lib/Frontend/CompilerInstance.cpp
@@ -8,16 +8,17 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/AST/ASTConsumer.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/Decl.h"
 #include "clang/Basic/Diagnostic.h"
 #include "clang/Basic/FileManager.h"
+#include "clang/Basic/PluginManager.h"
 #include "clang/Basic/SourceManager.h"
 #include "clang/Basic/TargetInfo.h"
 #include "clang/Basic/Version.h"
 #include "clang/Frontend/ChainedDiagnosticConsumer.h"
 #include "clang/Frontend/FrontendAction.h"
 #include "clang/Frontend/FrontendActions.h"
 #include "clang/Frontend/FrontendDiagnostic.h"
 #include "clang/Frontend/LogDiagnosticPrinter.h"
@@ -1241,8 +1242,26 @@ CompilerInstance::loadModule(SourceLocat
 }
 
 void CompilerInstance::makeModuleVisible(Module *Mod,
                                          Module::NameVisibilityKind Visibility,
                                          SourceLocation ImportLoc){
   ModuleManager->makeModuleVisible(Mod, Visibility, ImportLoc);
 }
 
+bool CompilerInstance::initializePlugins() {
+  assert(!hasPluginManager() && "Already loaded plugins");
+  PluginMgr.reset(new plugin::PluginManager);
+
+  ArrayRef<FrontendOptions::CompilerPlugin> CompilerPlugins =
+    getFrontendOpts().CompilerPlugins;
+  for (unsigned i = 0, e = CompilerPlugins.size(); i != e; ++i) {
+    std::string Error;
+    StringRef Path = CompilerPlugins[i].Plugin;
+    if (!PluginMgr->loadPlugin(Path, CompilerPlugins[i].Args, Error)) {
+      getDiagnostics().Report(diag::err_fe_unable_to_load_plugin)
+        << Path << Error;
+      return false;
+    }
+  }
+
+  return true;
+}
diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp
--- a/lib/Frontend/CompilerInvocation.cpp
+++ b/lib/Frontend/CompilerInvocation.cpp
@@ -664,16 +664,62 @@ static InputKind ParseFrontendArgs(Front
   for (int i = 0, e = Opts.AddPluginActions.size(); i != e; ++i) {
     for (arg_iterator it = Args.filtered_begin(OPT_plugin_arg),
            end = Args.filtered_end(); it != end; ++it) {
       if ((*it)->getValue(0) == Opts.AddPluginActions[i])
         Opts.AddPluginArgs[i].push_back((*it)->getValue(1));
     }
   }
 
+  std::vector<std::string> AllPlugins = Args.getAllArgValues(OPT_fplugin_EQ);
+  Opts.CompilerPlugins.resize(AllPlugins.size());
+
+  // Parse the plugin arguments. These are of the form
+  // -fplugin-<plugin name>-<arg name>[=<value>]. The command line parser gives
+  // it to us as -fplugin-<full fused string>, so we need to split these up as
+  // appropriate.
+  SmallVector<std::pair<std::string, std::string>, 4> ParsedPluginArgs;
+  for (arg_iterator it = Args.filtered_begin(OPT_fplugin_arg_),
+         end = Args.filtered_end(); it != end; ++it) {
+    // Split the argument into <name>-<arg>
+    StringRef FusedArg = (*it)->getValue();
+    size_t Boundary = FusedArg.find('-');
+    if (Boundary == std::string::npos) {
+      Diags.Report(diag::err_drv_unknown_argument) << FusedArg;
+      continue;
+    }
+    ParsedPluginArgs.push_back(std::make_pair(FusedArg.substr(0, Boundary),
+                                              FusedArg.substr(Boundary + 1)));
+  }
+
+  // Add the plugin arguments to a ParsedPluginArgs
+  for (int I = 0, E = AllPlugins.size(); I != E; ++I) {
+    StringRef PluginPath = AllPlugins[I];
+    Opts.CompilerPlugins[I].Plugin = PluginPath;
+    StringRef BaseName = llvm::sys::path::stem(PluginPath);
+    // Collect all of the plugins that correspond to this plugin.
+    for (SmallVector<std::pair<std::string, std::string>, 4>::iterator
+           PluginArg = ParsedPluginArgs.begin(), End = ParsedPluginArgs.end();
+         PluginArg != End; ++PluginArg) {
+      if (PluginArg->first == BaseName) {
+        // ArgAssign is in the form "name=value" or just plain "name", now we
+        // need to break it up into the form name, value pair.
+        StringRef ArgAssign = PluginArg->second;
+        size_t Boundary = ArgAssign.find("=");
+        // Treat no '=' as an arg with no value
+        if (Boundary == std::string::npos)
+          Opts.CompilerPlugins[I].Args.push_back(std::make_pair(ArgAssign, ""));
+        else
+          Opts.CompilerPlugins[I].Args.push_back(std::make_pair(
+            ArgAssign.substr(0, Boundary), ArgAssign.substr(Boundary + 1)));
+      }
+    }
+  }
+
+
   if (const Arg *A = Args.getLastArg(OPT_code_completion_at)) {
     Opts.CodeCompletionAt =
       ParsedSourceLocation::FromString(A->getValue());
     if (Opts.CodeCompletionAt.FileName.empty())
       Diags.Report(diag::err_drv_invalid_value)
         << A->getAsString(Args) << A->getValue();
   }
   Opts.DisableFree = Args.hasArg(OPT_disable_free);
diff --git a/lib/Frontend/FrontendAction.cpp b/lib/Frontend/FrontendAction.cpp
--- a/lib/Frontend/FrontendAction.cpp
+++ b/lib/Frontend/FrontendAction.cpp
@@ -6,16 +6,17 @@
 // License. See LICENSE.TXT for details.
 //
 //===----------------------------------------------------------------------===//
 
 #include "clang/Frontend/FrontendAction.h"
 #include "clang/AST/ASTConsumer.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/DeclGroup.h"
+#include "clang/Basic/PluginManager.h"
 #include "clang/Frontend/ASTUnit.h"
 #include "clang/Frontend/ChainedIncludesSource.h"
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Frontend/FrontendDiagnostic.h"
 #include "clang/Frontend/FrontendPluginRegistry.h"
 #include "clang/Frontend/LayoutOverrideSource.h"
 #include "clang/Frontend/MultiplexConsumer.h"
 #include "clang/Lex/HeaderSearch.h"
@@ -126,16 +127,19 @@ void FrontendAction::setCurrentInput(con
 }
 
 ASTConsumer* FrontendAction::CreateWrappedASTConsumer(CompilerInstance &CI,
                                                       StringRef InFile) {
   ASTConsumer* Consumer = CreateASTConsumer(CI, InFile);
   if (!Consumer)
     return 0;
 
+  if (CI.hasPluginManager())
+    Consumer = CI.getPluginManager().wrapASTConsumer(Consumer);
+
   if (CI.getFrontendOpts().AddPluginActions.size() == 0)
     return Consumer;
 
   // Make sure the non-plugin consumer is first, so that plugins can't
   // modifiy the AST.
   std::vector<ASTConsumer*> Consumers(1, Consumer);
 
   for (size_t i = 0, e = CI.getFrontendOpts().AddPluginActions.size();
@@ -166,16 +170,21 @@ bool FrontendAction::BeginSourceFile(Com
   setCurrentInput(Input);
   setCompilerInstance(&CI);
 
   StringRef InputFile = Input.getFile();
   bool HasBegunSourceFile = false;
   if (!BeginInvocation(CI))
     goto failure;
 
+  // Start by getting callback objects for all plugins; we'll add the actual
+  // objects from all the plugins later.
+  if (CI.hasPluginManager())
+    CI.getPluginManager().callOnTUHook(Input.getFile(), CI);
+
   // AST files follow a very different path, since they share objects via the
   // AST unit.
   if (Input.getKind() == IK_AST) {
     assert(!usesPreprocessorOnly() &&
            "Attempt to pass AST file to preprocessor only action!");
     assert(hasASTFileSupport() &&
            "This action does not have AST file support!");
 
@@ -258,16 +267,23 @@ bool FrontendAction::BeginSourceFile(Com
         return true;
       }
     }
   }
 
   // Set up the preprocessor.
   CI.createPreprocessor();
 
+  // Add preprocessor and diagnostics client callbacks.
+  if (CI.hasPluginManager()) {
+    plugin::PluginManager &PluginMgr = CI.getPluginManager();
+    PluginMgr.addPPCallbacks(CI.getPreprocessor());
+    PluginMgr.addDiagnosticConsumers(CI.getDiagnostics());
+  }
+
   // Inform the diagnostic client we are processing a source file.
   CI.getDiagnosticClient().BeginSourceFile(CI.getLangOpts(),
                                            &CI.getPreprocessor());
   HasBegunSourceFile = true;
 
   // Initialize the action.
   if (!BeginSourceFileAction(CI, InputFile))
     goto failure;
diff --git a/lib/FrontendTool/ExecuteCompilerInvocation.cpp b/lib/FrontendTool/ExecuteCompilerInvocation.cpp
--- a/lib/FrontendTool/ExecuteCompilerInvocation.cpp
+++ b/lib/FrontendTool/ExecuteCompilerInvocation.cpp
@@ -197,16 +197,20 @@ bool clang::ExecuteCompilerInvocation(Co
          e = Clang->getFrontendOpts().Plugins.size(); i != e; ++i) {
     const std::string &Path = Clang->getFrontendOpts().Plugins[i];
     std::string Error;
     if (llvm::sys::DynamicLibrary::LoadLibraryPermanently(Path.c_str(), &Error))
       Clang->getDiagnostics().Report(diag::err_fe_unable_to_load_plugin)
         << Path << Error;
   }
 
+  // The initialization method takes care of diagnostics.
+  if (!Clang->initializePlugins())
+    return false;
+
   // Honor -mllvm.
   //
   // FIXME: Remove this, one day.
   // This should happen AFTER plugins have been loaded!
   if (!Clang->getFrontendOpts().LLVMArgs.empty()) {
     unsigned NumArgs = Clang->getFrontendOpts().LLVMArgs.size();
     const char **Args = new const char*[NumArgs + 2];
     Args[0] = "clang (LLVM option parsing)";
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to