# HG changeset patch
# User Joshua Cranmer <[email protected]>
# Date 1325721797 21600
# Node ID 54d3b2ffe208220d3e185327c70fbb461b36a507
# Parent  049717543bac3b0b610a167e4b961e53ce2af02a
Implement a better plugin API for clang, v5

The main difference from the previous version is moving the plugin management
from CompilerInstance to a new PluginManager class.

diff --git a/docs/ClangPlugins.html b/docs/ClangPlugins.html
--- a/docs/ClangPlugins.html
+++ b/docs/ClangPlugins.html
@@ -16,65 +16,92 @@
 <p>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.</p>
 
 <!-- ======================================================================= -->
 <h2 id="intro">Introduction</h2>
 <!-- ======================================================================= -->
 
-<p>Clang Plugins run FrontendActions over code. See the
-<a href="RAVFrontendAction.html">FrontendAction tutorial</a> on how to write a
-FrontendAction using the RecursiveASTVisitor. In this tutorial, we'll
-demonstrate how to write a simple clang plugin.
-</p>
+<p>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.</p>
+
+<p>All of the exposed callbacks can be found in the
+<a href="../doxygen/classclang_1_1plugin_1_1TUCallbacks.html">Doxygen
+manual</a>. The most import of these callbacks is the ASTConsumer callback,
+which allows plugins to interact with the abstract syntax tree of code. See the
+<a href="RAVFrontendAction.html#consumer">FrontendAction tutorial</a> for how to
+write an ASTConsumer using the RecursiveASTVisitor and the
+<a href="IntroductionToTheClangAST.html">Clang AST introduction</a> for general
+information about the AST. In this tutorial, we'll demonstrate how to write a
+simple clang plugin.</p>
 
 <!-- ======================================================================= -->
-<h2 id="pluginactions">Writing a PluginASTAction</h2>
+<h2 id="hooks">Plugin hooks</h2>
 <!-- ======================================================================= -->
 
-<p>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.
-</p>
+<p>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:</p>
 <pre>
-  bool ParseArgs(const CompilerInstance &amp;CI,
-                 const std::vector&lt;std::string>&amp; args) {
-    for (unsigned i = 0, e = args.size(); i != e; ++i) {
-      if (args[i] == "-some-arg") {
-        // Handle the command line argument.
-      }
-    }
-    return true;
+void clang_plugin_on_tu(const plugin::PluginInfo &amp;Info,
+                        llvm::StringRef FileName,
+                        const CompilerInstance &amp;CI,
+                        plugin::TUCallbacks &amp;Callbacks) {
+  llvm::errs() &lt;&lt; "Invoking plugin " &lt;&lt; Info.PluginPath &lt;&lt; '\n';
+  for (unsigned i = 0; i &lt; Info.Arguments.size(); i++) {
+    llvm::errs() &lt;&lt; "Plugin argument: argument " &lt;&lt; Info.Arguments[i].first &lt;&lt;
+      " is set to value " &lt;&lt; Info.Arguments[i].second &lt;&lt; '\n';
   }
+  llvm::errs() &lt;&lt; "Found when compiling " &lt;&lt; FileName &lt;&lt; ":\n";
+  Callbacks.ASTCallback = new PrintFunctionsConsumer();
+}
 </pre>
 
 <!-- ======================================================================= -->
-<h2 id="registerplugin">Registering a plugin</h2>
+<h2 id="compatibility">Plugin compatibility</h2>
 <!-- ======================================================================= -->
 
-<p>A plugin is loaded from a dynamic library at runtime by the compiler. To register
-a plugin in a library, use FrontendPluginRegistry::Add:</p>
-<pre>
-  static FrontendPluginRegistry::Add&lt;MyPlugin> X("my-plugin-name", "my plugin description");
-</pre>
+<p>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.</p>
+
+<p>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.</p>
 
 <!-- ======================================================================= -->
 <h2 id="example">Putting it all together</h2>
 <!-- ======================================================================= -->
 
 <p>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 <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/examples/PrintFunctionNames/PrintFunctionNames.cpp?view=markup";>checked in version of PrintFunctionNames.cpp</a>.</p>
 <pre>
-#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) {
@@ -83,85 +110,54 @@ public:
       if (const NamedDecl *ND = dyn_cast&lt;NamedDecl>(D))
         llvm::errs() &lt;&lt; "top-level-decl: \"" &lt;&lt; ND->getNameAsString() &lt;&lt; "\"\n";
     }
 
     return true;
   }
 };
 
