about summary refs log tree commit diff
path: root/doc/languages-frameworks/gnome.xml
blob: 290057863b248b69f565d2bed15f504fee3b3a64 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="sec-language-gnome">
 <title>GNOME</title>

 <section xml:id="ssec-gnome-packaging">
  <title>Packaging GNOME applications</title>

  <para>
   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.
   <link xlink:href="#fun-wrapProgram">Wrapping</link> 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.
  </para>

  <section xml:id="ssec-gnome-settings">
   <title>Settings</title>

   <para>
    <link xlink:href="https://developer.gnome.org/gio/stable/GSettings.html">GSettings</link>
    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
    <filename>glib-2.0/schemas/gschemas.compiled</filename> files inside the
    directories of <envar>XDG_DATA_DIRS</envar>.
   </para>

   <para>
    On Linux, GSettings API is implemented using
    <link xlink:href="https://wiki.gnome.org/Projects/dconf">dconf</link>
    backend. You will need to add <literal>dconf</literal> GIO module to
    <envar>GIO_EXTRA_MODULES</envar> variable, otherwise the
    <literal>memory</literal> backend will be used and the saved settings will
    not be persistent.
   </para>

   <para>
    Last you will need the dconf database D-Bus service itself. You can enable
    it using <option>programs.dconf.enable</option>.
   </para>

   <para>
    Some applications will also require
    <package>gsettings-desktop-schemas</package> for things like reading proxy
    configuration or user interface customization. This dependency is often not
    mentioned by upstream, you should grep for
    <literal>org.gnome.desktop</literal> and
    <literal>org.gnome.system</literal> to see if the schemas are needed.
   </para>
  </section>

  <section xml:id="ssec-gnome-icons">
   <title>Icons</title>

   <para>
    When an application uses icons, an icon theme should be available in
    <envar>XDG_DATA_DIRS</envar>. The package for the default, icon-less
    <link xlink:href="https://www.freedesktop.org/wiki/Software/icon-theme/">hicolor-icon-theme</link>
    contains <link linkend="ssec-gnome-hooks-hicolor-icon-theme">a setup
    hook</link> that will pick up icon themes from
    <literal>buildInputs</literal> 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.
   </para>
  </section>

  <section xml:id="ssec-gnome-themes">
   <title>GTK Themes</title>

   <para>
    Previously, a GTK theme needed to be in <envar>XDG_DATA_DIRS</envar>. This
    is no longer necessary for most programs since GTK incorporated Adwaita
    theme. Some programs (for example, those designed for
    <link xlink:href="https://elementary.io/docs/human-interface-guidelines#human-interface-guidelines">elementary
    HIG</link>) might require a special theme like
    <package>pantheon.elementary-gtk-theme</package>.
   </para>
  </section>

  <section xml:id="ssec-gnome-typelibs">
   <title>GObject introspection typelibs</title>

   <para>
    <link xlink:href="https://wiki.gnome.org/Projects/GObjectIntrospection">GObject
    introspection</link> allows applications to use C libraries in other
    languages easily. It does this through <literal>typelib</literal> files
    searched in <envar>GI_TYPELIB_PATH</envar>.
   </para>
  </section>

  <section xml:id="ssec-gnome-plugins">
   <title>Various plug-ins</title>

   <para>
    If your application uses
    <link xlink:href="https://gstreamer.freedesktop.org/">GStreamer</link> or
    <link xlink:href="https://wiki.gnome.org/Projects/Grilo">Grilo</link>, you
    should set <envar>GST_PLUGIN_SYSTEM_PATH_1_0</envar> and
    <envar>GRL_PLUGIN_PATH</envar>, respectively.
   </para>
  </section>
 </section>

 <section xml:id="ssec-gnome-hooks">
  <title>Onto <package>wrapGAppsHook</package></title>

  <para>
   Given the requirements above, the package expression would become messy
   quickly:
