Title: Extendable Plugins
Slug: extendability

## Extendable Plugins

There are times when you will want to create a plugin that acts as a base for
another plugin. GPlugin accommodates this, but there are a number of things
that you as the author of an extendable plugin will have to do.

> **Note**: This tutorial is meant for authors of native plugins. While it is
> possible to extend non-native plugins, it is outside the scope of this
> document.

### Plugin Adjustments

The first and most important thing that needs to be done for this is to set the
[property@PluginInfo:bind-global] property to %TRUE for the [class@PluginInfo]
that you return from the `query` method of your plugin.

This allows other plugins to be able to see the symbols in your plugin. By
default, the symbols defined in plugins are not bound globally so other plugins
will not see them. This explicitly changes that behavior so that other plugins
can interact with your plugin.

> **Note**: This means all symbols from your plugin that are not static,
> including ones pulled in from libraries, will be visible to all other plugins
> and the application that GPlugin is being used in.
>
> If you have some symbols you would like to not expose you can prefix them
> with the `G_GNUC_INTERNAL` macro.

Once all of this is done there's nothing else that should need to happen to
your plugin code itself.

### Build System Adjustments

Just like when you build a shared library you will need to install your header
files in a location that dependent plugins are able to find them. This will of
course depend on your build system, but if you're using
[meson](https://mesonbuild.com/) you will want to look at the
[install_headers](https://mesonbuild.com/Reference-manual_functions.html#install_headers)
function.

Likewise you will also want to create a
[pkg-config](https://www.freedesktop.org/wiki/Software/pkg-config/) `pc` file
so that other plugins can find your header files easily. There's a
[module](https://mesonbuild.com/Pkgconfig-module.html) in meson that you can
use for this as well.

> **Note**: You do _NOT_ want to enter anything for the `libraries` field in
> the `pc` file. Doing so will link to the library at linking time instead of
> during runtime which is explicitly what you need. GPlugin will resolve this
> by loading all dependent plugins before loading the plugin that depends on
> them.

### GObject Introspection

You may also want to generate
[GObject Introspection](https://gi.readthedocs.io/) repository and type lib
files to allow plugins written in other languages to extend your plugin or to
create documentation with
[gi-docgen](https://gnome.pages.gitlab.gnome.org/gi-docgen/).

The native plugins have to register dynamic types which is incompatible with
how `g-ir-scanner` works when it is creating your gir and type lib files. To
make this work, there is a static library provided with GPlugin named
`gplugin-introspection`. You can use this static library to build an executable
that will be called by `g-ir-scanner` with everything setup for you. You should
be able to find it using pkg-config in your build system.

In most cases you'll just need to load a single plugin, and you can do that via
`gplugin_introspection_introspect_plugin()`. You'll need to pass in your
`argc` and `argv` for argument handling as well as the filename of the plugin
you want to load. In this example we pass `PLUGIN_FILENAME` in from the build
system as that knows the correct filename for the platform that we are building
for.

```c
#include <glib.h>

#include <gplugin-introspection.h>

int
main(int argc, char *argv[]) {
	return gplugin_introspection_introspect_plugin(&argc, &argv,
	                                               PLUGIN_FILENAME);
}
```

If for some reason you need to load multiple plugins you can use
`gplugin_introspection_introspect_plugins()` instead. This works the same as
`gplugin_introspection_introspect_plugin()` except it takes multiple plugin
filenames that must be terminated by a %NULL.

```c
#include <glib.h>

#include <gplugin-introspection.h>

int
main(int argc, char *argv[]) {
	return gplugin_introspection_introspect_plugins(&argc, &argv,
	                                                PLUGIN1_FILENAME,
	                                                PLUGIN2_FILENAME,
	                                                NULL);
}
```

Once this is all setup, you'll need to tell `g-ir-scanner` to use this
executable. In meson, this is done by passing the executable target as the
first argument to `gnome.generate_gir`. If you are calling `g-ir-scanner`
directly, you will need to use the `--program` option instead of the
`--library` option.