Packaging the App

Because we used a build system from the beginning, that took care of installing all the files at the correct places, packaging the app is really easy. We only have to add the necessary metadata so that it can be installed and launched from a graphical environment.

# Application Icon

The icon is what makes our application immediately recognizable by our users. It has to convey the function of the app, be visually appealing and yet be simple. To help with that, the GNOME Human Interface Guidelines have a guide for drawing such an icon.

We provide a full color scalable version, in data/icons/org.example.filebrowser.svg:

1<?xml version="1.0" encoding="UTF-8"?>
2<svg width="128" height="128" version="1.1" viewBox="0 0 128 128" xmlns="http://www.w3.org/2000/svg">
3	<rect x="24" y="8" width="80" height="112" rx="8" fill="#2b2b2b"/>
4	<rect x="24" y="8" width="80" height="104" rx="8" fill="#4f4f4f"/>
5	<path d="m64 36 1.8369 19.565 15.134-12.536-12.536 15.134 19.565 1.8369-19.565 1.8369 12.536 15.134-15.134-12.536-1.8369 19.565-1.8369-19.565-15.134 12.536 12.536-15.134-19.565-1.8369 19.565-1.8369-12.536-15.134 15.134 12.536z" fill="#c7c7c7"/>
6	<path d="m57.072 56 26.928-30.641-13.072 38.641z" fill="#c5245d"/>
7	<path d="m70.928 64-26.928 30.641 13.072-38.641z" fill="#fff"/>
8</svg>

As well as a symbolic version that the desktop environment may use if it needs a monochrome or high contrast icon, in data/icons/org.example.filebrowser-symbolic.svg:

1<?xml version="1.0" encoding="UTF-8"?>
2<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
3	<circle cx="8" cy="8" r="6" opacity=".5"/>
4	<path d="m6.2679 7.0002 5.7321-5.9282-2.2679 7.9282-5.7321 5.9282z"/>
5</svg>

Because these icons need to be accessible from outside the application, we can’t ship them in the resources. Instead, we have to install them on the system, in a well-known place: the apps category of the hicolor icon theme, following the Icon Theme specification.

In data/meson.build:

16# Install the application icons
17install_data(
18	'icons' / APP_ID + '.svg',
19	install_dir: get_option('datadir') / 'icons' / 'hicolor' / 'scalable' / 'apps'
20)
21install_data(
22	'icons' / APP_ID + '-symbolic.svg',
23	install_dir: get_option('datadir') / 'icons' / 'hicolor' / 'symbolic' / 'apps'
24)

And in meson.build, we tell the build system to update the icon cache after installation:

21gnome.post_install(
22	# ...
23	gtk_update_icon_cache: true
24)

# Desktop Launcher

Until now, we have run our application with a command line. That is not ideal for regular users, it would be better if they could graphically launch the application with a shortcut. Fortunately, that’s what the Desktop Entry Specification is about.

We have to write two files: one for the D-Bus service that will allow our application to be activated, and a second for presenting a shortcut to the user in their desktop environment.

Let’s start with the service file. In data/org.example.filebrowser.service, we put:

1[D-BUS Service]
2Name=@APP_ID@
3Exec=@BINDIR@/@APP_ID@ --gapplication-service

The Name field is the bus name that the application will own, the Exec field is the command that will be run. It will start the application in a special service mode, waiting to be activated.

Now, for the desktop file. In data/org.example.filebrowser.desktop, we put:

 1[Desktop Entry]
 2Name=File Browser
 3Comment=Browse files on your system
 4Icon=@APP_ID@
 5Exec=@BINDIR@/@APP_ID@
 6Type=Application
 7DBusActivatable=true
 8Categories=System;FileManager;
 9# Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon!
10Keywords=File;Browser;

The Name, Comment and Keywords fields are translatable, so don’t forget to add this file to po/POTFILES.

Now, in data/meson.build, we have to tell the build system where to install these files, as well as replace the placeholder values and translate the desktop file:

26# Translate and install the desktop file
27i18n.merge_file(
28	type: 'desktop',
29	input: configure_file(
30		input: APP_ID + '.desktop',
31		output: APP_ID + '.desktop.in',
32		configuration: {
33			'APP_ID': APP_ID,
34			'BINDIR': get_option('prefix') / get_option('bindir')
35		}
36	),
37	output: APP_ID + '.desktop',
38	po_dir: meson.project_source_root() / 'po',
39	install: true,
40	install_dir: get_option('datadir') / 'applications'
41)
42
43# Install the D-Bus service file
44configure_file(
45	input: APP_ID + '.service',
46	output: APP_ID + '.service',
47	configuration: {
48		'APP_ID': APP_ID,
49		'BINDIR': get_option('prefix') / get_option('bindir')
50	},
51	install: true,
52	install_dir: get_option('datadir') / 'dbus-1' / 'services'
53)

And in meson.build, we tell the build system to update the desktop database after installation:

21gnome.post_install(
22	# ...
23	update_desktop_database: true
24)

# AppStream Metadata

Our future users need a way of finding the application in their application store. For that, we have to provide several metadata, following the AppStream specification.

In data/org.example.filebrowser.metainfo.xml, we put this minimal metadata:

 1<?xml version="1.0" encoding="UTF-8"?>
 2<component type="desktop-application">
 3	<id>@APP_ID@</id>
 4
 5	<name>File Browser</name>
 6	<summary>Browse files on your system</summary>
 7	<description>
 8		<p>A wonderful application designed to use a lot of GTK4 features.</p>
 9	</description>
10
11	<metadata_license>CC-BY-SA-4.0</metadata_license>
12	<project_license>GPL-3.0-or-later</project_license>
13
14	<launchable type="desktop-id">@APP_ID@.desktop</launchable>
15	<provides>
16		<binary>@APP_ID@</binary>
17		<dbus type="user">@APP_ID@</dbus>
18	</provides>
19	<translation type="gettext">@APP_ID@</translation>
20</component>

It tells:

  • The id of the application
  • Its name
  • A short summary
  • A longer description
  • The SPDX License IDs of the metadata and the application
  • The name of the desktop file
  • The binary and dbus name the application provides
  • The translation domain, so that the user knows if the application is translated in their language

A lot more information can be added, like screenshots, release notes, links to the website, the repository, donations, etc. The AppStream documentation describes all the possible elements, with a lot of examples. The more metadata we add, the more attractive the application will appear, and the more likely to install it the user will be!

The metadata can be translated, so add this file to po/POTFILES.

Now, in data/meson.build, we tell the build system to translate and install it in the expected place:

55# Translate and install the AppStream metadata
56i18n.merge_file(
57	input: configure_file(
58		input: APP_ID + '.metainfo.xml',
59		output: APP_ID + '.metainfo.xml.in',
60		configuration: {
61			'APP_ID': APP_ID,
62		}
63	),
64	output: APP_ID + '.metainfo.xml',
65	type: 'xml',
66	po_dir: meson.project_source_root() / 'po',
67	install: true,
68	install_dir: get_option('datadir') / 'metainfo'
69)

Our application is now ready to be distributed! Since we used Flatpak all along, Flathub would be a natural place to publish it.