[Geany-Devel] New plugin loader mechanisms

Lex Trotman elextr at xxxxx
Wed Mar 18 21:23:34 UTC 2015

Hi Thomas,

In general this looks like an improvement to the current method of
setting up plugins, except it continues to require the plugin .so file
to be dlloaded just to populate the plugin manager.  That will run the
dll initialization code, and may therefore break Geany.  Have you
thought about having a separate plain data file with the data for the
plugin manager so the dll doesn't need loading until its activated by
the PM?

A few specific things in-line below.


On 19 March 2015 at 02:42, Thomas Martitz <kugel at rockbox.org> wrote:
> Hello,
> tl;dr -> scroll down
> I am working on a new plugin architecture that deals with some of the
> shortcomings of the current state. My primary motivation is to be able to
> use libpeas to load plugins, both C and non-C (Python!), as you might have
> learned from other threads I started. However the situation can be improved
> regardless of that goal.
> List of current shortcomings:
>  - (A separate change but nevertheless: ) Currently geany exports a pointer
> to a struct, that contains more structs, which contain function points to
> the API functions. Fortunately this is nicely hidden to developers via
> macros. But due to gtkbuilder all functions and nothing prevents plugins
> from accessing these. And the macros are awkward and strange anyway. There
> is currently the linkage-cleanup PR in the works which improves this by
> actually exporting the API functions, and _only_ the API functions to
> plugins.
>  - Global symbols. Plugins binaries have to export a number of global
> symbols (geany_{functions,data,plugin}, plugin_{init,...,cleanup}). This
> kind of sucks, because they pollute the global namespace (in theory).
> Luckily on unix or win32 systems this is not a problem because they can
> restrict the symbol visibility of shared libraries. It's still bad practice.
> Ideally plugins should have zero global symbols, everything being static or
> hidden to the plugin binary.
>  - The plugin entry points / callbacks are inconsistent w.r.t to the
> parameters they receive, and none receive some kind of a plugin handle
> referencing to the plugin itself (there is only the geany_plugin global).
>  - The plugin entry points / callbacks do not allow for the plugin associate
> private/user data with the plugin handle, except hand-maintain hash tables.
> This is not a problem for the most part because it can be stored in some
> plugin-wide static variable, however it does become problematic when you
> attempt to have one plugin act as a proxy for other plugins (see geanypy or
> my pluxy effort)
>  - The plugin does the ABI/API verification. We currently trust the plugins
> to use PLUGIN_VERSION_CHECK() or otherwise implement plugin_version_check()
> correctly. Plugins decide whether they are api/abi compatible with geany.
> Pure crazyness!
>  - Plugins cannot register plugins recursively. It would be awesome if a
> plugin could register a plugin on behalf of others, in such a manner that
> they appear in the PM dialog and can be handled by the user like normal
> plugins (*proper* proxy plugins are not possible).
> To improve the situation I propose the following mechaism and new plugin
> hooks:
> tl;dr <-
> Key functions
> gboolean geany_load_module(GeanyPlugin *, GModule *)
> gboolean geany_plugin_register(GeanyPlugin *, gint api, gint abi,
> PluginHooks *(see below), gpointer)
> The plugin defines a single global function, geany_load_module(GeanyPlugin
> *, GModule *). This is the only function that geany learns about using
> g_module_symbol().

Ok, the number of symbols went from 5-6 to 1 but we still have the
same name exported from each dll, so the original problem of symbols
still exists.  I don't think there is a solution really.

> And the only thing this function ought to do is to call
> geany_plugin_register(). This does 4 things
> 1) Provide the plugin handle to the plugin very early
> 2) Perform abi and abi checks, so this is finally done inside geany. Added
> bonus is that geany knows the requested api and can possibly apply
> backcompat workarounds on a per plugin basis (instead of globally), warn
> about very old plugins or even deny loading them.

This precludes the plugin from adapting itself to Geany since the
plugin is not supplied with the API and ABI numbers.  It would be good
if a plugin could do simple things like:

    if (ABI > 124) f1() else f2();

Also it would be good to consider the semantics of API and ABI since
there are some conditions where we cannot express a change in the
interface except for the hammer of an ABI break.

> 3) Register the remaining hooks and callbacks (see below)
> 4) Associate a userdata pointer to the plugin, geany will pass this pointer
> back to future calls into the plugin (good for proxies)

Good for lots of things :)

> In the future geany_plugin_register should be able to be used to register
> plugins recursivly, by passing the appropriate GeanyPlugin pointer, i.e.
> plugin A should be able to call geany_plugin_register(plugin_B, ...) to
> realize pluxies.
> Now to the plugin hooks:
> typedef struct _PluginHooks
> {
>     PluginCallback *callbacks;
>     void        (*set_info)  (GeanyPlugin *plugin, gpointer pdata);
>     void        (*init)      (GeanyPlugin *plugin, gpointer pdata);
>     GtkWidget*  (*configure) (GeanyPlugin *plugin, GtkDialog *dialog,
> gpointer pdata);
>     void        (*help)      (GeanyPlugin *plugin, gpointer pdata);
>     void        (*cleanup)   (GeanyPlugin *plugin, gpointer pdata);
> }
> PluginHooks;

Doing it this way also makes it easier to add extra functions or
versions with different prototypes.

> These are analogous and semantically equivalent to the current functions.
> The only difference is the parameters that are passed to them, and of course
> how they are registed. Additionally the existing PluginCallbacks pointer
> registered here for all plugins that want statically defined signal
> handlers.
> There is no version_check() hook because that's now done inside geany, and
> the long deprecated configure_single is phased out. Please also note that
> each hook receives the GeanyPlugin pointer and the userdata pointer, these
> are the same passed to geany_plugin_register.
> With this new mechanism, proxy plugins are more than doable. geanypy should
> benifit as well.
> I have implemented this so far and adapted the demo plugin. The code is
> at[1]. Please have a look and/or comment on my proposal or contribute your
> ideas. Thanks!
> [1] https://github.com/kugel-/geany/tree/new-plugins
> Best regards
> _______________________________________________
> Devel mailing list
> Devel at lists.geany.org
> https://lists.geany.org/cgi-bin/mailman/listinfo/devel

More information about the Devel mailing list