1 |
commit: 91dfeadf8408ca47b434c65753564b55a912f884 |
2 |
Author: Michał Górny <mgorny <AT> gentoo <DOT> org> |
3 |
AuthorDate: Wed Sep 20 22:07:15 2017 +0000 |
4 |
Commit: Ulrich Müller <ulm <AT> gentoo <DOT> org> |
5 |
CommitDate: Tue Sep 26 18:46:27 2017 +0000 |
6 |
URL: https://gitweb.gentoo.org/repo/gentoo.git/commit/?id=91dfeadf |
7 |
|
8 |
eapi7-ver.eclass: Ultra-fast algo for comparison |
9 |
|
10 |
eclass/eapi7-ver.eclass | 325 ++++++++++++++++++++++++++++++++---------------- |
11 |
1 file changed, 218 insertions(+), 107 deletions(-) |
12 |
|
13 |
diff --git a/eclass/eapi7-ver.eclass b/eclass/eapi7-ver.eclass |
14 |
index 1ad1cbe2edc..2644832574a 100644 |
15 |
--- a/eclass/eapi7-ver.eclass |
16 |
+++ b/eclass/eapi7-ver.eclass |
17 |
@@ -174,6 +174,81 @@ ver_rs() { |
18 |
echo "${comp[*]}" |
19 |
} |
20 |
|
21 |
+# @FUNCTION: _ver_validate |
22 |
+# @USAGE: <comp[0]>... |
23 |
+# @DESCRIPTION: |
24 |
+# Verify that the version component array passed as the argument |
25 |
+# validates according to the PMS version rules. Returns 0 if it does, |
26 |
+# 1 otherwise. |
27 |
+_ver_validate() { |
28 |
+ local prev=start |
29 |
+ |
30 |
+ while [[ ${1} || ${2} ]]; do |
31 |
+ local s=${1} |
32 |
+ local c=${2} |
33 |
+ |
34 |
+ if [[ -z ${s} ]]; then |
35 |
+ if [[ ${c} == [0-9]* ]]; then |
36 |
+ # number without preceding sep may be either: |
37 |
+ case ${prev} in |
38 |
+ # a. 1st version number |
39 |
+ start) prev=numeric;; |
40 |
+ # b. _foo suffix number |
41 |
+ suffix) prev=suffix_num;; |
42 |
+ # c. -rN revision number |
43 |
+ revision) prev=revision_num;; |
44 |
+ *) return 1;; |
45 |
+ esac |
46 |
+ elif [[ -n ${c} ]]; then |
47 |
+ # letter without preceding sep = letter after version |
48 |
+ [[ ${prev} == numeric ]] || return 1 |
49 |
+ [[ ${#c} -eq 1 ]] || return 1 |
50 |
+ prev=letter |
51 |
+ fi |
52 |
+ elif [[ -z ${c} ]]; then |
53 |
+ # trailing suffix? |
54 |
+ return 1 |
55 |
+ elif [[ ${s} == . ]]; then |
56 |
+ # number preceded by dot = numeric component |
57 |
+ [[ ${prev} == numeric ]] || return 1 |
58 |
+ elif [[ ${s} == _ ]]; then |
59 |
+ # _ implies _foo suffix |
60 |
+ case ${prev} in |
61 |
+ numeric|letter|suffix|suffix_num) ;; |
62 |
+ *) return 1;; |
63 |
+ esac |
64 |
+ |
65 |
+ case ${c} in |
66 |
+ alpha) ;; |
67 |
+ beta) ;; |
68 |
+ rc) ;; |
69 |
+ pre) ;; |
70 |
+ p) ;; |
71 |
+ *) return 1;; |
72 |
+ esac |
73 |
+ prev=suffix |
74 |
+ elif [[ ${s} == - ]]; then |
75 |
+ # - implies revision |
76 |
+ case ${prev} in |
77 |
+ numeric|letter|suffix|suffix_num) ;; |
78 |
+ *) return 1;; |
79 |
+ esac |
80 |
+ |
81 |
+ [[ ${c} == r ]] || return 1 |
82 |
+ prev=revision |
83 |
+ else |
84 |
+ return 1 |
85 |
+ fi |
86 |
+ |
87 |
+ shift 2 |
88 |
+ done |
89 |
+ |
90 |
+ # empty version string? |
91 |
+ [[ ${prev} != start ]] || return 1 |
92 |
+ |
93 |
+ return 0 |
94 |
+} |
95 |
+ |
96 |
# @FUNCTION: ver_test |
97 |
# @USAGE: [<v1>] <op> <v2> |
98 |
# @DESCRIPTION: |
99 |
@@ -183,127 +258,163 @@ ver_rs() { |
100 |
# revision parts), and the comparison is performed according to |
101 |
# the algorithm specified in the PMS. |
102 |
ver_test() { |
103 |
- local v1 v2 op i tail result |
104 |
- local -a v1comp v2comp |
105 |
- local match=( |
106 |
- "+([0-9])*(.+([0-9]))" # numeric components |
107 |
- "[a-z]" # letter component |
108 |
- "*(@(_alpha|_beta|_pre|_rc|_p)*([0-9]))" # suffixes |
109 |
- "-r+([0-9])" # revision |
110 |
- ) |
111 |
- |
112 |
- local LC_ALL=C shopt_save=$(shopt -p extglob) |
113 |
- shopt -s extglob |
114 |
- |
115 |
- if [[ $# -eq 2 ]]; then |
116 |
- v1=${PVR} |
117 |
- elif [[ $# -eq 3 ]]; then |
118 |
- v1=$1; shift |
119 |
+ local LC_ALL=C |
120 |
+ local va op vb |
121 |
+ |
122 |
+ if [[ $# -eq 3 ]]; then |
123 |
+ va=${1} |
124 |
+ shift |
125 |
else |
126 |
- die "${FUNCNAME}: bad number of arguments" |
127 |
+ va=${PVR} |
128 |
fi |
129 |
- op=$1 |
130 |
- v2=$2 |
131 |
+ |
132 |
+ [[ $# -eq 2 ]] || die "${FUNCNAME}: bad number of arguments" |
133 |
+ |
134 |
+ op=${1} |
135 |
+ vb=${2} |
136 |
|
137 |
case ${op} in |
138 |
-eq|-ne|-lt|-le|-gt|-ge) ;; |
139 |
*) die "${FUNCNAME}: invalid operator: ${op}" ;; |
140 |
esac |
141 |
|
142 |
- # Test for both versions being valid, and split them into parts |
143 |
- for (( i=0; i<4; i++ )); do |
144 |
- tail=${v1##${match[i]}} |
145 |
- v1comp[i]=${v1%"${tail}"} |
146 |
- v1=${tail} |
147 |
- tail=${v2##${match[i]}} |
148 |
- v2comp[i]=${v2%"${tail}"} |
149 |
- v2=${tail} |
150 |
- done |
151 |
- # There must not be any remaining tail, and the numeric part |
152 |
- # must be non-empty. All other parts are optional. |
153 |
- [[ -z ${v1} && -z ${v2} && -n ${v1comp[0]} && -n ${v2comp[0]} ]] \ |
154 |
- || die "${FUNCNAME}: invalid version" |
155 |
- |
156 |
- # Compare numeric components (PMS algorithm 3.2) |
157 |
- _ver_cmp_num() { |
158 |
- local a=(${1//./ }) b=(${2//./ }) |
159 |
- local an=${#a[@]} bn=${#b[@]} |
160 |
- local i |
161 |
- # First component |
162 |
- [[ 10#${a[0]} -gt 10#${b[0]} ]] && return 2 |
163 |
- [[ 10#${a[0]} -lt 10#${b[0]} ]] && return 1 |
164 |
- for (( i=1; i<an && i<bn; i++ )); do |
165 |
- # Other components (PMS algorithm 3.3) |
166 |
- if [[ ${a[i]} == 0* || ${b[i]} == 0* ]]; then |
167 |
- local ap=${a[i]%%*(0)} bp=${b[i]%%*(0)} |
168 |
- [[ ${ap} > ${bp} ]] && return 2 |
169 |
- [[ ${ap} < ${bp} ]] && return 1 |
170 |
+ # explicitly strip -r0[00000...] to avoid overcomplexifying the algo |
171 |
+ [[ ${va} == *-r0* && 10#${va#*-r} -eq 0 ]] && va=${va%-r*} |
172 |
+ [[ ${vb} == *-r0* && 10#${vb#*-r} -eq 0 ]] && vb=${vb%-r*} |
173 |
+ |
174 |
+ local comp compb |
175 |
+ _ver_split "${vb}" |
176 |
+ compb=( "${comp[@]}" ) |
177 |
+ _ver_split "${va}" |
178 |
+ |
179 |
+ _ver_validate "${comp[@]}" || die "${FUNCNAME}: invalid version: ${va}" |
180 |
+ _ver_validate "${compb[@]}" || die "${FUNCNAME}: invalid version: ${vb}" |
181 |
+ |
182 |
+ local i sa sb ca cb wa wb result=0 |
183 |
+ for (( i = 0;; i += 2 )); do |
184 |
+ sa=${comp[i]} |
185 |
+ ca=${comp[i+1]} |
186 |
+ sb=${compb[i]} |
187 |
+ cb=${compb[i+1]} |
188 |
+ |
189 |
+ # 1. determine the component type for Ca |
190 |
+ if [[ -z ${sa} ]]; then |
191 |
+ if [[ ${ca} == [0-9]* ]]; then |
192 |
+ # number without preceding sep may be either: |
193 |
+ # a. 1st version number |
194 |
+ # b. _foo suffix number |
195 |
+ # c. -rN revision number |
196 |
+ # weight is irrelevant since (a) occurs simultaneously, |
197 |
+ # and (b)/(c) use weight of preceding component |
198 |
+ wa=0 |
199 |
+ # method: plain numeric |
200 |
+ m=pn |
201 |
+ elif [[ -n ${ca} ]]; then |
202 |
+ # letter without preceding sep = letter after version |
203 |
+ # weight below numeric, lexical method |
204 |
+ wa=9 |
205 |
+ m=l |
206 |
else |
207 |
- [[ ${a[i]} -gt ${b[i]} ]] && return 2 |
208 |
- [[ ${a[i]} -lt ${b[i]} ]] && return 1 |
209 |
+ # empty == end of version string |
210 |
+ # weight below all positive stuff but above negative |
211 |
+ wa=6 |
212 |
+ m= |
213 |
fi |
214 |
- done |
215 |
- [[ ${an} -gt ${bn} ]] && return 2 |
216 |
- [[ ${an} -lt ${bn} ]] && return 1 |
217 |
- return 0 |
218 |
- } |
219 |
- |
220 |
- # Compare letter components (PMS algorithm 3.4) |
221 |
- _ver_cmp_let() { |
222 |
- local a=$1 b=$2 |
223 |
- [[ ${a} > ${b} ]] && return 2 |
224 |
- [[ ${a} < ${b} ]] && return 1 |
225 |
- return 0 |
226 |
- } |
227 |
- |
228 |
- # Compare suffixes (PMS algorithm 3.5) |
229 |
- _ver_cmp_suf() { |
230 |
- local a=(${1//_/ }) b=(${2//_/ }) |
231 |
- local an=${#a[@]} bn=${#b[@]} |
232 |
- local i |
233 |
- for (( i=0; i<an && i<bn; i++ )); do |
234 |
- # Compare each suffix (PMS algorithm 3.6) |
235 |
- if [[ ${a[i]%%*([0-9])} == "${b[i]%%*([0-9])}" ]]; then |
236 |
- [[ 10#${a[i]##*([a-z])} -gt 10#${b[i]##*([a-z])} ]] && return 2 |
237 |
- [[ 10#${a[i]##*([a-z])} -lt 10#${b[i]##*([a-z])} ]] && return 1 |
238 |
+ elif [[ ${sa} == . ]]; then |
239 |
+ # number preceded by dot = numeric component |
240 |
+ # highest weight, weird PMS 2+ component comparison |
241 |
+ wa=10 |
242 |
+ m=wn |
243 |
+ elif [[ ${sa} == _ ]]; then |
244 |
+ # _ implies _foo suffix |
245 |
+ # weights differ, comparison by weight |
246 |
+ case ${ca} in |
247 |
+ alpha) wa=2 ;; |
248 |
+ beta) wa=3 ;; |
249 |
+ rc) wa=4 ;; |
250 |
+ pre) wa=5 ;; |
251 |
+ p) wa=8 ;; |
252 |
+ *) die "Invalid version suffix: _${ca}";; |
253 |
+ esac |
254 |
+ m= |
255 |
+ elif [[ ${sa} == - ]]; then |
256 |
+ # - implies revision |
257 |
+ # weight below positive suffixes, no comparison |
258 |
+ [[ ${ca} == r ]] || die "Invalid version part: -${ca}" |
259 |
+ wa=7 |
260 |
+ m= |
261 |
+ fi |
262 |
+ |
263 |
+ # 2. determine the component type for Cb |
264 |
+ if [[ -z ${sb} ]]; then |
265 |
+ if [[ ${cb} == [0-9]* ]]; then |
266 |
+ wb=0 |
267 |
+ elif [[ -n ${cb} ]]; then |
268 |
+ wb=9 |
269 |
else |
270 |
- # Check for p first |
271 |
- [[ ${a[i]} == p*([0-9]) ]] && return 2 |
272 |
- [[ ${b[i]} == p*([0-9]) ]] && return 1 |
273 |
- # Hack: Use that alpha < beta < pre < rc alphabetically |
274 |
- [[ ${a[i]} > ${b[i]} ]] && return 2 || return 1 |
275 |
+ wb=6 |
276 |
fi |
277 |
- done |
278 |
- if [[ ${an} -gt ${bn} ]]; then |
279 |
- [[ ${a[bn]} == p*([0-9]) ]] && return 2 || return 1 |
280 |
- elif [[ ${an} -lt ${bn} ]]; then |
281 |
- [[ ${b[an]} == p*([0-9]) ]] && return 1 || return 2 |
282 |
+ elif [[ ${sb} == . ]]; then |
283 |
+ wb=10 |
284 |
+ elif [[ ${sb} == _ ]]; then |
285 |
+ case ${cb} in |
286 |
+ alpha) wb=2 ;; |
287 |
+ beta) wb=3 ;; |
288 |
+ rc) wb=4 ;; |
289 |
+ pre) wb=5 ;; |
290 |
+ p) wb=8 ;; |
291 |
+ *) die "Invalid version suffix: _${cb}";; |
292 |
+ esac |
293 |
+ elif [[ ${sb} == - ]]; then |
294 |
+ [[ ${cb} == r ]] || die "Invalid version part: -${cb}" |
295 |
+ wb=7 |
296 |
fi |
297 |
- return 0 |
298 |
- } |
299 |
- |
300 |
- # Compare revision components (PMS algorithm 3.7) |
301 |
- _ver_cmp_rev() { |
302 |
- local a=${1#-r} b=${2#-r} |
303 |
- [[ 10#${a} -gt 10#${b} ]] && return 2 |
304 |
- [[ 10#${a} -lt 10#${b} ]] && return 1 |
305 |
- return 0 |
306 |
- } |
307 |
- |
308 |
- # Version comparison top-level logic (PMS algorithm 3.1) |
309 |
- _ver_cmp_num "${v1comp[0]}" "${v2comp[0]}" && |
310 |
- _ver_cmp_let "${v1comp[1]}" "${v2comp[1]}" && |
311 |
- _ver_cmp_suf "${v1comp[2]}" "${v2comp[2]}" && |
312 |
- _ver_cmp_rev "${v1comp[3]}" "${v2comp[3]}" |
313 |
- |
314 |
- case $? in |
315 |
- 0) result=0 ;; # a = b |
316 |
- 1) result=-1 ;; # a < b |
317 |
- 2) result=1 ;; # a > b |
318 |
- *) die "${FUNCNAME}: invalid return code: $?" ;; |
319 |
- esac |
320 |
|
321 |
- ${shopt_save} |
322 |
+ # DEBUG |
323 |
+ #echo "$sa $ca [$wa] <$m> $sb $cb [$wb]" >&2 |
324 |
+ |
325 |
+ # 3. compare weights, we can proceed further only if weights match |
326 |
+ if [[ ${wa} -ne ${wb} ]]; then |
327 |
+ result=$(( wa - wb )) |
328 |
+ break |
329 |
+ fi |
330 |
+ |
331 |
+ # 4. both empty maybe? |
332 |
+ [[ -z ${ca} && -z ${cb} ]] && break |
333 |
+ |
334 |
+ # 5. compare components according to the algo |
335 |
+ |
336 |
+ # weird numeric is weird and reuses pn/l, so do it first |
337 |
+ # (PMS algo 3.3) |
338 |
+ if [[ ${m} == wn ]]; then |
339 |
+ if [[ ${ca} != 0* && ${cb} != 0* ]]; then |
340 |
+ # if neither of them starts with 0, use normal numeric |
341 |
+ m=pn |
342 |
+ else |
343 |
+ # strip trailing zeros |
344 |
+ while [[ ${ca} == *0 ]]; do ca=${ca::-1}; done |
345 |
+ while [[ ${cb} == *0 ]]; do cb=${cb::-1}; done |
346 |
+ m=l |
347 |
+ fi |
348 |
+ fi |
349 |
+ |
350 |
+ case ${m} in |
351 |
+ pn) # plain numeric |
352 |
+ if [[ 10#${ca} -ne 10#${cb} ]]; then |
353 |
+ result=$(( 10#${ca} - 10#${cb} )) |
354 |
+ break |
355 |
+ fi |
356 |
+ ;; |
357 |
+ l) # lexical |
358 |
+ if [[ ${ca} != ${cb} ]]; then |
359 |
+ [[ ${ca} > ${cb} ]] && result=1 || result=-1 |
360 |
+ break |
361 |
+ fi |
362 |
+ ;; |
363 |
+ '') ;; |
364 |
+ *) die "Unexpected comparison method m=${m}";; |
365 |
+ esac |
366 |
+ done |
367 |
|
368 |
test "${result}" "${op}" 0 |
369 |
} |