<programlisting>
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
'';
</programlisting>
   Fortunately, there is <package>wrapGAppsHook</package>, that does the
   wrapping for us. In particular, it works in conjunction with other setup
   hooks that will populate the variable:
   <itemizedlist>
    <listitem xml:id="ssec-gnome-hooks-wrapgappshook">
     <para>
      <package>wrapGAppsHook</package> itself will add the package’s
      <filename>share</filename> directory to <envar>XDG_DATA_DIRS</envar>.
     </para>
    </listitem>
    <listitem xml:id="ssec-gnome-hooks-glib">
     <para>
      <package>glib</package> setup hook will populate
      <envar>GSETTINGS_SCHEMAS_PATH</envar> and then
      <package>wrapGAppsHook</package> will prepend it to
      <envar>XDG_DATA_DIRS</envar>.
     </para>
    </listitem>
    <listitem xml:id="ssec-gnome-hooks-dconf">
     <para>
      <package>gnome3.dconf.lib</package> is a dependency of
      <package>wrapGAppsHook</package>, which then also adds it to the
      <envar>GIO_EXTRA_MODULES</envar> variable.
     </para>
    </listitem>
    <listitem xml:id="ssec-gnome-hooks-hicolor-icon-theme">
     <para>
      <package>hicolor-icon-theme</package>’s setup hook will add icon themes
      to <envar>XDG_ICON_DIRS</envar> which is prepended to
      <envar>XDG_DATA_DIRS</envar> by <package>wrapGAppsHook</package>.
     </para>
    </listitem>
    <listitem xml:id="ssec-gnome-hooks-gobject-introspection">
     <para>
      <package>gobject-introspection</package> setup hook populates
      <envar>GI_TYPELIB_PATH</envar> variable with
      <filename>lib/girepository-1.0</filename> directories of dependencies,
      which is then added to wrapper by <package>wrapGAppsHook</package>. It
      also adds <filename>share</filename> directories of dependencies to
      <envar>XDG_DATA_DIRS</envar>, which is intended to promote GIR files but
      it also
      <link xlink:href="https://github.com/NixOS/nixpkgs/issues/32790">pollutes
      the closures</link> of packages using <package>wrapGAppsHook</package>.
     </para>
     <warning>
      <para>
       The setup hook
       <link xlink:href="https://github.com/NixOS/nixpkgs/issues/56943">currently</link>
       does not work in expressions with <literal>strictDeps</literal> enabled,
       like Python packages. In those cases, you will need to disable it with
       <code>strictDeps = false;</code>.
      </para>
     </warning>
    </listitem>
    <listitem xml:id="ssec-gnome-hooks-gst-grl-plugins">
     <para>
      Setup hooks of <package>gst_all_1.gstreamer</package> and
      <package>gnome3.grilo</package> will populate the
      <envar>GST_PLUGIN_SYSTEM_PATH_1_0</envar> and
      <envar>GRL_PLUGIN_PATH</envar> variables, respectively, which will then
      be added to the wrapper by <literal>wrapGAppsHook</literal>.
     </para>
    </listitem>
   </itemizedlist>
  </para>

  <para>
   You can also pass additional arguments to <literal>makeWrapper</literal>
   using <literal>gappsWrapperArgs</literal> in <literal>preFixup</literal>
   hook:
<programlisting>
preFixup = ''
  gappsWrapperArgs+=(
    # Thumbnailers
    --prefix XDG_DATA_DIRS : "${gdk-pixbuf}/share"
    --prefix XDG_DATA_DIRS : "${librsvg}/share"
    --prefix XDG_DATA_DIRS : "${shared-mime-info}/share"
  )
'';
</programlisting>
  </para>
 </section>

 <section xml:id="ssec-gnome-updating">
  <title>Updating GNOME packages</title>

  <para>
   Most GNOME package offer
   <link linkend="var-passthru-updateScript"><literal>updateScript</literal></link>,
   it is therefore possible to update to latest source tarball by running
   <command>nix-shell maintainers/scripts/update.nix --argstr package
   gnome3.nautilus</command> or even en masse with <command>nix-shell
   maintainers/scripts/update.nix --argstr path gnome3</command>. Read the
   package’s <filename>NEWS</filename> file to see what changed.
  </para>
 </section>

 <section xml:id="ssec-gnome-common-issues">
  <title>Frequently encountered issues</title>

  <variablelist>
   <varlistentry xml:id="ssec-gnome-common-issues-no-schemas">
    <term>
     <computeroutput>GLib-GIO-ERROR **: <replaceable>06:04:50.903</replaceable>: No GSettings schemas are installed on the system</computeroutput>
    </term>
    <listitem>
     <para>
      There are no schemas avalable in <envar>XDG_DATA_DIRS</envar>.
      Temporarily add a random package containing schemas like
      <package>gsettings-desktop-schemas</package> to
      <literal>buildInputs</literal>.
      <link linkend="ssec-gnome-hooks-glib"><package>glib</package></link> and
      <link linkend="ssec-gnome-hooks-wrapgappshook"><package>wrapGAppsHook</package></link>
      setup hooks will take care of making the schemas available to application
      and you will see the actual missing schemas with the
      <link linkend="ssec-gnome-common-issues-missing-schema">next
      error</link>. Or you can try looking through the source code for the
      actual schemas used.
     </para>
    </listitem>
   </varlistentry>
   <varlistentry xml:id="ssec-gnome-common-issues-missing-schema">
    <term>
     <computeroutput>GLib-GIO-ERROR **: <replaceable>06:04:50.903</replaceable>: Settings schema <replaceable>org.gnome.foo</replaceable> is not installed</computeroutput>
    </term>
    <listitem>
     <para>
      Package is missing some GSettings schemas. You can find out the package
      containing the schema with <command>nix-locate
      <replaceable>org.gnome.foo</replaceable>.gschema.xml</command> and let
      the hooks handle the wrapping as
      <link linkend="ssec-gnome-common-issues-no-schemas">above</link>.
     </para>
    </listitem>
   </varlistentry>
   <varlistentry xml:id="ssec-gnome-common-issues-double-wrapped">
    <term>
     When using <package>wrapGAppsHook</package> with special derivers you can end up with double wrapped binaries.
    </term>
    <listitem>
     <para>
      This is because derivers like
      <function>python.pkgs.buildPythonApplication</function> or
      <function>qt5.mkDerivation</function> have setup-hooks automatically
      added that produce wrappers with <package>makeWrapper</package>. The
      simplest way to workaround that is to disable the
      <package>wrapGAppsHook</package> automatic wrapping with
      <code>dontWrapGApps = true;</code> and pass the arguments it intended to
      pass to <package>makeWrapper</package> to another.
     </para>
     <para>
      In the case of a Python application it could look like:
