1 |
>>>>> On Tue, 19 Sep 2017, Michał Górny wrote: |
2 |
|
3 |
>> EAPI 7 is introducing new version manipulation and comparison functions |
4 |
>> that aim to replace versionator.eclass. This eclass provides an 'early |
5 |
>> adopter' versions of those routines. |
6 |
>> |
7 |
>> It serves two goals: |
8 |
>> |
9 |
>> a. getting wider review and some real-life testing before |
10 |
>> the specification is set in stone, and |
11 |
>> |
12 |
>> b. making it possible to adapt ebuilds to the new routines early, |
13 |
>> reducing the future work of EAPI 7 porting. |
14 |
>> |
15 |
>> For more details on the new logic, please see the eclass documentation. |
16 |
>> Long story short, we are introducing three functions: |
17 |
>> |
18 |
>> 1. ver_cut -- to get substrings of the version string, |
19 |
>> |
20 |
>> 2. ver_rs -- to replace version separators via indices, |
21 |
>> |
22 |
>> 3. ver_test -- to compare two version numbers. |
23 |
>> |
24 |
>> The third function is not implemented in the eclass. It's meant to reuse |
25 |
>> the algorithms from the package manager, and the final implementation |
26 |
>> will most likely reuse the code from the package manager (e.g. via IPC). |
27 |
|
28 |
Meanwhile the ver_test function has been added to the eclass in the |
29 |
eapi7-ver branch. Please review the patch included below. |
30 |
|
31 |
> Merged now, with some documentation fixes and additional benchmark to |
32 |
> compare it with versionator.eclass. [...] |
33 |
|
34 |
For completeness, here are benchmark results for ver_test(), |
35 |
in comparison with version_is_at_least(). |
36 |
|
37 |
comparing |
38 |
real 6.78 6.80 6.81 6.76 6.75 => 6.78 avg |
39 |
user 6.73 6.75 6.75 6.71 6.69 => 6.73 avg |
40 |
comparing_versionator |
41 |
real 196.60 197.80 197.40 199.10 198.60 => 197.90 avg |
42 |
user 80.10 80.10 80.00 80.40 80.60 => 80.24 avg |
43 |
|
44 |
Ulrich |
45 |
|
46 |
-- 8< -- |
47 |
From 9c4953e2d2bdef1244cff025672489bf9c9ff72b Mon Sep 17 00:00:00 2001 |
48 |
From: =?UTF-8?q?Ulrich=20M=C3=BCller?= <ulm@g.o> |
49 |
Date: Wed, 20 Sep 2017 21:28:22 +0200 |
50 |
Subject: [PATCH] eapi7-ver.eclass: Implement ver_test(). |
51 |
MIME-Version: 1.0 |
52 |
Content-Type: text/plain; charset=UTF-8 |
53 |
Content-Transfer-Encoding: 8bit |
54 |
|
55 |
This should strictly follow Algorithms 3.1 to 3.7 specified in PMS: |
56 |
https://projects.gentoo.org/pms/6/pms.html#x1-310003.3 |
57 |
|
58 |
Thanks to Michał Górny for many optimizations, and for pointing out |
59 |
how much my initial implementation sucked. ;-) |
60 |
--- |
61 |
eclass/eapi7-ver.eclass | 111 +++++++++++++++++++++++++++++++++++- |
62 |
eclass/tests/eapi7-ver.sh | 107 ++++++++++++++++++++++++++++++++++ |
63 |
eclass/tests/eapi7-ver_benchmark.sh | 34 +++++++++++ |
64 |
3 files changed, 251 insertions(+), 1 deletion(-) |
65 |
|
66 |
diff --git a/eclass/eapi7-ver.eclass b/eclass/eapi7-ver.eclass |
67 |
index 6f8f0c0a1c37..ead9fac5e807 100644 |
68 |
--- a/eclass/eapi7-ver.eclass |
69 |
+++ b/eclass/eapi7-ver.eclass |
70 |
@@ -176,6 +176,94 @@ ver_rs() { |
71 |
echo "${comp[*]}" |
72 |
} |
73 |
|
74 |
+# @FUNCTION: _ver_compare |
75 |
+# @USAGE: <va> <vb> |
76 |
+# @RETURN: 1 if <va> < <vb>, 2 if <va> = <vb>, 3 if <va> > <vb> |
77 |
+# @INTERNAL |
78 |
+# @DESCRIPTION: |
79 |
+# Compare two versions <va> and <vb>. If <va> is less than, equal to, |
80 |
+# or greater than <vb>, return 1, 2, or 3 as exit status, respectively. |
81 |
+_ver_compare() { |
82 |
+ local va=${1} vb=${2} a an al as ar b bn bl bs br re |
83 |
+ |
84 |
+ re="^([0-9]+(\.[0-9]+)*)([a-z]?)((_(alpha|beta|pre|rc|p)[0-9]*)*)(-r[0-9]+)?$" |
85 |
+ |
86 |
+ [[ ${va} =~ ${re} ]] || die "${FUNCNAME}: invalid version: ${va}" |
87 |
+ an=${BASH_REMATCH[1]} |
88 |
+ al=${BASH_REMATCH[3]} |
89 |
+ as=${BASH_REMATCH[4]} |
90 |
+ ar=${BASH_REMATCH[7]} |
91 |
+ |
92 |
+ [[ ${vb} =~ ${re} ]] || die "${FUNCNAME}: invalid version: ${vb}" |
93 |
+ bn=${BASH_REMATCH[1]} |
94 |
+ bl=${BASH_REMATCH[3]} |
95 |
+ bs=${BASH_REMATCH[4]} |
96 |
+ br=${BASH_REMATCH[7]} |
97 |
+ |
98 |
+ # Compare numeric components (PMS algorithm 3.2) |
99 |
+ # First component |
100 |
+ a=${an%%.*} |
101 |
+ b=${bn%%.*} |
102 |
+ [[ 10#${a} -gt 10#${b} ]] && return 3 |
103 |
+ [[ 10#${a} -lt 10#${b} ]] && return 1 |
104 |
+ |
105 |
+ while [[ ${an} == *.* && ${bn} == *.* ]]; do |
106 |
+ # Other components (PMS algorithm 3.3) |
107 |
+ an=${an#*.} |
108 |
+ bn=${bn#*.} |
109 |
+ a=${an%%.*} |
110 |
+ b=${bn%%.*} |
111 |
+ if [[ ${a} == 0* || ${b} == 0* ]]; then |
112 |
+ # Remove trailing zeros |
113 |
+ while [[ ${a} == *0 ]]; do a=${a::-1}; done |
114 |
+ while [[ ${b} == *0 ]]; do b=${b::-1}; done |
115 |
+ [[ ${a} > ${b} ]] && return 3 |
116 |
+ [[ ${a} < ${b} ]] && return 1 |
117 |
+ else |
118 |
+ [[ ${a} -gt ${b} ]] && return 3 |
119 |
+ [[ ${a} -lt ${b} ]] && return 1 |
120 |
+ fi |
121 |
+ done |
122 |
+ [[ ${an} == *.* ]] && return 3 |
123 |
+ [[ ${bn} == *.* ]] && return 1 |
124 |
+ |
125 |
+ # Compare letter components (PMS algorithm 3.4) |
126 |
+ [[ ${al} > ${bl} ]] && return 3 |
127 |
+ [[ ${al} < ${bl} ]] && return 1 |
128 |
+ |
129 |
+ # Compare suffixes (PMS algorithm 3.5) |
130 |
+ as=${as#_}${as:+_} |
131 |
+ bs=${bs#_}${bs:+_} |
132 |
+ while [[ -n ${as} && -n ${bs} ]]; do |
133 |
+ # Compare each suffix (PMS algorithm 3.6) |
134 |
+ a=${as%%_*} |
135 |
+ b=${bs%%_*} |
136 |
+ if [[ ${a%%[0-9]*} == "${b%%[0-9]*}" ]]; then |
137 |
+ [[ 10#${a##*[a-z]} -gt 10#${b##*[a-z]} ]] && return 3 |
138 |
+ [[ 10#${a##*[a-z]} -lt 10#${b##*[a-z]} ]] && return 1 |
139 |
+ else |
140 |
+ # Check for p first |
141 |
+ [[ ${a%%[0-9]*} == p ]] && return 3 |
142 |
+ [[ ${b%%[0-9]*} == p ]] && return 1 |
143 |
+ # Hack: Use that alpha < beta < pre < rc alphabetically |
144 |
+ [[ ${a} > ${b} ]] && return 3 || return 1 |
145 |
+ fi |
146 |
+ as=${as#*_} |
147 |
+ bs=${bs#*_} |
148 |
+ done |
149 |
+ if [[ -n ${as} ]]; then |
150 |
+ [[ ${as} == p[_0-9]* ]] && return 3 || return 1 |
151 |
+ elif [[ -n ${bs} ]]; then |
152 |
+ [[ ${bs} == p[_0-9]* ]] && return 1 || return 3 |
153 |
+ fi |
154 |
+ |
155 |
+ # Compare revision components (PMS algorithm 3.7) |
156 |
+ [[ 10#${ar#-r} -gt 10#${br#-r} ]] && return 3 |
157 |
+ [[ 10#${ar#-r} -lt 10#${br#-r} ]] && return 1 |
158 |
+ |
159 |
+ return 2 |
160 |
+} |
161 |
+ |
162 |
# @FUNCTION: ver_test |
163 |
# @USAGE: [<v1>] <op> <v2> |
164 |
# @DESCRIPTION: |
165 |
@@ -185,5 +273,26 @@ ver_rs() { |
166 |
# revision parts), and the comparison is performed according to |
167 |
# the algorithm specified in the PMS. |
168 |
ver_test() { |
169 |
- die "${FUNCNAME}: not implemented" |
170 |
+ local LC_ALL=C |
171 |
+ local va op vb |
172 |
+ |
173 |
+ if [[ $# -eq 3 ]]; then |
174 |
+ va=${1} |
175 |
+ shift |
176 |
+ else |
177 |
+ va=${PVR} |
178 |
+ fi |
179 |
+ |
180 |
+ [[ $# -eq 2 ]] || die "${FUNCNAME}: bad number of arguments" |
181 |
+ |
182 |
+ op=${1} |
183 |
+ vb=${2} |
184 |
+ |
185 |
+ case ${op} in |
186 |
+ -eq|-ne|-lt|-le|-gt|-ge) ;; |
187 |
+ *) die "${FUNCNAME}: invalid operator: ${op}" ;; |
188 |
+ esac |
189 |
+ |
190 |
+ _ver_compare "${va}" "${vb}" |
191 |
+ test $? "${op}" 2 |
192 |
} |
193 |
diff --git a/eclass/tests/eapi7-ver.sh b/eclass/tests/eapi7-ver.sh |
194 |
index 8a96e4d29b1b..144bb2bddc3e 100755 |
195 |
--- a/eclass/tests/eapi7-ver.sh |
196 |
+++ b/eclass/tests/eapi7-ver.sh |
197 |
@@ -17,6 +17,15 @@ teq() { |
198 |
tend ${?} "returned: ${got}" |
199 |
} |
200 |
|
201 |
+teqr() { |
202 |
+ local expected=$1; shift |
203 |
+ tbegin "$* -> ${expected}" |
204 |
+ "$@" |
205 |
+ local ret=$? |
206 |
+ [[ ${ret} -eq ${expected} ]] |
207 |
+ tend $? "returned: ${ret}" |
208 |
+} |
209 |
+ |
210 |
txf() { |
211 |
tbegin "XFAIL: ${*}" |
212 |
local got=$("${@}" 2>&1) |
213 |
@@ -63,3 +72,101 @@ teq 1.2.3 ver_rs 3-5 . 1.2.3 |
214 |
txf ver_cut foo 1.2.3 |
215 |
txf ver_rs -3 _ a1b2c3d4e5 |
216 |
txf ver_rs 5-3 _ a1b2c3d4e5 |
217 |
+ |
218 |
+# Tests from Portage's test_vercmp.py |
219 |
+teqr 0 ver_test 6.0 -gt 5.0 |
220 |
+teqr 0 ver_test 5.0 -gt 5 |
221 |
+teqr 0 ver_test 1.0-r1 -gt 1.0-r0 |
222 |
+teqr 0 ver_test 999999999999999999 -gt 999999999999999998 # 18 digits |
223 |
+teqr 0 ver_test 1.0.0 -gt 1.0 |
224 |
+teqr 0 ver_test 1.0.0 -gt 1.0b |
225 |
+teqr 0 ver_test 1b -gt 1 |
226 |
+teqr 0 ver_test 1b_p1 -gt 1_p1 |
227 |
+teqr 0 ver_test 1.1b -gt 1.1 |
228 |
+teqr 0 ver_test 12.2.5 -gt 12.2b |
229 |
+teqr 0 ver_test 4.0 -lt 5.0 |
230 |
+teqr 0 ver_test 5 -lt 5.0 |
231 |
+teqr 0 ver_test 1.0_pre2 -lt 1.0_p2 |
232 |
+teqr 0 ver_test 1.0_alpha2 -lt 1.0_p2 |
233 |
+teqr 0 ver_test 1.0_alpha1 -lt 1.0_beta1 |
234 |
+teqr 0 ver_test 1.0_beta3 -lt 1.0_rc3 |
235 |
+teqr 0 ver_test 1.001000000000000001 -lt 1.001000000000000002 |
236 |
+teqr 0 ver_test 1.00100000000 -lt 1.001000000000000001 |
237 |
+teqr 0 ver_test 999999999999999998 -lt 999999999999999999 |
238 |
+teqr 0 ver_test 1.01 -lt 1.1 |
239 |
+teqr 0 ver_test 1.0-r0 -lt 1.0-r1 |
240 |
+teqr 0 ver_test 1.0 -lt 1.0-r1 |
241 |
+teqr 0 ver_test 1.0 -lt 1.0.0 |
242 |
+teqr 0 ver_test 1.0b -lt 1.0.0 |
243 |
+teqr 0 ver_test 1_p1 -lt 1b_p1 |
244 |
+teqr 0 ver_test 1 -lt 1b |
245 |
+teqr 0 ver_test 1.1 -lt 1.1b |
246 |
+teqr 0 ver_test 12.2b -lt 12.2.5 |
247 |
+teqr 0 ver_test 4.0 -eq 4.0 |
248 |
+teqr 0 ver_test 1.0 -eq 1.0 |
249 |
+teqr 0 ver_test 1.0-r0 -eq 1.0 |
250 |
+teqr 0 ver_test 1.0 -eq 1.0-r0 |
251 |
+teqr 0 ver_test 1.0-r0 -eq 1.0-r0 |
252 |
+teqr 0 ver_test 1.0-r1 -eq 1.0-r1 |
253 |
+teqr 1 ver_test 1 -eq 2 |
254 |
+teqr 1 ver_test 1.0_alpha -eq 1.0_pre |
255 |
+teqr 1 ver_test 1.0_beta -eq 1.0_alpha |
256 |
+teqr 1 ver_test 1 -eq 0.0 |
257 |
+teqr 1 ver_test 1.0-r0 -eq 1.0-r1 |
258 |
+teqr 1 ver_test 1.0-r1 -eq 1.0-r0 |
259 |
+teqr 1 ver_test 1.0 -eq 1.0-r1 |
260 |
+teqr 1 ver_test 1.0-r1 -eq 1.0 |
261 |
+teqr 1 ver_test 1.0 -eq 1.0.0 |
262 |
+teqr 1 ver_test 1_p1 -eq 1b_p1 |
263 |
+teqr 1 ver_test 1b -eq 1 |
264 |
+teqr 1 ver_test 1.1b -eq 1.1 |
265 |
+teqr 1 ver_test 12.2b -eq 12.2 |
266 |
+ |
267 |
+# A subset of tests from Paludis |
268 |
+teqr 0 ver_test 1.0_alpha -gt 1_alpha |
269 |
+teqr 0 ver_test 1.0_alpha -gt 1 |
270 |
+teqr 0 ver_test 1.0_alpha -lt 1.0 |
271 |
+teqr 0 ver_test 1.2.0.0_alpha7-r4 -gt 1.2_alpha7-r4 |
272 |
+teqr 0 ver_test 0001 -eq 1 |
273 |
+teqr 0 ver_test 01 -eq 001 |
274 |
+teqr 0 ver_test 0001.1 -eq 1.1 |
275 |
+teqr 0 ver_test 01.01 -eq 1.01 |
276 |
+teqr 0 ver_test 1.010 -eq 1.01 |
277 |
+teqr 0 ver_test 1.00 -eq 1.0 |
278 |
+teqr 0 ver_test 1.0100 -eq 1.010 |
279 |
+teqr 0 ver_test 1-r00 -eq 1-r0 |
280 |
+ |
281 |
+# Additional tests |
282 |
+teqr 0 ver_test 0_rc99 -lt 0 |
283 |
+teqr 0 ver_test 011 -eq 11 |
284 |
+teqr 0 ver_test 019 -eq 19 |
285 |
+teqr 0 ver_test 1.2 -eq 001.2 |
286 |
+teqr 0 ver_test 1.2 -gt 1.02 |
287 |
+teqr 0 ver_test 1.2a -lt 1.2b |
288 |
+teqr 0 ver_test 1.2_pre1 -gt 1.2_pre1_beta2 |
289 |
+teqr 0 ver_test 1.2_pre1 -lt 1.2_pre1_p2 |
290 |
+teqr 0 ver_test 1.00 -lt 1.0.0 |
291 |
+teqr 0 ver_test 1.010 -eq 1.01 |
292 |
+teqr 0 ver_test 1.01 -lt 1.1 |
293 |
+teqr 0 ver_test 1.2_pre08-r09 -eq 1.2_pre8-r9 |
294 |
+ |
295 |
+# Bad number or ordering of arguments |
296 |
+txf ver_test 1 |
297 |
+txf ver_test 1 -lt 2 3 |
298 |
+txf ver_test -lt 1 2 |
299 |
+ |
300 |
+# Bad operators |
301 |
+txf ver_test 1 "<" 2 |
302 |
+txf ver_test 1 lt 2 |
303 |
+txf ver_test 1 -foo 2 |
304 |
+ |
305 |
+# Malformed versions |
306 |
+txf ver_test "" -ne 1 |
307 |
+txf ver_test 1. -ne 1 |
308 |
+txf ver_test 1ab -ne 1 |
309 |
+txf ver_test b -ne 1 |
310 |
+txf ver_test 1-r1_pre -ne 1 |
311 |
+txf ver_test 1-pre1 -ne 1 |
312 |
+txf ver_test 1_foo -ne 1 |
313 |
+txf ver_test 1_pre1.1 -ne 1 |
314 |
+txf ver_test 1-r1.0 -ne 1 |
315 |
diff --git a/eclass/tests/eapi7-ver_benchmark.sh b/eclass/tests/eapi7-ver_benchmark.sh |
316 |
index 1de26444c9b3..c46713713368 100755 |
317 |
--- a/eclass/tests/eapi7-ver_benchmark.sh |
318 |
+++ b/eclass/tests/eapi7-ver_benchmark.sh |
319 |
@@ -76,6 +76,38 @@ replacing_versionator() { |
320 |
done >/dev/null |
321 |
} |
322 |
|
323 |
+comparing() { |
324 |
+ local x |
325 |
+ for x in {1..1000}; do |
326 |
+ ver_test 1b_p1 -le 1_p1 |
327 |
+ ver_test 1.1b -le 1.1 |
328 |
+ ver_test 12.2.5 -le 12.2b |
329 |
+ ver_test 4.0 -le 5.0 |
330 |
+ ver_test 5 -le 5.0 |
331 |
+ ver_test 1.0_pre2 -le 1.0_p2 |
332 |
+ ver_test 1.0_alpha2 -le 1.0_p2 |
333 |
+ ver_test 1.0_alpha1 -le 1.0_beta1 |
334 |
+ ver_test 1.0_beta3 -le 1.0_rc3 |
335 |
+ ver_test 1.001000000000000001 -le 1.001000000000000002 |
336 |
+ done |
337 |
+} |
338 |
+ |
339 |
+comparing_versionator() { |
340 |
+ local x |
341 |
+ for x in {1..100}; do |
342 |
+ version_is_at_least 1b_p1 1_p1 |
343 |
+ version_is_at_least 1.1b 1.1 |
344 |
+ version_is_at_least 12.2.5 12.2b |
345 |
+ version_is_at_least 4.0 5.0 |
346 |
+ version_is_at_least 5 5.0 |
347 |
+ version_is_at_least 1.0_pre2 1.0_p2 |
348 |
+ version_is_at_least 1.0_alpha2 1.0_p2 |
349 |
+ version_is_at_least 1.0_alpha1 1.0_beta1 |
350 |
+ version_is_at_least 1.0_beta3 1.0_rc3 |
351 |
+ version_is_at_least 1.001000000000000001 1.001000000000000002 |
352 |
+ done |
353 |
+} |
354 |
+ |
355 |
get_times() { |
356 |
local factor=${1}; shift |
357 |
echo "${*}" |
358 |
@@ -111,3 +143,5 @@ get_times 1 cutting |
359 |
get_times 10 cutting_versionator |
360 |
get_times 1 replacing |
361 |
get_times 10 replacing_versionator |
362 |
+get_times 1 comparing |
363 |
+get_times 10 comparing_versionator |
364 |
-- |
365 |
2.14.1 |