about summary refs log tree commit diff
path: root/nixpkgs/pkgs/build-support
diff options
context:
space:
mode:
Diffstat (limited to 'nixpkgs/pkgs/build-support')
-rw-r--r--nixpkgs/pkgs/build-support/alternatives/blas/default.nix31
-rw-r--r--nixpkgs/pkgs/build-support/alternatives/lapack/default.nix29
-rw-r--r--nixpkgs/pkgs/build-support/appimage/default.nix2
-rw-r--r--nixpkgs/pkgs/build-support/build-fhs-userenv-bubblewrap/env.nix26
-rw-r--r--nixpkgs/pkgs/build-support/build-fhs-userenv/env.nix26
-rw-r--r--nixpkgs/pkgs/build-support/cc-wrapper/add-flags.sh18
-rwxr-xr-xnixpkgs/pkgs/build-support/dotnet/nuget-to-nix/nuget-to-nix.sh2
-rw-r--r--nixpkgs/pkgs/build-support/fetchpatch/default.nix4
-rw-r--r--nixpkgs/pkgs/build-support/setup-hooks/auto-patchelf.py329
-rw-r--r--nixpkgs/pkgs/build-support/setup-hooks/auto-patchelf.sh331
-rw-r--r--nixpkgs/pkgs/build-support/setup-hooks/make-wrapper.sh18
11 files changed, 467 insertions, 349 deletions
diff --git a/nixpkgs/pkgs/build-support/alternatives/blas/default.nix b/nixpkgs/pkgs/build-support/alternatives/blas/default.nix
index 8917784bc6c3..7186675a5c62 100644
--- a/nixpkgs/pkgs/build-support/alternatives/blas/default.nix
+++ b/nixpkgs/pkgs/build-support/alternatives/blas/default.nix
@@ -1,7 +1,7 @@
 { lib, stdenv
-, lapack-reference, openblasCompat, openblas
+, lapack-reference, openblas
 , isILP64 ? false
-, blasProvider ? if isILP64 then openblas else openblasCompat }:
+, blasProvider ? openblas }:
 
 let
   blasFortranSymbols = [
@@ -32,10 +32,13 @@ let
 
 
   blasImplementation = lib.getName blasProvider;
+  blasProvider' = if blasImplementation == "mkl"
+                  then blasProvider
+                  else blasProvider.override { blas64 = isILP64; };
 
 in
 
-assert isILP64 -> (blasImplementation == "openblas" && blasProvider.blas64) || blasImplementation == "mkl";
+assert isILP64 -> blasImplementation == "mkl" || blasProvider'.blas64;
 
 stdenv.mkDerivation {
   pname = "blas";
@@ -43,13 +46,13 @@ stdenv.mkDerivation {
 
   outputs = [ "out" "dev" ];
 
-  meta = (blasProvider.meta or {}) // {
+  meta = (blasProvider'.meta or {}) // {
     description = "${lib.getName blasProvider} with just the BLAS C and FORTRAN ABI";
   };
 
   passthru = {
     inherit isILP64;
-    provider = blasProvider;
+    provider = blasProvider';
     implementation = blasImplementation;
   };
 
@@ -62,10 +65,10 @@ stdenv.mkDerivation {
   installPhase = (''
   mkdir -p $out/lib $dev/include $dev/lib/pkgconfig
 
-  libblas="${lib.getLib blasProvider}/lib/libblas${canonicalExtension}"
+  libblas="${lib.getLib blasProvider'}/lib/libblas${canonicalExtension}"
 
   if ! [ -e "$libblas" ]; then
-    echo "$libblas does not exist, ${blasProvider.name} does not provide libblas."
+    echo "$libblas does not exist, ${blasProvider'.name} does not provide libblas."
     exit 1
   fi
 
@@ -79,11 +82,11 @@ stdenv.mkDerivation {
 
 '' + (if stdenv.hostPlatform.parsed.kernel.execFormat.name == "elf" then ''
   patchelf --set-soname libblas${canonicalExtension} $out/lib/libblas${canonicalExtension}
-  patchelf --set-rpath "$(patchelf --print-rpath $out/lib/libblas${canonicalExtension}):${lib.getLib blasProvider}/lib" $out/lib/libblas${canonicalExtension}
+  patchelf --set-rpath "$(patchelf --print-rpath $out/lib/libblas${canonicalExtension}):${lib.getLib blasProvider'}/lib" $out/lib/libblas${canonicalExtension}
 '' else if stdenv.hostPlatform.isDarwin then ''
   install_name_tool \
     -id $out/lib/libblas${canonicalExtension} \
-    -add_rpath ${lib.getLib blasProvider}/lib \
+    -add_rpath ${lib.getLib blasProvider'}/lib \
     $out/lib/libblas${canonicalExtension}
 '' else "") + ''
 
@@ -99,10 +102,10 @@ Libs: -L$out/lib -lblas
 Cflags: -I$dev/include
 EOF
 
-  libcblas="${lib.getLib blasProvider}/lib/libcblas${canonicalExtension}"
+  libcblas="${lib.getLib blasProvider'}/lib/libcblas${canonicalExtension}"
 
   if ! [ -e "$libcblas" ]; then
-    echo "$libcblas does not exist, ${blasProvider.name} does not provide libcblas."
+    echo "$libcblas does not exist, ${blasProvider'.name} does not provide libcblas."
     exit 1
   fi
 
@@ -111,11 +114,11 @@ EOF
 
 '' + (if stdenv.hostPlatform.parsed.kernel.execFormat.name == "elf" then ''
   patchelf --set-soname libcblas${canonicalExtension} $out/lib/libcblas${canonicalExtension}
-  patchelf --set-rpath "$(patchelf --print-rpath $out/lib/libcblas${canonicalExtension}):${lib.getLib blasProvider}/lib" $out/lib/libcblas${canonicalExtension}
+  patchelf --set-rpath "$(patchelf --print-rpath $out/lib/libcblas${canonicalExtension}):${lib.getLib blasProvider'}/lib" $out/lib/libcblas${canonicalExtension}
 '' else if stdenv.hostPlatform.isDarwin then ''
   install_name_tool \
     -id $out/lib/libcblas${canonicalExtension} \
-    -add_rpath ${lib.getLib blasProvider}/lib \
+    -add_rpath ${lib.getLib blasProvider'}/lib \
     $out/lib/libcblas${canonicalExtension}
 '' else "") + ''
   if [ "$out/lib/libcblas${canonicalExtension}" != "$out/lib/libcblas${stdenv.hostPlatform.extensions.sharedLibrary}" ]; then
@@ -135,6 +138,6 @@ EOF
   mkdir -p $out/nix-support
   echo 'export MKL_INTERFACE_LAYER=${lib.optionalString isILP64 "I"}LP64,GNU' > $out/nix-support/setup-hook
   ln -s $out/lib/libblas${canonicalExtension} $out/lib/libmkl_rt${stdenv.hostPlatform.extensions.sharedLibrary}
-  ln -sf ${blasProvider}/include/* $dev/include
+  ln -sf ${blasProvider'}/include/* $dev/include
 '');
 }
diff --git a/nixpkgs/pkgs/build-support/alternatives/lapack/default.nix b/nixpkgs/pkgs/build-support/alternatives/lapack/default.nix
index 61da183c835d..80fb7be8c1e1 100644
--- a/nixpkgs/pkgs/build-support/alternatives/lapack/default.nix
+++ b/nixpkgs/pkgs/build-support/alternatives/lapack/default.nix
@@ -1,7 +1,7 @@
 { lib, stdenv
-, lapack-reference, openblasCompat, openblas
+, lapack-reference, openblas
 , isILP64 ? false
-, lapackProvider ? if isILP64 then openblas else openblasCompat }:
+, lapackProvider ? openblas }:
 
 let
 
@@ -11,10 +11,13 @@ let
                        else stdenv.hostPlatform.extensions.sharedLibrary;
 
   lapackImplementation = lib.getName lapackProvider;
+  lapackProvider' = if lapackImplementation == "mkl"
+                  then lapackProvider
+                  else lapackProvider.override { blas64 = isILP64; };
 
 in
 
-assert isILP64 -> (lapackImplementation == "openblas" && lapackProvider.blas64) || lapackImplementation == "mkl";
+assert isILP64 -> lapackImplementation == "mkl" || lapackProvider'.blas64;
 
 stdenv.mkDerivation {
   pname = "lapack";
@@ -22,13 +25,13 @@ stdenv.mkDerivation {
 
   outputs = [ "out" "dev" ];
 
-  meta = (lapackProvider.meta or {}) // {
-    description = "${lib.getName lapackProvider} with just the LAPACK C and FORTRAN ABI";
+  meta = (lapackProvider'.meta or {}) // {
+    description = "${lib.getName lapackProvider'} with just the LAPACK C and FORTRAN ABI";
   };
 
   passthru = {
     inherit isILP64;
-    provider = lapackProvider;
+    provider = lapackProvider';
     implementation = lapackImplementation;
   };
 
@@ -41,10 +44,10 @@ stdenv.mkDerivation {
   installPhase = (''
   mkdir -p $out/lib $dev/include $dev/lib/pkgconfig
 
-  liblapack="${lib.getLib lapackProvider}/lib/liblapack${canonicalExtension}"
+  liblapack="${lib.getLib lapackProvider'}/lib/liblapack${canonicalExtension}"
 
   if ! [ -e "$liblapack" ]; then
-    echo "$liblapack does not exist, ${lapackProvider.name} does not provide liblapack."
+    echo "$liblapack does not exist, ${lapackProvider'.name} does not provide liblapack."
     exit 1
   fi
 
@@ -53,7 +56,7 @@ stdenv.mkDerivation {
 
 '' + (if stdenv.hostPlatform.parsed.kernel.execFormat.name == "elf" then ''
   patchelf --set-soname liblapack${canonicalExtension} $out/lib/liblapack${canonicalExtension}
-  patchelf --set-rpath "$(patchelf --print-rpath $out/lib/liblapack${canonicalExtension}):${lapackProvider}/lib" $out/lib/liblapack${canonicalExtension}
+  patchelf --set-rpath "$(patchelf --print-rpath $out/lib/liblapack${canonicalExtension}):${lapackProvider'}/lib" $out/lib/liblapack${canonicalExtension}
 '' else "") + ''
 
   if [ "$out/lib/liblapack${canonicalExtension}" != "$out/lib/liblapack${stdenv.hostPlatform.extensions.sharedLibrary}" ]; then
@@ -70,10 +73,10 @@ Cflags: -I$dev/include
 Libs: -L$out/lib -llapack
 EOF
 
-  liblapacke="${lib.getLib lapackProvider}/lib/liblapacke${canonicalExtension}"
+  liblapacke="${lib.getLib lapackProvider'}/lib/liblapacke${canonicalExtension}"
 
   if ! [ -e "$liblapacke" ]; then
-    echo "$liblapacke does not exist, ${lapackProvider.name} does not provide liblapacke."
+    echo "$liblapacke does not exist, ${lapackProvider'.name} does not provide liblapacke."
     exit 1
   fi
 
@@ -82,7 +85,7 @@ EOF
 
 '' + (if stdenv.hostPlatform.parsed.kernel.execFormat.name == "elf" then ''
   patchelf --set-soname liblapacke${canonicalExtension} $out/lib/liblapacke${canonicalExtension}
-  patchelf --set-rpath "$(patchelf --print-rpath $out/lib/liblapacke${canonicalExtension}):${lib.getLib lapackProvider}/lib" $out/lib/liblapacke${canonicalExtension}
+  patchelf --set-rpath "$(patchelf --print-rpath $out/lib/liblapacke${canonicalExtension}):${lib.getLib lapackProvider'}/lib" $out/lib/liblapacke${canonicalExtension}
 '' else "") + ''
 
   if [ -f "$out/lib/liblapacke.so.3" ]; then
@@ -102,6 +105,6 @@ EOF
   mkdir -p $out/nix-support
   echo 'export MKL_INTERFACE_LAYER=${lib.optionalString isILP64 "I"}LP64,GNU' > $out/nix-support/setup-hook
   ln -s $out/lib/liblapack${canonicalExtension} $out/lib/libmkl_rt${stdenv.hostPlatform.extensions.sharedLibrary}
-  ln -sf ${lapackProvider}/include/* $dev/include
+  ln -sf ${lapackProvider'}/include/* $dev/include
 '');
 }
diff --git a/nixpkgs/pkgs/build-support/appimage/default.nix b/nixpkgs/pkgs/build-support/appimage/default.nix
index 4d302156f707..0bee6ce065ad 100644
--- a/nixpkgs/pkgs/build-support/appimage/default.nix
+++ b/nixpkgs/pkgs/build-support/appimage/default.nix
@@ -68,6 +68,8 @@ rec {
       xdg-utils
       iana-etc
       krb5
+      gsettings-desktop-schemas
+      hicolor-icon-theme # dont show a gtk warning about hicolor not being installed
     ];
 
     # list of libraries expected in an appimage environment:
diff --git a/nixpkgs/pkgs/build-support/build-fhs-userenv-bubblewrap/env.nix b/nixpkgs/pkgs/build-support/build-fhs-userenv-bubblewrap/env.nix
index 1c4e2b607696..d0f88df44537 100644
--- a/nixpkgs/pkgs/build-support/build-fhs-userenv-bubblewrap/env.nix
+++ b/nixpkgs/pkgs/build-support/build-fhs-userenv-bubblewrap/env.nix
@@ -93,6 +93,32 @@ let
     paths = [ etcPkg ldconfig ] ++ basePkgs ++ targetPaths;
     extraOutputsToInstall = [ "out" "lib" "bin" ] ++ extraOutputsToInstall;
     ignoreCollisions = true;
+    postBuild = ''
+      if [[ -d  $out/share/gsettings-schemas/ ]]; then
+          # Recreate the standard schemas directory if its a symlink to make it writable
+          if [[ -L $out/share/glib-2.0 ]]; then
+              ln -s $(readlink $out/share/glib-2.0) $out/share/glib-2.0.old
+              rm -rf $out/share/glib-2.0
+          fi
+
+          mkdir -p $out/share/glib-2.0/schemas
+
+          # symlink any schema files or overrides to the standard schema directory
+          if [[ -e $out/share/glib-2.0.old/schemas ]]; then
+              ln -fs $out/share/glib-2.0.old/schemas/*.xml $out/share/glib-2.0/schemas
+              ln -fs $out/share/glib-2.0.old/schemas/*.gsettings-schemas.override $out/share/glib-2.0/schemas
+          fi
+
+          for d in $out/share/gsettings-schemas/*; do
+              # Force symlink, in case there are duplicates
+              ln -fs $d/glib-2.0/schemas/*.xml $out/share/glib-2.0/schemas
+              ln -fs $d/glib-2.0/schemas/*.gschema.override $out/share/glib-2.0/schemas
+          done
+
+          # and compile them
+          ${pkgs.glib.dev}/bin/glib-compile-schemas $out/share/glib-2.0/schemas
+      fi
+    '';
   };
 
   staticUsrProfileMulti = buildEnv {
diff --git a/nixpkgs/pkgs/build-support/build-fhs-userenv/env.nix b/nixpkgs/pkgs/build-support/build-fhs-userenv/env.nix
index abf577baa2d1..7251500f76c5 100644
--- a/nixpkgs/pkgs/build-support/build-fhs-userenv/env.nix
+++ b/nixpkgs/pkgs/build-support/build-fhs-userenv/env.nix
@@ -134,6 +134,32 @@ let
     paths = [ etcPkg ] ++ basePkgs ++ targetPaths;
     extraOutputsToInstall = [ "out" "lib" "bin" ] ++ extraOutputsToInstall;
     ignoreCollisions = true;
+    postBuild = ''
+      if [[ -d  $out/share/gsettings-schemas/ ]]; then
+          # Recreate the standard schemas directory if its a symlink to make it writable
+          if [[ -L $out/share/glib-2.0 ]]; then
+              ln -s $(readlink $out/share/glib-2.0) $out/share/glib-2.0.old
+              rm -rf $out/share/glib-2.0
+          fi
+
+          mkdir -p $out/share/glib-2.0/schemas
+
+          # symlink any schema files or overrides to the standard schema directory
+          if [[ -e $out/share/glib-2.0.old/schemas ]]; then
+              ln -fs $out/share/glib-2.0.old/schemas/*.xml $out/share/glib-2.0/schemas
+              ln -fs $out/share/glib-2.0.old/schemas/*.gsettings-schemas.override $out/share/glib-2.0/schemas
+          fi
+
+          for d in $out/share/gsettings-schemas/*; do
+              # Force symlink, in case there are duplicates
+              ln -fs $d/glib-2.0/schemas/*.xml $out/share/glib-2.0/schemas
+              ln -fs $d/glib-2.0/schemas/*.gschema.override $out/share/glib-2.0/schemas
+          done
+
+          # and compile them
+          ${pkgs.glib.dev}/bin/glib-compile-schemas $out/share/glib-2.0/schemas
+      fi
+    '';
   };
 
   staticUsrProfileMulti = buildEnv {
diff --git a/nixpkgs/pkgs/build-support/cc-wrapper/add-flags.sh b/nixpkgs/pkgs/build-support/cc-wrapper/add-flags.sh
index a6e991914e6b..c59118d6c09e 100644
--- a/nixpkgs/pkgs/build-support/cc-wrapper/add-flags.sh
+++ b/nixpkgs/pkgs/build-support/cc-wrapper/add-flags.sh
@@ -33,6 +33,20 @@ NIX_CFLAGS_COMPILE_@suffixSalt@="-B@out@/bin/ $NIX_CFLAGS_COMPILE_@suffixSalt@"
 # Export and assign separately in order that a failing $(..) will fail
 # the script.
 
+# Currently bootstrap-tools does not split glibc, and gcc files into
+# separate directories. As a workaround we want resulting cflags to be
+# ordered as: crt1-cflags libc-cflags cc-cflags. Otherwise we mix crt/libc.so
+# from different libc as seen in
+#   https://github.com/NixOS/nixpkgs/issues/158042
+#
+# Note that below has reverse ordering as we prepend flags one-by-one.
+# Once bootstrap-tools is split into different directories we can stop
+# relying on flag ordering below.
+
+if [ -e @out@/nix-support/cc-cflags ]; then
+    NIX_CFLAGS_COMPILE_@suffixSalt@="$(< @out@/nix-support/cc-cflags) $NIX_CFLAGS_COMPILE_@suffixSalt@"
+fi
+
 if [[ "$cInclude" = 1 ]] && [ -e @out@/nix-support/libc-cflags ]; then
     NIX_CFLAGS_COMPILE_@suffixSalt@="$(< @out@/nix-support/libc-cflags) $NIX_CFLAGS_COMPILE_@suffixSalt@"
 fi
@@ -49,10 +63,6 @@ if [ -e @out@/nix-support/libcxx-ldflags ]; then
     NIX_CXXSTDLIB_LINK_@suffixSalt@+=" $(< @out@/nix-support/libcxx-ldflags)"
 fi
 
-if [ -e @out@/nix-support/cc-cflags ]; then
-    NIX_CFLAGS_COMPILE_@suffixSalt@="$(< @out@/nix-support/cc-cflags) $NIX_CFLAGS_COMPILE_@suffixSalt@"
-fi
-
 if [ -e @out@/nix-support/gnat-cflags ]; then
     NIX_GNATFLAGS_COMPILE_@suffixSalt@="$(< @out@/nix-support/gnat-cflags) $NIX_GNATFLAGS_COMPILE_@suffixSalt@"
 fi
diff --git a/nixpkgs/pkgs/build-support/dotnet/nuget-to-nix/nuget-to-nix.sh b/nixpkgs/pkgs/build-support/dotnet/nuget-to-nix/nuget-to-nix.sh
index 2d02767814e0..d2e7882caf6d 100755
--- a/nixpkgs/pkgs/build-support/dotnet/nuget-to-nix/nuget-to-nix.sh
+++ b/nixpkgs/pkgs/build-support/dotnet/nuget-to-nix/nuget-to-nix.sh
@@ -9,7 +9,7 @@ fi
 
 pkgs=$1
 tmpfile=$(mktemp /tmp/nuget-to-nix.XXXXXX)
-trap 'rm -f "$tmpfile" EXIT
+trap "rm -f ${tmpfile}" EXIT
 
 echo "{ fetchNuGet }: ["
 
diff --git a/nixpkgs/pkgs/build-support/fetchpatch/default.nix b/nixpkgs/pkgs/build-support/fetchpatch/default.nix
index 740baa8cff5f..6e25b2d6ecc5 100644
--- a/nixpkgs/pkgs/build-support/fetchpatch/default.nix
+++ b/nixpkgs/pkgs/build-support/fetchpatch/default.nix
@@ -54,9 +54,9 @@ fetchurl ({
       "$tmpfile" > "$out"
 
     if [ ! -s "$out" ]; then
-      echo "error: Filtered patch '$out$' is empty (while the original patch file was not)!" 1>&2
+      echo "error: Filtered patch '$out' is empty (while the original patch file was not)!" 1>&2
       echo "Check your includes and excludes." 1>&2
-      echo "Normalizd patch file was:" 1>&2
+      echo "Normalized patch file was:" 1>&2
       cat "$tmpfile" 1>&2
       exit 1
     fi
diff --git a/nixpkgs/pkgs/build-support/setup-hooks/auto-patchelf.py b/nixpkgs/pkgs/build-support/setup-hooks/auto-patchelf.py
new file mode 100644
index 000000000000..26fd623e3da0
--- /dev/null
+++ b/nixpkgs/pkgs/build-support/setup-hooks/auto-patchelf.py
@@ -0,0 +1,329 @@
+#!/usr/bin/env python3
+
+from collections import defaultdict
+from contextlib import contextmanager
+from dataclasses import dataclass
+from elftools.common.exceptions import ELFError # type: ignore
+from elftools.elf.dynamic import DynamicSection # type: ignore
+from elftools.elf.elffile import ELFFile # type: ignore
+from elftools.elf.enums import ENUM_E_TYPE, ENUM_EI_OSABI # type: ignore
+from itertools import chain
+from pathlib import Path, PurePath
+
+from typing import Tuple, Optional, Iterator, List, DefaultDict, Set
+
+import argparse
+import os
+import pprint
+import subprocess
+import sys
+
+
+
+@contextmanager
+def open_elf(path: Path) -> Iterator[ELFFile]:
+    with path.open('rb') as stream:
+        yield ELFFile(stream)
+
+
+def is_static_executable(elf: ELFFile) -> bool:
+    # Statically linked executables have an ELF type of EXEC but no INTERP.
+    return (elf.header["e_type"] == 'ET_EXEC'
+            and not elf.get_section_by_name(".interp"))
+
+
+def is_dynamic_executable(elf: ELFFile) -> bool:
+    # We do not require an ELF type of EXEC. This also catches
+    # position-independent executables, as they typically have an INTERP
+    # section but their ELF type is DYN.
+    return bool(elf.get_section_by_name(".interp"))
+
+
+def get_dependencies(elf: ELFFile) -> List[str]:
+    dependencies = []
+    # This convoluted code is here on purpose. For some reason, using
+    # elf.get_section_by_name(".dynamic") does not always return an
+    # instance of DynamicSection, but that is required to call iter_tags
+    for section in elf.iter_sections():
+        if isinstance(section, DynamicSection):
+            for tag in section.iter_tags('DT_NEEDED'):
+                dependencies.append(tag.needed)
+            break # There is only one dynamic section
+
+    return dependencies
+
+
+def get_rpath(elf: ELFFile) -> List[str]:
+    # This convoluted code is here on purpose. For some reason, using
+    # elf.get_section_by_name(".dynamic") does not always return an
+    # instance of DynamicSection, but that is required to call iter_tags
+    for section in elf.iter_sections():
+        if isinstance(section, DynamicSection):
+            for tag in section.iter_tags('DT_RUNPATH'):
+                return tag.runpath.split(':')
+
+            for tag in section.iter_tags('DT_RPATH'):
+                return tag.rpath.split(':')
+
+            break # There is only one dynamic section
+
+    return []
+
+
+def get_arch(elf: ELFFile) -> str:
+    return elf.get_machine_arch()
+
+
+def get_osabi(elf: ELFFile) -> str:
+    return elf.header["e_ident"]["EI_OSABI"]
+
+
+def osabi_are_compatible(wanted: str, got: str) -> bool:
+    """
+    Tests whether two OS ABIs are compatible, taking into account the
+    generally accepted compatibility of SVR4 ABI with other ABIs.
+    """
+    if not wanted or not got:
+        # One of the types couldn't be detected, so as a fallback we'll
+        # assume they're compatible.
+        return True
+
+    # Generally speaking, the base ABI (0x00), which is represented by
+    # readelf(1) as "UNIX - System V", indicates broad compatibility
+    # with other ABIs.
+    #
+    # TODO: This isn't always true. For example, some OSes embed ABI
+    # compatibility into SHT_NOTE sections like .note.tag and
+    # .note.ABI-tag.  It would be prudent to add these to the detection
+    # logic to produce better ABI information.
+    if wanted == 'ELFOSABI_SYSV':
+        return True
+
+    # Similarly here, we should be able to link against a superset of
+    # features, so even if the target has another ABI, this should be
+    # fine.
+    if got == 'ELFOSABI_SYSV':
+        return True
+
+    # Otherwise, we simply return whether the ABIs are identical.
+    return wanted == got
+
+
+def glob(path: Path, pattern: str, recursive: bool) -> Iterator[Path]:
+    return path.rglob(pattern) if recursive else path.glob(pattern)
+
+
+cached_paths: Set[Path] = set()
+soname_cache: DefaultDict[Tuple[str, str], List[Tuple[Path, str]]] = defaultdict(list)
+
+
+def populate_cache(initial: List[Path], recursive: bool =False) -> None:
+    lib_dirs = list(initial)
+
+    while lib_dirs:
+        lib_dir = lib_dirs.pop(0)
+
+        if lib_dir in cached_paths:
+            continue
+
+        cached_paths.add(lib_dir)
+
+        for path in glob(lib_dir, "*.so*", recursive):
+            if not path.is_file():
+                continue
+
+            resolved = path.resolve()
+            try:
+                with open_elf(path) as elf:
+                    osabi = get_osabi(elf)
+                    arch = get_arch(elf)
+                    rpath = [Path(p) for p in get_rpath(elf)
+                                     if p and '$ORIGIN' not in p]
+                    lib_dirs += rpath
+                    soname_cache[(path.name, arch)].append((resolved.parent, osabi))
+
+            except ELFError:
+                # Not an ELF file in the right format
+                pass
+
+
+def find_dependency(soname: str, soarch: str, soabi: str) -> Optional[Path]:
+    for lib, libabi in soname_cache[(soname, soarch)]:
+        if osabi_are_compatible(soabi, libabi):
+            return lib
+    return None
+
+
+@dataclass
+class Dependency:
+    file: Path              # The file that contains the dependency
+    name: Path              # The name of the dependency
+    found: bool = False     # Whether it was found somewhere
+
+
+def auto_patchelf_file(path: Path, runtime_deps: list[Path]) -> list[Dependency]:
+    try:
+        with open_elf(path) as elf:
+
+            if is_static_executable(elf):
+                # No point patching these
+                print(f"skipping {path} because it is statically linked")
+                return []
+
+            if elf.num_segments() == 0:
+                # no segment (e.g. object file)
+                print(f"skipping {path} because it contains no segment")
+                return []
+
+            file_arch = get_arch(elf)
+            if interpreter_arch != file_arch:
+                # Our target architecture is different than this file's
+                # architecture, so skip it.
+                print(f"skipping {path} because its architecture ({file_arch})"
+                      f" differs from target ({interpreter_arch})")
+                return []
+
+            file_osabi = get_osabi(elf)
+            if not osabi_are_compatible(interpreter_osabi, file_osabi):
+                print(f"skipping {path} because its OS ABI ({file_osabi}) is"
+                      f" not compatible with target ({interpreter_osabi})")
+                return []
+
+            file_is_dynamic_executable = is_dynamic_executable(elf)
+
+            file_dependencies = map(Path, get_dependencies(elf))
+
+    except ELFError:
+        return []
+
+    rpath = []
+    if file_is_dynamic_executable:
+        print("setting interpreter of", path)
+        subprocess.run(
+                ["patchelf", "--set-interpreter", interpreter_path.as_posix(), path.as_posix()],
+                check=True)
+        rpath += runtime_deps
+
+    print("searching for dependencies of", path)
+    dependencies = []
+    # Be sure to 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 the dependencies.
+    for dep in file_dependencies:
+        if dep.is_absolute() and dep.is_file():
+            # This is an absolute path. If it exists, just use it.
+            # Otherwise, we probably want this to produce an error when
+            # checked (because just updating the rpath won't satisfy
+            # it).
+            continue
+        elif (libc_lib / dep).is_file():
+            # This library exists in libc, and will be correctly
+            # resolved by the linker.
+            continue
+
+        if found_dependency := find_dependency(dep.name, file_arch, file_osabi):
+            rpath.append(found_dependency)
+            dependencies.append(Dependency(path, dep, True))
+            print(f"    {dep} -> found: {found_dependency}")
+        else:
+            dependencies.append(Dependency(path, dep, False))
+            print(f"    {dep} -> not found!")
+
+    # Dedup the rpath
+    rpath_str = ":".join(dict.fromkeys(map(Path.as_posix, rpath)))
+
+    if rpath:
+        print("setting RPATH to:", rpath_str)
+        subprocess.run(
+                ["patchelf", "--set-rpath", rpath_str, path.as_posix()],
+                check=True)
+
+    return dependencies
+
+
+def auto_patchelf(
+        paths_to_patch: List[Path],
+        lib_dirs: List[Path],
+        runtime_deps: List[Path],
+        recursive: bool =True,
+        ignore_missing: bool =False) -> None:
+
+    if not paths_to_patch:
+        sys.exit("No paths to patch, stopping.")
+
+    # Add all shared objects of the current output path to the cache,
+    # before lib_dirs, so that they are chosen first in find_dependency.
+    populate_cache(paths_to_patch, recursive)
+    populate_cache(lib_dirs)
+
+    dependencies = []
+    for path in chain.from_iterable(glob(p, '*', recursive) for p in paths_to_patch):
+        if not path.is_symlink() and path.is_file():
+            dependencies += auto_patchelf_file(path, runtime_deps)
+
+    missing = [dep for dep in dependencies if not dep.found]
+
+    # Print a summary of the missing dependencies at the end
+    for dep in missing:
+        print(f"auto-patchelf could not satisfy dependency {dep.name} wanted by {dep.file}")
+
+    if missing and not ignore_missing:
+        sys.exit('auto-patchelf failed to find all the required dependencies.\n'
+                 'Add the missing dependencies to --libs or use --ignore-missing.')
+
+
+def main() -> None:
+    parser = argparse.ArgumentParser(
+        prog="auto-patchelf",
+        description='auto-patchelf tries as hard as possible to patch the'
+                    ' provided binary files by looking for compatible'
+                    'libraries in the provided paths.')
+    parser.add_argument(
+        "--ignore-missing",
+        action="store_true",
+        help="Do not fail when some dependencies are not found.")
+    parser.add_argument(
+        "--no-recurse",
+        dest="recursive",
+        action="store_false",
+        help="Patch only the provided paths, and ignore their children")
+    parser.add_argument(
+        "--paths", nargs="*", type=Path,
+        help="Paths whose content needs to be patched.")
+    parser.add_argument(
+        "--libs", nargs="*", type=Path,
+        help="Paths where libraries are searched for.")
+    parser.add_argument(
+        "--runtime-dependencies", nargs="*", type=Path,
+        help="Paths to prepend to the runtime path of executable binaries.")
+
+    print("automatically fixing dependencies for ELF files")
+    args = parser.parse_args()
+    pprint.pprint(vars(args))
+
+    auto_patchelf(
+        args.paths,
+        args.libs,
+        args.runtime_dependencies,
+        args.recursive,
+        args.ignore_missing)
+
+
+interpreter_path: Path  = None # type: ignore
+interpreter_osabi: str  = None # type: ignore
+interpreter_arch: str   = None # type: ignore
+libc_lib: Path          = None # type: ignore
+
+if __name__ == "__main__":
+    nix_support = Path(os.environ['NIX_BINTOOLS']) / 'nix-support'
+    interpreter_path = Path((nix_support / 'dynamic-linker').read_text().strip())
+    libc_lib = Path((nix_support / 'orig-libc').read_text().strip()) / 'lib'
+
+    with open_elf(interpreter_path) as interpreter:
+        interpreter_osabi = get_osabi(interpreter)
+        interpreter_arch = get_arch(interpreter)
+
+    if interpreter_arch and interpreter_osabi and interpreter_path and libc_lib:
+        main()
+    else:
+        sys.exit("Failed to parse dynamic linker (ld) properties.")
diff --git a/nixpkgs/pkgs/build-support/setup-hooks/auto-patchelf.sh b/nixpkgs/pkgs/build-support/setup-hooks/auto-patchelf.sh
index 4b3a1c5c3909..9822674196ae 100644
--- a/nixpkgs/pkgs/build-support/setup-hooks/auto-patchelf.sh
+++ b/nixpkgs/pkgs/build-support/setup-hooks/auto-patchelf.sh
@@ -1,289 +1,21 @@
 #!/usr/bin/env bash
 
 declare -a autoPatchelfLibs
-declare -Ag autoPatchelfFailedDeps
+declare -a extraAutoPatchelfLibs
 
 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)
-}
-
 # shellcheck disable=SC2154
 # (targetOffset is referenced but not assigned.)
 addEnvHooks "$targetOffset" gatherLibraries
 
-isExecutable() {
-    # For dynamically linked ELF files it would be enough to check just for the
-    # INTERP section. However, we won't catch statically linked executables as
-    # they only have an ELF type of EXEC but no INTERP.
-    #
-    # So what we do here is just check whether *either* the ELF type is EXEC
-    # *or* there is an INTERP section. This also catches position-independent
-    # executables, as they typically have an INTERP section but their ELF type
-    # is DYN.
-    isExeResult="$(LANG=C $READELF -h -l "$1" 2> /dev/null \
-        | grep '^ *Type: *EXEC\>\|^ *INTERP\>')"
-    # not using grep -q, because it can cause Broken pipe
-    # https://unix.stackexchange.com/questions/305547/broken-pipe-when-grepping-output-but-only-with-i-flag
-    [ -n "$isExeResult" ]
-}
-
-# We cache dependencies so that we don't need to search through all of them on
-# every consecutive call to findDependency.
-declare -Ag autoPatchelfCachedDepsAssoc
-declare -ag autoPatchelfCachedDeps
-
-addToDepCache() {
-    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
-declare -gi doneRecursiveSearch=0
-declare -g foundDependency
-
-getDepsFromElfBinary() {
-    # NOTE: This does not use runPatchelf because it may encounter non-ELF
-    # files. Caller is expected to check the return code if needed.
-    patchelf --print-needed "$1" 2> /dev/null
-}
-
-getRpathFromElfBinary() {
-    # NOTE: This does not use runPatchelf because it may encounter non-ELF
-    # files. Caller is expected to check the return code if needed.
-    local rpath
-    IFS=':' read -ra rpath < <(patchelf --print-rpath "$1" 2> /dev/null) || return $?
-
-    printf "%s\n" "${rpath[@]}"
-}
-
-populateCacheForDep() {
-    local so="$1"
-    local rpath found
-    rpath="$(getRpathFromElfBinary "$so")" || return 1
-
-    for found in $(getDepsFromElfBinary "$so"); do
-        local rpathElem
-        for rpathElem in $rpath; do
-            # Ignore empty element or $ORIGIN magic variable which should be
-            # deterministically resolved by adding this package's library
-            # files early anyway.
-            #
-            # shellcheck disable=SC2016
-            # (Expressions don't expand in single quotes, use double quotes for
-            # that.)
-            if [[ -z "$rpathElem" || "$rpathElem" == *'$ORIGIN'* ]]; then
-                continue
-            fi
-
-            local soname="${found%.so*}"
-            local foundso=
-            for foundso in "$rpathElem/$soname".so*; do
-                addToDepCache "$foundso"
-            done
-
-            # Found in this element of the rpath, no need to check others.
-            if [ -n "$foundso" ]; then
-                break
-            fi
-        done
-    done
-
-    # Not found in any rpath element.
-    return 1
-}
-
-populateCacheWithRecursiveDeps() {
-    # Dependencies may add more to the end of this array, so we use a counter
-    # with while instead of a regular for loop here.
-    local -i i=0
-    while [ $i -lt ${#autoPatchelfCachedDeps[@]} ]; do
-        populateCacheForDep "${autoPatchelfCachedDeps[$i]}"
-        i=$i+1
-    done
-}
-
-getBinArch() {
-    $OBJDUMP -f "$1" 2> /dev/null | sed -ne 's/^architecture: *\([^,]\+\).*/\1/p'
-}
-
-# Returns the specific OS ABI for an ELF file in the format produced by
-# readelf(1), like "UNIX - System V" or "UNIX - GNU".
-getBinOsabi() {
-    $READELF -h "$1" 2> /dev/null | sed -ne 's/^[ \t]*OS\/ABI:[ \t]*\(.*\)/\1/p'
-}
-
-# Tests whether two OS ABIs are compatible, taking into account the generally
-# accepted compatibility of SVR4 ABI with other ABIs.
-areBinOsabisCompatible() {
-    local wanted="$1"
-    local got="$2"
-
-    if [[ -z "$wanted" || -z "$got" ]]; then
-        # One of the types couldn't be detected, so as a fallback we'll assume
-        # they're compatible.
-        return 0
-    fi
-
-    # Generally speaking, the base ABI (0x00), which is represented by
-    # readelf(1) as "UNIX - System V", indicates broad compatibility with other
-    # ABIs.
-    #
-    # TODO: This isn't always true. For example, some OSes embed ABI
-    # compatibility into SHT_NOTE sections like .note.tag and .note.ABI-tag.
-    # It would be prudent to add these to the detection logic to produce better
-    # ABI information.
-    if [[ "$wanted" == "UNIX - System V" ]]; then
-        return 0
-    fi
-
-    # Similarly here, we should be able to link against a superset of features,
-    # so even if the target has another ABI, this should be fine.
-    if [[ "$got" == "UNIX - System V" ]]; then
-        return 0
-    fi
-
-    # Otherwise, we simply return whether the ABIs are identical.
-    if [[ "$wanted" == "$got" ]]; then
-        return 0
-    fi
-
-    return 1
-}
-
-# NOTE: If you want to use this function outside of the autoPatchelf function,
-# keep in mind that the dependency cache is only valid inside the subshell
-# spawned by the autoPatchelf function, so invoking this directly will possibly
-# rebuild the dependency cache. See the autoPatchelf function below for more
-# information.
-findDependency() {
-    local filename="$1"
-    local arch="$2"
-    local osabi="$3"
-    local lib dep
-
-    if [ $depCacheInitialised -eq 0 ]; then
-        for lib in "${autoPatchelfLibs[@]}"; do
-            for so in "$lib/"*.so*; do addToDepCache "$so"; done
-        done
-        depCacheInitialised=1
-    fi
-
-    for dep in "${autoPatchelfCachedDeps[@]}"; do
-        if [ "$filename" = "${dep##*/}" ]; then
-            if [ "$(getBinArch "$dep")" = "$arch" ] && areBinOsabisCompatible "$osabi" "$(getBinOsabi "$dep")"; then
-                foundDependency="$dep"
-                return 0
-            fi
-        fi
-    done
-
-    # Populate the dependency cache with recursive dependencies *only* if we
-    # didn't find the right dependency so far and afterwards run findDependency
-    # again, but this time with $doneRecursiveSearch set to 1 so that it won't
-    # recurse again (and thus infinitely).
-    if [ $doneRecursiveSearch -eq 0 ]; then
-        populateCacheWithRecursiveDeps
-        doneRecursiveSearch=1
-        findDependency "$filename" "$arch" || return 1
-        return 0
-    fi
-    return 1
-}
-
-autoPatchelfFile() {
-    local dep rpath="" toPatch="$1"
-
-    local interpreter
-    interpreter="$(< "$NIX_BINTOOLS/nix-support/dynamic-linker")"
-
-    local interpreterArch interpreterOsabi toPatchArch toPatchOsabi
-    interpreterArch="$(getBinArch "$interpreter")"
-    interpreterOsabi="$(getBinOsabi "$interpreter")"
-    toPatchArch="$(getBinArch "$toPatch")"
-    toPatchOsabi="$(getBinOsabi "$toPatch")"
-
-    if [ "$interpreterArch" != "$toPatchArch" ]; then
-        # Our target architecture is different than this file's architecture,
-        # so skip it.
-        echo "skipping $toPatch because its architecture ($toPatchArch) differs from target ($interpreterArch)" >&2
-        return 0
-    elif ! areBinOsabisCompatible "$interpreterOsabi" "$toPatchOsabi"; then
-        echo "skipping $toPatch because its OS ABI ($toPatchOsabi) is not compatible with target ($interpreterOsabi)" >&2
-        return 0
-    fi
-
-    if isExecutable "$toPatch"; then
-        runPatchelf --set-interpreter "$interpreter" "$toPatch"
-        # shellcheck disable=SC2154
-        # (runtimeDependencies is referenced but not assigned.)
-        if [ -n "$runtimeDependencies" ]; then
-            for dep in $runtimeDependencies; do
-                rpath="$rpath${rpath:+:}$dep/lib"
-            done
-        fi
-    fi
-
-    local libcLib
-    libcLib="$(< "$NIX_BINTOOLS/nix-support/orig-libc")/lib"
-
-    echo "searching for dependencies of $toPatch" >&2
-
-    local missing
-    missing="$(getDepsFromElfBinary "$toPatch")" || return 0
-
-    # 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.
-
-    for dep in $missing; do
-        if [[ "$dep" == /* ]]; then
-            # This is an absolute path. If it exists, just use it. Otherwise,
-            # we probably want this to produce an error when checked (because
-            # just updating the rpath won't satisfy it).
-            if [ -f "$dep" ]; then
-                continue
-            fi
-        elif [ -f "$libcLib/$dep" ]; then
-            # This library exists in libc, and will be correctly resolved by
-            # the linker.
-            continue
-        fi
-
-        echo -n "  $dep -> " >&2
-        if findDependency "$dep" "$toPatchArch" "$toPatchOsabi"; then
-            rpath="$rpath${rpath:+:}${foundDependency%/*}"
-            echo "found: $foundDependency" >&2
-        else
-            echo "not found!" >&2
-            autoPatchelfFailedDeps["$dep"]="$toPatch"
-        fi
-    done
-
-    if [ -n "$rpath" ]; then
-        echo "setting RPATH to: $rpath" >&2
-        runPatchelf --set-rpath "$rpath" "$toPatch"
-    fi
-}
-
 # Can be used to manually add additional directories with shared object files
 # to be included for the next autoPatchelf invocation.
 addAutoPatchelfSearchPath() {
     local -a findOpts=()
 
-    # XXX: Somewhat similar to the one in the autoPatchelf function, maybe make
-    #      it DRY someday...
     while [ $# -gt 0 ]; do
         case "$1" in
             --) shift; break;;
@@ -296,15 +28,19 @@ addAutoPatchelfSearchPath() {
         esac
     done
 
-    while IFS= read -r -d '' file; do
-        addToDepCache "$file"
+    local dir=
+    while IFS= read -r -d '' dir; do
+        extraAutoPatchelfLibs+=("$dir")
     done <  <(find "$@" "${findOpts[@]}" \! -type d \
-            \( -name '*.so' -o -name '*.so.*' \) -print0)
+            \( -name '*.so' -o -name '*.so.*' \) -print0 \
+            | sed -z 's#/[^/]*$##' \
+            | uniq -z
+        )
 }
 
+
 autoPatchelf() {
     local norecurse=
-
     while [ $# -gt 0 ]; do
         case "$1" in
             --) shift; break;;
@@ -317,47 +53,14 @@ autoPatchelf() {
         esac
     done
 
-    if [ $# -eq 0 ]; then
-        echo "autoPatchelf: No paths to patch specified." >&2
-        return 1
-    fi
-
-    echo "automatically fixing dependencies for ELF files" >&2
-
-    # Add all shared objects of the current output path to the start of
-    # autoPatchelfCachedDeps so that it's chosen first in findDependency.
-    addAutoPatchelfSearchPath ${norecurse:+--no-recurse} -- "$@"
-
-    while IFS= read -r -d $'\0' file; do
-      isELF "$file" || continue
-      segmentHeaders="$(LANG=C $READELF -l "$file")"
-      # Skip if the ELF file doesn't have segment headers (eg. object files).
-      # not using grep -q, because it can cause Broken pipe
-      grep -q '^Program Headers:' <<<"$segmentHeaders" || continue
-      if isExecutable "$file"; then
-          # Skip if the executable is statically linked.
-          grep -q "^ *INTERP\\>" <<<"$segmentHeaders" || 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
-    # shellcheck disable=SC2154
-    # (autoPatchelfIgnoreMissingDeps is referenced but not assigned.)
-    if [[ $depsMissing == 1 && -z "$autoPatchelfIgnoreMissingDeps" ]]; then
-      echo "Add the missing dependencies to the build inputs or set autoPatchelfIgnoreMissingDeps=true"
-      exit 1
-    fi
+    local runtimeDependenciesArray=($runtimeDependencies)
+    @pythonInterpreter@ @autoPatchelfScript@                            \
+        ${norecurse:+--no-recurse}                                      \
+        ${autoPatchelfIgnoreMissingDeps:+--ignore-missing}              \
+        --paths "$@"                                                    \
+        --libs "${autoPatchelfLibs[@]}"                                 \
+               "${extraAutoPatchelfLibs[@]}"                            \
+        --runtime-dependencies "${runtimeDependenciesArray[@]/%//lib}"
 }
 
 # XXX: This should ultimately use fixupOutputHooks but we currently don't have
diff --git a/nixpkgs/pkgs/build-support/setup-hooks/make-wrapper.sh b/nixpkgs/pkgs/build-support/setup-hooks/make-wrapper.sh
index 604096135c20..fa6065832894 100644
--- a/nixpkgs/pkgs/build-support/setup-hooks/make-wrapper.sh
+++ b/nixpkgs/pkgs/build-support/setup-hooks/make-wrapper.sh
@@ -51,7 +51,19 @@ makeWrapper() {
         local varName="$2"    # name of list variable to add to
         local separator="$3"  # character used to separate elements of list
         local value="$4"      # one value, or multiple values separated by `separator`, to add to list
-        if test -n "$value"; then
+
+        # Disable file globbing, since bash will otherwise try to find
+        # filenames matching the the value to be prefixed/suffixed if
+        # it contains characters considered wildcards, such as `?` and
+        # `*`. We want the value as is, except we also want to split
+        # it on on the separator; hence we can't quote it.
+        local reenableGlob=0
+        if [[ ! -o noglob ]]; then
+            reenableGlob=1
+        fi
+        set -o noglob
+
+        if [[ -n "$value" ]]; then
             local old_ifs=$IFS
             IFS=$separator
 
@@ -86,6 +98,10 @@ makeWrapper() {
             done
             IFS=$old_ifs
         fi
+
+        if (( reenableGlob )); then
+            set +o noglob
+        fi
     }
 
     mkdir -p "$(dirname "$wrapper")"