[cfe-commits] [PATCH] Introduce a plugin architecture
pidgeot18 at gmail.com
pidgeot18 at gmail.com
Mon Mar 5 08:58:28 PST 2012
examples/PrintFunctionNames/PrintFunctionNames.cpp | 68 +++---
examples/PrintFunctionNames/PrintFunctionNames.exports | 2 +-
examples/PrintFunctionNames/README.txt | 12 +-
include/clang/Basic/Plugin.h | 95 +++++++++
include/clang/Driver/CC1Options.td | 4 +
include/clang/Driver/Options.td | 2 +
include/clang/Frontend/FrontendOptions.h | 7 +
lib/Driver/Tools.cpp | 4 +
lib/Frontend/CompilerInvocation.cpp | 40 ++++
lib/FrontendTool/ExecuteCompilerInvocation.cpp | 163 ++++++++++++++++-
10 files changed, 352 insertions(+), 45 deletions(-)
# HG changeset patch
# User Joshua Cranmer <Pidgeot18 at gmail.com>
# Date 1325721797 21600
# Node ID 32744ae33da253c873d464209c97a76441d25c59
# Parent 78b3e55ff7624825022225734078ec56a0e8b535
[mq]: clang-plugin
diff --git a/examples/PrintFunctionNames/PrintFunctionNames.cpp b/examples/PrintFunctionNames/PrintFunctionNames.cpp
--- a/examples/PrintFunctionNames/PrintFunctionNames.cpp
+++ b/examples/PrintFunctionNames/PrintFunctionNames.cpp
@@ -7,65 +7,59 @@
//
//===----------------------------------------------------------------------===//
//
// 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 {
-
+static int NumFuncs = 0;
class PrintFunctionsConsumer : public ASTConsumer {
public:
virtual bool HandleTopLevelDecl(DeclGroupRef DG) {
for (DeclGroupRef::iterator i = DG.begin(), e = DG.end(); i != e; ++i) {
const Decl *D = *i;
- if (const NamedDecl *ND = dyn_cast<NamedDecl>(D))
+ if (const NamedDecl *ND = dyn_cast<NamedDecl>(D)) {
llvm::errs() << "top-level-decl: \"" << ND->getNameAsString() << "\"\n";
+ ++NumFuncs;
+ }
}
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");
+extern "C" {
+
+bool clang_plugin_init(int argc, plugin::PluginArg *argv,
+ plugin::Version *version) {
+ 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,
+ plugin::PluginFileCallbacks *callbacks) {
+ llvm::errs() << "Found when compiling " << fileName << ":\n";
+ callbacks->astConsumer = new PrintFunctionsConsumer();
+}
+
+void clang_plugin_destroy() {
+ llvm::errs() << "Total functions found: " << NumFuncs << '\n';
+}
+}
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 -cc1 -fplugin=../../Debug+Asserts/lib/libPrintFunctionNames.so some-input-file.c
+$ clang -cc1 -fplugin=../../Debug+Asserts/lib/libPrintFunctionNames.so -fplugin-arg-libPrintFunctionNames-example=some-input some-input-file.c
+$ clang -cc1 -fp-load ../../Debug+Asserts/lib/libPrintFunctionNames.so -fplugin-arg-iibPrintFunctionNames-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 -cc1 -fplugin=../../Debug+Asserts/lib/libPrintFunctionNames.dylib some-input-file.c
+$ clang -cc1 -fplugin=../../Debug+Asserts/lib/libPrintFunctionNames.dylib -fplugin-arg-libPrintFunctionNames-example=some-input some-input-file.c
+$ clang -cc1 -fp-load ../../Debug+Asserts/lib/libPrintFunctionNames.dylio -fplugin-arg-iibPrintFunctionNames-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,95 @@
+//===--- 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 llvm {
+ class StringRef;
+}
+
+namespace clang {
+
+ class ASTConsumer;
+ class PPCallbacks;
+ class DiagnosticConsumer;
+
+namespace plugin {
+
+struct PluginArg {
+ const char *name;
+ const char *value;
+};
+
+struct Version {
+ unsigned major;
+ unsigned minor;
+ unsigned subminor;
+};
+
+struct PluginFileCallbacks {
+ ASTConsumer *astConsumer;
+ PPCallbacks *ppCallback;
+ DiagnosticConsumer *diagnostics;
+};
+
+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, PluginArg *argv, Version *version);
+
+/**
+ * Callback that is called before a function 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 callbacks The pointer to the callbacks that this plugin will define for
+ * this file.
+ */
+extern void clang_plugin_begin_file(llvm::StringRef fileName,
+ PluginFileCallbacks *callbacks);
+
+/**
+ * Callback that is called after a function has been compiled.
+ *
+ * \arg fileName The name of the function being called.
+ */
+extern void clang_plugin_end_file(llvm::StringRef fileName);
+
+/**
+ * Callback that is called after all functions have been compiled.
+ */
+extern void clang_plugin_destroy();
+}
+
+} // namespace plugin
+} // namespace clang
+
+#endif
diff --git a/include/clang/Driver/CC1Options.td b/include/clang/Driver/CC1Options.td
--- a/include/clang/Driver/CC1Options.td
+++ b/include/clang/Driver/CC1Options.td
@@ -362,16 +362,20 @@ def load : Separate<"-load">, MetaVarNam
HelpText<"Load the named plugin (dynamic shared object)">;
def plugin : Separate<"-plugin">, MetaVarName<"<name>">,
HelpText<"Use the named plugin action instead of the default action (use \"help\" to list available options)">;
def plugin_arg : JoinedAndSeparate<"-plugin-arg-">,
MetaVarName<"<name> <arg>">,
HelpText<"Pass <arg> to plugin <name>">;
def add_plugin : Separate<"-add-plugin">, MetaVarName<"<name>">,
HelpText<"Use the named plugin action in addition to the default action">;
+def fplugin : Joined<"-fplugin=">,
+ HelpText<"Load the named compiler plugin">;
+def fplugin_arg : Joined<"-fplugin-arg-">,
+ HelpText<"Pass <arg> to plugin <name>">;
def resource_dir : Separate<"-resource-dir">,
HelpText<"The directory which holds the compiler resource files">;
def version : Flag<"-version">,
HelpText<"Print the compiler version">;
def _version : Flag<"--version">, Alias<version>;
def Action_Group : OptionGroup<"<action group>">;
let Group = Action_Group in {
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
@@ -483,16 +483,18 @@ def foutput_class_dir_EQ : Joined<"-fout
def fpack_struct : Flag<"-fpack-struct">, Group<f_Group>;
def fno_pack_struct : Flag<"-fno-pack-struct">, Group<f_Group>;
def fpack_struct_EQ : Joined<"-fpack-struct=">, Group<f_Group>;
def fpascal_strings : Flag<"-fpascal-strings">, Group<f_Group>;
def fpch_preprocess : Flag<"-fpch-preprocess">, Group<f_Group>;
def fpic : Flag<"-fpic">, Group<f_Group>;
def fpie : Flag<"-fpie">, Group<f_Group>, Flags<[NoArgumentUnused]>;
def fno_pie : Flag<"-fno-pie">, Group<f_Group>, Flags<[NoArgumentUnused]>;
+def fplugin : Joined<"-fplugin=">, Group<f_Group>;
+def fplugin_arg : Joined<"-fplugin-arg-">, Group<f_Group>;
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>;
def freorder_blocks : Flag<"-freorder-blocks">, Group<clang_ignored_f_Group>;
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
@@ -146,16 +146,23 @@ 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;
+ /// The list of compiler plugins to load.
+ std::vector<std::string> CompilerPlugins;
+
+ /// Argments for the above compiler plugins
+ std::vector<std::vector<std::pair<std::string, std::string> > >
+ CompilerPluginArgs;
+
/// \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
@@ -1376,16 +1376,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);
+ 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/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp
--- a/lib/Frontend/CompilerInvocation.cpp
+++ b/lib/Frontend/CompilerInvocation.cpp
@@ -1395,16 +1395,56 @@ 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));
}
}
+ Opts.CompilerPlugins = Args.getAllArgValues(OPT_fplugin);
+ Opts.CompilerPluginArgs.resize(Opts.CompilerPlugins.size());
+
+ // Parse the plugin arguments
+ std::vector<std::pair<std::string, std::string> > ParsedPluginArgs;
+ for (arg_iterator it = Args.filtered_begin(OPT_fplugin_arg),
+ end = Args.filtered_end(); it != end; ++it) {
+ std::string value = (*it)->getValue(Args);
+ size_t boundary = value.find('-');
+ if (boundary == std::string::npos) {
+ Diags.Report(diag::err_drv_unknown_argument) << value;
+ continue;
+ }
+ ParsedPluginArgs.push_back(std::pair<std::string, std::string>(
+ value.substr(0, boundary), value.substr(boundary + 1)));
+ }
+
+ // Add the plugin arguments to a parsedPluginArgs
+ for (int i = 0, e = Opts.CompilerPlugins.size(); i != e; ++i) {
+ std::string base = llvm::sys::path::stem(Opts.CompilerPlugins[i]);
+ for (std::vector<std::pair<std::string, std::string> >::iterator
+ it = ParsedPluginArgs.begin(), end = ParsedPluginArgs.end();
+ it != end; ++it) {
+ if (it->first == base) {
+ std::string argpair = it->second;
+ size_t boundary = argpair.find("=");
+ // Treat no '=' as an arg with no value
+ if (boundary == std::string::npos) {
+ Opts.CompilerPluginArgs[i].push_back(
+ std::pair<std::string, std::string>(argpair, ""));
+ } else {
+ Opts.CompilerPluginArgs[i].push_back(
+ std::pair<std::string, std::string>(argpair.substr(0, boundary),
+ argpair.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/FrontendTool/ExecuteCompilerInvocation.cpp b/lib/FrontendTool/ExecuteCompilerInvocation.cpp
--- a/lib/FrontendTool/ExecuteCompilerInvocation.cpp
+++ b/lib/FrontendTool/ExecuteCompilerInvocation.cpp
@@ -10,29 +10,137 @@
// This file holds ExecuteCompilerInvocation(). It is split into its own file to
// minimize the impact of pulling in essentially everything else in Clang.
//
//===----------------------------------------------------------------------===//
#include "clang/FrontendTool/Utils.h"
#include "clang/StaticAnalyzer/Frontend/FrontendActions.h"
#include "clang/ARCMigrate/ARCMTActions.h"
+#include "clang/Basic/Plugin.h"
+#include "clang/Basic/Version.inc"
#include "clang/CodeGen/CodeGenAction.h"
#include "clang/Driver/CC1Options.h"
#include "clang/Driver/OptTable.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Frontend/ChainedDiagnosticConsumer.h"
#include "clang/Frontend/CompilerInvocation.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Frontend/FrontendDiagnostic.h"
#include "clang/Frontend/FrontendPluginRegistry.h"
+#include "clang/Frontend/MultiplexConsumer.h"
#include "clang/Rewrite/FrontendActions.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/DynamicLibrary.h"
using namespace clang;
+namespace {
+ class Plugin {
+ llvm::sys::DynamicLibrary PluginLibrary;
+ clang::plugin::PluginFileCallbacks callbacks;
+ public:
+ Plugin(llvm::sys::DynamicLibrary library) : PluginLibrary(library) {}
+ clang::plugin::PluginFileCallbacks *getCallbacks() { return &callbacks; }
+ intptr_t getFunctionCall(const char *name) {
+ return (intptr_t)PluginLibrary.getAddressOfSymbol(name);
+ }
+
+ typedef void (*BeginFileType)(StringRef, clang::plugin::PluginFileCallbacks*);
+ typedef void (*EndFileFnTy)(StringRef);
+ typedef void (*DestroyTy)();
+ };
+ class PluginWrapperAction : public WrapperFrontendAction {
+ std::vector<Plugin> plugins;
+ std::string CurrentFile;
+ public:
+ PluginWrapperAction(FrontendAction *WrappedAction,
+ std::vector<Plugin> &plugins)
+ : WrapperFrontendAction(WrappedAction), plugins(plugins) {}
+
+ virtual bool BeginSourceFileAction(CompilerInstance &CI,
+ StringRef Filename);
+ virtual ASTConsumer *CreateASTConsumer(CompilerInstance &CI,
+ StringRef InFile);
+ virtual void EndSourceFileAction();
+
+ virtual ~PluginWrapperAction();
+ };
+}
+
+bool PluginWrapperAction::BeginSourceFileAction(CompilerInstance &CI,
+ StringRef Filename) {
+ if (!WrapperFrontendAction::BeginSourceFileAction(CI, Filename))
+ return false;
+
+ CurrentFile = Filename;
+ for (std::vector<Plugin>::iterator it = plugins.begin(); it != plugins.end();
+ ++it) {
+ // Retrieve the callbacks, and clear all of the values to NULL
+ clang::plugin::PluginFileCallbacks *callbacks = it->getCallbacks();
+ memset(callbacks, 0, sizeof(clang::plugin::PluginFileCallbacks));
+
+ // Get the callbacks from the plugin
+ Plugin::BeginFileType beginFunc = (Plugin::BeginFileType)it->getFunctionCall(
+ "clang_plugin_begin_file");
+ if (!beginFunc)
+ continue;
+ beginFunc(Filename, callbacks);
+
+ // ASTConsumer is handled by GetASTConsumer; others are handled now
+ if (callbacks->ppCallback)
+ CI.getPreprocessor().addPPCallbacks(callbacks->ppCallback);
+ if (callbacks->diagnostics)
+ {
+ // The front end has already run this, so let's do this now.
+ callbacks->diagnostics->BeginSourceFile(CI.getLangOpts(),
+ &CI.getPreprocessor());
+ CI.getDiagnostics().setClient(new ChainedDiagnosticConsumer(
+ &CI.getDiagnosticClient(), callbacks->diagnostics));
+ }
+ }
+
+ return true;
+}
+
+ASTConsumer *PluginWrapperAction::CreateASTConsumer(CompilerInstance &CI,
+ StringRef InFile) {
+ std::vector<ASTConsumer*> Consumers(1,
+ WrapperFrontendAction::CreateASTConsumer(CI, InFile));
+ for (std::vector<Plugin>::iterator it = plugins.begin(); it != plugins.end();
+ ++it) {
+ if (ASTConsumer *consumer = it->getCallbacks()->astConsumer) {
+ Consumers.push_back(consumer);
+ }
+ }
+ return new MultiplexConsumer(Consumers);
+}
+
+void PluginWrapperAction::EndSourceFileAction()
+{
+ WrapperFrontendAction::EndSourceFileAction();
+ for (std::vector<Plugin>::iterator it = plugins.begin(); it != plugins.end();
+ ++it) {
+ Plugin::EndFileFnTy EndFileFunc =
+ (Plugin::EndFileFnTy)it->getFunctionCall("clang_plugin_end_file");
+ if (EndFileFunc)
+ EndFileFunc(CurrentFile);
+ }
+}
+
+PluginWrapperAction::~PluginWrapperAction() {
+ for (std::vector<Plugin>::iterator it = plugins.begin(); it != plugins.end();
+ ++it) {
+ Plugin::DestroyTy destroyFunc = (Plugin::DestroyTy)
+ it->getFunctionCall("clang_plugin_destroy");
+ if (destroyFunc)
+ destroyFunc();
+ }
+}
+
static FrontendAction *CreateFrontendBaseAction(CompilerInstance &CI) {
using namespace clang::frontend;
switch (CI.getFrontendOpts().ProgramAction) {
case ASTDump: return new ASTDumpAction();
case ASTDumpXML: return new ASTDumpXMLAction();
case ASTPrint: return new ASTPrintAction();
case ASTView: return new ASTViewAction();
@@ -141,16 +249,68 @@ 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;
}
+ std::vector<Plugin> plugins;
+ for (unsigned i = 0,
+ e = Clang->getFrontendOpts().CompilerPlugins.size(); i != e; ++i) {
+ const std::string &Path = Clang->getFrontendOpts().CompilerPlugins[i];
+
+ // Try loading the library
+ std::string Error;
+ llvm::sys::DynamicLibrary plugin =
+ llvm::sys::DynamicLibrary::getPermanentLibrary(Path.c_str(), &Error);
+ if (!plugin.isValid()) {
+ Clang->getDiagnostics().Report(diag::err_fe_unable_to_load_plugin)
+ << Path << Error;
+ continue;
+ }
+
+ // If the library has been loaded, it's time to run plugin_init.
+ // Construct the version
+ clang::plugin::Version pluginVersion = {
+ CLANG_VERSION_MAJOR,
+ CLANG_VERSION_MINOR,
+#ifdef CLANG_VERSION_PATCHLEVEL
+ CLANG_VERSION_PATCHLEVEL
+#else
+ 0
+#endif
+ };
+
+ // Construct the arguments
+ std::vector<std::pair<std::string, std::string> > &pluginArgs =
+ Clang->getFrontendOpts().CompilerPluginArgs[i];
+ plugin::PluginArg *args = new plugin::PluginArg[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 (*init_func)(int argc, clang::plugin::PluginArg *argv,
+ clang::plugin::Version *version);
+ init_func plugin_init = (init_func)(intptr_t)
+ plugin.getAddressOfSymbol("clang_plugin_init");
+ if (!plugin_init) {
+ Clang->getDiagnostics().Report(diag::err_fe_unable_to_load_plugin)
+ << Path << "Could not find clang_plugin_init";
+ } else if (!plugin_init(pluginArgs.size(), args, &pluginVersion)) {
+ Clang->getDiagnostics().Report(diag::err_fe_unable_to_load_plugin)
+ << Path << "Plugin initialization failed";
+ } else {
+ plugins.push_back(Plugin(plugin));
+ }
+
+ delete [] args;
+ }
+
// 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)";
@@ -166,17 +326,18 @@ bool clang::ExecuteCompilerInvocation(Co
ento::printCheckerHelp(llvm::outs(), Clang->getFrontendOpts().Plugins);
return 0;
}
// If there were errors in processing arguments, don't do anything else.
bool Success = false;
if (!Clang->getDiagnostics().hasErrorOccurred()) {
// Create and execute the frontend action.
- OwningPtr<FrontendAction> Act(CreateFrontendAction(*Clang));
+ OwningPtr<FrontendAction> Act(
+ new PluginWrapperAction(CreateFrontendAction(*Clang), plugins));
if (Act) {
Success = Clang->ExecuteAction(*Act);
if (Clang->getFrontendOpts().DisableFree)
Act.take();
}
}
return Success;
More information about the cfe-commits
mailing list