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


Reply via email to