diff --git a/gplugin-introspection/gplugin-introspection.h b/gplugin-introspection/gplugin-introspection.h
new file mode 100644
--- /dev/null
+++ b/gplugin-introspection/gplugin-introspection.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2011-2023 Gary Kramlich <grim@reaperworld.com>
+ *
+ * This library 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) any later version.
+ *
+ * This library 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 this library; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef GPLUGIN_INTROSPECTION_H
+#define GPLUGIN_INTROSPECTION_H
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+int gplugin_introspection_introspect_plugin(
+	int *argc,
+	char ***argv,
+	const char *filename);
+int gplugin_introspection_introspect_plugins(int *argc, char ***argv, ...)
+	G_GNUC_NULL_TERMINATED;
+
+G_END_DECLS
+
+#endif /* GPLUGIN_INTROSPECTION_H */
# HG changeset patch
# Node ID afd7944332ce
# Parent  7371e0ba5b32
diff --git a/gplugin-introspection/gplugin-introspection.c b/gplugin-introspection/gplugin-introspection.c
new file mode 100644
--- /dev/null
+++ b/gplugin-introspection/gplugin-introspection.c
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2011-2023 Gary Kramlich <grim@reaperworld.com>
+ *
+ * This library 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) any later version.
+ *
+ * This library 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 this library; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+
+#include <gio/gio.h>
+
+#include <gplugin.h>
+#include <gplugin-native.h>
+
+#include <girepository.h>
+
+#include "gplugin-introspection.h"
+
+/******************************************************************************
+ * Helpers
+ *****************************************************************************/
+static char *
+gplugin_introspection_get_ir_dump_directory(
+	int *argc,
+	char ***argv,
+	GError **error)
+{
+	GOptionContext *ctx = NULL;
+	char *irdump = NULL;
+	GOptionEntry entries[] = {
+		{"introspect-dump", 'i', 0, G_OPTION_ARG_STRING, &irdump, NULL, NULL},
+		{NULL}};
+
+	ctx = g_option_context_new("");
+	g_option_context_set_summary(
+		ctx,
+		_("Loads plugins so they can be scanned by g-ir-scanner"));
+	g_option_context_set_translation_domain(ctx, GETTEXT_PACKAGE);
+	g_option_context_add_main_entries(ctx, entries, GETTEXT_PACKAGE);
+
+	g_option_context_parse(ctx, argc, argv, error);
+	g_clear_pointer(&ctx, g_option_context_free);
+
+	return irdump;
+}
+
+static GPluginPlugin *
+gplugin_introspection_query_and_load_plugin(
+	GPluginLoader *loader,
+	const char *filename,
+	GError **error)
+{
+	GPluginPlugin *plugin = NULL;
+	GError *local_error = NULL;
+	gboolean loaded = FALSE;
+
+	plugin = gplugin_loader_query_plugin(loader, filename, &local_error);
+	if(local_error != NULL) {
+		g_clear_object(&plugin);
+
+		g_propagate_error(error, local_error);
+
+		return NULL;
+	}
+
+	if(!GPLUGIN_IS_PLUGIN(plugin)) {
+		g_set_error_literal(
+			error,
+			GPLUGIN_DOMAIN,
+			0,
+			"plugin unexpectedly is invalid");
+
+		return NULL;
+	}
+
+	loaded = gplugin_loader_load_plugin(loader, plugin, &local_error);
+	if(local_error != NULL) {
+		g_clear_object(&plugin);
+
+		g_propagate_error(error, local_error);
+
+		return NULL;
+	}
+
+	if(!loaded) {
+		g_set_error(
+			error,
+			GPLUGIN_DOMAIN,
+			0,
+			_("Failed to load plugin %s: unknown error"),
+			filename);
+
+		g_clear_object(&plugin);
+
+		return NULL;
+	}
+
+	return plugin;
+}
+
+static void
+gplugin_introspection_introspect_unload_plugins(
+	GPluginLoader *loader,
+	GListModel *plugins)
+{
+	for(guint i = 0; i < g_list_model_get_n_items(plugins); i++) {
+		GPluginPlugin *plugin = NULL;
+
+		plugin = g_list_model_get_item(plugins, i);
+
+		gplugin_loader_unload_plugin(loader, plugin, TRUE, NULL);
+
+		g_clear_object(&plugin);
+	}
+}
+
+/******************************************************************************
+ * Public API
+ *****************************************************************************/
+int
+gplugin_introspection_introspect_plugin(
+	int *argc,
+	char ***argv,
+	const char *filename)
+{
+	return gplugin_introspection_introspect_plugins(argc, argv, filename, NULL);
+}
+
+int
+gplugin_introspection_introspect_plugins(int *argc, char ***argv, ...)
+{
+	GPluginLoader *loader = NULL;
+	GError *error = NULL;
+	GListStore *plugins = NULL;
+	char *irdump = NULL;
+	const char *filename = NULL;
+	va_list args;
+
+	irdump = gplugin_introspection_get_ir_dump_directory(argc, argv, &error);
+	if(error != NULL) {
+		g_print(
+			_("Failed to parse command line arguments: %s\n"),
+			error->message);
+		g_free(irdump);
+
+		return 1;
+	}
+
+	if(irdump == NULL || irdump[0] == '\0') {
+		g_print(_("The introspect-dump option was not provided. This tool is"
+				  "only meant to be called by g-ir-scanner directly."));
+
+		return 1;
+	}
+
+	/* Create an instance of the native loader to load the plugins. */
+	loader = g_object_new(GPLUGIN_TYPE_NATIVE_LOADER, NULL);
+
+	/* Load our plugins. We store them in a GListStore so we can easily unload
+	 * and unref them later. We can't use GPLUGIN_TYPE_PLUGIN as it's an
+	 * interface, so just force it down to GObject to make it work.
+	 */
+	plugins = g_list_store_new(G_TYPE_OBJECT);
+
+	va_start(args, argv);
+	while((filename = va_arg(args, const char *)) != NULL) {
+		GPluginPlugin *plugin = NULL;
+
+		plugin = gplugin_introspection_query_and_load_plugin(
+			loader,
+			filename,
+			&error);
+		if(error != NULL) {
+			g_print(
+				_("Failed to query or load plugin %s: %s\n"),
+				filename,
+				error->message);
+
+			gplugin_introspection_introspect_unload_plugins(
+				loader,
+				G_LIST_MODEL(plugins));
+			g_clear_object(&plugins);
+
+			g_free(irdump);
+			g_clear_object(&plugin);
+			g_clear_object(&loader);
+
+			return 1;
+		}
+
+		g_list_store_append(plugins, plugin);
+
+		/* Remove our reference to the plugin as we don't need it any more. */
+		g_clear_object(&plugin);
+	}
+	va_end(args);
+
+	/* Now that all of the plugins have been loaded, run the scanner. */
+	if(!g_irepository_dump(irdump, &error)) {
+		g_print("g_irepository_dump() failed: %s\n", error->message);
+
+		g_free(irdump);
+		gplugin_introspection_introspect_unload_plugins(
+			loader,
+			G_LIST_MODEL(plugins));
+		g_clear_object(&plugins);
+
+		g_clear_object(&loader);
+
+		return 1;
+	}
+
+	gplugin_introspection_introspect_unload_plugins(
+		loader,
+		G_LIST_MODEL(plugins));
+	g_clear_object(&plugins);
+	g_clear_object(&loader);
+	g_free(irdump);
+
+	return 0;
+}
diff --git a/gplugin-introspection/meson.build b/gplugin-introspection/meson.build
new file mode 100644
--- /dev/null
+++ b/gplugin-introspection/meson.build
@@ -0,0 +1,44 @@
+if not get_option('introspection')
+	subdir_done()
+endif
+
+SOURCES = [
+	'gplugin-introspection.c',
+]
+
+HEADERS = [
+	'gplugin-introspection.h',
+]
+
+GOBJECT_INTROSPECTION = dependency('gobject-introspection-1.0', version : '>=1.0.0')
+
+gplugin_introspection_inc = include_directories('.')
+
+gplugin_introspection = static_library(
+	'gplugin-introspection',
+	sources : SOURCES + HEADERS,
+	dependencies : [gplugin_dep, GIO, GLIB, GOBJECT, GOBJECT_INTROSPECTION],
+	install : true,
+)
+
+gplugin_introspection_dep = declare_dependency(
+	include_directories: [toplevel_inc, gplugin_introspection_inc],
+	link_with : [gplugin, gplugin_introspection],
+	sources : SOURCES,
+	dependencies : [GLIB, GOBJECT, GOBJECT_INTROSPECTION],
+)
+
+meson.override_dependency('gplugin-introspection', gplugin_introspection_dep)
+
+pkgconfig.generate(
+	gplugin_introspection,
+	name : 'gplugin-introspection',
+	description : 'A helper library for glugin native plugins and gobject introspection',
+	filebase : 'gplugin-introspection',
+	libraries : [gplugin],
+	requires : [GLIB, GOBJECT, GMODULE],
+)
+
+install_headers(HEADERS, subdir : 'gplugin-1.0')
+
+subdir('tests')
diff --git a/gplugin-introspection/tests/generate.c b/gplugin-introspection/tests/generate.c
new file mode 100644
--- /dev/null
+++ b/gplugin-introspection/tests/generate.c
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2011-2023 Gary Kramlich <grim@reaperworld.com>
+ *
+ * This library 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) any later version.
+ *
+ * This library 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 this library; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+
+#include <gplugin-introspection.h>
+
+int
+main(int argc, char *argv[])
+{
+	return gplugin_introspection_introspect_plugin(
+		&argc,
+		&argv,
+		PLUGIN_FILENAME);
+}
diff --git a/gplugin-introspection/tests/introspectablecore.c b/gplugin-introspection/tests/introspectablecore.c
new file mode 100644
--- /dev/null
+++ b/gplugin-introspection/tests/introspectablecore.c
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2011-2023 Gary Kramlich <grim@reaperworld.com>
+ *
+ * This library 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) any later version.
+ *
+ * This library 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 this library; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+
+#include <gplugin.h>
+#include <gplugin-native.h>
+
+#include "introspectabletype.h"
+
+static GPluginPluginInfo *
+introspectable_query(G_GNUC_UNUSED GError **error)
+{
+	return gplugin_plugin_info_new(
+		"test-gplugin/introspection",
+		GPLUGIN_NATIVE_PLUGIN_ABI_VERSION,
+		"bind-global",
+		TRUE,
+		NULL);
+}
+
+static gboolean
+introspectable_load(GPluginPlugin *plugin, G_GNUC_UNUSED GError **error)
+{
+	introspectable_type_register(G_TYPE_MODULE(plugin));
+
+	return TRUE;
+}
+
+static gboolean
+introspectable_unload(
+	G_GNUC_UNUSED GPluginPlugin *plugin,
+	G_GNUC_UNUSED gboolean shutdown,
+	G_GNUC_UNUSED GError **error)
+{
+	return TRUE;
+}
+
+GPLUGIN_NATIVE_PLUGIN_DECLARE(introspectable)
\ No newline at end of file
diff --git a/gplugin-introspection/tests/introspectabletype.h b/gplugin-introspection/tests/introspectabletype.h
new file mode 100644
--- /dev/null
+++ b/gplugin-introspection/tests/introspectabletype.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2011-2023 Gary Kramlich <grim@reaperworld.com>
+ *
+ * This library 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) any later version.
+ *
+ * This library 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 this library; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef INTROSPECTABLE_TYPE_H
+#define INTROSPECTABLE_TYPE_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define INTROSPECTABLE_TYPE_TYPE (introspectable_type_get_type())
+G_DECLARE_FINAL_TYPE(
+	IntrospectableType,
+	introspectable_type,
+	INTROSPECTABLE,
+	TYPE,
+	GObject)
+
+G_GNUC_INTERNAL void introspectable_type_register(GTypeModule *module);
+
+G_END_DECLS
+
+#endif /* INTROSPECTABLE_TYPE_H */
diff --git a/gplugin-introspection/tests/introspectabletype.c b/gplugin-introspection/tests/introspectabletype.c
new file mode 100644
--- /dev/null
+++ b/gplugin-introspection/tests/introspectabletype.c
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2011-2023 Gary Kramlich <grim@reaperworld.com>
+ *
+ * This library 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) any later version.
+ *
+ * This library 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 this library; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <gplugin.h>
+#include <gplugin-native.h>
+
+#include "introspectabletype.h"
+
+enum {
+	PROP_0,
+	PROP_DUMMY,
+	N_PROPERTIES,
+};
+static GParamSpec *properties[N_PROPERTIES] = {
+	NULL,
+};
+
+struct _IntrospectableType {
+	GObject gparent;
+
+	gboolean dummy;
+};
+
+G_DEFINE_DYNAMIC_TYPE(IntrospectableType, introspectable_type, G_TYPE_OBJECT)
+
+static void
+introspectable_type_get_property(
+	GObject *obj,
+	guint param_id,
+	GValue *value,
+	GParamSpec *pspec)
+{
+	IntrospectableType *type = INTROSPECTABLE_TYPE(obj);
+
+	switch(param_id) {
+		case PROP_DUMMY:
+			g_value_set_boolean(value, type->dummy);
+			break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+	}
+}
+
+static void
+introspectable_type_set_property(
+	GObject *obj,
+	guint param_id,
+	const GValue *value,
+	GParamSpec *pspec)
+{
+	IntrospectableType *type = INTROSPECTABLE_TYPE(obj);
+
+	switch(param_id) {
+		case PROP_DUMMY:
+			type->dummy = g_value_get_boolean(value);
+			break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+	}
+}
+
+static void
+introspectable_type_init(G_GNUC_UNUSED IntrospectableType *inst)
+{
+}
+
+static void
+introspectable_type_class_finalize(G_GNUC_UNUSED IntrospectableTypeClass *klass)
+{
+}
+
+static void
+introspectable_type_class_init(IntrospectableTypeClass *klass)
+{
+	GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+
+	obj_class->get_property = introspectable_type_get_property;
+	obj_class->set_property = introspectable_type_set_property;
+
+	/**
+	 * IntrospectableType:dummy:
+	 *
+	 * If it's a dummy or not.
+	 */
+	properties[PROP_DUMMY] = g_param_spec_boolean(
+		"dummy",
+		"dummy",
+		"Whether or not this object is a dummy",
+		FALSE,
+		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+	g_object_class_install_properties(obj_class, N_PROPERTIES, properties);
+}
+
+void
+introspectable_type_register(GTypeModule *module)
+{
+	introspectable_type_register_type(module);
+}
diff --git a/gplugin-introspection/tests/meson.build b/gplugin-introspection/tests/meson.build
new file mode 100644
--- /dev/null
+++ b/gplugin-introspection/tests/meson.build
@@ -0,0 +1,34 @@
+SOURCES = [
+	'introspectablecore.c',
+	'introspectabletype.c',
+	'introspectabletype.h',
+]
+
+introspectable = shared_module(
+	'introspectable',
+	SOURCES,
+	name_prefix : '',
+	dependencies : [gplugin_dep, GLIB, GOBJECT])
+
+if get_option('introspection')
+	GIR = dependency('gobject-introspection-1.0', version : '>=1.0.0')
+
+	plugin_path = introspectable.full_path()
+
+	exe_introspectable = executable('introspectable',
+		sources : 'generate.c',
+		dependencies : [gplugin_dep, gplugin_introspection_dep, GLIB, GOBJECT, GIR],
+		c_args : [f'-DPLUGIN_FILENAME="@plugin_path@"'],
+		install : false)
+
+	introspectable_gir = gnome.generate_gir(
+		exe_introspectable,
+		sources : SOURCES,
+		includes : ['GObject-2.0', gplugin_gir[0]],
+		namespace : 'Introspectable',
+		symbol_prefix : 'introspectable',
+		nsversion : '1.0',
+		install : false,
+		export_packages : ['introspectable'],
+		extra_args : ['--quiet'])
+endif
diff --git a/gplugin/gplugin-native-plugin.h b/gplugin/gplugin-native-plugin.h
--- a/gplugin/gplugin-native-plugin.h
+++ b/gplugin/gplugin-native-plugin.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011-2021 Gary Kramlich <grim@reaperworld.com>
+ * Copyright (C) 2011-2023 Gary Kramlich <grim@reaperworld.com>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
diff --git a/gplugin/reference/extendable-plugins.md b/gplugin/reference/extendable-plugins.md
new file mode 100644
--- /dev/null
+++ b/gplugin/reference/extendable-plugins.md
@@ -0,0 +1,113 @@
+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.
\ No newline at end of file
diff --git a/gplugin/reference/gplugin.toml.in b/gplugin/reference/gplugin.toml.in
--- a/gplugin/reference/gplugin.toml.in
+++ b/gplugin/reference/gplugin.toml.in
@@ -38,6 +38,7 @@
 # The same order will be used when generating the index
 content_files = [
   "embedding.md",
+  "extendable-plugins.md",
   "genie.md",
   "lua.md",
   "native-plugins.md",
diff --git a/gplugin/reference/meson.build b/gplugin/reference/meson.build
--- a/gplugin/reference/meson.build
+++ b/gplugin/reference/meson.build
@@ -4,6 +4,7 @@
 
 gplugin_doc_content_files = [
 	'embedding.md',
+	'extendable-plugins.md',
 	'genie.md',
 	'lua.md',
 	'native-plugins.md',
diff --git a/meson.build b/meson.build
--- a/meson.build
+++ b/meson.build
@@ -35,6 +35,7 @@
 endif
 
 GLIB = dependency('glib-2.0', version : '>=2.70.0')
+GIO = dependency('gio-2.0')
 GOBJECT = dependency('gobject-2.0')
 
 # we separate gmodule out so our test aren't linked to it
@@ -119,6 +120,7 @@
 # Subdirectories
 ###############################################################################
 subdir('gplugin')
+subdir('gplugin-introspection')
 subdir('gplugin-gtk4')
 subdir('gplugin-gtk4-viewer', if_found: GTK4)
 subdir('gplugin-query')
