summary refs log tree commit diff
path: root/pkgs/build-support/setup-hooks
diff options
context:
space:
mode:
Diffstat (limited to 'pkgs/build-support/setup-hooks')
-rw-r--r--pkgs/build-support/setup-hooks/auto-patchelf.sh80
-rw-r--r--pkgs/build-support/setup-hooks/copy-desktop-items.sh42
-rw-r--r--pkgs/build-support/setup-hooks/reproducible-builds.sh4
-rw-r--r--pkgs/build-support/setup-hooks/wrap-gapps-hook/default.nix4
4 files changed, 98 insertions, 32 deletions
diff --git a/pkgs/build-support/setup-hooks/auto-patchelf.sh b/pkgs/build-support/setup-hooks/auto-patchelf.sh
index 4f7c0c14304c9..49e84f84ceb33 100644
--- a/pkgs/build-support/setup-hooks/auto-patchelf.sh
+++ b/pkgs/build-support/setup-hooks/auto-patchelf.sh
@@ -1,9 +1,16 @@
 declare -a autoPatchelfLibs
+declare -Ag autoPatchelfFailedDeps
 
 gatherLibraries() {
     autoPatchelfLibs+=("$1/lib")
 }
 
+# wrapper around patchelf to raise proper error messages
+# containing the tried file name and command
+runPatchelf() {
+  patchelf "$@" || (echo "Command failed: patchelf $*" && exit 1)
+}
+
 addEnvHooks "$targetOffset" gatherLibraries
 
 isExecutable() {
@@ -23,14 +30,19 @@ isExecutable() {
 
 # We cache dependencies so that we don't need to search through all of them on
 # every consecutive call to findDependency.
-declare -a cachedDependencies
+declare -Ag autoPatchelfCachedDepsAssoc
+declare -ag autoPatchelfCachedDeps
+
 
 addToDepCache() {
-    local existing
-    for existing in "${cachedDependencies[@]}"; do
-        if [ "$existing" = "$1" ]; then return; fi
-    done
-    cachedDependencies+=("$1")
+    if [[ ${autoPatchelfCachedDepsAssoc[$1]+f} ]]; then return; fi
+
+    # store deps in an assoc. array for efficient lookups
+    # otherwise findDependency would have quadratic complexity
+    autoPatchelfCachedDepsAssoc["$1"]=""
+
+    # also store deps in normal array to maintain their order
+    autoPatchelfCachedDeps+=("$1")
 }
 
 declare -gi depCacheInitialised=0
@@ -43,9 +55,8 @@ getDepsFromSo() {
 
 populateCacheWithRecursiveDeps() {
     local so found foundso
-    for so in "${cachedDependencies[@]}"; do
+    for so in "${autoPatchelfCachedDeps[@]}"; do
         for found in $(getDepsFromSo "$so"); do
-            local libdir="${found%/*}"
             local base="${found##*/}"
             local soname="${base%.so*}"
             for foundso in "${found%/*}/$soname".so*; do
@@ -76,7 +87,7 @@ findDependency() {
         depCacheInitialised=1
     fi
 
-    for dep in "${cachedDependencies[@]}"; do
+    for dep in "${autoPatchelfCachedDeps[@]}"; do
         if [ "$filename" = "${dep##*/}" ]; then
             if [ "$(getSoArch "$dep")" = "$arch" ]; then
                 foundDependency="$dep"
@@ -101,9 +112,10 @@ findDependency() {
 autoPatchelfFile() {
     local dep rpath="" toPatch="$1"
 
-    local interpreter="$(< "$NIX_CC/nix-support/dynamic-linker")"
+    local interpreter
+    interpreter="$(< "$NIX_CC/nix-support/dynamic-linker")"
     if isExecutable "$toPatch"; then
-        patchelf --set-interpreter "$interpreter" "$toPatch"
+        runPatchelf --set-interpreter "$interpreter" "$toPatch"
         if [ -n "$runtimeDependencies" ]; then
             for dep in $runtimeDependencies; do
                 rpath="$rpath${rpath:+:}$dep/lib"
@@ -115,9 +127,10 @@ autoPatchelfFile() {
 
     # We're going to find all dependencies based on ldd output, so we need to
     # clear the RPATH first.
-    patchelf --remove-rpath "$toPatch"
+    runPatchelf --remove-rpath "$toPatch"
 
-    local missing="$(
+    local missing
+    missing="$(
         ldd "$toPatch" 2> /dev/null | \
             sed -n -e 's/^[\t ]*\([^ ]\+\) => not found.*/\1/p'
     )"
@@ -125,7 +138,6 @@ autoPatchelfFile() {
     # This ensures that we get the output of all missing dependencies instead
     # of failing at the first one, because it's more useful when working on a
     # new package where you don't yet know its dependencies.
-    local -i depNotFound=0
 
     for dep in $missing; do
         echo -n "  $dep -> " >&2
@@ -134,18 +146,13 @@ autoPatchelfFile() {
             echo "found: $foundDependency" >&2
         else
             echo "not found!" >&2
-            depNotFound=1
+            autoPatchelfFailedDeps["$dep"]="$toPatch"
         fi
     done
 
-    # This makes sure the builder fails if we didn't find a dependency, because
-    # the stdenv setup script is run with set -e. The actual error is emitted
-    # earlier in the previous loop.
-    [ $depNotFound -eq 0 -o -n "$autoPatchelfIgnoreMissingDeps" ]
-
     if [ -n "$rpath" ]; then
         echo "setting RPATH to: $rpath" >&2
-        patchelf --set-rpath "$rpath" "$toPatch"
+        runPatchelf --set-rpath "$rpath" "$toPatch"
     fi
 }
 
@@ -168,10 +175,10 @@ addAutoPatchelfSearchPath() {
         esac
     done
 
-    cachedDependencies+=(
-        $(find "$@" "${findOpts[@]}" \! -type d \
-               \( -name '*.so' -o -name '*.so.*' \))
-    )
+    for file in \
+      $(find "$@" "${findOpts[@]}" \! -type d \
+          \( -name '*.so' -o -name '*.so.*' \))
+    do addToDepCache "$file"; done
 }
 
 autoPatchelf() {
@@ -197,14 +204,9 @@ autoPatchelf() {
     echo "automatically fixing dependencies for ELF files" >&2
 
     # Add all shared objects of the current output path to the start of
-    # cachedDependencies so that it's choosen first in findDependency.
+    # autoPatchelfCachedDeps so that it's chosen first in findDependency.
     addAutoPatchelfSearchPath ${norecurse:+--no-recurse} -- "$@"
 
-    # Here we actually have a subshell, which also means that
-    # $cachedDependencies is final at this point, so whenever we want to run
-    # findDependency outside of this, the dependency cache needs to be rebuilt
-    # from scratch, so keep this in mind if you want to run findDependency
-    # outside of this function.
     while IFS= read -r -d $'\0' file; do
       isELF "$file" || continue
       segmentHeaders="$(LANG=C $READELF -l "$file")"
@@ -215,8 +217,24 @@ autoPatchelf() {
           # Skip if the executable is statically linked.
           [ -n "$(echo "$segmentHeaders" | grep "^ *INTERP\\>")" ] || continue
       fi
+      # Jump file if patchelf is unable to parse it
+      # Some programs contain binary blobs for testing,
+      # which are identified as ELF but fail to be parsed by patchelf
+      patchelf "$file" || continue
       autoPatchelfFile "$file"
     done < <(find "$@" ${norecurse:+-maxdepth 1} -type f -print0)
+
+    # fail if any dependencies were not found and
+    # autoPatchelfIgnoreMissingDeps is not set
+    local depsMissing=0
+    for failedDep in "${!autoPatchelfFailedDeps[@]}"; do
+      echo "autoPatchelfHook could not satisfy dependency $failedDep wanted by ${autoPatchelfFailedDeps[$failedDep]}"
+      depsMissing=1
+    done
+    if [[ $depsMissing == 1 && -z "$autoPatchelfIgnoreMissingDeps" ]]; then
+      echo "Add the missing dependencies to the build inputs or set autoPatchelfIgnoreMissingDeps=true"
+      exit 1
+    fi
 }
 
 # XXX: This should ultimately use fixupOutputHooks but we currently don't have
diff --git a/pkgs/build-support/setup-hooks/copy-desktop-items.sh b/pkgs/build-support/setup-hooks/copy-desktop-items.sh
new file mode 100644
index 0000000000000..f96a10f33d5cb
--- /dev/null
+++ b/pkgs/build-support/setup-hooks/copy-desktop-items.sh
@@ -0,0 +1,42 @@
+# shellcheck shell=bash
+
+# Setup hook that installs specified desktop items.
+#
+# Example usage in a derivation:
+#
+#   { …, makeDesktopItem, copyDesktopItems, … }:
+#
+#   let desktopItem = makeDesktopItem { … }; in
+#   stdenv.mkDerivation {
+#     …
+#     nativeBuildInputs = [ copyDesktopItems ];
+#
+#     desktopItems =  [ desktopItem ];
+#     …
+#   }
+#
+# This hook will copy files which are either given by full path
+# or all '*.desktop' files placed inside the 'share/applications'
+# folder of each `desktopItems` argument.
+
+postInstallHooks+=(copyDesktopItems)
+
+copyDesktopItems() {
+    if [ "${dontCopyDesktopItems-}" = 1 ]; then return; fi
+
+    if [ -z "$desktopItems" ]; then
+        return
+    fi
+
+    for desktopItem in $desktopItems; do
+        if [[ -f "$desktopItem" ]]; then
+            echo "Copying '$f' into '$out/share/applications'"
+            install -D -m 444 -t "$out"/share/applications "$f"
+        else
+            for f in "$desktopItem"/share/applications/*.desktop; do
+                echo "Copying '$f' into '$out/share/applications'"
+                install -D -m 444 -t "$out"/share/applications "$f"
+            done
+        fi
+    done
+}
diff --git a/pkgs/build-support/setup-hooks/reproducible-builds.sh b/pkgs/build-support/setup-hooks/reproducible-builds.sh
new file mode 100644
index 0000000000000..2d8db6ff7d3cf
--- /dev/null
+++ b/pkgs/build-support/setup-hooks/reproducible-builds.sh
@@ -0,0 +1,4 @@
+# Use the last part of the out path as hash input for the build.
+# This should ensure that it is deterministic across rebuilds of the same
+# derivation and not easily collide with other builds.
+export NIX_CFLAGS_COMPILE+=" -frandom-seed=${out##*/}"
diff --git a/pkgs/build-support/setup-hooks/wrap-gapps-hook/default.nix b/pkgs/build-support/setup-hooks/wrap-gapps-hook/default.nix
index 5a87893d9726f..d0ea088bf71e8 100644
--- a/pkgs/build-support/setup-hooks/wrap-gapps-hook/default.nix
+++ b/pkgs/build-support/setup-hooks/wrap-gapps-hook/default.nix
@@ -3,6 +3,7 @@
 , makeSetupHook
 , makeWrapper
 , gobject-introspection
+, isGraphical ? true
 , gtk3
 , librsvg
 , dconf
@@ -21,7 +22,7 @@ makeSetupHook {
     # Unfortunately, it also requires the user to have dconf
     # D-Bus service enabled globally (e.g. through a NixOS module).
     dconf.lib
-  ] ++ [
+  ] ++ lib.optionals isGraphical [
     # TODO: remove this, packages should depend on GTK explicitly.
     gtk3
 
@@ -30,6 +31,7 @@ makeSetupHook {
     # 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