Gentoo Archives: gentoo-commits

From: "Ulrich Müller" <ulm@g.o>
To: gentoo-commits@l.g.o
Subject: [gentoo-commits] repo/gentoo:master commit in: eclass/
Date: Tue, 26 Sep 2017 18:46:40
Message-Id: 1506451587.91dfeadf8408ca47b434c65753564b55a912f884.ulm@gentoo
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 }