<programlisting>
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[@]}"
  ];
}
</programlisting>
      And for a QT app like:
<programlisting>
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[@]}"
  ];
}
</programlisting>
     </para>
    </listitem>
   </varlistentry>
   <varlistentry xml:id="ssec-gnome-common-issues-unwrappable-package">
    <term>
     I am packaging a project that cannot be wrapped, like a library or GNOME Shell extension.
    </term>
    <listitem>
     <para>
      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:
      <itemizedlist>
       <listitem xml:id="ssec-gnome-common-issues-unwrappable-package-gnome-shell-ext">
        <para>
         <link xlink:href="https://github.com/NixOS/nixpkgs/blob/7bb8f05f12ca3cff9da72b56caa2f7472d5732bc/pkgs/desktops/gnome-3/core/gnome-shell-extensions/default.nix#L21-L24">Replacing
         a <envar>GI_TYPELIB_PATH</envar> in GNOME Shell extension</link>          we are using <function>substituteAll</function> to include the path to
         a typelib into a patch.
        </para>
       </listitem>
       <listitem xml:id="ssec-gnome-common-issues-unwrappable-package-gsettings">
        <para>
         The following examples are hardcoding GSettings schema paths. To get
         the schema paths we use the functions
         <itemizedlist>
          <listitem>
           <para>
            <function>glib.getSchemaPath</function> Takes a nix package
            attribute as an argument.
           </para>
          </listitem>
          <listitem>
           <para>
            <function>glib.makeSchemaPath</function> Takes a package output
            like <literal>$out</literal> and a derivation name. You should use
            this if the schemas you need to hardcode are in the same
            derivation.
           </para>
          </listitem>
         </itemizedlist>
        </para>
        <para xml:id="ssec-gnome-common-issues-unwrappable-package-gsettings-vala">
         <link xlink:href="https://github.com/NixOS/nixpkgs/blob/7bb8f05f12ca3cff9da72b56caa2f7472d5732bc/pkgs/desktops/pantheon/apps/elementary-files/default.nix#L78-L86">Hard-coding
         GSettings schema path in Vala plug-in (dynamically loaded
         library)</link>  here, <function>substituteAll</function> cannot be
         used since the schema comes from the same package preventing us from
         pass its path to the function, probably due to a
         <link xlink:href="https://github.com/NixOS/nix/issues/1846">Nix
         bug</link>.
        </para>
        <para xml:id="ssec-gnome-common-issues-unwrappable-package-gsettings-c">
         <link xlink:href="https://github.com/NixOS/nixpkgs/blob/29c120c065d03b000224872251bed93932d42412/pkgs/development/libraries/glib-networking/default.nix#L31-L34">Hard-coding
         GSettings schema path in C library</link>  nothing special other
         than using
         <link xlink:href="https://github.com/NixOS/nixpkgs/pull/67957#issuecomment-527717467">Coccinelle
         patch</link> to generate the patch itself.
        </para>
       </listitem>
      </itemizedlist>
     </para>
    </listitem>
   </varlistentry>
   <varlistentry xml:id="ssec-gnome-common-issues-weird-location">
    <term>
     I need to wrap a binary outside <filename>bin</filename> and <filename>libexec</filename> directories.
    </term>
    <listitem>
     <para>
      You can manually trigger the wrapping with <function>wrapGApp</function> in <literal>preFixup</literal> phase. It takes a path to a program as a first argument; the remaining arguments are passed directly to <function xlink:href="#fun-wrapProgram">wrapProgram</function> function.
     </para>
    </listitem>
   </varlistentry>
  </variablelist>
 </section>
</section>