Thanks for your reply, but I still have some concerns about this.
On 10/19/2012 8:11 AM, Zac Medico wrote:
Regardless of EAPI, don't call the helpers that die in EAPI 4 unless you
want the function to die when the helpers fail, and use helper || die so
it behaves the same regardless of EAPI.
Took me a while, but I think I see why this is correct, now (mostly --
see below). The source of my confusion was a mistaken assumption that
die() would not respect PORTAGE_NONFATAL. But isolated-functions.sh has
roughly:
die()
{
[[ ${PORTAGE_NONFATAL} == 1 ]] && {
echo "${FUNCNAME[1]}: WARNING: $*" >&2
return 1
}
.
.
So -- correct me if I'm wrong -- this all works as follows:
o helpers always handle errors by remembering the exit code of some
error-prone operation, and, if that exit code is nonzero, invoking
__helpers_die with some description of the problem, and finally
returning the remembered exit code.
In EAPI[0-3], this will dump the error text to stderr and return
the remembered exit code.
In >=EAPI4, this will call isolated-functions.sh's die(), which will
swoop in and terminate everything before it has the chance to
go any further, effectively ensuring that __helpers_die never
returns -- /unless/ PORTAGE_NONFATAL is 1, in which case, it will
prepend "__helpers_die:" to the error message, but otherwise
behave as in EAPI[0-3].
o die incantations in an ebuild or eclass will either directly or
indirectly call die() in isolated-functions.sh, which still works
as described above when used in these contexts -- which is to say,
that die is nonfatal when PORTAGE_NONFATAL is set.
o PORTAGE_NONFATAL is only supposed to be manipulated by means of
the nonfatal API, which is only callable in >=EAPI4, which means
that unless somebody "cheated" by directly messing with the
variable, in EAPI[0-3], die() never returns (like "exit")
It's okay to die in older EAPIs, as long as you're not changing the
> behavior of a previously existing eclass function.
ACK
Previously existing eclass functions that only start to
die in EAPI 4 are okay, since they only start to die when the ebuild
developer bumps the EAPI.
ACK
If I indeed understand the facts correctly, I'm still a bit
uncomfortable with your advice to just use "helper || die" in eclass
code. It seems to me that if I follow this recipe, >=EAPI4 kinda works
OK, but EAPI[0-3] doesn't.
Specifically, an EAPI[0-3] ebuild author will not have any means at his
or her disposal to handle errors that occur in eclass utility functions.
The error-handling semantics of eclass utility functions would be like
EAPI4, except without nonfatal around to provide relief from auto-die.
EAPI[0-3] conventions encourage the habit of writing ebuild code like
normal bash script code, which is to say, that authors expect to be able
to write code like
invoke_fn || ... # handle error, probably by die()ing but maybe not
But eclasses that auto-die violate this expectation and create a
situation where ebuild authors must be mindful of whether a particular
function is a helper-function or an eclass utility function to correctly
anticipate whether auto-death will occur.
This seems to be by design and may not be so horrible as I make it sound
-- for example, it would be pretty weird to expect eclass code written
before EAPI4 to suddenly start working differently in EAPI[0-3].
But should we really write new eclass code to behave this way as well?
Worse, eclasses authored in the pre-EAPI4 era ubiquitously assume that
die() never returns, and do things like:
efoo() {
[[ ${EBUILD_PHASE} != "prepare" ]] &&
die "efoo can only be used during src_prepare"
.
.
# stuff that isn't safe if ${EBUILD_PHASE} != "prepare"
.
.
}
Since, in EAPI4, no means is provided to specify that a particular
invocation of die() is unconditionally terminal, (except directly
manipulating PORTAGE_NONFATAL, which doesn't seem to be encouraged), the
only non-encapsulation-breaking non-EAPI-specific solution would be to
re-code every presumed-terminal use of die() to look something like:
failure_prone_action
ret=$?
if [[ ${ret} != 0 ]] ; then
die "Error message"
return ${ret}
fi
I can imagine no reasonable way to avoid this without using aliases...
or perhaps some construct like:
do_or_die() {
diemsg="$1"
shift
"$@" || { ret=$?; die "${diemsg}"; return ${ret}; }
}
efoo() {
do_or_die "error message 1" \
failure_prone_action1 arg1 arg2 arg3 && \
safe_action1 arg1 && \
safe_action2 arg1 arg2 && \
do_or_die "error message 2" \
failure_prone_action2 arg1 arg2 && \
.
.
Hopefully I'm missing something here...?
-gmt