48bd55e50d
The patterns used were OK for the output of "file -NF$'\t'": it would put a space after the separator (the default separator being ":"). But not for the output of "file -b": we need to pad the result with a space to use the same patterns. Putting a space at the beggining is convenient, because it allows to match independently for "* sh" or "* bash", otherwise "*sh" would consume "bash", too. Other uses of "file -b" in scripts/ don't suffer from this problem.
201 lines
5.5 KiB
Bash
Executable File
201 lines
5.5 KiB
Bash
Executable File
#!/bin/sh -efu
|
|
#
|
|
# Copyright (C) 2000,2003 Dmitry V. Levin <ldv@altlinux.org>
|
|
# Copyright (C) 2007 Alexey Tourbin <at@altlinux.org>
|
|
# Copyright (C) 2016 Ivan Zakharyaschev <imz@altlinux.org>
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 2 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software
|
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
#
|
|
|
|
. @RPMCONFIGDIR@/functions
|
|
. @RPMCONFIGDIR@/find-package
|
|
|
|
print_sh_number_from_shebang()
|
|
{
|
|
sed -rn -e '1 s,^\#![[:space:]]*([^[:space:]]*/bin/env[[:space:]]+)?[^[:space:]]*sh([0-9]+)([[:space:]].*)?$,\2,p'
|
|
}
|
|
|
|
shREQ= shPROV=
|
|
ShellReq()
|
|
{
|
|
local f="$1"; shift
|
|
|
|
local t sh sh_num
|
|
t=$(file -bL "$f") || Fatal "${t:-$f: file type not available}"
|
|
sh_num="$(print_sh_number_from_shebang <"$f")"
|
|
case " $t" in
|
|
*"Bourne-Again shell script text"*)
|
|
sh=/bin/bash"$sh_num" ;;
|
|
*" bash script text"*)
|
|
sh=/bin/bash"$sh_num" ;;
|
|
*)
|
|
sh=/bin/sh"$sh_num" ;;
|
|
esac
|
|
|
|
local out line1
|
|
if ! out=$($sh --rpm-requires "$f"); then
|
|
# sh --rpm-requires failed, and stderr is already there.
|
|
# We are almost dead. The last chance to escape is to see
|
|
# if the shell is used only to re-exec another interpreter, e.g.
|
|
# exec tclsh "$0" "$@"
|
|
if line1=$(egrep -m1 -v '^[[:space:]]*(#|$)' "$f"); then
|
|
set -- $line1
|
|
if [ $# -gt 1 ] && [ "$1" = exec ]; then
|
|
Info "$f is $2 script!"
|
|
# We do no more than shebang.req does. If the script
|
|
# is not executable, shebang.req.files must have
|
|
# already issued "executable not executable" warning.
|
|
if [ -x "$f" ]; then
|
|
FindPackage "$f" "$2"
|
|
fi
|
|
return 0
|
|
fi
|
|
fi
|
|
Fatal "$f: $sh --rpm-requires failed"
|
|
fi
|
|
|
|
[ -n "$out" ] || return 0
|
|
|
|
# This function is a "closure": parent variables are available without
|
|
# explicit argument passing. I checked that it works at least with some
|
|
# modern shells. Why I ever need a separate function is because my $EDITOR
|
|
# is not as perfect as to provide decent syntax highlight for subshells.
|
|
CleanupRpmRequires()
|
|
{
|
|
printf '%s\n' "$out" |
|
|
while read -r line; do
|
|
# NB: grep and sed are expensive here.
|
|
case "$line" in
|
|
# Basic sanity check for --rpm-requires output.
|
|
# Better command/path validation is performed in FindPackage.
|
|
'executable('*[A-Za-z0-9]*')' | 'function('*[A-Za-z0-9]*')' ) ;;
|
|
*) Info "$f: invalid $sh --rpm-requires output: $line"; continue ;;
|
|
esac
|
|
# Allow e.g. executable(\ls) and executable("ls").
|
|
set -- $(IFS="($IFS\\'\")"; echo $line)
|
|
case $# in
|
|
2) ;;
|
|
*) Info "$f: invalid $sh --rpm-requires output: $line"; continue ;;
|
|
esac
|
|
case "$2" in
|
|
/*) printf '%s\t%s\n' "$1" "$2"; continue ;;
|
|
esac
|
|
case "$(PATH= type -t -- "$2")" in
|
|
alias|keyword|builtin) continue ;;
|
|
*) printf '%s\t%s\n' "$1" "$2" ;;
|
|
esac
|
|
done
|
|
}
|
|
out=$(CleanupRpmRequires)
|
|
[ -n "$out" ] || return 0
|
|
|
|
# Now that the output is sane I can fold dups.
|
|
out=$(printf '%s\n' "$out" |LC_COLLATE=C sort -u)
|
|
|
|
# Self-requires elimination: first pass.
|
|
# Consider e.g. /etc/rc.d/init.d/functions has both
|
|
# executable(failure) and function(failure).
|
|
# This means that failure() is used before being defined.
|
|
# This is okay since it is actually used in another function.
|
|
# We want to keep only the function(failure).
|
|
GetProv()
|
|
{
|
|
printf '%s\n' "$out" |
|
|
while read -r t r; do
|
|
[ "$t" = function ] || continue
|
|
printf '%s\n' "$r"
|
|
done
|
|
}
|
|
local prov
|
|
prov=$(GetProv)
|
|
|
|
GetReq()
|
|
{
|
|
printf '%s\n' "$out" |
|
|
while read -r t r; do
|
|
[ "$t" = executable ] || continue
|
|
if [ -n "$prov" ] && printf '%s\n' "$prov" |fgrep -qs -x -e "$r"; then
|
|
Verbose "$f: $r() is used before its definition"
|
|
continue
|
|
fi
|
|
printf '%s\n' "$r"
|
|
done
|
|
}
|
|
local req
|
|
req=$(GetReq)
|
|
|
|
AddReqProv()
|
|
{
|
|
[ -z "$1" ] || printf '%s\n' "$1"
|
|
[ -n "$2" ] || return 0
|
|
printf '%s\n' "$2" |
|
|
while read -r r; do
|
|
printf '%s\t%s\n' "$f" "$r"
|
|
done
|
|
}
|
|
shREQ=$(AddReqProv "$shREQ" "$req")
|
|
shPROV=$(AddReqProv "$shPROV" "$prov")
|
|
}
|
|
|
|
ShellReqEND()
|
|
{
|
|
[ -n "$shREQ" ] || return 0
|
|
Debug "shREQ=$shREQ"
|
|
Debug "shPROV=$shPROV"
|
|
|
|
# Self-requires elimination: second pass.
|
|
# If ANY file has function(foo), skip all dependencies on execuatable(foo).
|
|
printf '%s\n' "$shREQ" |
|
|
while IFS=$'\t' read -r f r; do
|
|
CheckSelfReq()
|
|
{
|
|
[ -n "$shPROV" ] || return 0
|
|
printf '%s\n' "$shPROV" |
|
|
while IFS=$'\t' read -r f2 r2; do
|
|
if [ "$r" = "$r2" ]; then
|
|
printf '%s\n' "$f2"
|
|
fi
|
|
done
|
|
}
|
|
local self_req
|
|
self_req=$(CheckSelfReq)
|
|
if [ -n "$self_req" ]; then
|
|
Verbose "$f: $r() is defined in" $self_req
|
|
continue
|
|
fi
|
|
|
|
local dir
|
|
dir=${f#${RPM_BUILD_ROOT-}}
|
|
dir=${dir%/*}
|
|
CheckDirProv()
|
|
{
|
|
fgrep -qs -x -e "$r" "$1/.provides.sh" || return
|
|
}
|
|
if [ -n "${RPM_BUILD_ROOT-}" ] && CheckDirProv "$RPM_BUILD_ROOT$dir"; then
|
|
# I think that .provides.sh must provide this thingy.
|
|
# Otherwise we get an unmet depedency, which is good --
|
|
# one has to fix the .provides.sh early.
|
|
printf '%s(%s)\n' "$dir" "$r"
|
|
elif CheckDirProv "$dir"; then
|
|
printf '%s(%s)\n' "$dir" "$r"
|
|
else
|
|
FindPackage "$f" "$r"
|
|
fi
|
|
done
|
|
}
|
|
|
|
ArgvFileAction ShellReq "$@"
|
|
ShellReqEND
|