nixpkgs/pkgs/build-support/setup-hooks/strip.sh
Thomas Watson 4d6d293fad setup-hooks/strip: uniqify files by inode number before stripping
https://github.com/NixOS/nixpkgs/pull/246164 but for hardlinks.

Mesa, among other packages, has binaries that are linked together and
can end up corrupted when the same binary is stripped through two
different names.

To resolve this, print out the device and inode number before each file
name, sort/uniq based on that, then cut it back out before stripping.

The symlink resolution logic is removed as the same file accessed
through two different links in `$paths` will necessarily have the same
numbers. File/directory within the paths listed in `$paths` are
correctly not (and were never) processed due to the `-type f` predicate
and (implied) `-P` option to `find`.
2024-05-24 16:53:14 -05:00

103 lines
4.4 KiB
Bash

# This setup hook strips libraries and executables in the fixup phase.
fixupOutputHooks+=(_doStrip)
_doStrip() {
# We don't bother to strip build platform code because it shouldn't make it
# to $out anyways---if it does, that's a bigger problem that a lack of
# stripping will help catch.
local -ra flags=(dontStripHost dontStripTarget)
local -ra debugDirs=(stripDebugList stripDebugListTarget)
local -ra allDirs=(stripAllList stripAllListTarget)
local -ra stripCmds=(STRIP STRIP_FOR_TARGET)
local -ra ranlibCmds=(RANLIB RANLIB_FOR_TARGET)
# TODO(structured-attrs): This doesn't work correctly if one of
# the items in strip*List or strip*Flags contains a space,
# even with structured attrs enabled. This is OK for now
# because very few packages set any of these, and it doesn't
# affect any of them.
#
# After __structuredAttrs = true is universal, come back and
# push arrays all the way through this logic.
# Strip only host paths by default. Leave targets as is.
stripDebugList=${stripDebugList[*]:-lib lib32 lib64 libexec bin sbin}
stripDebugListTarget=${stripDebugListTarget[*]:-}
stripAllList=${stripAllList[*]:-}
stripAllListTarget=${stripAllListTarget[*]:-}
local i
for i in ${!stripCmds[@]}; do
local -n flag="${flags[$i]}"
local -n debugDirList="${debugDirs[$i]}"
local -n allDirList="${allDirs[$i]}"
local -n stripCmd="${stripCmds[$i]}"
local -n ranlibCmd="${ranlibCmds[$i]}"
# `dontStrip` disables them all
if [[ "${dontStrip-}" || "${flag-}" ]] || ! type -f "${stripCmd-}" 2>/dev/null 1>&2
then continue; fi
stripDirs "$stripCmd" "$ranlibCmd" "$debugDirList" "${stripDebugFlags[*]:--S -p}"
stripDirs "$stripCmd" "$ranlibCmd" "$allDirList" "${stripAllFlags[*]:--s -p}"
done
}
stripDirs() {
local cmd="$1"
local ranlibCmd="$2"
local paths="$3"
local stripFlags="$4"
local excludeFlags=()
local pathsNew=
[ -z "$cmd" ] && echo "stripDirs: Strip command is empty" 1>&2 && exit 1
[ -z "$ranlibCmd" ] && echo "stripDirs: Ranlib command is empty" 1>&2 && exit 1
local pattern
if [ -n "${stripExclude:-}" ]; then
for pattern in "${stripExclude[@]}"; do
excludeFlags+=(-a '!' '(' -name "$pattern" -o -wholename "$prefix/$pattern" ')' )
done
fi
local p
for p in ${paths}; do
if [ -e "$prefix/$p" ]; then
pathsNew="${pathsNew} $prefix/$p"
fi
done
paths=${pathsNew}
if [ -n "${paths}" ]; then
echo "stripping (with command $cmd and flags $stripFlags) in $paths"
local striperr
striperr="$(mktemp --tmpdir="$TMPDIR" 'striperr.XXXXXX')"
# Make sure we process files only once. `strip`ping the same file through different
# links in parallel can corrupt it:
# https://github.com/NixOS/nixpkgs/issues/246147#issuecomment-1657072039
# Do not strip lib/debug. This is a directory used by setup-hooks/separate-debug-info.sh.
# Print out each file's device and inode (which will be the same if two files are hardlinked
# or are the same file found through different symlinks), followed by its path...
find $paths -type f "${excludeFlags[@]}" -a '!' -path "$prefix/lib/debug/*" -printf '%D-%i,%p\0' |
# ... sort/uniq by device/inode, then cut them out and keep the path, ...
sort -t, -k1,1 -u -z | cut -d, -f2- -z |
# and finally strip each unique path in parallel.
xargs -r -0 -n1 -P "$NIX_BUILD_CORES" -- $cmd $stripFlags 2>"$striperr" || exit_code=$?
# xargs exits with status code 123 if some but not all of the
# processes fail. We don't care if some of the files couldn't
# be stripped, so ignore specifically this code.
[[ "$exit_code" = 123 || -z "$exit_code" ]] || (cat "$striperr" 1>&2 && exit 1)
rm "$striperr"
# 'strip' does not normally preserve archive index in .a files.
# This usually causes linking failures against static libs like:
# ld: ...-i686-w64-mingw32-stage-final-gcc-13.0.0-lib/i686-w64-mingw32/lib/libstdc++.dll.a:
# error adding symbols: archive has no index; run ranlib to add one
# Restore the index by running 'ranlib'.
find $paths -name '*.a' -type f -exec $ranlibCmd '{}' \; 2>/dev/null
fi
}