Flatpak as a developer platform¶
This sections contains an overview on how Flatpak can be used as part of a developer workflow.
CI Integration¶
Flatpak can be integrated with the CI pipelines and bundles can be produced as artifacts for quick testing or it can be exported to a Flatpak repository on a webserver to provide a Nightly Flatpak package.
The CI setup will vary based on the code host and the CI service being used. A basic workflow for exporting to Gitlab pages is provided in Hosting a repository which can be used as a starting point.
GNOME and KDE also has their own respective CI setup for doing this, which can be also be used for inspiration.
Flatpak Github Actions can be used for GitHub.
Running tests¶
Flatpak Builder can also run tests with run-tests, test-rule, test-commands, test-args
.
run-tests: true
in a module will run tests for that module after
installing.
test-rule
is the default target to build when running tests. For
meson
an cmake-ninja
buildsystems it defaults to test
,
otherwise check
for cmake
and autotools
. Setting it to an
empty string like test-rule: ''
will disable the target.
test-commands
is an array of additional commands that will be run
during tests.
run-tests: true
test-rule: ''
test-commands:
- xvfb-run tests/test_foo
test-args
is used to provide finish-args for tests. These do not
affect the normal installation.
build-options:
test-args:
- "--socket=x11"
- "--share=network"
Parallel nigthly and stable applications¶
In general stable and nightly versions or any two parallel branches of an application should have different application IDs. This prevents many potential conflicts such as incompatible configuration files or overlapping well-known D-Bus names. This is mandatory if the application uses its own well-known D-Bus name.
A standard way is to use the .Devel
suffix to the original Flatpak ID.
The internal application ID, appstream ID, launchable, icon tags,
icon files, desktop files, Dbus service names etc. must follow the new
Flatpak ID for everything to work seamlessly for the user.
This can be integrated with buildsystem profiles, so simply building
with -Dprofile=Devel
for example will build the Nightly application
and the default will build the stable appliction.
Buildsystem handling¶
This is an example for the Meson buildsystem.
The src
subdirectory will contain the main source code of the
application and data
subdirectory will have application metadata
such as desktop file, metainfo file, icons and the po
subdirectory
will have translation files.
The root meson.build
parts should be something like this:
i18n = import('i18n')
profile = get_option('profile')
if profile == 'development'
application_id = 'org.example.coolapp.Devel'
else
application_id = 'org.example.coolapp'
endif
config_h = configuration_data()
config_h.set_quoted('APPLICATION_ID', application_id)
config_h.set_quoted('PROFILE', profile)
config_h.set_quoted('GETTEXT_PACKAGE', 'coolapp')
config_h.set_quoted('PACKAGE_VERSION', meson.project_version())
configure_file(
output: 'config.h',
configuration: config_h,
)
configuration_inc = include_directories('.')
subdir('data')
subdir('src')
subdir('po')
summary({
'Profile': get_option('profile'),
}, section: 'Development')
The root meson-options.txt
should have something like:
option('profile', type: 'combo', choices: ['default', 'development'], value: 'default')
Now under src/meson.build
or in any subdirectory this can be
specified in a target.
coolapp = executable('coolapp', 'main.c',
include_directories: configuration_inc,
dependencies: libcoolapp_dep,
install: true,
)
Now in data/meson.build
, the icon, desktop file and metainfo file
handling can be specified.
Place a desktop template file called org.example.coolapp.desktop.in.in
in data/
with the contents
[Desktop Entry]
Name=Cool App
Exec=coolapp
Icon=@icon@
Terminal=false
Type=Application
Categories=Utility;
StartupNotify=true
The corresponding meson bits for the desktop file should be
desktop_conf = configuration_data()
desktop_conf.set('icon', application_id)
desktop_file = i18n.merge_file(
input: configure_file(
input: files('org.example.coolapp.desktop.in.in'),
output: 'org.example.coolapp.desktop.in',
configuration: desktop_conf,
),
output: '@0@.desktop'.format(application_id),
type: 'desktop',
po_dir: '../po',
install: true,
install_dir: get_option('datadir') / 'applications',
)
desktop_utils = find_program('desktop-file-validate', required: false)
if desktop_utils.found()
test('Validate desktop file', desktop_utils, args: [desktop_file])
endif
Appstream can be handled in the same meson.build
. Place a metainfo
template file in data/
. Any tag containing part of the application
ID should be templated. Ususally for desktop applications this involves
only the id
and launchable
tag, but the icon
tag, if present
should also use it.
<?xml version="1.0" encoding="UTF-8"?>
<component type="desktop">
<id>@appid@</id>
...
<launchable type="desktop-id">@appid@.desktop</launchable>
...
</component>
The corresponding meson.build part for metainfo should be
metainfo_conf = configuration_data()
metainfo_conf.set('appid', application_id)
appstream_file = i18n.merge_file(
input: configure_file(
input: files('org.example.coolapp.metainfo.xml.in.in'),
output: 'org.example.coolapp.metainfo.xml.in',
configuration: metainfo_conf
),
output: '@0@.metainfo.xml'.format(application_id),
po_dir: '../po',
install: true,
install_dir: get_option('datadir') / 'metainfo',
)
appstreamcli = find_program('appstreamcli', required: false)
if (appstreamcli.found())
test('Validate metainfo file',
appstreamcli,
args: ['validate', '--no-net', '--explain', appstream_file],
workdir: meson.current_build_dir()
)
endif
Finally icons can be placed in data/icons/hicolor/{scalable, symbolic}/apps
.
Two icons should be provided org.example.coolapp.svg
and org.example.coolapp.Devel.svg
.
The meson.build should have
icondir = join_paths('icons', 'hicolor', 'scalable', 'apps')
install_data(
join_paths(icondir, '@0@.svg'.format(application_id)),
install_dir: join_paths(datadir, icondir),
rename: '@0@.svg'.format(application_id)
)
icondir = join_paths('icons', 'hicolor', 'symbolic', 'apps')
install_data(
join_paths(icondir, 'org.example.coolapp-symbolic.svg'),
install_dir: join_paths(datadir, icondir),
rename: '@0@-symbolic.svg'.format(application_id)
)
Any gschemea file should also be similarly handled if present.
install_data('org.example.coolapp.gschema.xml',
rename: '@0@.gschema.xml'.format(application_id),
install_dir: get_option('datadir') / 'glib-2.0' / 'schemas',
)
Any dbus service file if present should be handled. The org.example.coolapp.service.in
should have Name=@appid@
service_conf = configuration_data()
service_conf.set('appid', application_id)
service_conf.set('bindir', join_paths(prefix, bindir))
configure_file(
input: 'org.example.coolapp.service.in',
output: '@0@.service'.format(application_id),
configuration: service_conf,
install_dir: servicedir
)
The same pattern can be used for search provider files if present.
The org.example.coolapp.service.ini.in
should look like
DesktopId=@appid@.desktop
BusName=@appid@
ObjectPath=/org/gnome/AppName@profile@/SearchProvider
and the corresponding meson.build:
search_provider_conf = configuration_data()
search_provider_conf.set('appid', application_id)
search_provider_conf.set('profile', profile)
configure_file(
configuration: search_provider_conf,
input: files('org.example.coolapp.service.ini.in'),
install_dir: join_paths(datadir, 'gnome-shell', 'search-providers'),
output: '@0@.search-provider.ini'.format(application_id)
)
Application handling¶
The correct application ID must be supplied when initiating the
application. For example in main.c
#include "config.h"
...
int
main (int argc, char **argv)
{
...
g_set_prgname (APPLICATION_ID);
gtk_window_set_default_icon_name (APPLICATION_ID);
coolapp = g_object_new (APP_NAME_TYPE_APPLICATION,
"application-id", APPLICATION_ID,
"flags", G_APPLICATION_HANDLES_OPEN,
NULL);
g_application_run (G_APPLICATION (coolapp), argc, argv);
return 0;
}
Similarly it can be handled in the about dialogue of the application and elsewhere.
Manifest handling¶
Finally there must be two manifests for Devel and stable - org.example.coolapp.Devel.yaml
and org.example.coolapp.yaml
respectively.
The manifest must have the correct id
property. The devel manifest
should use id: org.example.coolapp.Devel
and the stable manifest
should use id: org.example.coolapp
.
In config-opts
for the application module, -Dprofile=Devel
can be
passed to build the Devel application.
In case, buildsystem handling for desktop file, icon and metainfo file
is absent rename-desktop-file, rename-appdata-file, rename-mime-file, rename-icon
can be used rename the files to match the .Devel
application ID.
desktop-file-name-prefix
or desktop-file-name-suffix
can be used
to add a prefix or suffix to the desktop file name respectively.
id: org.example.coolapp.Devel
...
rename-desktop-file: org.example.coolapp.desktop
rename-appdata-file: org.example.coolapp.metainfo.xml
rename-icon: org.example.coolapp
desktop-file-name-suffix: ' (Nightly)'
The main drawback of modifying it in place with Flatpak Builder is that, the application ID, D-Bus names and configuration location cannot be changed. This might cause integration issues with desktop environment, potential configuration conflicts, or the application might not run in some cases when the D-Bus name is already in use. This would prevent running the stable and nightly applications parallely.
In case the application ID remains static, for GTK based applications
the --name
flag can be passed to the main binary with the correct
application ID; --class
and StartupWMClass
in the
desktop file to fix the window class.
Additional tools¶
Electron Builder supports exporting single file Flatpak bundles. Please also see the Electron application packaging guide Electron.
GNOME Builder is an IDE that can integrate with the development workflow of GNOME applications. Qt Creator and Qt Design Studio are both available as Flatpaks from Flathub along with a variety of IDEs.
Freedesktop SDK and GNOME Nightly hosts nightly versions of the
org.freedesktop.Platform
andorg.gnome.Platform
runtimes and SDKs.GNOME OS is an immutable Flatpak first system that can also be used to build and test applications on upcoming GNOME versions.
Freedesktop SDK also builds Mesa from the git main branch. Please see the docs on how to use that.
Flathub hosts extensions for lots of tooling and languages that can be used to build applications.