Flatpaking application plugins
Sometimes you simply do not want to bundle everything in a single package such as optional plugins with large dependencies or third party plugins that are not supported. In this post I’ll show you how to handle this with Flatpak using HexChat as an example.
Flatpak has a feature called extensions that allows a package to be mounted within another package. This is used in a variety of ways but it can be used by any application as a way to insert any optional bits. So lets see how to define one (details omitted for brevity):
{
"app-id": "io.github.Hexchat",
"add-extensions": {
"io.github.Hexchat.Plugin": {
"version": "20.08",
"directory": "extensions",
"add-ld-path": "lib",
"merge-dirs": "lib/hexchat/plugins",
"subdirectories": true,
"no-autodownload": true,
"autodelete": true
}
},
"modules": [
{
"name": "hexchat",
"post-install": [
"install -d /app/extensions"
]
}
]
}
The exact details of these are best documented in the Extension section of man flatpak-metadata
but I’ll go over the ones used here:
io.github.Hexchat.Pluginis the name of the extension point and all extensions will have the same prefix.versionallows you to have parallel installations of extensions if you break ABI or API for example,20.08refers to the runtime used as each runtime is a major ABI break.directorysets a subdirectory where everything is mounted relative to your prefix so/app/extensionsis where they will go.subdirectoriesallows you to have multiple extensions and each one will get their own subdirectory. Soio.github.Hexchat.Plugin.Perlis mounted at/app/extensions/Perl.merge-dirswill merge the contents of subdirectories that match these paths (relative to their prefix). So for this case the contents of/app/extensions/Perl/lib/hexchat/pluginsand/app/extensions/Python/lib/hexchat/pluginswill both be in/app/extensions/lib/hexchat/plugins. This allows limiting the complexity of your loader to only need to look in one directory (applications will need to be configured/patched to look there).add-ld-pathadds a path, relative to extensions prefix, to the library path so for example/app/extensions/Python/lib/libpython.socan be loaded.no-autodownloadwill not automatically install all extensions which is the default.autodeletewill remove all extensions when the application is removed.
So now that we defined an extension point lets make an extension:
{
"id": "io.github.Hexchat.Plugin.Perl",
"branch": "20.08",
"runtime": "io.github.Hexchat",
"runtime-version": "stable",
"sdk": "org.gnome.Sdk//42",
"build-extension": true,
"separate-locales": false,
"appstream-compose": false,
"build-options": {
"prefix": "/app/extensions/Perl",
"env": {
"PATH": "/app/extensions/Perl/bin:/app/bin:/usr/bin"
}
},
"modules": [
{
"name": "perl"
},
{
"name": "hexchat-perl",
"post-install": [
"install -Dm644 plugins/perl/perl.so ${FLATPAK_DEST}/lib/hexchat/plugins/perl.so",
"install -Dm644 --target-directory=${FLATPAK_DEST}/share/metainfo data/misc/io.github.Hexchat.Plugin.Perl.metainfo.xml",
"appstream-compose --basename=io.github.Hexchat.Plugin.Perl --prefix=${FLATPAK_DEST} --origin=flatpak io.github.Hexchat.Plugin.Perl"
]
}
]
}
So again going over some key points quickly: id has the correct prefix, branch refers to the extension version,
build-extension should be obvious, runtime is what defines the extension-point. Some less obvious things to make note of is that your extensions prefix will not be in $PATH or $PKG_CONFIG_PATH by default
so you may need to set them (see build-options in man flatpak-manifest). $FLATPAK_DEST is also
defined as your extensions prefix though not everything expands variables.
While not required you also should install appstream metainfo for easy discover-ability. For example:
<?xml version="1.0" encoding="UTF-8"?>
<component type="addon">
<id>io.github.Hexchat.Plugin.Perl</id>
<extends>io.github.Hexchat</extends>
<name>Perl Plugin</name>
<summary>Provides a scripting interface in Perl</summary>
<url type="homepage">https://hexchat.github.io/</url>
<project_license>GPL-2.0+</project_license>
<metadata_license>CC0-1.0</metadata_license>
<update_contact>tingping_AT_fedoraproject.org</update_contact>
</component>
Which will be shown in GNOME-Software:
