GNOMEPackaging GNOME applications
Programs in the GNOME universe are written in various languages but they all
use GObject-based libraries like GLib, GTK or GStreamer. These libraries are
often modular, relying on looking into certain directories to find their
modules. However, due to Nix’s specific file system organization, this
will fail without our intervention. Fortunately, the libraries usually allow
overriding the directories through environment variables, either natively or
thanks to a patch in nixpkgs.
Wrapping the executables to
ensure correct paths are available to the application constitutes a
significant part of packaging a modern desktop application. In this section,
we will describe various modules needed by such applications, environment
variables needed to make the modules load, and finally a script that will do
the work for us.
Settings
GSettings
API is often used for storing settings. GSettings schemas are required, to
know the type and other metadata of the stored values. GLib looks for
glib-2.0/schemas/gschemas.compiled files inside the
directories of XDG_DATA_DIRS.
On Linux, GSettings API is implemented using
dconf
backend. You will need to add dconf GIO module to
GIO_EXTRA_MODULES variable, otherwise the
memory backend will be used and the saved settings will
not be persistent.
Last you will need the dconf database D-Bus service itself. You can enable
it using .
Some applications will also require
gsettings-desktop-schemas for things like reading proxy
configuration or user interface customization. This dependency is often not
mentioned by upstream, you should grep for
org.gnome.desktop and
org.gnome.system to see if the schemas are needed.
Icons
When an application uses icons, an icon theme should be available in
XDG_DATA_DIRS. The package for the default, icon-less
hicolor-icon-theme
contains a setup
hook that will pick up icon themes from
buildInputs and pass it to our wrapper. Unfortunately,
relying on that would mean every user has to download the theme included in
the package expression no matter their preference. For that reason, we
leave the installation of icon theme on the user. If you use one of the
desktop environments, you probably already have an icon theme installed.
GTK Themes
Previously, a GTK theme needed to be in XDG_DATA_DIRS. This
is no longer necessary for most programs since GTK incorporated Adwaita
theme. Some programs (for example, those designed for
elementary
HIG) might require a special theme like
pantheon.elementary-gtk-theme.
GObject introspection typelibs
GObject
introspection allows applications to use C libraries in other
languages easily. It does this through typelib files
searched in GI_TYPELIB_PATH.
Various plug-ins
If your application uses
GStreamer or
Grilo, you
should set GST_PLUGIN_SYSTEM_PATH_1_0 and
GRL_PLUGIN_PATH, respectively.
Onto wrapGAppsHook
Given the requirements above, the package expression would become messy
quickly:
preFixup = ''
for f in $(find $out/bin/ $out/libexec/ -type f -executable); do
wrapProgram "$f" \
--prefix GIO_EXTRA_MODULES : "${getLib gnome3.dconf}/lib/gio/modules" \
--prefix XDG_DATA_DIRS : "$out/share" \
--prefix XDG_DATA_DIRS : "$out/share/gsettings-schemas/${name}" \
--prefix XDG_DATA_DIRS : "${gsettings-desktop-schemas}/share/gsettings-schemas/${gsettings-desktop-schemas.name}" \
--prefix XDG_DATA_DIRS : "${hicolor-icon-theme}/share" \
--prefix GI_TYPELIB_PATH : "${lib.makeSearchPath "lib/girepository-1.0" [ pango json-glib ]}"
done
'';
Fortunately, there is wrapGAppsHook, that does the
wrapping for us. In particular, it works in conjunction with other setup
hooks that will populate the variable:
wrapGAppsHook itself will add the package’s
share directory to XDG_DATA_DIRS.
glib setup hook will populate
GSETTINGS_SCHEMAS_PATH and then
wrapGAppsHook will prepend it to
XDG_DATA_DIRS.
gnome3.dconf.lib is a dependency of
wrapGAppsHook, which then also adds it to the
GIO_EXTRA_MODULES variable.
hicolor-icon-theme’s setup hook will add icon themes
to XDG_ICON_DIRS which is prepended to
XDG_DATA_DIRS by wrapGAppsHook.
gobject-introspection setup hook populates
GI_TYPELIB_PATH variable with
lib/girepository-1.0 directories of dependencies,
which is then added to wrapper by wrapGAppsHook. It
also adds share directories of dependencies to
XDG_DATA_DIRS, which is intended to promote GIR files but
it also
pollutes
the closures of packages using wrapGAppsHook.
The setup hook
currently
does not work in expressions with strictDeps enabled,
like Python packages. In those cases, you will need to disable it with
strictDeps = false;.
Setup hooks of gst_all_1.gstreamer and
gnome3.grilo will populate the
GST_PLUGIN_SYSTEM_PATH_1_0 and
GRL_PLUGIN_PATH variables, respectively, which will then
be added to the wrapper by wrapGAppsHook.
You can also pass additional arguments to makeWrapper
using gappsWrapperArgs in preFixup
hook:
preFixup = ''
gappsWrapperArgs+=(
# Thumbnailers
--prefix XDG_DATA_DIRS : "${gdk-pixbuf}/share"
--prefix XDG_DATA_DIRS : "${librsvg}/share"
--prefix XDG_DATA_DIRS : "${shared-mime-info}/share"
)
'';
Updating GNOME packages
Most GNOME package offer
updateScript,
it is therefore possible to update to latest source tarball by running
nix-shell maintainers/scripts/update.nix --argstr package
gnome3.nautilus or even en masse with nix-shell
maintainers/scripts/update.nix --argstr path gnome3. Read the
package’s NEWS file to see what changed.
Frequently encountered issuesGLib-GIO-ERROR **: 06:04:50.903: No GSettings schemas are installed on the system
There are no schemas avalable in XDG_DATA_DIRS.
Temporarily add a random package containing schemas like
gsettings-desktop-schemas to
buildInputs.
glib and
wrapGAppsHook
setup hooks will take care of making the schemas available to application
and you will see the actual missing schemas with the
next
error. Or you can try looking through the source code for the
actual schemas used.
GLib-GIO-ERROR **: 06:04:50.903: Settings schema ‘org.gnome.foo’ is not installed
Package is missing some GSettings schemas. You can find out the package
containing the schema with nix-locate
org.gnome.foo.gschema.xml and let
the hooks handle the wrapping as
above.
When using wrapGAppsHook with special derivers you can end up with double wrapped binaries.
This is because derivers like
python.pkgs.buildPythonApplication or
qt5.mkDerivation have setup-hooks automatically
added that produce wrappers with makeWrapper. The
simplest way to workaround that is to disable the
wrapGAppsHook automatic wrapping with
dontWrapGApps = true; and pass the arguments it intended to
pass to makeWrapper to another.
In the case of a Python application it could look like:
python3.pkgs.buildPythonApplication {
pname = "gnome-music";
version = "3.32.2";
nativeBuildInputs = [
wrapGAppsHook
gobject-introspection
...
];
dontWrapGApps = true;
# Arguments to be passed to `makeWrapper`, only used by buildPython*
makeWrapperArgs = [
"\${gappsWrapperArgs[@]}"
];
}
And for a QT app like:
mkDerivation {
pname = "calibre";
version = "3.47.0";
nativeBuildInputs = [
wrapGAppsHook
qmake
...
];
dontWrapGApps = true;
# Arguments to be passed to `makeWrapper`, only used by qt5’s mkDerivation
qtWrapperArgs [
"\${gappsWrapperArgs[@]}"
];
}
I am packaging a project that cannot be wrapped, like a library or GNOME Shell extension.
You can rely on applications depending on the library set the necessary
environment variables but that it often easy to miss. Instead we
recommend to patch the paths in the source code whenever possible. Here
are some examples:
Replacing
a GI_TYPELIB_PATH in GNOME Shell extension –
we are using substituteAll to include the path to
a typelib into a patch.
The following examples are hardcoding GSettings schema paths. To get
the schema paths we use the functions
glib.getSchemaPath Takes a nix package
attribute as an argument.
glib.makeSchemaPath Takes a package output
like $out and a derivation name. You should use
this if the schemas you need to hardcode are in the same
derivation.
Hard-coding
GSettings schema path in Vala plug-in (dynamically loaded
library) – here, substituteAll cannot be
used since the schema comes from the same package preventing us from
pass its path to the function, probably due to a
Nix
bug.
Hard-coding
GSettings schema path in C library – nothing special other
than using
Coccinelle
patch to generate the patch itself.