Gentoo Archives: gentoo-dev

From: "Gregory M. Turner" <gmt@×××××.us>
To: gentoo-dev@l.g.o
Subject: [gentoo-dev] eclass error-handling post-EAPI4
Date: Fri, 19 Oct 2012 14:24:57
Message-Id: 5081627B.1040707@malth.us
1 I'm cooking up some eclass functions and it occurred to me that perhaps
2 I had some responsibility regarding EAPI4's new error handling
3 semantics. After looking into it, it seems that, superficially, the
4 answer to my question is "no, the EAPI4 changes only apply to ebuild
5 helpers."
6
7 Even so, this got me thinking: if ebuild helpers are going to warn(*) in
8 EAPI<4 and die in EAPI>=4 (unless somebody used "nonfatal"), then, those
9 error-handling semantics are going to bubble up through my eclass to its
10 heirs (assuming my eclass uses helpers).
11
12 The problem I see with that is that a consumer of my eclass function is
13 effectively required to anticipate these EAPI helper-function outcomes,
14 which is fair-enough if s/he's consuming them directly, but pretty
15 unreasonable if they're just consuming my eclass.... In effect, this
16 seems to create a situation where they can't know what to expect,
17 without wading through my code, unless I go in and document "in EAPI4+
18 it does X, but in EAPI3, it does Y". Which, let's face it, most eclass
19 authors probably won't get around to doing.
20
21 For example, suppose poop.eclass has a function "epoop" which performs
22 the following steps:
23
24 o Downloads the latest "post" from the
25 http://www.unnecessaryquotes.com/ "website" using "wget"
26 o Strips out the top 3 posts from piped wget output using
27 a bunch of sed and awk magic. Saves each post-processed post as
28 "${S}/$(mktemp -u $( printf %100s |tr " " "X" )).xml" and
29 remembers these filenames in three hard-coded variables.
30 o Installs the posts as documentation using dodoc statements
31 o Returns "$(( $(date +'%s') % 3 ))"
32
33 Let's say I started writing this code before I knew anything about EAPI
34 4, and the current epoop documentation states that:
35
36 o errors in the wget and postprocessing steps will result in a return
37 code of '3'
38 o errors from dodoc will result in a return code of '3' + the
39 return-code of dodoc
40 o epoop() succeeds every third second and otherwise fails, returning
41 the number of seconds ago that the API could have been invoked
42 successfully as the error code (WTF: this is tounge-in-cheek:
43 $(( $(date +'%s') % 3 )) is the current UNIX time in
44 seconds, modulo 3, an extremely pointless and arbitrary success
45 criterion for my hypothetical function that will cause it to
46 randomly fail 66% of the times it's invoked and runs to completion.
47 If you don't find this amusing, that's OK; it's not terribly
48 important to the matter at hand.)
49
50 Well, now I have an awkward situation. In EAPI's 0-3, the function
51 works as documented. In EAPI 4+, however, epoop() will die if any error
52 occurs in dodoc. I can fix this by replacing the dodoc code -- let's
53 say it originally was coded like:
54
55 dodoc ${docfile1} || { r=$?; eerror dodoc1; return $((r+3)); }
56 dodoc ${docfile2} || { r=$?; eerror dodoc2; return $((r+3)); }
57 dodoc ${docfile3} || { r=$?; eerror dodoc3; return $((r+3)); }
58
59 -- with code like:
60
61 .
62 .
63 if has "${EAPI:-0}" 0 1 2 3 ; then
64 nonfatal dodoc ${docfile2}
65 else
66 dodoc ${docfile2}
67 fi || { r=$?; eerror dodoc2; return $((r+3)); }
68 .
69 .
70
71 but I will have to do something like this for each of the three dodoc
72 statements. Seems like a real PITA.
73
74 Obviously, I've chosen a very silly example, and perhaps I've
75 cherry-picked a solution that illustrates my point -- but it's not hard
76 to imagine having to make fairly significant changes to an eclass in
77 order to preserve its pre-EAPI4 semantics in EAPI4+.
78
79 The conclusion I'm tempted to draw is that it makes more sense to bring
80 my eclass error-handling behavior into line with the EAPI-specific
81 behavior of helper-functions than it does to gum up helper usage in my
82 eclass with a bunch of EAPI-specific error-handling spaghetti-code.
83
84 i.e., returning to epoop: instead of the above solution, I might leave
85 the dodoc code alone, and instead implement something vaguely like
86 __helpers_die, i.e.:
87
88 # note: stupidly clever -- not how i'd implement this irl
89 _poop_die_maybe() {
90 local ret="$?" eapi="${EAPI:-0}"
91 eapi=${eapi::1}
92 (( (3-eapi)*(PORTAGE_NONFATAL-1) > 0 )) && die "$@"
93 eerror "$@"
94 return ${ret} # pass through pre-invocation $? unchanged
95 }
96
97 With this in place, it's not terribly difficult to bring the
98 error-handling/EAPI semantics of epoop() into line with those of the
99 helper functions across all EAPIs. For example, where we had
100
101 return $(( $(date +'%s') % 3 ))
102
103 we might now put
104
105 ret="$(( $(date +'%s') % 3 ))"
106 (( ret == 0 )) || \
107 _poop_die_maybe "epoop: invoked ${ret} seconds too late"
108 return "${ret}"
109
110 Or, if it was the last statement in the epoop function, just:
111
112 ( exit $(( $(date +'%s') % 3 )) ) || \
113 _poop_die_maybe "epoop: invoked $? seconds too late"
114
115 I hope my gist isn't getting lost in the particulars of this silly
116 example. My point is: EAPI4 changes the behavior of eclasses if those
117 eclasses consume helper functions, which could lead to some pretty
118 muddled error-handling semantics without some kind of clarification or
119 reimplementation. Rather than dance around EAPI4 in our eclasses,
120 perhaps it's more elegant to try to approximate the helper-function
121 error handling semantics at the eclass level, across all EAPI's, which
122 would mean dying for "any kind of error" in EAPI4+ and warning and
123 returning failure codes in EAPI3-.
124
125 Stated more strongly, perhaps EAPI auto-die effectively sets us on a
126 course where eventually "everything else" really ought to work the same
127 way as the PMS mandates for helpers, avoiding an otherwise confusing
128 situation where ebuild authors constantly need to second-guess eclass
129 behavior: "yes, but specifically how does it behave in EAPI4+/3-, so I
130 can put die() in the right places?"
131
132 If so, this raises a second question: should portage provide some kind
133 of facility to uh.. facilitate the needs of eclass authors looking to
134 implement these error-handling patterns, rather than leave the correct
135 implementation of something like _epoop_die_maybe() "as an exercise to
136 the coder"?
137
138 ... does anyone see a better approach? Perhaps this a non-issue for
139 some reason I'm not grokking? I'd love to hear your thoughts -- the
140 exact "right" answer isn't so clear to me, and, frankly, I'd like to
141 move on to my eclass writing.
142
143 Thanks,
144
145 -gmt
146
147 --------
148
149 * Hey, can someone explain why, when __helpers_die decides die()ing is
150 not called-for (due either to EAPI<4 or PORTAGE_NONFATAL==1), it
151 unceremoniously dumps the error message to stderr, rather than, i.e.,
152 calling one of the __elog_base-consuming functions? I really don't get
153 that.

Replies

Subject Author
Re: [gentoo-dev] eclass error-handling post-EAPI4 Zac Medico <zmedico@g.o>