# HG changeset patch
# User Joshua Cranmer <[email protected]>
# Date 1325721797 21600
# Node ID 283465d22f9bb35c7153a04559878d6768f771b3
# Parent 3792540de8f25143209dabcd61970e7272200415
Implement a better plugin API for clang, v3
In this version, I've moved from using a wrapper action (which emulated the old
plugin model more closely) to installing plugins directly to the compiler
instance, which is better for adding future extension hooks, like custom
attributes.
diff --git a/docs/ClangPlugins.html b/docs/ClangPlugins.html
--- a/docs/ClangPlugins.html
+++ b/docs/ClangPlugins.html
@@ -16,65 +16,79 @@
<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
+<p>Clang Plugins run a set of callbacks over code. These callbacks are functions
+that the plugin exports that the compiler uses over the course of compiling
+code. These functions need not be implemented (except for clang_plugin_init). By
+use of these hooks, compilers can affect or observer the course of compilation
+by using several callbacks. The most important callback 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. In this tutorial, we'll
demonstrate how to write a simple clang plugin.
</p>
<!-- ======================================================================= -->
-<h2 id="pluginactions">Writing a PluginASTAction</h2>
+<h2 id="init">Arguments and initialization</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 first function called when a plugin is loaded is clang_plugin_init. This
+function returns a boolean indicating whether or not the plugin can run on the
+code. As arguments, this function is given the arguments of the plugin as well
+as the version of clang being run. An example looks like this:</p>
<pre>
- 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.
+ bool clang_plugin_init(int argc, plugin::PluginArg *argv,
+ plugin::Version *version) {
+ // Check the version of clang
+ if (version->major != CLANG_VERSION_MAJOR ||
+ version->minor != CLANG_VERSION_MINOR)
+ return false;
+
+ for (int i = 0; i < argc; ++i) {
+ if (!strcmp(argv[i].name, "-some-arg")) {
+ // Handle the command line argument
}
}
return true;
}
</pre>
<!-- ======================================================================= -->
-<h2 id="registerplugin">Registering a plugin</h2>
+<h2 id="callbacks">Setting callbacks</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>
+<p>The most important plugin hook is the one that is called before each file is
+compiled: clang_plugin_begin_file. This allows plugins to add their own custom
+ASTConsumer (among other compiler hooks).</p>
<pre>
- static FrontendPluginRegistry::Add<MyPlugin> X("my-plugin-name", "my plugin description");
+ void clang_plugin_begin_file(llvm::StringRef fileName,
+ const CompilerInstance &CI, plugin::PluginFileCallbacks *callbacks) {
+ callbacks->astConsumer = new PrintFunctionsConsumer();
+ }
</pre>
<!-- ======================================================================= -->
<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 +97,58 @@ 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 clang_plugin_init(int argc, plugin::PluginArg *argv,
+ plugin::Version *version) {
+ // It is dangerous to use plugins with versions that it wasn't compiled for.
+ // This causes us to bail out before we can do things like crash clang.
+ if (version->major != CLANG_VERSION_MAJOR ||
+ version->minor != CLANG_VERSION_MINOR)
+ return false;
- 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";
- }
-
-};
-
+ llvm::errs() << "Number of arguments: " << argc << '\n';
+ for (int i = 0; i < argc; ++i)
+ llvm::errs() << "Plugin argument: arg " << argv[i].name
+ << " is set to value " << argv[i].value << '\n';
+ return true;
}
-static FrontendPluginRegistry::Add<PrintFunctionNamesAction>
-X("print-fns", "print function names");
+void clang_plugin_begin_file(llvm::StringRef fileName,
+ const CompilerInstance &CI, plugin::PluginFileCallbacks *callbacks) {
+ llvm::errs() << "Found when compiling " << fileName << ":\n";
+ callbacks->astConsumer = 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-<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-<plugin-name>-<arg>=<value> or
+-fplugin-arg-<plugin-name>-<arg>. These options work the same when run
+both with the normal driver and the cc1 process.</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>
<pre>
$ 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
+ -fplugin=$BD/lib/PrintFunctionNames.so
</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,30 @@ 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");
+bool clang_plugin_init(int argc, plugin::PluginArg *argv,
+ plugin::Version *version) {
+ // It is dangerous to use plugins with versions that it wasn't compiled for.
+ // This causes us to bail out before we can do things like crash clang.
+ if (version->major != CLANG_VERSION_MAJOR ||
+ version->minor != CLANG_VERSION_MINOR)
+ return false;
+
+ llvm::errs() << "Number of arguments: " << argc << '\n';
+ for (int i = 0; i < argc; ++i)
+ llvm::errs() << "Plugin argument: arg " << argv[i].name
+ << " is set to value " << argv[i].value << '\n';
+ return true;
+}
+
+void clang_plugin_begin_file(llvm::StringRef fileName,
+ const CompilerInstance &CI, plugin::PluginFileCallbacks &callbacks) {
+ llvm::errs() << "Found when compiling " << fileName << ":\n";
+ callbacks.astConsumer = 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,113 @@
+//===--- 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/StringRef.h"
+
+namespace clang {
+
+class ASTConsumer;
+class CompilerInstance;
+class DiagnosticConsumer;
+class PPCallbacks;
+
+namespace plugin {
+
+/**
+ * An individual argument for a plugin.
+ *
+ * Plugin arguments are specified as -fplugin-arg-<plugin>-<name>=<value> or
+ * -fplugin-arg-<plugin>-<name> attributes. In the latter case, the value
+ * attribute would be an empty string.
+ */
+struct PluginArg {
+ const char *name;
+ const char *value;
+};
+
+/**
+ * A struct representing the version of clang.
+ *
+ * This version is different from other accessors that clang exposes in order to
+ * retain ABI 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 patch level version of clang, if present. This value shouldn't affect
+ /// API or ABI-level compatibility with clang for most uses. This corresponds
+ /// to CLANG_VERSION_PATCHLEVEL if it is defined.
+ unsigned subminor;
+};
+
+/**
+ * This is a set of callbacks which should be filled in by the plugin to
+ * hook into various parts of the compiler.
+ */
+struct PluginFileCallbacks {
+ PluginFileCallbacks() : astConsumer(0), ppCallback(0), diagnostics(0) {}
+ /// A callback that allows plugins to view (but not modify) the AST of a file.
+ ASTConsumer *astConsumer;
+ /// A callback that allows plugins to be notified of preprocessor directives.
+ PPCallbacks *ppCallback;
+ /// A callback that allows plugins to be notified of warnings and errors.
+ DiagnosticConsumer *diagnostics;
+};
+
+} // namespace plugin
+} // namespace clang
+
+// 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 to initialize the plugin.
+/// \note This is the only function whose ABI compatibility is guaranteed to
+/// remain stable. All plugins should verify that the version passed into
+/// this function is the same version that they were compiled with and
+/// abort if this is not the case. To detect the version compiled with,
+/// include clang/Basic/Version.h and check CLANG_VERSION_MAJOR and
+/// CLANG_VERSION_MINOR.
+/// \arg argc The number of arguments in the argv array
+/// \arg argv An array of the arguments passed to this plugin from the
+/// command line.
+/// \arg version The version of the compiler being called.
+/// \return Whether or not the plugin initialization succeeded. If initialization
+/// fails, the compilation process is terminated.
+extern bool clang_plugin_init(int Argc, clang::plugin::PluginArg *Argv,
+ clang::plugin::Version *Version);
+
+/// Callback that is called before a file is compiled.
+///
+/// Before this function is called, the callbacks object will have all of its
+/// pointers set to null. All non-null callbacks will be deleted by the compiler
+/// internally, so one should always return a new pointer for every file.
+///
+/// \arg fileName The name of the function being called.
+/// \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_begin_file(llvm::StringRef FileName,
+ const clang::CompilerInstance &CI,
+ clang::plugin::PluginFileCallbacks &Callbacks);
+
+/// Callback that is called after a file has been compiled.
+extern void clang_plugin_end_file();
+
+/// Callback that is called after all files have been compiled.
+extern void clang_plugin_destroy();
+}
+
+#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
@@ -595,16 +595,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
@@ -6,23 +6,25 @@
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_FRONTEND_COMPILERINSTANCE_H_
#define LLVM_CLANG_FRONTEND_COMPILERINSTANCE_H_
#include "clang/Frontend/CompilerInvocation.h"
+#include "clang/Basic/Plugin.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Lex/ModuleLoader.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/IntrusiveRefCntPtr.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/OwningPtr.h"
+#include "llvm/Support/DynamicLibrary.h"
#include <cassert>
#include <list>
#include <string>
#include <utility>
namespace llvm {
class raw_fd_ostream;
class Timer;
@@ -125,16 +127,55 @@ 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;
+public:
+ /// Contains the data needed for a single plugin.
+ class Plugin {
+ /// The library that contains the plugin itself
+ mutable llvm::sys::DynamicLibrary PluginLibrary;
+
+ /// The structure that contains all of the callbacks for the library
+ mutable plugin::PluginFileCallbacks Callbacks;
+ public:
+ Plugin(llvm::sys::DynamicLibrary library) : PluginLibrary(library),
+ Callbacks() {}
+ plugin::PluginFileCallbacks &getCallbacks() const { return Callbacks; }
+ intptr_t getPluginFunction(const char *name) const {
+ return (intptr_t)PluginLibrary.getAddressOfSymbol(name);
+ }
+
+/// Helper macro to call a given function in a plugin:
+/// CALL_PLUGIN_FUNCTION(plugin, clang_plugin_some_func, (arg1, arg2))
+#define CALL_PLUGIN_FUNCTION(plugin, fname, Args) \
+ do { \
+ CompilerInstance::Plugin::fname Func_ = \
+ (CompilerInstance::Plugin::fname)(plugin)->getPluginFunction(#fname); \
+ if (Func_) \
+ Func_ Args; \
+ } while (0)
+
+ // These typedefs must have the same signatures as the ones in
+ // clang/Basic/Plugin.h, including the same name (it makes the helper macro
+ // above work properly).
+ typedef void (*clang_plugin_begin_file)(StringRef, const CompilerInstance &,
+ plugin::PluginFileCallbacks &);
+ typedef void (*clang_plugin_end_file)();
+ typedef void (*clang_plugin_destroy)();
+ };
+
+private:
+ /// The list of plugins that are currently loaded.
+ SmallVector<Plugin, 4> Plugins;
+
void operator=(const CompilerInstance &); // DO NOT IMPLEMENT
CompilerInstance(const CompilerInstance&); // DO NOT IMPLEMENT
public:
CompilerInstance();
~CompilerInstance();
/// @name High-Level Operations
/// {
@@ -258,16 +299,20 @@ public:
TargetOptions &getTargetOpts() {
return Invocation->getTargetOpts();
}
const TargetOptions &getTargetOpts() const {
return Invocation->getTargetOpts();
}
+ ArrayRef<Plugin> getPlugins() const {
+ return Plugins;
+ }
+
/// }
/// @name Diagnostics Engine
/// {
bool hasDiagnostics() const { return Diagnostics != 0; }
/// Get the current diagnostics engine.
DiagnosticsEngine &getDiagnostics() const {
@@ -646,16 +691,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
@@ -157,16 +157,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/Driver/Tools.cpp b/lib/Driver/Tools.cpp
--- a/lib/Driver/Tools.cpp
+++ b/lib/Driver/Tools.cpp
@@ -1617,16 +1617,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
@@ -672,16 +672,22 @@ bool CompilerInstance::ExecuteAction(Fro
Act.Execute();
Act.EndSourceFile();
}
}
// Notify the diagnostic client that all files were processed.
getDiagnostics().getClient()->finish();
+ // Destroy the plugins
+ for (SmallVector<Plugin, 4>::iterator it = Plugins.begin();
+ it != Plugins.end(); ++it)
+ CALL_PLUGIN_FUNCTION(it, clang_plugin_destroy, ());
+ Plugins.clear();
+
if (getDiagnosticOpts().ShowCarets) {
// We can have multiple diagnostics sharing one diagnostic client.
// Get the total number of warnings/errors from the client.
unsigned NumWarnings = getDiagnostics().getClient()->getNumWarnings();
unsigned NumErrors = getDiagnostics().getClient()->getNumErrors();
if (NumWarnings)
OS << NumWarnings << " warning" << (NumWarnings == 1 ? "" : "s");
@@ -1083,8 +1089,62 @@ Module *CompilerInstance::loadModule(Sou
ImportLoc, Module,
Path.back().second));
}
LastModuleImportLoc = ImportLoc;
LastModuleImportResult = Module;
return Module;
}
+
+bool CompilerInstance::initializePlugins() {
+ ArrayRef<FrontendOptions::CompilerPlugin> CompilerPlugins =
+ getFrontendOpts().CompilerPlugins;
+ for (unsigned i = 0, e = CompilerPlugins.size(); i != e; ++i) {
+ StringRef Path = CompilerPlugins[i].Plugin;
+
+ // Try loading the library
+ std::string Error;
+ llvm::sys::DynamicLibrary Library =
+ llvm::sys::DynamicLibrary::getPermanentLibrary(Path.data(), &Error);
+ if (!Library.isValid()) {
+ getDiagnostics().Report(diag::err_fe_unable_to_load_plugin)
+ << Path << Error;
+ return false;
+ }
+
+ // If the library has been loaded, it's time to run plugin_init.
+ // Construct the version
+ plugin::Version PluginVersion = {
+ CLANG_VERSION_MAJOR,
+ CLANG_VERSION_MINOR,
+#ifdef CLANG_VERSION_PATCHLEVEL
+ CLANG_VERSION_PATCHLEVEL
+#else
+ 0
+#endif
+ };
+
+ // Construct the arguments
+ ArrayRef<std::pair<std::string, std::string> > PluginArgs =
+ CompilerPlugins[i].Args;
+ SmallVector<plugin::PluginArg, 4> Args(PluginArgs.size());
+ for (unsigned i = 0; i < PluginArgs.size(); i++) {
+ Args[i].name = PluginArgs[i].first.c_str();
+ Args[i].value = PluginArgs[i].second.c_str();
+ }
+ typedef bool (*InitFuncTy)(int, plugin::PluginArg *, plugin::Version *);
+ InitFuncTy PluginInit = (InitFuncTy)(intptr_t)
+ Library.getAddressOfSymbol("clang_plugin_init");
+ if (!PluginInit) {
+ getDiagnostics().Report(diag::err_fe_unable_to_load_plugin)
+ << Path << "Could not find clang_plugin_init";
+ return false;
+ } else if (!PluginInit(PluginArgs.size(), Args.data(), &PluginVersion)) {
+ getDiagnostics().Report(diag::err_fe_unable_to_load_plugin)
+ << Path << "Plugin initialization failed";
+ return false;
+ } else
+ Plugins.push_back(Plugin(Library));
+ }
+
+ return true;
+}
diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp
--- a/lib/Frontend/CompilerInvocation.cpp
+++ b/lib/Frontend/CompilerInvocation.cpp
@@ -1506,16 +1506,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
@@ -9,16 +9,17 @@
#include "clang/Frontend/FrontendAction.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclGroup.h"
#include "clang/Lex/HeaderSearch.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Frontend/ASTUnit.h"
+#include "clang/Frontend/ChainedDiagnosticConsumer.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/Parse/ParseAST.h"
#include "clang/Serialization/ASTDeserializationListener.h"
@@ -123,17 +124,18 @@ void FrontendAction::setCurrentInput(con
}
ASTConsumer* FrontendAction::CreateWrappedASTConsumer(CompilerInstance &CI,
StringRef InFile) {
ASTConsumer* Consumer = CreateASTConsumer(CI, InFile);
if (!Consumer)
return 0;
- if (CI.getFrontendOpts().AddPluginActions.size() == 0)
+ if (CI.getFrontendOpts().AddPluginActions.size() == 0 &&
+ CI.getPlugins().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();
i != e; ++i) {
@@ -147,29 +149,44 @@ ASTConsumer* FrontendAction::CreateWrapp
OwningPtr<PluginASTAction> P(it->instantiate());
FrontendAction* c = P.get();
if (P->ParseArgs(CI, CI.getFrontendOpts().AddPluginArgs[i]))
Consumers.push_back(c->CreateASTConsumer(CI, InFile));
}
}
}
+ for (ArrayRef<CompilerInstance::Plugin>::iterator it = CI.getPlugins().begin();
+ it != CI.getPlugins().end(); ++it)
+ if (ASTConsumer *Consumer = it->getCallbacks().astConsumer)
+ Consumers.push_back(Consumer);
+
return new MultiplexConsumer(Consumers);
}
bool FrontendAction::BeginSourceFile(CompilerInstance &CI,
const FrontendInputFile &Input) {
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.
+ for (ArrayRef<CompilerInstance::Plugin>::iterator it = CI.getPlugins().begin();
+ it != CI.getPlugins().end(); ++it) {
+ plugin::PluginFileCallbacks &Callbacks = it->getCallbacks();
+
+ // Get the Callbacks from the plugin
+ CALL_PLUGIN_FUNCTION(it, clang_plugin_begin_file, (Input.File, CI, Callbacks));
+ }
+
// 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 +237,28 @@ bool FrontendAction::BeginSourceFile(Com
goto failure;
return true;
}
// Set up the preprocessor.
CI.createPreprocessor();
+ // Add preprocessor and diagnostics client callbacks.
+ for (ArrayRef<CompilerInstance::Plugin>::iterator it = CI.getPlugins().begin();
+ it != CI.getPlugins().end(); ++it) {
+ plugin::PluginFileCallbacks &Callbacks = it->getCallbacks();
+
+ if (Callbacks.ppCallback)
+ CI.getPreprocessor().addPPCallbacks(Callbacks.ppCallback);
+ if (Callbacks.diagnostics)
+ CI.getDiagnostics().setClient(new ChainedDiagnosticConsumer(
+ &CI.getDiagnosticClient(), Callbacks.diagnostics));
+ }
+
// 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;
@@ -386,16 +415,20 @@ void FrontendAction::EndSourceFile() {
CI.resetAndLeakASTContext();
CI.resetAndLeakPreprocessor();
CI.resetAndLeakSourceManager();
CI.resetAndLeakFileManager();
}
setCompilerInstance(0);
setCurrentInput(FrontendInputFile());
+
+ for (ArrayRef<CompilerInstance::Plugin>::iterator it = CI.getPlugins().begin();
+ it != CI.getPlugins().end(); ++it)
+ CALL_PLUGIN_FUNCTION(it, clang_plugin_end_file, ());
}
//===----------------------------------------------------------------------===//
// Utility Actions
//===----------------------------------------------------------------------===//
void ASTFrontendAction::ExecuteAction() {
CompilerInstance &CI = getCompilerInstance();
diff --git a/lib/FrontendTool/ExecuteCompilerInvocation.cpp b/lib/FrontendTool/ExecuteCompilerInvocation.cpp
--- a/lib/FrontendTool/ExecuteCompilerInvocation.cpp
+++ b/lib/FrontendTool/ExecuteCompilerInvocation.cpp
@@ -153,16 +153,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