edo.eclass: enhance edob for usage with noisy commands

Normally, edob can, or rather should, not be used with noisy commands,
i.e., commands that produce an output. This is because the output
destroys the concept of ebegin and eend, where the eend marker is shown
on the same line that is produced by ebegin.

However, it sometimes would be nice to use edob with noisy commands, but
this means to redirect stdout and stderr of those commands. Instead of
redirecting the output to /dev/null, we save the output in a log file
under T. This allows us to present the output to the user in case the
command fails, making it furthermore part of the build.log, which we
expect users to attach to bug reports.

Closes: https://github.com/gentoo/gentoo/pull/36117
Signed-off-by: Florian Schmaus <flow@gentoo.org>
This commit is contained in:
Florian Schmaus
2024-04-04 17:22:25 +02:00
parent 918822253d
commit fa90907e9d
2 changed files with 171 additions and 10 deletions

View File

@@ -1,4 +1,4 @@
# Copyright 2022 Gentoo Authors
# Copyright 2022-2024 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2
# @ECLASS: edo.eclass
@@ -12,10 +12,16 @@
# This eclass provides the 'edo' command, and an 'edob' variant for ebegin/eend,
# which logs the command used verbosely and dies (exits) on failure.
#
# This eclass should be used only where needed to give a more verbose log, e.g.
# for invoking non-standard ./configure scripts, or building objects/binaries
# directly within ebuilds via compiler invocations. It is NOT to be used
# in place of generic 'command || die' where verbosity is unnecessary.
# The 'edo' command should be used only where needed to give a more verbose log,
# e.g. for invoking non-standard ./configure scripts, or building
# objects/binaries directly within ebuilds via compiler invocations. It is NOT
# to be used in place of generic 'command || die' where verbosity is
# unnecessary.
#
# The 'edob' command can be used for long running commands, even if
# those commands produce output. The 'edob' command will suppress the
# command's output and only present it if the command returned with a
# non-zero exit status.
case ${EAPI} in
7|8) ;;
*) die "${ECLASS}: EAPI ${EAPI:-0} not supported" ;;
@@ -35,14 +41,56 @@ edo() {
}
# @FUNCTION: edob
# @USAGE: <command> [<args>...]
# @USAGE: [-l <log-name>] [-m <message>] <command> [<args>...]
# @DESCRIPTION:
# Executes 'command' with ebegin & eend with any given arguments and exits
# on failure unless called under 'nonfatal'.
# on failure unless called under 'nonfatal'. This function redirects
# stdout and stderr to a log file. The content of the log file is shown
# if the command returns with a non-zero exit status.
#
# If -m <message> is provided, then invokes ebegin with <message>, otherwise
# a default message is used. If -l <log-name> is provided, then <log-name> is
# used to construct the name of the log file where stdout and stderr of the
# command is redirected to.
edob() {
ebegin "Running $@"
"$@"
eend $? || die -n "Failed to run command: $@"
local message
local log_name
while true; do
case "${1}" in
-l|-m)
[[ $# -lt 2 ]] && die "Must provide an argument to ${1}"
case "${1}" in
-l)
log_name="${2}"
;;
-m)
message="${2}"
;;
esac
shift 2
;;
*)
break
;;
esac
done
[[ -z ${message} ]] && message="Running $@"
[[ -z ${log_name} ]] && log_name="$(basename ${1})"
local log_file="${T}/${log_name}.log"
[[ -f ${log_file} ]] && die "Log file ${log_file} exists. Consider using \"edob -l\""
ebegin "${message}"
"$@" &> "${log_file}"
local ret=$?
if ! eend $ret; then
cat "${log_file}"
die -n "Command \"$@\" failed with exit status $ret"
fi
}
fi

113
eclass/tests/edo.sh Executable file
View File

@@ -0,0 +1,113 @@
#!/bin/bash
# Copyright 1999-2024 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2
EAPI=8
source tests-common.sh || exit
inherit edo
make_some_noise() {
echo "Here is some noise:"
echo "${1:?Must provide some noise}"
echo "EoN"
}
test_edob_simple() {
tbegin "edob with output test"
(
edob make_some_noise foo
eend $?
) &> "${T}/edob.out"
local res=$?
if [[ $res -ne 0 ]]; then
tend $res
return 0
fi
local log_file="${T}/make_some_noise.log"
local second_line="$(sed -n '2p' "${log_file}")"
[[ "${second_line}" == "foo" ]];
tend $? "Unexpected output, found \"${second_line}\", expected \"foo\""
rm "${log_file}" || die
}
test_edob_explicit_log_name() {
tbegin "edob with explicit logfile name"
(
edob -l mylog make_some_noise bar
eend $?
) &> "${T}/edob.out"
local res=$?
if [[ $res -ne 0 ]]; then
cat "${T}/edob.out"
tend $res
return 0
fi
local log_file="${T}/mylog.log"
local second_line="$(sed -n '2p' "${log_file}")"
[[ "${second_line}" == "bar" ]];
tend $? "Unexpected output, found \"${second_line}\", expected \"foo\""
rm "${log_file}" || die
}
test_edob_explicit_message() {
tbegin "edob with explicit message"
(
edob -m "Making some noise" make_some_noise baz
eend $?
) &> "${T}/edob.out"
local res=$?
if [[ $res -ne 0 ]]; then
cat "${T}/edob.out"
tend $res
return 0
fi
local log_file="${T}/make_some_noise.log"
local second_line="$(sed -n '2p' "${log_file}")"
[[ "${second_line}" == "baz" ]];
tend $? "Unexpected output, found \"${second_line}\", expected \"baz\""
rm "${log_file}" || die
}
test_edob_failure() {
make_some_noise_and_fail() {
make_some_noise "$@"
return 1
}
tbegin "edob with failing command"
(
edob -m "Making some noise" make_some_noise_and_fail quz
eend $?
) &> "${T}/edob.out"
local res=$?
# Now, this time we expect res to be exactly '1'.
if [[ $res -ne 1 ]]; then
tend 1
return 1
fi
local log_file="${T}/make_some_noise_and_fail.log"
local second_line="$(sed -n '2p' "${log_file}")"
[[ "${second_line}" == "quz" ]];
tend $? "Unexpected output, found \"${second_line}\", expected \"quz\""
rm "${log_file}" || die
local fourth_line_of_edob_out="$(sed -n '4p' "${T}/edob.out")"
[[ "${fourth_line_of_edob_out}" == "quz" ]];
tend $? "Unexpected output, found \"${fourth_line_of_edob_out}\", expected \"quz\""
}
test_edob_simple
test_edob_explicit_log_name
test_edob_explicit_message
test_edob_failure
texit