redhat-rpm-config/SOURCES/brp-mangle-shebangs

166 lines
4.4 KiB
Bash
Executable File

#!/bin/bash -eu
# If using normal root, avoid changing anything.
if [ -z "$RPM_BUILD_ROOT" -o "$RPM_BUILD_ROOT" = "/" ]; then
exit 0
fi
exclude_files=""
exclude_files_from=""
exclude_shebangs=""
exclude_shebangs_from=""
usage() {
local verbose=$1 && shift
local outfile=$1 && shift
local status=$1 && shift
(
echo 'usage: brp-mangle-shebangs [--files <regexp>] [--files-from <file>] [--shebangs <regexp>] [--shebangs-from <file>]'
if [ "${verbose}" == "yes" ]; then
echo ' --files: extended regexp of files to ignore'
echo ' --files-from: file containing a list of extended regexps of files to ignore'
echo ' --shebangs: extended regexp of shebangs to ignore'
echo ' --shebangs-from: file containing a list of extended regexps of shebangs to ignore'
fi
) >>${outfile}
exit ${status}
}
while [ $# -gt 0 ] ; do
case "$1" in
--files)
exclude_files="${2}"
shift
;;
--files=*)
exclude_files="${1##--files=}"
;;
--files-from)
exclude_files_from="${2}"
shift
;;
--files-from=*)
exclude_files_from="${1##--files-from=}"
;;
--shebangs)
exclude_shebangs="${2}"
shift
;;
--shebangs=*)
exclude_shebangs="${1##--shebangs=}"
;;
--shebangs-from)
exclude_shebangs_from="${2}"
shift
;;
--shebangs-from=*)
exclude_shebangs_from="${1##--shebangs-from=}"
;;
--help|--usage|"-?"|-h)
usage yes /dev/stdout 0
;;
*)
echo "Unknown option \"${1}\"" 1>&2
usage no /dev/stderr 1
;;
esac
shift
done
cd "$RPM_BUILD_ROOT"
# Large packages such as kernel can have thousands of executable files.
# We take care to not fork/exec thousands of "file"s and "grep"s,
# but run just two of them.
# (Take care to exclude filenames which would mangle "file" output).
find -executable -type f ! -path '*:*' ! -path $'*\n*' \
| file -N --mime-type -f - \
| grep -P ".+(?=: (text/|application/javascript))" \
| {
fail=0
while IFS= read -r line; do
f=${line%%:*}
# Remove the dot
path="${f#.}"
if [ -n "$exclude_files" ]; then
echo "$path" | grep -q -E "$exclude_files" && continue
fi
if [ -n "$exclude_files_from" ]; then
echo "$path" | grep -q -E -f "$exclude_files_from" && continue
fi
if ! read shebang_line < "$f"; then
echo >&2 "*** WARNING: Cannot read the first line from $f, removing executable bit"
ts=$(stat -c %y "$f")
chmod -x "$f"
touch -d "$ts" "$f"
continue
fi
orig_shebang="${shebang_line#\#!}"
if [ "$orig_shebang" = "$shebang_line" ]; then
echo >&2 "*** WARNING: $f is executable but has no shebang, removing executable bit"
ts=$(stat -c %y "$f")
chmod -x "$f"
touch -d "$ts" "$f"
continue
fi
# Trim spaces
while shebang="${orig_shebang// / }"; [ "$shebang" != "$orig_shebang" ]; do
orig_shebang="$shebang"
done
# Treat "#! /path/to " as "#!/path/to"
orig_shebang="${orig_shebang# }"
shebang="$orig_shebang"
if [ -z "$shebang" ]; then
echo >&2 "*** WARNING: $f is executable but has empty shebang, removing executable bit"
ts=$(stat -c %y "$f")
chmod -x "$f"
touch -d "$ts" "$f"
continue
fi
if [ -n "${shebang##/*}" ]; then
echo >&2 "*** ERROR: $f has shebang which doesn't start with '/' ($shebang)"
fail=1
continue
fi
if ! { echo "$shebang" | grep -q -P "^/(?:usr/)?(?:bin|sbin)/"; }; then
continue
fi
# Replace "special" env shebang:
# /whatsoever/env -whatever /whatever/foo → /whatever/foo
shebang=$(echo "$shebang" | sed -r -e 's@^(.+)/env( -[^ ]+)* /(.+)$@/\3@')
# /whatsoever/env -whatever foo → /whatsoever/foo
shebang=$(echo "$shebang" | sed -r -e 's@^(.+/)env( -[^ ]+)* (.+)$@\1\3@')
# If the shebang now starts with /bin, change it to /usr/bin
# https://bugzilla.redhat.com/show_bug.cgi?id=1581757
shebang=$(echo "$shebang" | sed -r -e 's@^/bin/@/usr/bin/@')
# Replace ambiguous python with python2
py_shebang=$(echo "$shebang" | sed -r -e 's@/usr/bin/python(\s|$)@/usr/bin/python2\1@')
if [ "$shebang" != "$py_shebang" ]; then
echo >&2 "*** ERROR: ambiguous python shebang in $path: #!$orig_shebang. Change it to python3 (or python2) explicitly."
fail=1
elif [ "#!$shebang" != "#!$orig_shebang" ]; then
echo "mangling shebang in $path from $orig_shebang to #!$shebang"
ts=$(stat -c %y "$f")
sed -i -e "1c #!$shebang" "$f"
touch -d "$ts" "$f"
fi
done
exit $fail
}