wrapGAppsHook: only wrap out/$outputBin by default (#526688)

This commit is contained in:
Stefan Frijters 2026-06-16 20:01:39 +00:00 committed by GitHub
commit 720365f01c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 211 additions and 2 deletions

View file

@ -105,7 +105,7 @@ Given the requirements above, the package expression would become messy quickly:
}
```
Fortunately, we have a [family of hooks]{#ssec-gnome-hooks-wrapgappshook} that automate this. They work in conjunction with other setup hooks that populate environment variables, and will then wrap all executables in `bin` and `libexec` directories using said variables.
Fortunately, we have a [family of hooks]{#ssec-gnome-hooks-wrapgappshook} that automate this. They work in conjunction with other setup hooks that populate environment variables, and will then wrap all executables in `bin` and `libexec` directories using said variables. If a package has multiple outputs, these hooks will work on `outputBin` by default, or on the outputs listed in `wrapGAppsInOutputs` if set.
- [`wrapGAppsHook3`]{#ssec-gnome-hooks-wrapgappshook3} for GTK 3 apps. For convenience, it also adds `dconf.lib` for a GIO module implementing a GSettings backend using `dconf`, `gtk3` for GSettings schemas, and `librsvg` for GdkPixbuf loader to the closure.
- [`wrapGAppsHook4`]{#ssec-gnome-hooks-wrapgappshook4} for GTK 4 apps. Same as `wrapGAppsHook3` but replaces `gtk3` with `gtk4`.

View file

@ -76,7 +76,136 @@ makeSetupHook {
];
};
# The wrapper for executable files should add path to dconf GIO module.
# Simple derivation containing a program and a daemon, but split over multiple outputs.
basic-multiple-outputs = stdenv.mkDerivation {
name = "basic-multiple-outputs";
src = sample-project;
outputs = [
"out"
"lib"
];
strictDeps = true;
nativeBuildInputs = [ wrapGAppsHook3 ];
installFlags = [
"bin-foo"
"libexec-bar"
];
postInstall = ''
mkdir -p $lib
mv $out/libexec $lib
# Wrapper will want to append this to XDG_DATA_DIRS, but should not cause a cyclic dependency;
# i.e. only "out" will be wrapped.
mkdir -p $out/share
'';
};
# Simple derivation containing a program and a daemon, but using a non-default output
# Executables in "bin" should be handled correctly automatically
basic-bin-output = stdenv.mkDerivation {
name = "basic-bin-output";
src = sample-project;
outputs = [
"bin"
"dev"
"lib"
"out"
];
strictDeps = true;
nativeBuildInputs = [ wrapGAppsHook3 ];
installFlags = [
"bin-foo"
"libexec-bar"
];
postInstall = ''
mkdir -p $lib
mv $out/libexec $lib
mkdir -p $bin
mv $out/bin $bin
'';
};
# Simple derivation containing a program and a daemon, but using a non-default output
basic-other-outputs = stdenv.mkDerivation {
name = "basic-other-outputs";
src = sample-project;
outputs = [
"dev"
"lib"
"out"
];
strictDeps = true;
nativeBuildInputs = [ wrapGAppsHook3 ];
installFlags = [
"bin-foo"
"libexec-bar"
];
wrapGAppsInOutputs = [
"dev"
"lib"
];
postInstall = ''
mkdir -p $lib
mv $out/libexec $lib
mkdir -p $dev
mv $out/bin $dev
'';
};
# Simple derivation containing a program and a daemon, but using non-default outputs
# that are explicitly referenced via wrapGAppsInOutputs
basic-other-outputs-structuredattrs = stdenv.mkDerivation {
name = "basic-other-outputs-structuredAttrs";
__structuredAttrs = true;
src = sample-project;
outputs = [
"dev"
"lib"
"out"
];
strictDeps = true;
nativeBuildInputs = [ wrapGAppsHook3 ];
installFlags = [
"bin-foo"
"libexec-bar"
];
wrapGAppsInOutputs = [
"dev"
"lib"
];
postInstall = ''
mkdir -p $lib
mv $out/libexec $lib
mkdir -p $dev
mv $out/bin $dev
'';
};
# Simple derivation containing a program and a daemon, but using non-default outputs
# that are explicitly referenced via wrapGAppsInOutputs, while structuredAttrs are enabled
# so that it is a proper array.
basic-contains-dconf =
let
tested = basic;
@ -92,6 +221,64 @@ makeSetupHook {
''
);
# The wrapper for executable files should add path to dconf GIO module.
basic-multiple-outputs-contains-dconf =
let
tested = basic-multiple-outputs;
in
testLib.runTest "basic-multiple-outputs-contains-dconf" (
testLib.skip stdenv.hostPlatform.isDarwin ''
${expectSomeLineContainingYInFileXToMentionZ "${tested}/bin/foo" "GIO_EXTRA_MODULES"
"${dconf.lib}/lib/gio/modules"
}
''
);
# The wrapper for executable files should add path to dconf GIO module.
basic-bin-output-contains-dconf =
let
tested = basic-bin-output;
in
testLib.runTest "basic-bin-output-contains-dconf" (
testLib.skip stdenv.hostPlatform.isDarwin ''
${expectSomeLineContainingYInFileXToMentionZ "${tested}/bin/foo" "GIO_EXTRA_MODULES"
"${dconf.lib}/lib/gio/modules"
}
''
);
# The wrapper for executable files should add path to dconf GIO module.
basic-other-outputs-contains-dconf =
let
tested = basic-other-outputs;
in
testLib.runTest "basic-other-outputs-contains-dconf" (
testLib.skip stdenv.hostPlatform.isDarwin ''
${expectSomeLineContainingYInFileXToMentionZ "${tested.dev}/bin/foo" "GIO_EXTRA_MODULES"
"${dconf.lib}/lib/gio/modules"
}
${expectSomeLineContainingYInFileXToMentionZ "${tested.lib}/libexec/bar" "GIO_EXTRA_MODULES"
"${dconf.lib}/lib/gio/modules"
}
''
);
# The wrapper for executable files should add path to dconf GIO module.
basic-other-outputs-contains-dconf-structuredattrs =
let
tested = basic-other-outputs-structuredattrs;
in
testLib.runTest "basic-other-outputs-structuredattrs-contains-dconf" (
testLib.skip stdenv.hostPlatform.isDarwin ''
${expectSomeLineContainingYInFileXToMentionZ "${tested.dev}/bin/foo" "GIO_EXTRA_MODULES"
"${dconf.lib}/lib/gio/modules"
}
${expectSomeLineContainingYInFileXToMentionZ "${tested.lib}/libexec/bar" "GIO_EXTRA_MODULES"
"${dconf.lib}/lib/gio/modules"
}
''
);
basic-contains-gdk-pixbuf =
let
tested = basic;

View file

@ -42,15 +42,36 @@ wrapGApp() {
wrapProgram "$program" "${gappsWrapperArgs[@]}" "$@"
}
_wrapGAppsHookMayRunForOutput() {
local -r output="$1"
if [[ -v wrapGAppsInOutputs ]]; then
local allowedOutput
# Support both structuredAttrs on and off
local -a allowedOutputs
concatTo allowedOutputs wrapGAppsInOutputs
for allowedOutput in "${allowedOutputs[@]}"; do
[ "$allowedOutput" = "$output" ] && return 0
done
else
[ "$outputBin" = "$output" ] && return 0
fi
return 1
}
declare -gA wrapGAppsHookHasRunForOutput
# Note: $gappsWrapperArgs still gets defined even if ${dontWrapGApps-} is set.
# $output is brought into scope via fixupPhase() in pkgs/stdenv/generic/setup.sh
wrapGAppsHook() {
# guard against running multiple times for the same output (e.g. due to propagation)
[ "${wrapGAppsHookHasRunForOutput["$output"]:-}" = 1 ] && return 0
wrapGAppsHookHasRunForOutput["$output"]=1
# guard against running for outputs we don't want to run it for
_wrapGAppsHookMayRunForOutput "$output" || return 0
if [[ -z "${dontWrapGApps:-}" ]]; then
local targetDirsThatExist targetDirsRealPath targetDirs targetDir
targetDirsThatExist=()
targetDirsRealPath=()
@ -71,6 +92,7 @@ wrapGAppsHook() {
# wrap links to binaries that point outside targetDirs
# Note: links to binaries within targetDirs do not need
# to be wrapped as the binaries have already been wrapped
local linkPathReal targetPath
if [[ ${#targetDirsThatExist[@]} -ne 0 ]]; then
find "${targetDirsThatExist[@]}" -type l -xtype f -executable -print0 |
while IFS= read -r -d '' linkPath; do