-class PrintFunctionNamesAction : public PluginASTAction {
-protected:
-  ASTConsumer *CreateASTConsumer(CompilerInstance &amp;CI, llvm::StringRef) {
-    return new PrintFunctionsConsumer();
-  }
-
-  bool ParseArgs(const CompilerInstance &amp;CI,
-                 const std::vector&lt;std::string>&amp; args) {
-    for (unsigned i = 0, e = args.size(); i != e; ++i) {
-      llvm::errs() &lt;&lt; "PrintFunctionNames arg = " &lt;&lt; args[i] &lt;&lt; "\n";
-
-      // Example error handling.
-      if (args[i] == "-an-error") {
-        DiagnosticsEngine &amp;D = CI.getDiagnostics();
-        unsigned DiagID = D.getCustomDiagID(
-          DiagnosticsEngine::Error, "invalid argument '" + args[i] + "'");
-        D.Report(DiagID);
-        return false;
-      }
-    }
-    if (args.size() &amp;&amp; args[0] == "help")
-      PrintHelp(llvm::errs());
-
-    return true;
-  }
-  void PrintHelp(llvm::raw_ostream&amp; ros) {
-    ros &lt;&lt; "Help for PrintFunctionNames plugin goes here\n";
-  }
-
-};
-
 }
 
-static FrontendPluginRegistry::Add&lt;PrintFunctionNamesAction>
-X("print-fns", "print function names");
+DECLARE_PLUGIN_COMPATIBILITY();
+
+void clang_plugin_on_tu(const plugin::PluginInfo &amp;Info,
+                        llvm::StringRef FileName,
+                        const CompilerInstance &amp;CI,
+                        plugin::TUCallbacks &amp;Callbacks) {
+  llvm::errs() &lt;&lt; "Invoking plugin " &lt;&lt; Info.PluginPath &lt;&lt; '\n';
+  for (unsigned i = 0; i &lt; Info.Arguments.size(); i++) {
+    llvm::errs() &lt;&lt; "Plugin argument: argument " &lt;&lt; Info.Arguments[i].first &lt;&lt;
+      " is set to value " &lt;&lt; Info.Arguments[i].second &lt;&lt; '\n';
+  }
+  llvm::errs() &lt;&lt; "Found when compiling " &lt;&lt; FileName &lt;&lt; ":\n";
+  Callbacks.ASTCallback = new PrintFunctionsConsumer();
+}
 </pre>
 
 <!-- ======================================================================= -->
 <h2 id="running">Running the plugin</h2>
 <!-- ======================================================================= -->
 
-<p>To run a plugin, the dynamic library containing the plugin registry must be
-loaded via the -load command line option. This will load all plugins that are
-registered, and you can select the plugins to run by specifying the -plugin
-option. Additional parameters for the plugins can be passed with -plugin-arg-&lt;plugin-name>.</p>
+<p>To run a plugin, you merely need to specify the plugin to be loaded using the
+-fplugin command line options. Additional parameters for the plugins can be
+passed with -fplugin-arg-&lt;plugin-name>-&lt;arg>=&lt;value> or
+-fplugin-arg-&lt;plugin-name>-&lt;arg>. The plugin name is the basename of the
+file minus the extension, so for a plugin whose library is
+<tt>libplugin.so</tt>, the name to use would be <tt>libplugin</tt>. These
+options do not require the use of the <tt>-cc1</tt> or <tt>-Xclang</tt> options
+to work properly.</p>
 
-<p>Note that those options must reach clang's cc1 process. There are two
-ways to do so:</p>
-<ul>
-<li>
-Directly call the parsing process by using the -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.
-</li>
-<li>
-Use clang as usual, but prefix all arguments to the cc1 process with -Xclang.
-</li>
-</ul>
-<p>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:</p>
+<p>For example, to run the print-function-names plugin over a source file named
+<kbd>example.cpp</kbd>, first build the plugin, and then call clang with the
+plugin from the source tree:</p>
 <pre>
   $ export BD=/path/to/build/directory
   $ (cd $BD &amp;&amp; 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
 </pre>
 
 <p>Also see the print-function-name plugin example's
 <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/examples/PrintFunctionNames/README.txt?view=markup";>README</a></p>
 
 
 
 </div>
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/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) {
@@ -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,82 @@
+//===--- 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 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);
+  }
+};
+
+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.
+  DiagnosticConsumer *wrapDiagnostics(DiagnosticConsumer *Original);
+};
+
+} // 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
@@ -603,16 +603,20 @@ def fpack_struct_EQ : Joined<"-fpack-str
   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
@@ -40,16 +40,20 @@ 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
@@ -125,16 +129,19 @@ 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;
+
   void operator=(const CompilerInstance &);  // DO NOT IMPLEMENT
   CompilerInstance(const CompilerInstance&); // DO NOT IMPLEMENT
 public:
   CompilerInstance();
   ~CompilerInstance();
 
   /// @name High-Level Operations
   /// {
