1 |
Thanks for your reply, but I still have some concerns about this. |
2 |
|
3 |
On 10/19/2012 8:11 AM, Zac Medico wrote: |
4 |
> Regardless of EAPI, don't call the helpers that die in EAPI 4 unless you |
5 |
> want the function to die when the helpers fail, and use helper || die so |
6 |
> it behaves the same regardless of EAPI. |
7 |
|
8 |
Took me a while, but I think I see why this is correct, now (mostly -- |
9 |
see below). The source of my confusion was a mistaken assumption that |
10 |
die() would not respect PORTAGE_NONFATAL. But isolated-functions.sh has |
11 |
roughly: |
12 |
|
13 |
die() |
14 |
{ |
15 |
[[ ${PORTAGE_NONFATAL} == 1 ]] && { |
16 |
echo "${FUNCNAME[1]}: WARNING: $*" >&2 |
17 |
return 1 |
18 |
} |
19 |
. |
20 |
. |
21 |
|
22 |
So -- correct me if I'm wrong -- this all works as follows: |
23 |
|
24 |
o helpers always handle errors by remembering the exit code of some |
25 |
error-prone operation, and, if that exit code is nonzero, invoking |
26 |
__helpers_die with some description of the problem, and finally |
27 |
returning the remembered exit code. |
28 |
|
29 |
In EAPI[0-3], this will dump the error text to stderr and return |
30 |
the remembered exit code. |
31 |
|
32 |
In >=EAPI4, this will call isolated-functions.sh's die(), which will |
33 |
swoop in and terminate everything before it has the chance to |
34 |
go any further, effectively ensuring that __helpers_die never |
35 |
returns -- /unless/ PORTAGE_NONFATAL is 1, in which case, it will |
36 |
prepend "__helpers_die:" to the error message, but otherwise |
37 |
behave as in EAPI[0-3]. |
38 |
|
39 |
o die incantations in an ebuild or eclass will either directly or |
40 |
indirectly call die() in isolated-functions.sh, which still works |
41 |
as described above when used in these contexts -- which is to say, |
42 |
that die is nonfatal when PORTAGE_NONFATAL is set. |
43 |
|
44 |
o PORTAGE_NONFATAL is only supposed to be manipulated by means of |
45 |
the nonfatal API, which is only callable in >=EAPI4, which means |
46 |
that unless somebody "cheated" by directly messing with the |
47 |
variable, in EAPI[0-3], die() never returns (like "exit") |
48 |
|
49 |
> It's okay to die in older EAPIs, as long as you're not changing the |
50 |
> behavior of a previously existing eclass function. |
51 |
|
52 |
ACK |
53 |
|
54 |
> Previously existing eclass functions that only start to |
55 |
> die in EAPI 4 are okay, since they only start to die when the ebuild |
56 |
> developer bumps the EAPI. |
57 |
|
58 |
ACK |
59 |
|
60 |
If I indeed understand the facts correctly, I'm still a bit |
61 |
uncomfortable with your advice to just use "helper || die" in eclass |
62 |
code. It seems to me that if I follow this recipe, >=EAPI4 kinda works |
63 |
OK, but EAPI[0-3] doesn't. |
64 |
|
65 |
Specifically, an EAPI[0-3] ebuild author will not have any means at his |
66 |
or her disposal to handle errors that occur in eclass utility functions. |
67 |
The error-handling semantics of eclass utility functions would be like |
68 |
EAPI4, except without nonfatal around to provide relief from auto-die. |
69 |
|
70 |
EAPI[0-3] conventions encourage the habit of writing ebuild code like |
71 |
normal bash script code, which is to say, that authors expect to be able |
72 |
to write code like |
73 |
|
74 |
invoke_fn || ... # handle error, probably by die()ing but maybe not |
75 |
|
76 |
But eclasses that auto-die violate this expectation and create a |
77 |
situation where ebuild authors must be mindful of whether a particular |
78 |
function is a helper-function or an eclass utility function to correctly |
79 |
anticipate whether auto-death will occur. |
80 |
|
81 |
This seems to be by design and may not be so horrible as I make it sound |
82 |
-- for example, it would be pretty weird to expect eclass code written |
83 |
before EAPI4 to suddenly start working differently in EAPI[0-3]. |
84 |
|
85 |
But should we really write new eclass code to behave this way as well? |
86 |
|
87 |
Worse, eclasses authored in the pre-EAPI4 era ubiquitously assume that |
88 |
die() never returns, and do things like: |
89 |
|
90 |
efoo() { |
91 |
[[ ${EBUILD_PHASE} != "prepare" ]] && |
92 |
die "efoo can only be used during src_prepare" |
93 |
. |
94 |
. |
95 |
# stuff that isn't safe if ${EBUILD_PHASE} != "prepare" |
96 |
. |
97 |
. |
98 |
} |
99 |
|
100 |
Since, in EAPI4, no means is provided to specify that a particular |
101 |
invocation of die() is unconditionally terminal, (except directly |
102 |
manipulating PORTAGE_NONFATAL, which doesn't seem to be encouraged), the |
103 |
only non-encapsulation-breaking non-EAPI-specific solution would be to |
104 |
re-code every presumed-terminal use of die() to look something like: |
105 |
|
106 |
failure_prone_action |
107 |
ret=$? |
108 |
if [[ ${ret} != 0 ]] ; then |
109 |
die "Error message" |
110 |
return ${ret} |
111 |
fi |
112 |
|
113 |
I can imagine no reasonable way to avoid this without using aliases... |
114 |
or perhaps some construct like: |
115 |
|
116 |
do_or_die() { |
117 |
diemsg="$1" |
118 |
shift |
119 |
"$@" || { ret=$?; die "${diemsg}"; return ${ret}; } |
120 |
} |
121 |
|
122 |
efoo() { |
123 |
do_or_die "error message 1" \ |
124 |
failure_prone_action1 arg1 arg2 arg3 && \ |
125 |
safe_action1 arg1 && \ |
126 |
safe_action2 arg1 arg2 && \ |
127 |
do_or_die "error message 2" \ |
128 |
failure_prone_action2 arg1 arg2 && \ |
129 |
. |
130 |
. |
131 |
|
132 |
Hopefully I'm missing something here...? |
133 |
|
134 |
-gmt |