Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gdk-pixbuf: multiple module files #153275

Draft
wants to merge 10 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions doc/languages-frameworks/gnome.section.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,11 @@ To allow software to use various virtual file systems, `gvfs` package can be als

### GdkPixbuf loaders {#ssec-gnome-gdk-pixbuf-loaders}

GTK applications typically use [GdkPixbuf](https://developer.gnome.org/gdk-pixbuf/stable/) to load images. But `gdk-pixbuf` package only supports basic bitmap formats like JPEG, PNG or TIFF, requiring to use third-party loader modules for other formats. This is especially painful since GTK itself includes SVG icons, which cannot be rendered without a loader provided by `librsvg`.
GTK applications typically use [GdkPixbuf](https://developer.gnome.org/gdk-pixbuf/stable/) to load images. But `gdk-pixbuf` package only supports basic bitmap formats like JPEG, PNG or TIFF, requiring third-party loader modules for other formats. GTK itself includes SVG icons, which cannot be rendered without a loader provided by `librsvg`.

Unlike other libraries mentioned in this section, GdkPixbuf only supports a single value in its controlling environment variable `GDK_PIXBUF_MODULE_FILE`. It is supposed to point to a cache file containing information about the available loaders. Each loader package will contain a `lib/gdk-pixbuf-2.0/2.10.0/loaders.cache` file describing the default loaders in `gdk-pixbuf` package plus the loader contained in the package itself. If you want to use multiple third-party loaders, you will need to create your own cache file manually. Fortunately, this is pretty rare as [not many loaders exist](https://gitlab.gnome.org/federico/gdk-pixbuf-survey/blob/master/src/modules.md).
Each loader package will contain a `lib/gdk-pixbuf-2.0/2.10.0/loaders.cache` file containing information about the available loaders. GdkPixbuf looks for these files in the `GDK_PIXBUF_MODULE_FILE` environment variable. Although upstream GdkPixbuf only supports a single file, in nixpkgs it is patched to accept multiple files, separated by `:`.

`gdk-pixbuf` contains [a setup hook](#ssec-gnome-hooks-gdk-pixbuf) that sets `GDK_PIXBUF_MODULE_FILE` from dependencies but as mentioned in further section, it is pretty limited. Loaders should propagate this setup hook.
[`wrapGAppsHook`](#ssec-gnome-hooks-wrapgappshook) handles setting `GDK_PIXBUF_MODULE_FILE` for GTK apps. If you're manually constructing a wrapper, consider `--prefix`ing the variable rather than overwriting it, so that users can extend your program's capabilities through `services.xserver.gdk-pixbuf.modulePackages`.

### Icons {#ssec-gnome-icons}

Expand Down Expand Up @@ -106,7 +106,7 @@ For convenience, it also adds `dconf.lib` for a GIO module implementing a GSetti

- []{#ssec-gnome-hooks-glib} `glib` setup hook will populate `GSETTINGS_SCHEMAS_PATH` and then `wrapGAppsHook` will prepend it to `XDG_DATA_DIRS`.

- []{#ssec-gnome-hooks-gdk-pixbuf} `gdk-pixbuf` setup hook will populate `GDK_PIXBUF_MODULE_FILE` with the path to biggest `loaders.cache` file from the dependencies containing [GdkPixbuf loaders](#ssec-gnome-gdk-pixbuf-loaders). This works fine when there are only two packages containing loaders (`gdk-pixbuf` and e.g. `librsvg`) – it will choose the second one, reasonably expecting that it will be bigger since it describes extra loader in addition to the default ones. But when there are more than two loader packages, this logic will break. One possible solution would be constructing a custom cache file for each package containing a program like `services/x11/gdk-pixbuf.nix` NixOS module does. `wrapGAppsHook` copies the `GDK_PIXBUF_MODULE_FILE` environment variable into the produced wrapper.
- []{#ssec-gnome-hooks-gdk-pixbuf} `wrapGAppsHook` will automatically prepend `GDK_PIXBUF_MODULE_FILE` with modules from `gdk-pixbuf` and `librsvg`. To include additional gdk-pixbuf modules, set `extraGdkPixbufModules` with an array of packages.
jtojnar marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add []{#setup-hook-gdk-pixbuf} so that the deleted setup hook section points here as well.


- []{#ssec-gnome-hooks-gtk-drop-icon-theme-cache} One of `gtk3`’s setup hooks will remove `icon-theme.cache` files from package’s icon theme directories to avoid conflicts. Icon theme packages should prevent this with `dontDropIconThemeCache = true;`.

Expand Down
4 changes: 0 additions & 4 deletions doc/stdenv/stdenv.chapter.md
Original file line number Diff line number Diff line change
Expand Up @@ -1077,10 +1077,6 @@ Adds the `share/texmf-nix` subdirectory of each build input to the `TEXINPUTS` e

Sets the `QTDIR` environment variable to Qt’s path.

### gdk-pixbuf {#setup-hook-gdk-pixbuf}

Exports `GDK_PIXBUF_MODULE_FILE` environment variable to the builder. Add librsvg package to `buildInputs` to get svg support. See also the [setup hook description in GNOME platform docs](#ssec-gnome-hooks-gdk-pixbuf).

### GHC {#ghc}

Creates a temporary package database and registers every Haskell build input in it (TODO: how?).
Expand Down
11 changes: 11 additions & 0 deletions nixos/doc/manual/release-notes/rl-2205.section.md
Original file line number Diff line number Diff line change
Expand Up @@ -621,6 +621,17 @@ In addition to numerous new and upgraded packages, this release has the followin
To import that data after to the upgrade, run
`sudo -u epgstation epgstation run v1migrate /tmp/epgstation.bak`

- How `gdk-pixbuf` modules are set has changed.
cwyc marked this conversation as resolved.
Show resolved Hide resolved
- `gdk-pixbuf` has been patched to accept multiple `loaders.cache` files in `$GDK_PIXBUF_MODULE_FILE`, separated by `:`
- Setting modules through `wrapGAppsHook`:
`wrapGAppsHook` now automatically includes modules from `gdk-pixbuf` and `librsvg`. This is backwards-compatible for most packages.
It no longer looks for a module file in `$GDK_PIXBUF_MODULE_FILE` in the build environment, `gdk-pixbuf` no longer sets it when added to `buildInputs`, and you no longer need to add `gdk-pixbuf` or `librsvg` to `buildInputs` unless the program directly depends on it.
To include additional gdk-pixbuf modules, set `extraGdkPixbufModules` with an array of packages.
- Manually wrapping `$GDK_PIXBUF_MODULE_FILE`:
The `librsvg` loaders.cache still contains all of the loaders from `gdk-pixbuf`. This might change in the future, so it is recommended that you explicitly include both.
As this change allows users to extend programs with their own loaders, it is recommended that you `--prefix` rather than `--set` `$GDK_PIXBUF_MODULE_FILE` with your values.
For example: `makeWrapper ... --prefix GDK_PIXBUF_MODULE_FILE : "${librsvg}/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache:${gdk-pixbuf}/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache"`
cwyc marked this conversation as resolved.
Show resolved Hide resolved

- `switch-to-configuration` (the script that is run when running `nixos-rebuild switch` for example) has been reworked
* The interface that allows activation scripts to restart units has been streamlined. Restarting and reloading is now done by a single file `/run/nixos/activation-restart-list` that honors `restartIfChanged` and `reloadIfChanged` of the units.
* Preferring to reload instead of restarting can still be achieved using `/run/nixos/activation-reload-list`.
Expand Down
31 changes: 31 additions & 0 deletions pkgs/build-support/setup-hooks/wrap-gapps-hook/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
, callPackage
, wrapGAppsHook
, writeTextFile
, gdk-pixbuf
}:

makeSetupHook {
Expand Down Expand Up @@ -37,6 +38,9 @@ makeSetupHook {
makeWrapper
];
substitutions = {
standardGdkPixbufModules = lib.makeSearchPathOutput "lib" gdk-pixbuf.cacheFile [ gdk-pixbuf librsvg ];
gdkPixbufCacheFile = gdk-pixbuf.cacheFile;

passthru.tests = let
sample-project = ./tests/sample-project;

Expand Down Expand Up @@ -64,6 +68,33 @@ makeSetupHook {
''
);

# Stopgap as no modules have been packaged yet
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is desirable to use fake modules here even after they have been packaged to allow testing wrapGAppsHook changes with minimal changes.

mock-pixbuf-module = stdenv.mkDerivation {
name = "mock-gdk-pixbuf-module";
outputs = ["out" "lib"];
dontUnpack = true;
buildPhase = ''
mkdir -p $out
mkdir -p $lib/$(dirname ${gdk-pixbuf.cacheFile})
touch $lib/${gdk-pixbuf.cacheFile}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To clean this up, it might be nice to move this to the sample-project and then use installFlags = [ "loaders.cache" ];.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think they should be separate, since the sample-project is wrapped by the setup hook and the mock loader is an input for the wrapper.

Copy link
Member

@jtojnar jtojnar Jun 3, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I meant move it to the Makefile in sample-project, then you will be able to use src & installFlags.

'';
dontInstall = true;
dontFixup = true;
};

pixbuf-modules = basic.overrideAttrs (old: {extraGdkPixbufModules = [mock-pixbuf-module];});

pixbuf-modules-check = let
tested = pixbuf-modules;
in testLib.runTest "pixbuf-modules-check" ''
${expectSomeLineContainingYInFileXToMentionZ "${tested}/bin/foo" "GDK_PIXBUF_MODULE_FILE" "${gdk-pixbuf}/${gdk-pixbuf.cacheFile}"}
${expectSomeLineContainingYInFileXToMentionZ "${tested}/bin/foo" "GDK_PIXBUF_MODULE_FILE" "${librsvg}/${gdk-pixbuf.cacheFile}"}
${expectSomeLineContainingYInFileXToMentionZ "${tested}/bin/foo" "GDK_PIXBUF_MODULE_FILE" "${mock-pixbuf-module}/${gdk-pixbuf.cacheFile}"}
${expectSomeLineContainingYInFileXToMentionZ "${tested}/libexec/bar" "GDK_PIXBUF_MODULE_FILE" "${gdk-pixbuf}/${gdk-pixbuf.cacheFile}"}
${expectSomeLineContainingYInFileXToMentionZ "${tested}/libexec/bar" "GDK_PIXBUF_MODULE_FILE" "${librsvg}/${gdk-pixbuf.cacheFile}"}
${expectSomeLineContainingYInFileXToMentionZ "${tested}/libexec/bar" "GDK_PIXBUF_MODULE_FILE" "${mock-pixbuf-module}/${gdk-pixbuf.cacheFile}"}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should not this be mock-pixbuf-module.lib?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I was wrong to put the mock loader it in $lib, I've changed it to $out. But the checks passed, brings up the issue that the wrapper doesn't check for the cache file's existence.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would probably make sense to have a variant of mock-pixbuf-module with outputs = [ "bin" "out" ]; for testing.

'';

# Simple derivation containing a gobject-introspection typelib.
typelib-Mahjong = stdenv.mkDerivation {
name = "typelib-Mahjong";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,13 @@ find_gio_modules() {
addEnvHooks "${targetOffset:?}" find_gio_modules

gappsWrapperArgsHook() {
if [ -n "$GDK_PIXBUF_MODULE_FILE" ]; then
gappsWrapperArgs+=(--set GDK_PIXBUF_MODULE_FILE "$GDK_PIXBUF_MODULE_FILE")

gappsWrapperArgs+=(--prefix GDK_PIXBUF_MODULE_FILE : "@standardGdkPixbufModules@")

if [ -n "$extraGdkPixbufModules" ]; then
for pkg in $extraGdkPixbufModules; do
gappsWrapperArgs+=(--prefix GDK_PIXBUF_MODULE_FILE : "$pkg/@gdkPixbufCacheFile@")
cwyc marked this conversation as resolved.
Show resolved Hide resolved
done
fi

if [ -n "$GSETTINGS_SCHEMAS_PATH" ]; then
Expand Down
7 changes: 5 additions & 2 deletions pkgs/development/libraries/gdk-pixbuf/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
, doCheck ? false
, makeWrapper
, lib
, callPackage
, withIntrospection ? (stdenv.buildPlatform == stdenv.hostPlatform)
, gobject-introspection
}:
Expand All @@ -42,6 +43,8 @@ stdenv.mkDerivation rec {
patches = [
# Move installed tests to a separate output
./installed-tests-path.patch
# Allow for multiple loaders.cache files specified in $GDK_PIXBUF_MODULE_FILE, delimited by ":"
./multiple-module-files.patch
cwyc marked this conversation as resolved.
Show resolved Hide resolved
];

# gdk-pixbuf-thumbnailer is not wrapped therefore strictDeps will work
Expand Down Expand Up @@ -128,8 +131,6 @@ stdenv.mkDerivation rec {
# The tests take an excessive amount of time (> 1.5 hours) and memory (> 6 GB).
inherit doCheck;

setupHook = ./setup-hook.sh;

separateDebugInfo = stdenv.isLinux;

passthru = {
Expand All @@ -140,10 +141,12 @@ stdenv.mkDerivation rec {

tests = {
installedTests = nixosTests.installed-tests.gdk-pixbuf;
multiple-module-files = callPackage ./multiple-module-files-test {};
};

# gdk_pixbuf_moduledir variable from gdk-pixbuf-2.0.pc
moduleDir = "lib/gdk-pixbuf-2.0/2.10.0/loaders";
cacheFile = "lib/gdk-pixbuf-2.0/2.10.0/loaders.cache";
};

meta = with lib; {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Simple tests for multiple module files functionality.
# In the future, would like to test precedence, more than two files, unexpected inputs/fuzzing.
{ runCommand, gdk-pixbuf, librsvg}:
runCommand "pixbuf-mmf-test" {} ''
mkdir -p $out
set +e

function test {
cwyc marked this conversation as resolved.
Show resolved Hide resolved
modulepath=$1
testfile=$2
GDK_PIXBUF_MODULE_FILE=$modulepath ${gdk-pixbuf}/bin/gdk-pixbuf-thumbnailer $testfile $out/$(basename $testfile) > /dev/null
cwyc marked this conversation as resolved.
Show resolved Hide resolved
}

test ${gdk-pixbuf}/${gdk-pixbuf.cacheFile} ${./sample.svg}
if ! (($?)); then
>&2 echo "This shouldn't happen? SVG parsing succeeded even though librsvg wasn't supplied."
exit 1
fi

test ${gdk-pixbuf}/${gdk-pixbuf.cacheFile}:${librsvg}/${gdk-pixbuf.cacheFile} ${./sample.svg}
if (($?)); then
>&2 echo "Test failed."
cwyc marked this conversation as resolved.
Show resolved Hide resolved
exit 1
fi

test ${librsvg}/${gdk-pixbuf.cacheFile}:${gdk-pixbuf}/${gdk-pixbuf.cacheFile} ${./sample.svg}
if (($?)); then
>&2 echo "Test failed."
exit 1
fi

set -e
exit 0
''
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
32 changes: 32 additions & 0 deletions pkgs/development/libraries/gdk-pixbuf/multiple-module-files.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
diff --git a/gdk-pixbuf/gdk-pixbuf-io.c b/gdk-pixbuf/gdk-pixbuf-io.c
index 1827811..eda0c29 100644
--- a/gdk-pixbuf/gdk-pixbuf-io.c
+++ b/gdk-pixbuf/gdk-pixbuf-io.c
@@ -668,13 +668,24 @@ static gboolean
gdk_pixbuf_io_init (void)
{
char *module_file;
- gboolean ret;
+ gboolean ret = FALSE;

gdk_pixbuf_io_init_builtin ();
#ifdef USE_GMODULE
module_file = gdk_pixbuf_get_module_file ();
+ gchar **files = g_strsplit(module_file, ":", -1);
+
+ int num_files = 0;
+ while(files[num_files] != NULL) num_files++;
+
+ for(int i = num_files - 1; i >= 0; i--){ /* reverse iteration so earlier files have precedence */
+ if(files[i][0] != '\0'){ /* :: yields an empty split string, we should ignore */
+ ret |= gdk_pixbuf_io_init_modules (files[i], NULL);
+ }
+ }
+
+ g_strfreev(files);
#endif
- ret = gdk_pixbuf_io_init_modules (module_file, NULL);
g_free (module_file);
return ret;
}

17 changes: 0 additions & 17 deletions pkgs/development/libraries/gdk-pixbuf/setup-hook.sh

This file was deleted.