aboutsummaryrefslogtreecommitdiff
path: root/nixpkgs/pkgs/build-support/setup-hooks/wrap-gapps-hook
diff options
context:
space:
mode:
Diffstat (limited to 'nixpkgs/pkgs/build-support/setup-hooks/wrap-gapps-hook')
-rw-r--r--nixpkgs/pkgs/build-support/setup-hooks/wrap-gapps-hook/default.nix175
-rw-r--r--nixpkgs/pkgs/build-support/setup-hooks/wrap-gapps-hook/tests/lib.nix30
-rw-r--r--nixpkgs/pkgs/build-support/setup-hooks/wrap-gapps-hook/tests/sample-project/Makefile30
-rw-r--r--nixpkgs/pkgs/build-support/setup-hooks/wrap-gapps-hook/wrap-gapps-hook.sh93
4 files changed, 328 insertions, 0 deletions
diff --git a/nixpkgs/pkgs/build-support/setup-hooks/wrap-gapps-hook/default.nix b/nixpkgs/pkgs/build-support/setup-hooks/wrap-gapps-hook/default.nix
new file mode 100644
index 00000000000..5a87893d972
--- /dev/null
+++ b/nixpkgs/pkgs/build-support/setup-hooks/wrap-gapps-hook/default.nix
@@ -0,0 +1,175 @@
+{ stdenv
+, lib
+, makeSetupHook
+, makeWrapper
+, gobject-introspection
+, gtk3
+, librsvg
+, dconf
+, callPackage
+, wrapGAppsHook
+, writeTextFile
+}:
+
+makeSetupHook {
+ deps = lib.optionals (!stdenv.isDarwin) [
+ # It is highly probable that a program will use GSettings,
+ # at minimum through GTK file chooser dialogue.
+ # Let’s add a GIO module for “dconf” GSettings backend
+ # to avoid falling back to “memory” backend. This is
+ # required for GSettings-based settings to be persisted.
+ # Unfortunately, it also requires the user to have dconf
+ # D-Bus service enabled globally (e.g. through a NixOS module).
+ dconf.lib
+ ] ++ [
+ # TODO: remove this, packages should depend on GTK explicitly.
+ gtk3
+
+ # librsvg provides a module for gdk-pixbuf to allow rendering
+ # SVG icons. Most icon themes are SVG-based and so are some
+ # graphics in GTK (e.g. cross for closing window in window title bar)
+ # so it is pretty much required for applications using GTK.
+ librsvg
+
+ # We use the wrapProgram function.
+ makeWrapper
+ ];
+ substitutions = {
+ passthru.tests = let
+ sample-project = ./tests/sample-project;
+
+ testLib = callPackage ./tests/lib.nix { };
+ inherit (testLib) expectSomeLineContainingYInFileXToMentionZ;
+ in rec {
+ # Simple derivation containing a program and a daemon.
+ basic = stdenv.mkDerivation {
+ name = "basic";
+
+ src = sample-project;
+
+ nativeBuildInputs = [ wrapGAppsHook ];
+
+ installFlags = [ "bin-foo" "libexec-bar" ];
+ };
+
+ # The wrapper for executable files should add path to dconf GIO module.
+ basic-contains-dconf = let
+ tested = basic;
+ in testLib.runTest "basic-contains-dconf" (
+ testLib.skip stdenv.isDarwin ''
+ ${expectSomeLineContainingYInFileXToMentionZ "${tested}/bin/foo" "GIO_EXTRA_MODULES=" "${dconf.lib}/lib/gio/modules"}
+ ${expectSomeLineContainingYInFileXToMentionZ "${tested}/libexec/bar" "GIO_EXTRA_MODULES=" "${dconf.lib}/lib/gio/modules"}
+ ''
+ );
+
+ # Simple derivation containing a gobject-introspection typelib.
+ typelib-Mahjong = stdenv.mkDerivation {
+ name = "typelib-Mahjong";
+
+ src = sample-project;
+
+ installFlags = [ "typelib-Mahjong" ];
+ };
+
+ # Simple derivation using a typelib.
+ typelib-user = stdenv.mkDerivation {
+ name = "typelib-user";
+
+ src = sample-project;
+
+ nativeBuildInputs = [
+ gobject-introspection
+ wrapGAppsHook
+ ];
+
+ buildInputs = [
+ typelib-Mahjong
+ ];
+
+ installFlags = [ "bin-foo" "libexec-bar" ];
+ };
+
+ # Testing cooperation with gobject-introspection setup hook,
+ # which should populate GI_TYPELIB_PATH variable with paths
+ # to typelibs among the derivation’s dependencies.
+ # The resulting GI_TYPELIB_PATH should be picked up by the wrapper.
+ typelib-user-has-gi-typelib-path = let
+ tested = typelib-user;
+ in testLib.runTest "typelib-user-has-gi-typelib-path" ''
+ ${expectSomeLineContainingYInFileXToMentionZ "${tested}/bin/foo" "GI_TYPELIB_PATH=" "${typelib-Mahjong}/lib/girepository-1.0"}
+ ${expectSomeLineContainingYInFileXToMentionZ "${tested}/libexec/bar" "GI_TYPELIB_PATH=" "${typelib-Mahjong}/lib/girepository-1.0"}
+ '';
+
+ # Simple derivation containing a gobject-introspection typelib in lib output.
+ typelib-Bechamel = stdenv.mkDerivation {
+ name = "typelib-Bechamel";
+
+ outputs = [ "out" "lib" ];
+
+ src = sample-project;
+
+ makeFlags = [
+ "LIBDIR=${placeholder "lib"}/lib"
+ ];
+
+ installFlags = [ "typelib-Bechamel" ];
+ };
+
+ # Simple derivation using a typelib from non-default output.
+ typelib-multiout-user = stdenv.mkDerivation {
+ name = "typelib-multiout-user";
+
+ src = sample-project;
+
+ nativeBuildInputs = [
+ gobject-introspection
+ wrapGAppsHook
+ ];
+
+ buildInputs = [
+ typelib-Bechamel
+ ];
+
+ installFlags = [ "bin-foo" "libexec-bar" ];
+ };
+
+ # Testing cooperation with gobject-introspection setup hook,
+ # which should populate GI_TYPELIB_PATH variable with paths
+ # to typelibs among the derivation’s dependencies,
+ # even when they are not in default output.
+ # The resulting GI_TYPELIB_PATH should be picked up by the wrapper.
+ typelib-multiout-user-has-gi-typelib-path = let
+ tested = typelib-multiout-user;
+ in testLib.runTest "typelib-multiout-user-has-gi-typelib-path" ''
+ ${expectSomeLineContainingYInFileXToMentionZ "${tested}/bin/foo" "GI_TYPELIB_PATH=" "${typelib-Bechamel.lib}/lib/girepository-1.0"}
+ ${expectSomeLineContainingYInFileXToMentionZ "${tested}/libexec/bar" "GI_TYPELIB_PATH=" "${typelib-Bechamel.lib}/lib/girepository-1.0"}
+ '';
+
+ # Simple derivation that contains a typelib as well as a program using it.
+ typelib-self-user = stdenv.mkDerivation {
+ name = "typelib-self-user";
+
+ src = sample-project;
+
+ nativeBuildInputs = [
+ gobject-introspection
+ wrapGAppsHook
+ ];
+
+ installFlags = [ "typelib-Cow" "bin-foo" "libexec-bar" ];
+ };
+
+ # Testing cooperation with gobject-introspection setup hook,
+ # which should add the path to derivation’s own typelibs
+ # to GI_TYPELIB_PATH variable.
+ # The resulting GI_TYPELIB_PATH should be picked up by the wrapper.
+ # https://github.com/NixOS/nixpkgs/issues/85515
+ typelib-self-user-has-gi-typelib-path = let
+ tested = typelib-self-user;
+ in testLib.runTest "typelib-self-user-has-gi-typelib-path" ''
+ ${expectSomeLineContainingYInFileXToMentionZ "${tested}/bin/foo" "GI_TYPELIB_PATH=" "${typelib-self-user}/lib/girepository-1.0"}
+ ${expectSomeLineContainingYInFileXToMentionZ "${tested}/libexec/bar" "GI_TYPELIB_PATH=" "${typelib-self-user}/lib/girepository-1.0"}
+ '';
+ };
+ };
+} ./wrap-gapps-hook.sh
diff --git a/nixpkgs/pkgs/build-support/setup-hooks/wrap-gapps-hook/tests/lib.nix b/nixpkgs/pkgs/build-support/setup-hooks/wrap-gapps-hook/tests/lib.nix
new file mode 100644
index 00000000000..1757bdbbe25
--- /dev/null
+++ b/nixpkgs/pkgs/build-support/setup-hooks/wrap-gapps-hook/tests/lib.nix
@@ -0,0 +1,30 @@
+{ runCommand
+}:
+
+rec {
+ runTest = name: body: runCommand name { } ''
+ set -o errexit
+ ${body}
+ touch $out
+ '';
+
+ skip = cond: text:
+ if cond then ''
+ echo "Skipping test $name" > /dev/stderr
+ '' else text;
+
+ fail = text: ''
+ echo "FAIL: $name: ${text}" > /dev/stderr
+ exit 1
+ '';
+
+ expectSomeLineContainingYInFileXToMentionZ = file: filter: expected: ''
+ if ! cat "${file}" | grep "${filter}"; then
+ ${fail "The file “${file}” should include a line containing “${filter}”."}
+ fi
+
+ if ! cat "${file}" | grep "${filter}" | grep ${expected}; then
+ ${fail "The file “${file}” should include a line containing “${filter}” that also contains “${expected}”."}
+ fi
+ '';
+}
diff --git a/nixpkgs/pkgs/build-support/setup-hooks/wrap-gapps-hook/tests/sample-project/Makefile b/nixpkgs/pkgs/build-support/setup-hooks/wrap-gapps-hook/tests/sample-project/Makefile
new file mode 100644
index 00000000000..5d234db11a0
--- /dev/null
+++ b/nixpkgs/pkgs/build-support/setup-hooks/wrap-gapps-hook/tests/sample-project/Makefile
@@ -0,0 +1,30 @@
+PREFIX = $(out)
+BINDIR = $(PREFIX)/bin
+LIBEXECDIR = $(PREFIX)/libexec
+LIBDIR = $(PREFIX)/lib
+TYPELIBDIR = $(LIBDIR)/girepository-1.0
+
+all:
+ echo "Compiling…"
+install:
+ echo "Installing…"
+
+bin:
+ mkdir -p $(BINDIR)
+# Adds `bin-${foo}` targets, that install `${foo}` executable to `$(BINDIR)`.
+bin-%: bin
+ touch $(BINDIR)/$(@:bin-%=%)
+ chmod +x $(BINDIR)/$(@:bin-%=%)
+
+libexec:
+ mkdir -p $(LIBEXECDIR)
+# Adds `libexec-${foo}` targets, that install `${foo}` executable to `$(LIBEXECDIR)`.
+libexec-%: libexec
+ touch $(LIBEXECDIR)/$(@:libexec-%=%)
+ chmod +x $(LIBEXECDIR)/$(@:libexec-%=%)
+
+typelib:
+ mkdir -p $(TYPELIBDIR)
+# Adds `typelib-${foo}` targets, that install `${foo}-1.0.typelib` file to `$(TYPELIBDIR)`.
+typelib-%: typelib
+ touch $(TYPELIBDIR)/$(@:typelib-%=%)-1.0.typelib
diff --git a/nixpkgs/pkgs/build-support/setup-hooks/wrap-gapps-hook/wrap-gapps-hook.sh b/nixpkgs/pkgs/build-support/setup-hooks/wrap-gapps-hook/wrap-gapps-hook.sh
new file mode 100644
index 00000000000..1a46e075dbe
--- /dev/null
+++ b/nixpkgs/pkgs/build-support/setup-hooks/wrap-gapps-hook/wrap-gapps-hook.sh
@@ -0,0 +1,93 @@
+# shellcheck shell=bash
+gappsWrapperArgs=()
+
+find_gio_modules() {
+ if [ -d "$1/lib/gio/modules" ] && [ -n "$(ls -A "$1/lib/gio/modules")" ] ; then
+ gappsWrapperArgs+=(--prefix GIO_EXTRA_MODULES : "$1/lib/gio/modules")
+ fi
+}
+
+addEnvHooks "${targetOffset:?}" find_gio_modules
+
+gappsWrapperArgsHook() {
+ if [ -n "$GDK_PIXBUF_MODULE_FILE" ]; then
+ gappsWrapperArgs+=(--set GDK_PIXBUF_MODULE_FILE "$GDK_PIXBUF_MODULE_FILE")
+ fi
+
+ if [ -n "$XDG_ICON_DIRS" ]; then
+ gappsWrapperArgs+=(--prefix XDG_DATA_DIRS : "$XDG_ICON_DIRS")
+ fi
+
+ if [ -n "$GSETTINGS_SCHEMAS_PATH" ]; then
+ gappsWrapperArgs+=(--prefix XDG_DATA_DIRS : "$GSETTINGS_SCHEMAS_PATH")
+ fi
+
+ # Check for prefix as well
+ if [ -d "${prefix:?}/share" ]; then
+ gappsWrapperArgs+=(--prefix XDG_DATA_DIRS : "$prefix/share")
+ fi
+
+ if [ -d "$prefix/lib/gio/modules" ] && [ -n "$(ls -A "$prefix/lib/gio/modules")" ]; then
+ gappsWrapperArgs+=(--prefix GIO_EXTRA_MODULES : "$prefix/lib/gio/modules")
+ fi
+
+ for v in ${wrapPrefixVariables:-} GST_PLUGIN_SYSTEM_PATH_1_0 GI_TYPELIB_PATH GRL_PLUGIN_PATH; do
+ if [ -n "${!v}" ]; then
+ gappsWrapperArgs+=(--prefix "$v" : "${!v}")
+ fi
+ done
+}
+
+preFixupPhases+=" gappsWrapperArgsHook"
+
+wrapGApp() {
+ local program="$1"
+ shift 1
+ wrapProgram "$program" "${gappsWrapperArgs[@]}" "$@"
+}
+
+# Note: $gappsWrapperArgs still gets defined even if ${dontWrapGApps-} is set.
+wrapGAppsHook() {
+ # guard against running multiple times (e.g. due to propagation)
+ [ -z "$wrapGAppsHookHasRun" ] || return 0
+ wrapGAppsHookHasRun=1
+
+ if [[ -z "${dontWrapGApps:-}" ]]; then
+ targetDirsThatExist=()
+ targetDirsRealPath=()
+
+ # wrap binaries
+ targetDirs=("${prefix}/bin" "${prefix}/libexec")
+ for targetDir in "${targetDirs[@]}"; do
+ if [[ -d "${targetDir}" ]]; then
+ targetDirsThatExist+=("${targetDir}")
+ targetDirsRealPath+=("$(realpath "${targetDir}")/")
+ find "${targetDir}" -type f -executable -print0 |
+ while IFS= read -r -d '' file; do
+ echo "Wrapping program '${file}'"
+ wrapGApp "${file}"
+ done
+ fi
+ done
+
+ # 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
+ if [[ ${#targetDirsThatExist[@]} -ne 0 ]]; then
+ find "${targetDirsThatExist[@]}" -type l -xtype f -executable -print0 |
+ while IFS= read -r -d '' linkPath; do
+ linkPathReal=$(realpath "${linkPath}")
+ for targetPath in "${targetDirsRealPath[@]}"; do
+ if [[ "$linkPathReal" == "$targetPath"* ]]; then
+ echo "Not wrapping link: '$linkPath' (already wrapped)"
+ continue 2
+ fi
+ done
+ echo "Wrapping link: '$linkPath'"
+ wrapGApp "${linkPath}"
+ done
+ fi
+ fi
+}
+
+fixupOutputHooks+=(wrapGAppsHook)