blob: 17327922104b5c47843cfd944884b66f6c82cbcf [file] [log] [blame]
#!/bin/bash
# Copyright 1999-2018 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
source "${PORTAGE_BIN_PATH}/eapi.sh" || exit 1
if ___eapi_has_version_functions; then
source "${PORTAGE_BIN_PATH}/eapi7-ver-funcs.sh" || exit 1
fi
# We need this next line for "die" and "assert". It expands
# It _must_ preceed all the calls to die and assert.
shopt -s expand_aliases
alias save_IFS='[ "${IFS:-unset}" != "unset" ] && old_IFS="${IFS}"'
alias restore_IFS='if [ "${old_IFS:-unset}" != "unset" ]; then IFS="${old_IFS}"; unset old_IFS; else unset IFS; fi'
assert() {
local x pipestatus=${PIPESTATUS[*]}
for x in $pipestatus ; do
[[ $x -eq 0 ]] || die "$@"
done
}
__assert_sigpipe_ok() {
# When extracting a tar file like this:
#
# bzip2 -dc foo.tar.bz2 | tar xof -
#
# For some tar files (see bug #309001), tar will
# close its stdin pipe when the decompressor still has
# remaining data to be written to its stdout pipe. This
# causes the decompressor to be killed by SIGPIPE. In
# this case, we want to ignore pipe writers killed by
# SIGPIPE, and trust the exit status of tar. We refer
# to the bash manual section "3.7.5 Exit Status"
# which says, "When a command terminates on a fatal
# signal whose number is N, Bash uses the value 128+N
# as the exit status."
local x pipestatus=${PIPESTATUS[*]}
for x in $pipestatus ; do
# Allow SIGPIPE through (128 + 13)
if [[ $x -ne 0 && $x -ne ${PORTAGE_SIGPIPE_STATUS:-141} ]]
then
__helpers_die "$@"
return 1
fi
done
# Require normal success for the last process (tar).
if [[ $x -ne 0 ]]; then
__helpers_die "$@"
return 1
fi
}
shopt -s extdebug
# __dump_trace([number of funcs on stack to skip],
# [whitespacing for filenames],
# [whitespacing for line numbers])
__dump_trace() {
local funcname="" sourcefile="" lineno="" s="yes" n p
declare -i strip=${1:-1}
local filespacing=$2 linespacing=$3
# The __qa_call() function and anything before it are portage internals
# that the user will not be interested in. Therefore, the stack trace
# should only show calls that come after __qa_call().
(( n = ${#FUNCNAME[@]} - 1 ))
(( p = ${#BASH_ARGV[@]} ))
while (( n > 0 )) ; do
[ "${FUNCNAME[${n}]}" == "__qa_call" ] && break
(( p -= ${BASH_ARGC[${n}]} ))
(( n-- ))
done
if (( n == 0 )) ; then
(( n = ${#FUNCNAME[@]} - 1 ))
(( p = ${#BASH_ARGV[@]} ))
fi
eerror "Call stack:"
while (( n > ${strip} )) ; do
funcname=${FUNCNAME[${n} - 1]}
sourcefile=$(basename "${BASH_SOURCE[${n}]}")
lineno=${BASH_LINENO[${n} - 1]}
# Display function arguments
args=
if [[ ${#BASH_ARGV[@]} -gt 0 ]]; then
for (( j = 1 ; j <= ${BASH_ARGC[${n} - 1]} ; ++j )); do
newarg=${BASH_ARGV[$(( p - j - 1 ))]}
args="${args:+${args} }'${newarg}'"
done
(( p -= ${BASH_ARGC[${n} - 1]} ))
fi
eerror " $(printf "%${filespacing}s" "${sourcefile}"), line $(printf "%${linespacing}s" "${lineno}"): Called ${funcname}${args:+ ${args}}"
(( n-- ))
done
}
nonfatal() {
if ! ___eapi_has_nonfatal; then
die "$FUNCNAME() not supported in this EAPI"
fi
if [[ $# -lt 1 ]]; then
die "$FUNCNAME(): Missing argument"
fi
PORTAGE_NONFATAL=1 "$@"
}
__bashpid() {
# The BASHPID variable is new to bash-4.0, so add a hack for older
# versions. This must be used like so:
# ${BASHPID:-$(__bashpid)}
sh -c 'echo ${PPID}'
}
__helpers_die() {
if ___eapi_helpers_can_die && [[ ${PORTAGE_NONFATAL} != 1 ]]; then
die "$@"
else
echo -e "$@" >&2
fi
}
die() {
# restore PATH since die calls basename & sed
# TODO: make it pure bash
[[ -n ${_PORTAGE_ORIG_PATH} ]] && PATH=${_PORTAGE_ORIG_PATH}
set +x # tracing only produces useless noise here
local IFS=$' \t\n'
if ___eapi_die_can_respect_nonfatal && [[ $1 == -n ]]; then
shift
if [[ ${PORTAGE_NONFATAL} == 1 ]]; then
[[ $# -gt 0 ]] && eerror "$*"
return 1
fi
fi
set +e
if [ -n "${QA_INTERCEPTORS}" ] ; then
# die was called from inside inherit. We need to clean up
# QA_INTERCEPTORS since sed is called below.
unset -f ${QA_INTERCEPTORS}
unset QA_INTERCEPTORS
fi
local n filespacing=0 linespacing=0
# setup spacing to make output easier to read
(( n = ${#FUNCNAME[@]} - 1 ))
while (( n > 0 )) ; do
[ "${FUNCNAME[${n}]}" == "__qa_call" ] && break
(( n-- ))
done
(( n == 0 )) && (( n = ${#FUNCNAME[@]} - 1 ))
while (( n >= 0 )); do
sourcefile=${BASH_SOURCE[${n}]} sourcefile=${sourcefile##*/}
lineno=${BASH_LINENO[${n}]}
((filespacing < ${#sourcefile})) && filespacing=${#sourcefile}
((linespacing < ${#lineno})) && linespacing=${#lineno}
(( n-- ))
done
# When a helper binary dies automatically in EAPI 4 and later, we don't
# get a stack trace, so at least report the phase that failed.
local phase_str=
[[ -n $EBUILD_PHASE ]] && phase_str=" ($EBUILD_PHASE phase)"
eerror "ERROR: ${CATEGORY}/${PF}::${PORTAGE_REPO_NAME} failed${phase_str}:"
eerror " ${*:-(no error message)}"
eerror
# __dump_trace is useless when the main script is a helper binary
local main_index
(( main_index = ${#BASH_SOURCE[@]} - 1 ))
if has ${BASH_SOURCE[$main_index]##*/} ebuild.sh misc-functions.sh ; then
__dump_trace 2 ${filespacing} ${linespacing}
eerror " $(printf "%${filespacing}s" "${BASH_SOURCE[1]##*/}"), line $(printf "%${linespacing}s" "${BASH_LINENO[0]}"): Called die"
eerror "The specific snippet of code:"
# This scans the file that called die and prints out the logic that
# ended in the call to die. This really only handles lines that end
# with '|| die' and any preceding lines with line continuations (\).
# This tends to be the most common usage though, so let's do it.
# Due to the usage of appending to the hold space (even when empty),
# we always end up with the first line being a blank (thus the 2nd sed).
sed -n \
-e "# When we get to the line that failed, append it to the
# hold space, move the hold space to the pattern space,
# then print out the pattern space and quit immediately
${BASH_LINENO[0]}{H;g;p;q}" \
-e '# If this line ends with a line continuation, append it
# to the hold space
/\\$/H' \
-e '# If this line does not end with a line continuation,
# erase the line and set the hold buffer to it (thus
# erasing the hold buffer in the process)
/[^\]$/{s:^.*$::;h}' \
"${BASH_SOURCE[1]}" \
| sed -e '1d' -e 's:^:RETAIN-LEADING-SPACE:' \
| while read -r n ; do eerror " ${n#RETAIN-LEADING-SPACE}" ; done
eerror
fi
# Only call die hooks here if we are executed via ebuild.sh or
# misc-functions.sh, since those are the only cases where the environment
# contains the hook functions. When necessary (like for __helpers_die), die
# hooks are automatically called later by a misc-functions.sh invocation.
if has ${BASH_SOURCE[$main_index]##*/} ebuild.sh misc-functions.sh && \
[[ ${EBUILD_PHASE} != depend ]] ; then
local x
for x in $EBUILD_DEATH_HOOKS; do
${x} "$@" >&2 1>&2
done
> "$PORTAGE_BUILDDIR/.die_hooks"
fi
if [[ -n ${PORTAGE_LOG_FILE} ]] ; then
eerror "Build log: ${PORTAGE_LOG_FILE}"
if [[ ${PORTAGE_LOG_FILE} != ${T}/* ]] && \
! has fail-clean ${FEATURES} ; then
# Display path to symlink in ${T}, as requested in bug #412865.
local log_ext=log
[[ ${PORTAGE_LOG_FILE} != *.log ]] && log_ext+=.${PORTAGE_LOG_FILE##*.}
eerror "Stable log symlink: ${T}/build.${log_ext}"
fi
fi
if [ -f "${T}/environment" ] ; then
:
elif [ -d "${T}" ] ; then
{
set
export
} > "${T}/die.env"
fi
eerror "CWD: $(pwd)"
eerror "S: ${S}"
[[ -n $PORTAGE_EBUILD_EXIT_FILE ]] && > "$PORTAGE_EBUILD_EXIT_FILE"
[[ -n $PORTAGE_IPC_DAEMON ]] && "$PORTAGE_BIN_PATH"/ebuild-ipc exit 1
# subshell die support
[[ ${BASHPID:-$(__bashpid)} == ${EBUILD_MASTER_PID} ]] || kill -s SIGTERM ${EBUILD_MASTER_PID}
exit 1
}
__quiet_mode() {
[[ ${PORTAGE_QUIET} -eq 1 ]]
}
__vecho() {
__quiet_mode || echo "$(date +%H:%M:%S)" "$@" >&2
}
# Internal logging function, don't use this in ebuilds
__elog_base() {
local messagetype
[ -z "${1}" -o -z "${T}" -o ! -d "${T}/logging" ] && return 1
case "${1}" in
INFO|WARN|ERROR|LOG|QA)
messagetype="${1}"
shift
;;
*)
__vecho -e " ${BAD}*${NORMAL} Invalid use of internal function __elog_base(), next message will not be logged"
return 1
;;
esac
echo -e "$@" | while read -r ; do
echo "$messagetype $REPLY" >> \
"${T}/logging/${EBUILD_PHASE:-other}"
done
return 0
}
eqawarn() {
__elog_base QA "$*"
[[ ${RC_ENDCOL} != "yes" && ${LAST_E_CMD} == "ebegin" ]] && echo >&2
echo -e "$@" | while read -r ; do
__vecho " $WARN*$NORMAL $REPLY"
done
LAST_E_CMD="eqawarn"
return 0
}
elog() {
__elog_base LOG "$*"
[[ ${RC_ENDCOL} != "yes" && ${LAST_E_CMD} == "ebegin" ]] && echo >&2
echo -e "$@" | while read -r ; do
echo " $GOOD*$NORMAL $REPLY" >&2
done
LAST_E_CMD="elog"
return 0
}
einfo() {
__elog_base INFO "$*"
[[ ${RC_ENDCOL} != "yes" && ${LAST_E_CMD} == "ebegin" ]] && echo >&2
echo -e "$@" | while read -r ; do
echo " $GOOD*$NORMAL $REPLY" >&2
done
LAST_E_CMD="einfo"
return 0
}
einfon() {
__elog_base INFO "$*"
[[ ${RC_ENDCOL} != "yes" && ${LAST_E_CMD} == "ebegin" ]] && echo >&2
echo -ne " ${GOOD}*${NORMAL} $*" >&2
LAST_E_CMD="einfon"
return 0
}
ewarn() {
__elog_base WARN "$*"
[[ ${RC_ENDCOL} != "yes" && ${LAST_E_CMD} == "ebegin" ]] && echo >&2
echo -e "$@" | while read -r ; do
echo " $WARN*$NORMAL $RC_INDENTATION$REPLY" >&2
done
LAST_E_CMD="ewarn"
return 0
}
eerror() {
__elog_base ERROR "$*"
[[ ${RC_ENDCOL} != "yes" && ${LAST_E_CMD} == "ebegin" ]] && echo >&2
echo -e "$@" | while read -r ; do
echo " $BAD*$NORMAL $RC_INDENTATION$REPLY" >&2
done
LAST_E_CMD="eerror"
return 0
}
ebegin() {
local msg="$*" dots spaces=${RC_DOT_PATTERN//?/ }
if [[ -n ${RC_DOT_PATTERN} ]] ; then
dots=$(printf "%$(( COLS - 3 - ${#RC_INDENTATION} - ${#msg} - 7 ))s" '')
dots=${dots//${spaces}/${RC_DOT_PATTERN}}
msg="${msg}${dots}"
else
msg="${msg} ..."
fi
einfon "${msg}"
[[ ${RC_ENDCOL} == "yes" ]] && echo >&2
LAST_E_LEN=$(( 3 + ${#RC_INDENTATION} + ${#msg} ))
LAST_E_CMD="ebegin"
return 0
}
__eend() {
local retval=${1:-0} efunc=${2:-eerror} msg
shift 2
if [[ ${retval} == "0" ]] ; then
msg="${BRACKET}[ ${GOOD}ok${BRACKET} ]${NORMAL}"
else
if [[ -n $* ]] ; then
${efunc} "$*"
fi
msg="${BRACKET}[ ${BAD}!!${BRACKET} ]${NORMAL}"
fi
if [[ ${RC_ENDCOL} == "yes" ]] ; then
echo -e "${ENDCOL} ${msg}" >&2
else
[[ ${LAST_E_CMD} == ebegin ]] || LAST_E_LEN=0
printf "%$(( COLS - LAST_E_LEN - 7 ))s%b\n" '' "${msg}" >&2
fi
return ${retval}
}
eend() {
local retval=${1:-0}
shift
__eend ${retval} eerror "$*"
LAST_E_CMD="eend"
return ${retval}
}
__unset_colors() {
COLS=80
ENDCOL=
GOOD=
WARN=
BAD=
NORMAL=
HILITE=
BRACKET=
}
__set_colors() {
COLS=${COLUMNS:-0} # bash's internal COLUMNS variable
# Avoid wasteful stty calls during the "depend" phases.
# If stdout is a pipe, the parent process can export COLUMNS
# if it's relevant. Use an extra subshell for stty calls, in
# order to redirect "/dev/tty: No such device or address"
# error from bash to /dev/null.
[[ $COLS == 0 && $EBUILD_PHASE != depend ]] && \
COLS=$(set -- $( ( stty size </dev/tty ) 2>/dev/null || echo 24 80 ) ; echo $2)
(( COLS > 0 )) || (( COLS = 80 ))
# Now, ${ENDCOL} will move us to the end of the
# column; irregardless of character width
ENDCOL=$'\e[A\e['$(( COLS - 8 ))'C'
if [ -n "${PORTAGE_COLORMAP}" ] ; then
eval ${PORTAGE_COLORMAP}
else
GOOD=$'\e[32;01m'
WARN=$'\e[33;01m'
BAD=$'\e[31;01m'
HILITE=$'\e[36;01m'
BRACKET=$'\e[34;01m'
NORMAL=$'\e[0m'
fi
}
RC_ENDCOL="yes"
RC_INDENTATION=''
RC_DEFAULT_INDENT=2
RC_DOT_PATTERN=''
case "${NOCOLOR:-false}" in
yes|true)
__unset_colors
;;
no|false)
__set_colors
;;
esac
if [[ -z ${USERLAND} ]] ; then
case $(uname -s) in
*BSD|DragonFly)
export USERLAND="BSD"
;;
*)
export USERLAND="GNU"
;;
esac
fi
if [[ -z ${XARGS} ]] ; then
case ${USERLAND} in
BSD)
if type -P gxargs > /dev/null; then
export XARGS="gxargs -r"
else
export XARGS="xargs"
fi
;;
*)
export XARGS="xargs -r"
;;
esac
fi
hasq() {
has $EBUILD_PHASE prerm postrm || eqawarn \
"QA Notice: The 'hasq' function is deprecated (replaced by 'has')"
has "$@"
}
hasv() {
if has "$@" ; then
echo "$1"
return 0
fi
return 1
}
has() {
local needle=$1
shift
local x
for x in "$@"; do
[ "${x}" = "${needle}" ] && return 0
done
return 1
}
__repo_attr() {
local appropriate_section=0 exit_status=1 line saved_extglob_shopt=$(shopt -p extglob)
shopt -s extglob
while read line; do
[[ ${appropriate_section} == 0 && ${line} == "[$1]" ]] && appropriate_section=1 && continue
[[ ${appropriate_section} == 1 && ${line} == "["*"]" ]] && appropriate_section=0 && continue
# If a conditional expression like [[ ${line} == $2*( )=* ]] is used
# then bash-3.2 produces an error like the following when the file is
# sourced: syntax error in conditional expression: unexpected token `('
# Therefore, use a regular expression for compatibility.
if [[ ${appropriate_section} == 1 && ${line} =~ ^${2}[[:space:]]*= ]]; then
echo "${line##$2*( )=*( )}"
exit_status=0
break
fi
done <<< "${PORTAGE_REPOSITORIES}"
eval "${saved_extglob_shopt}"
return ${exit_status}
}
# eqaquote <string>
#
# outputs parameter escaped for quoting
__eqaquote() {
local v=${1} esc=''
# quote backslashes
v=${v//\\/\\\\}
# quote the quotes
v=${v//\"/\\\"}
# quote newlines
while read -r; do
echo -n "${esc}${REPLY}"
esc='\n'
done <<<"${v}"
}
# eqatag <tag> [-v] [<key>=<value>...] [/<relative-path>...]
#
# output (to qa.log):
# - tag: <tag>
# data:
# <key1>: "<value1>"
# <key2>: "<value2>"
# files:
# - "<path1>"
# - "<path2>"
__eqatag() {
local tag i filenames=() data=() verbose=
if [[ ${1} == -v ]]; then
verbose=1
shift
fi
tag=${1}
shift
[[ -n ${tag} ]] || die "${FUNCNAME}: no tag specified"
# collect data & filenames
for i; do
if [[ ${i} == /* ]]; then
filenames+=( "${i}" )
[[ -n ${verbose} ]] && eqawarn " ${i}"
elif [[ ${i} == *=* ]]; then
data+=( "${i}" )
else
die "${FUNCNAME}: invalid parameter: ${i}"
fi
done
(
echo "- tag: ${tag}"
if [[ ${#data[@]} -gt 0 ]]; then
echo " data:"
for i in "${data[@]}"; do
echo " ${i%%=*}: \"$(__eqaquote "${i#*=}")\""
done
fi
if [[ ${#filenames[@]} -gt 0 ]]; then
echo " files:"
for i in "${filenames[@]}"; do
echo " - \"$(__eqaquote "${i}")\""
done
fi
) >> "${T}"/qa.log
}
true