about summary refs log tree commit diff
path: root/pkgs/build-support
diff options
context:
space:
mode:
authorDavHau <hsngrmpf+github@gmail.com>2020-10-20 19:09:32 +0700
committerDavHau <hsngrmpf+github@gmail.com>2020-10-20 19:09:32 +0700
commit5c382b7f0e4705f447ac554dcfc7069849766710 (patch)
tree69757663d8c9dd6c605b1ef9f4de835863669e30 /pkgs/build-support
parenta2ee5cbb0513ee0623bc93aa1af74f172080ce6b (diff)
downloadnixlib-5c382b7f0e4705f447ac554dcfc7069849766710.tar
nixlib-5c382b7f0e4705f447ac554dcfc7069849766710.tar.gz
nixlib-5c382b7f0e4705f447ac554dcfc7069849766710.tar.bz2
nixlib-5c382b7f0e4705f447ac554dcfc7069849766710.tar.lz
nixlib-5c382b7f0e4705f447ac554dcfc7069849766710.tar.xz
nixlib-5c382b7f0e4705f447ac554dcfc7069849766710.tar.zst
nixlib-5c382b7f0e4705f447ac554dcfc7069849766710.zip
autoPatchelfHook: optimize performance, better error handling
Diffstat (limited to 'pkgs/build-support')
-rw-r--r--pkgs/build-support/setup-hooks/auto-patchelf.sh72
1 files changed, 45 insertions, 27 deletions
diff --git a/pkgs/build-support/setup-hooks/auto-patchelf.sh b/pkgs/build-support/setup-hooks/auto-patchelf.sh
index 4f7c0c14304c..7df454f4f017 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 maintian their order
+    autoPatchelfCachedDeps+=("$1")
 }
 
 declare -gi depCacheInitialised=0
@@ -43,7 +55,7 @@ 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##*/}"
@@ -76,7 +88,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"
@@ -103,7 +115,7 @@ autoPatchelfFile() {
 
     local 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,7 +127,7 @@ 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="$(
         ldd "$toPatch" 2> /dev/null | \
@@ -134,18 +146,13 @@ autoPatchelfFile() {
             echo "found: $foundDependency" >&2
         else
             echo "not found!" >&2
-            depNotFound=1
+            autoPatchelfFailedDeps["$dep"]=""
         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 choosen 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"
+      depsMissing=1
+    done
+    if [ $depsMissing == 1 -a -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