diff --git a/eclass/edo.eclass b/eclass/edo.eclass index c2e7ed60083f4..ed8ec8d3201e2 100644 --- a/eclass/edo.eclass +++ b/eclass/edo.eclass @@ -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: [...] +# @USAGE: [-l ] [-m ] [...] # @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 is provided, then invokes ebegin with , otherwise +# a default message is used. If -l is provided, then 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 diff --git a/eclass/tests/edo.sh b/eclass/tests/edo.sh new file mode 100755 index 0000000000000..cac03e0401ba8 --- /dev/null +++ b/eclass/tests/edo.sh @@ -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