about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--doc/stdenv.xml31
-rw-r--r--pkgs/build-support/setup-hooks/auto-patchelf.sh67
2 files changed, 83 insertions, 15 deletions
diff --git a/doc/stdenv.xml b/doc/stdenv.xml
index 40ff17aafa091..4c19b2867b5ac 100644
--- a/doc/stdenv.xml
+++ b/doc/stdenv.xml
@@ -2428,12 +2428,31 @@ addEnvHooks "$hostOffset" myBashFunction
       <para>
        This is a special setup hook which helps in packaging proprietary
        software in that it automatically tries to find missing shared library
-       dependencies of ELF files. All packages within the
-       <envar>runtimeDependencies</envar> environment variable are
-       unconditionally added to executables, which is useful for programs that
-       use <citerefentry>
-       <refentrytitle>dlopen</refentrytitle>
-       <manvolnum>3</manvolnum> </citerefentry> to load libraries at runtime.
+       dependencies of ELF files based on the given
+       <varname>buildInputs</varname> and <varname>nativeBuildInputs</varname>.
+      </para>
+      <para>
+       You can also specify a <envar>runtimeDependencies</envar> environment
+       variable which lists dependencies that are unconditionally added to all
+       executables.
+      </para>
+      <para>
+       This is useful for programs that use <citerefentry>
+        <refentrytitle>dlopen</refentrytitle>
+        <manvolnum>3</manvolnum>
+       </citerefentry> to load libraries at runtime.
+      </para>
+      <para>
+        In certain situations you may want to run the main command
+        (<command>autoPatchelf</command>) of the setup hook on a file or a set
+        of directories instead of unconditionally patching all outputs. This
+        can be done by setting the <envar>dontAutoPatchelf</envar> environment
+        variable to a non-empty value.
+      </para>
+      <para>
+        The <command>autoPatchelf</command> command also recognizes a
+        <parameter class="command">--no-recurse</parameter> command line flag,
+        which prevents it from recursing into subdirectories.
       </para>
      </listitem>
     </varlistentry>
diff --git a/pkgs/build-support/setup-hooks/auto-patchelf.sh b/pkgs/build-support/setup-hooks/auto-patchelf.sh
index d1ae317ff9a44..5bedd1a9f9caf 100644
--- a/pkgs/build-support/setup-hooks/auto-patchelf.sh
+++ b/pkgs/build-support/setup-hooks/auto-patchelf.sh
@@ -147,15 +147,56 @@ autoPatchelfFile() {
     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;;
+            --no-recurse) shift; findOpts+=("-maxdepth" 1);;
+            --*)
+                echo "addAutoPatchelfSearchPath: ERROR: Invalid command line" \
+                     "argument: $1" >&2
+                return 1;;
+            *) break;;
+        esac
+    done
+
+    cachedDependencies+=(
+        $(find "$@" "${findOpts[@]}" \! -type d \
+               \( -name '*.so' -o -name '*.so.*' \))
+    )
+}
+
 autoPatchelf() {
+    local norecurse=
+
+    while [ $# -gt 0 ]; do
+        case "$1" in
+            --) shift; break;;
+            --no-recurse) shift; norecurse=1;;
+            --*)
+                echo "autoPatchelf: ERROR: Invalid command line" \
+                     "argument: $1" >&2
+                return 1;;
+            *) break;;
+        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
     # cachedDependencies so that it's choosen first in findDependency.
-    cachedDependencies+=(
-        $(find "$prefix" \! -type d \( -name '*.so' -o -name '*.so.*' \))
-    )
-    local elffile
+    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
@@ -164,12 +205,15 @@ autoPatchelf() {
     # outside of this function.
     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).
+      echo "$segmentHeaders" | grep -q '^Program Headers:' || continue
       if isExecutable "$file"; then
           # Skip if the executable is statically linked.
-          LANG=C readelf -l "$file" | grep -q "^ *INTERP\\>" || continue
+          echo "$segmentHeaders" | grep -q "^ *INTERP\\>" || continue
       fi
       autoPatchelfFile "$file"
-    done < <(find "$prefix" -type f -print0)
+    done < <(find "$@" ${norecurse:+-maxdepth 1} -type f -print0)
 }
 
 # XXX: This should ultimately use fixupOutputHooks but we currently don't have
@@ -180,6 +224,11 @@ autoPatchelf() {
 # So what we do here is basically run in postFixup and emulate the same
 # behaviour as fixupOutputHooks because the setup hook for patchelf is run in
 # fixupOutput and the postFixup hook runs later.
-postFixupHooks+=(
-    'for output in $outputs; do prefix="${!output}" autoPatchelf; done'
-)
+postFixupHooks+=('
+    if [ -z "$dontAutoPatchelf" ]; then
+        autoPatchelf -- $(for output in $outputs; do
+            [ -e "${!output}" ] || continue
+            echo "${!output}"
+        done)
+    fi
+')