From 32f545cdf031ebe3718791f18e8fb6b6141fd081 Mon Sep 17 00:00:00 2001 From: Matthew Barnes Date: Fri, 28 Aug 2009 20:21:54 -0400 Subject: Simplify EPlugin loading at startup. - Require all EPlugin and EPluginHook subtypes be registered before loading plugins. This drastically simplifies the EPlugin/EPluginHook negotiation. - Turn most EPluginHook subtypes into GTypeModules and register their types from an e_module_load() function (does not include shell hooks). - Convert EPluginLib and the Mono and Python bindings to GTypeModules and register their types from an e_module_load() function, and kill EPluginTypeHook. --- modules/plugin-lib/Makefile.am | 20 ++ modules/plugin-lib/e-plugin-lib.c | 249 +++++++++++++++++++++++ modules/plugin-lib/e-plugin-lib.h | 92 +++++++++ modules/plugin-lib/evolution-module-plugin-lib.c | 41 ++++ 4 files changed, 402 insertions(+) create mode 100644 modules/plugin-lib/Makefile.am create mode 100644 modules/plugin-lib/e-plugin-lib.c create mode 100644 modules/plugin-lib/e-plugin-lib.h create mode 100644 modules/plugin-lib/evolution-module-plugin-lib.c (limited to 'modules/plugin-lib') diff --git a/modules/plugin-lib/Makefile.am b/modules/plugin-lib/Makefile.am new file mode 100644 index 0000000000..cce60902fa --- /dev/null +++ b/modules/plugin-lib/Makefile.am @@ -0,0 +1,20 @@ +AM_CPPFLAGS = \ + -DG_LOG_DOMAIN=\"evolution-plugin-lib\" \ + -I$(top_srcdir) \ + $(E_UTIL_CFLAGS) + +module_LTLIBRARIES = libevolution-module-plugin-lib.la + +libevolution_module_plugin_lib_la_SOURCES = \ + evolution-module-plugin-lib.c \ + e-plugin-lib.c \ + e-plugin-lib.h + +libevolution_module_plugin_lib_la_LIBADD = \ + $(top_builddir)/e-util/libeutil.la \ + $(E_UTIL_LIBS) + +libevolution_module_plugin_lib_la_LDFLAGS = \ + -module -avoid-version $(NO_UNDEFINED) + +-include $(top_srcdir)/git.mk diff --git a/modules/plugin-lib/e-plugin-lib.c b/modules/plugin-lib/e-plugin-lib.c new file mode 100644 index 0000000000..c7a0233bb3 --- /dev/null +++ b/modules/plugin-lib/e-plugin-lib.c @@ -0,0 +1,249 @@ +/* + * e-plugin-lib.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#include "e-plugin-lib.h" + +#include + +static gpointer parent_class; +static GType plugin_lib_type; + +/* TODO: + We need some way to manage lifecycle. + We need some way to manage state. + + Maybe just the g module init method will do, or we could add + another which returns context. + + There is also the question of per-instance context, e.g. for config + pages. +*/ + +static gint +plugin_lib_loadmodule (EPlugin *plugin) +{ + EPluginLib *plugin_lib = E_PLUGIN_LIB (plugin); + EPluginLibEnableFunc enable; + + if (plugin_lib->module != NULL) + return 0; + + if (plugin_lib->location == NULL) { + g_warning ("Location not set in plugin '%s'", plugin->name); + return -1; + } + + if ((plugin_lib->module = g_module_open (plugin_lib->location, 0)) == NULL) { + g_warning ("can't load plugin '%s': %s", plugin_lib->location, g_module_error ()); + return -1; + } + + if (g_module_symbol (plugin_lib->module, "e_plugin_lib_enable", (gpointer)&enable)) { + if (enable (plugin_lib, TRUE) != 0) { + plugin->enabled = FALSE; + g_module_close (plugin_lib->module); + plugin_lib->module = NULL; + return -1; + } + } + + return 0; +} + +static gpointer +plugin_lib_invoke (EPlugin *plugin, const gchar *name, gpointer data) +{ + EPluginLib *plugin_lib = E_PLUGIN_LIB (plugin); + EPluginLibFunc cb; + + if (!plugin->enabled) { + g_warning ("trying to invoke '%s' on disabled plugin '%s'", name, plugin->id); + return NULL; + } + + if (plugin_lib_loadmodule (plugin) != 0) + return NULL; + + if (!g_module_symbol (plugin_lib->module, name, (gpointer)&cb)) { + g_warning ("Cannot resolve symbol '%s' in plugin '%s' (not exported?)", name, plugin_lib->location); + return NULL; + } + + return cb (plugin_lib, data); +} + +static gpointer +plugin_lib_get_symbol (EPlugin *plugin, const gchar *name) +{ + EPluginLib *plugin_lib = E_PLUGIN_LIB (plugin); + gpointer symbol; + + if (plugin_lib_loadmodule (plugin) != 0) + return NULL; + + if (!g_module_symbol (plugin_lib->module, name, &symbol)) + return NULL; + + return symbol; +} + +static gint +plugin_lib_construct (EPlugin *plugin, xmlNodePtr root) +{ + EPluginLib *plugin_lib = E_PLUGIN_LIB (plugin); + + /* Set the location before chaining up, as some EPluginHooks + * will cause the module to load during hook construction. */ + + plugin_lib->location = e_plugin_xml_prop (root, "location"); + + if (plugin_lib->location == NULL) { + g_warning ("Library plugin '%s' has no location", plugin->id); + return -1; + } +#ifdef G_OS_WIN32 + { + gchar *mapped_location = + e_util_rplugin_libace_prefix (EVOLUTION_PREFIX, + e_util_get_prefix (), + plugin_lib->location); + g_free (plugin_lib->location); + plugin_lib->location = mapped_location; + } +#endif + + /* Chain up to parent's construct() method. */ + if (E_PLUGIN_CLASS (parent_class)->construct (plugin, root) == -1) + return -1; + + /* If we're enabled, check for the load-on-startup property */ + if (plugin->enabled) { + xmlChar *tmp; + + tmp = xmlGetProp (root, (const guchar *)"load-on-startup"); + if (tmp) { + if (plugin_lib_loadmodule (plugin) != 0) { + xmlFree (tmp); + return -1; + } + xmlFree (tmp); + } + } + + return 0; +} + +static GtkWidget * +plugin_lib_get_configure_widget (EPlugin *plugin) +{ + EPluginLib *plugin_lib = E_PLUGIN_LIB (plugin); + EPluginLibGetConfigureWidgetFunc get_configure_widget; + + if (plugin_lib_loadmodule (plugin) != 0) { + return NULL; + } + + if (g_module_symbol (plugin_lib->module, "e_plugin_lib_get_configure_widget", (gpointer)&get_configure_widget)) { + return (GtkWidget*) get_configure_widget (plugin_lib); + } + return NULL; +} + +static void +plugin_lib_enable (EPlugin *plugin, gint state) +{ + EPluginLib *plugin_lib = E_PLUGIN_LIB (plugin); + EPluginLibEnableFunc enable; + + E_PLUGIN_CLASS (parent_class)->enable (plugin, state); + + /* if we're disabling and it isn't loaded, nothing to do */ + if (!state && plugin_lib->module == NULL) + return; + + /* this will noop if we're disabling since we tested it above */ + if (plugin_lib_loadmodule (plugin) != 0) + return; + + if (g_module_symbol (plugin_lib->module, "e_plugin_lib_enable", (gpointer) &enable)) { + if (enable (plugin_lib, state) != 0) + return; + } +} + +static void +plugin_lib_finalize (GObject *object) +{ + EPluginLib *plugin_lib = E_PLUGIN_LIB (object); + + g_free (plugin_lib->location); + + if (plugin_lib->module) + g_module_close (plugin_lib->module); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +plugin_lib_class_init (EPluginClass *class) +{ + GObjectClass *object_class; + + parent_class = g_type_class_peek_parent (class); + + object_class = G_OBJECT_CLASS (class); + object_class->finalize = plugin_lib_finalize; + + class->construct = plugin_lib_construct; + class->invoke = plugin_lib_invoke; + class->get_symbol = plugin_lib_get_symbol; + class->enable = plugin_lib_enable; + class->get_configure_widget = plugin_lib_get_configure_widget; + class->type = "shlib"; +} + +GType +e_plugin_lib_get_type (void) +{ + return plugin_lib_type; +} + +void +e_plugin_lib_register_type (GTypeModule *type_module) +{ + static const GTypeInfo type_info = { + sizeof (EPluginLibClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) plugin_lib_class_init, + (GClassFinalizeFunc) NULL, + NULL, /* class_data */ + sizeof (EPluginLib), + 0, /* n_preallocs */ + (GInstanceInitFunc) NULL, + NULL /* value_table */ + }; + + plugin_lib_type = g_type_module_register_type ( + type_module, E_TYPE_PLUGIN, + "EPluginLib", &type_info, 0); +} diff --git a/modules/plugin-lib/e-plugin-lib.h b/modules/plugin-lib/e-plugin-lib.h new file mode 100644 index 0000000000..b485d9625e --- /dev/null +++ b/modules/plugin-lib/e-plugin-lib.h @@ -0,0 +1,92 @@ +/* + * e-plugin-lib.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifndef E_PLUGIN_LIB_H +#define E_PLUGIN_LIB_H + +#include +#include + +/* Standard GObject macros */ +#define E_TYPE_PLUGIN_LIB \ + (e_plugin_lib_get_type ()) +#define E_PLUGIN_LIB(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_PLUGIN_LIB, EPluginLib)) +#define E_PLUGIN_LIB_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_PLUGIN_LIB, EPluginLibClass)) +#define E_IS_PLUGIN_LIB(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_PLUGIN_LIB)) +#define E_IS_PLUGIN_LIB_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_PLUGIN_LIB)) +#define E_PLUGIN_LIB_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_PLUGIN_LIB, EPluginLibClass)) + +G_BEGIN_DECLS + +typedef struct _EPluginLib EPluginLib; +typedef struct _EPluginLibClass EPluginLibClass; + +/* The callback signature used for epluginlib methods */ +typedef gpointer (*EPluginLibFunc) (EPluginLib *ep, gpointer data); + +/* The setup method, this will be called when the plugin is + * initialized. In the future it may also be called when the plugin + * is disabled. */ +typedef gint (*EPluginLibEnableFunc) (EPluginLib *ep, gint enable); + +typedef gpointer (*EPluginLibGetConfigureWidgetFunc) (EPluginLib *ep); + +/** + * struct _EPluginLib - + * + * @plugin: Superclass. + * @location: The filename of the shared object. + * @module: The GModule once it is loaded. + * + * This is a concrete EPlugin class. It loads and invokes dynamically + * loaded libraries using GModule. The shared object isn't loaded + * until the first callback is invoked. + * + * When the plugin is loaded, and if it exists, "e_plugin_lib_enable" + * will be invoked to initialize the plugin. + **/ +struct _EPluginLib { + EPlugin parent; + + gchar *location; + GModule *module; +}; + +struct _EPluginLibClass { + EPluginClass parent_class; +}; + +GType e_plugin_lib_get_type (void); +void e_plugin_lib_register_type (GTypeModule *type_module); + +G_END_DECLS + +#endif /* E_PLUGIN_LIB_H */ diff --git a/modules/plugin-lib/evolution-module-plugin-lib.c b/modules/plugin-lib/evolution-module-plugin-lib.c new file mode 100644 index 0000000000..833ca5906d --- /dev/null +++ b/modules/plugin-lib/evolution-module-plugin-lib.c @@ -0,0 +1,41 @@ +/* + * evolution-module-plugin-lib.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#include + +#include "e-plugin-lib.h" + +/* Module Entry Points */ +void e_module_load (GTypeModule *type_module); +void e_module_unload (GTypeModule *type_module); + +G_MODULE_EXPORT void +e_module_load (GTypeModule *type_module) +{ + /* Register dynamically loaded types. */ + + e_plugin_lib_register_type (type_module); +} + +G_MODULE_EXPORT void +e_module_unload (GTypeModule *type_module) +{ +} -- cgit v1.2.3