@@ -351,16 +358,27 @@ 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 {
     assert(Context && "Compiler instance has no AST context!");
     return *Context;
@@ -646,16 +664,21 @@ public:
   /// \return True on success.
   static bool InitializeSourceManager(StringRef InputFile,
                 SrcMgr::CharacteristicKind Kind,
                 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 Module *loadModule(SourceLocation ImportLoc, ModuleIdPath Path,
                              Module::NameVisibilityKind Visibility,
                              bool IsInclusionDirective);
 };
 
 } // end namespace clang
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
@@ -161,16 +161,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/PluginManager.cpp b/lib/Basic/PluginManager.cpp
new file mode 100644
--- /dev/null
+++ b/lib/Basic/PluginManager.cpp
@@ -0,0 +1,96 @@
+//===--- 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/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);
+  }
+}
+
+DiagnosticConsumer *
+PluginManager::wrapDiagnostics(DiagnosticConsumer *Consumer) {
+  for (SmallVector<Plugin, 4>::iterator it = Plugins.begin();
+       it != Plugins.end(); ++it) {
+    if (it->getCallbacks().DiagnosticsCallback)
+      Consumer = new ChainedDiagnosticConsumer(Consumer,
+        it->getCallbacks().DiagnosticsCallback);
+  }
+  return Consumer;
+}
diff --git a/lib/Driver/Tools.cpp b/lib/Driver/Tools.cpp
--- a/lib/Driver/Tools.cpp
+++ b/lib/Driver/Tools.cpp
@@ -1630,16 +1630,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_);
+
   // Perform argument translation for LLVM backend. This
   // takes some care in reconciling with llvm-gcc. The
   // issue is that llvm-gcc translates these options based on
   // the values in cc1, whereas we are processing based on
   // the driver arguments.
 
   // This comes from the default translation the driver + cc1
   // would do to enable flag_pic.
diff --git a/lib/Frontend/CompilerInstance.cpp b/lib/Frontend/CompilerInstance.cpp
--- a/lib/Frontend/CompilerInstance.cpp
+++ b/lib/Frontend/CompilerInstance.cpp
@@ -9,16 +9,17 @@
 
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Sema/Sema.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/Lex/HeaderSearch.h"
 #include "clang/Lex/Preprocessor.h"
 #include "clang/Lex/PTHManager.h"
 #include "clang/Frontend/ChainedDiagnosticConsumer.h"
 #include "clang/Frontend/FrontendAction.h"
@@ -1083,8 +1084,27 @@ Module *CompilerInstance::loadModule(Sou
                                            ImportLoc, Module, 
                                            Path.back().second));
   }
   
   LastModuleImportLoc = ImportLoc;
   LastModuleImportResult = Module;
   return Module;
 }
+
+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
@@ -1519,16 +1519,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(Args, 0) == Opts.AddPluginActions[i])
         Opts.AddPluginArgs[i].push_back((*it)->getValue(Args, 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(Args);
+    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(Args));
     if (Opts.CodeCompletionAt.FileName.empty())
       Diags.Report(diag::err_drv_invalid_value)
         << A->getAsString(Args) << A->getValue(Args);
   }
   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/Lex/HeaderSearch.h"
 #include "clang/Lex/Preprocessor.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"
@@ -123,16 +124,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();
@@ -160,16 +164,21 @@ bool FrontendAction::BeginSourceFile(Com
   assert(!Instance && "Already processing a source file!");
   assert(!Input.File.empty() && "Unexpected empty filename!");
   setCurrentInput(Input);
   setCompilerInstance(&CI);
 
   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.File, CI);
+
   // AST files follow a very different path, since they share objects via the
   // AST unit.
   if (Input.Kind == IK_AST) {
     assert(!usesPreprocessorOnly() &&
            "Attempt to pass AST file to preprocessor only action!");
     assert(hasASTFileSupport() &&
            "This action does not have AST file support!");
 
@@ -220,16 +229,24 @@ bool FrontendAction::BeginSourceFile(Com
       goto failure;
 
     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());
+    CI.getDiagnostics().setClient(PluginMgr.wrapDiagnostics(
+      CI.getDiagnostics().takeClient()));
+  }
+
   // Inform the diagnostic client we are processing a source file.
   CI.getDiagnosticClient().BeginSourceFile(CI.getLangOpts(),
                                            &CI.getPreprocessor());
 
   // Initialize the action.
   if (!BeginSourceFileAction(CI, Input.File))
     goto failure;
 
diff --git a/lib/FrontendTool/ExecuteCompilerInvocation.cpp b/lib/FrontendTool/ExecuteCompilerInvocation.cpp
--- a/lib/FrontendTool/ExecuteCompilerInvocation.cpp
+++ b/lib/FrontendTool/ExecuteCompilerInvocation.cpp
@@ -154,16 +154,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