Gentoo Archives: gentoo-dev

From: "Michał Górny" <mgorny@g.o>
To: gentoo-dev@l.g.o
Cc: "Michał Górny" <mgorny@g.o>
Subject: [gentoo-dev] [PATCH] eapi7-ver.eclass: 'Early adopter' version of EAPI 7 version manip
Date: Fri, 08 Sep 2017 11:19:44
Message-Id: 20170908111923.8809-1-mgorny@gentoo.org
1 EAPI 7 is introducing new version manipulation and comparison functions
2 that aim to replace versionator.eclass. This eclass provides an 'early
3 adopter' versions of those routines.
4
5 It serves two goals:
6
7 a. getting wider review and some real-life testing before
8 the specification is set in stone, and
9
10 b. making it possible to adapt ebuilds to the new routines early,
11 reducing the future work of EAPI 7 porting.
12
13 For more details on the new logic, please see the eclass documentation.
14 Long story short, we are introducing three functions:
15
16 1. ver_cut -- to get substrings of the version string,
17
18 2. ver_rs -- to replace version separators via indices,
19
20 3. ver_test -- to compare two version numbers.
21
22 The third function is not implemented in the eclass. It's meant to reuse
23 the algorithms from the package manager, and the final implementation
24 will most likely reuse the code from the package manager (e.g. via IPC).
25 ---
26 eclass/eapi7-ver.eclass | 181 ++++++++++++++++++++++++++++++++++++
27 eclass/tests/eapi7-ver.sh | 65 +++++++++++++
28 eclass/tests/eapi7-ver:benchmark.sh | 76 +++++++++++++++
29 3 files changed, 322 insertions(+)
30 create mode 100644 eclass/eapi7-ver.eclass
31 create mode 100755 eclass/tests/eapi7-ver.sh
32 create mode 100755 eclass/tests/eapi7-ver:benchmark.sh
33
34 diff --git a/eclass/eapi7-ver.eclass b/eclass/eapi7-ver.eclass
35 new file mode 100644
36 index 000000000000..c82da6192c94
37 --- /dev/null
38 +++ b/eclass/eapi7-ver.eclass
39 @@ -0,0 +1,181 @@
40 +# Copyright 1999-2017 Gentoo Foundation
41 +# Distributed under the terms of the GNU General Public License v2
42 +
43 +# @ECLASS: eapi7-ver.eclass
44 +# @MAINTAINER:
45 +# PMS team <pms@g.o>
46 +# @AUTHOR:
47 +# Ulrich Müller <ulm@g.o>
48 +# Michał Górny <mgorny@g.o>
49 +# @BLURB: Testing implementation of EAPI 7 version manipulators
50 +# @DESCRIPTION:
51 +# A stand-alone implementation of the version manipulation functions
52 +# aimed for EAPI 7. Intended to be used for wider testing of
53 +# the proposed functions and to allow ebuilds to switch to the new
54 +# model early, with minimal change needed for actual EAPI 7.
55 +#
56 +# https://bugs.gentoo.org/482170
57 +#
58 +# Note: version comparison function is not included currently.
59 +#
60 +# Version strings
61 +# ===============
62 +# The functions support arbitrary version strings consisting of version
63 +# components interspersed with (possibly empty) version separators.
64 +#
65 +# A version component can either consist purely of digits ([0-9]+) or
66 +# purely of uppercase and lowercase letters ([a-zA-Z]+). Any other
67 +# character is treated as a version separator.
68 +#
69 +# The version is processed left-to-right, and each successive component
70 +# is assigned numbers starting with 1. The components are either split
71 +# on version separators or on boundaries between digits and letters
72 +# (in which case the separator between the components is empty).
73 +# Version separators are assigned numbers starting with 1 for
74 +# the separator between 1st and 2nd components. As a special case,
75 +# if the version string starts with a separator, it is assigned index 0.
76 +#
77 +# Examples:
78 +#
79 +# 1.2b-alpha4 -> 1 . 2 '' b - alpha '' 4
80 +# c s c s c s c s c
81 +# 1 1 2 2 3 3 4 4 5
82 +#
83 +# .11. -> . 11 .
84 +# s c s
85 +# 0 1 1
86 +#
87 +# Ranges
88 +# ======
89 +# A range can be specified as 'm' for m-th version component, 'm-'
90 +# for all components starting with m-th or 'm-n' for components starting
91 +# at m-th and ending at n-th (inclusive). If the range spans outside
92 +# the version string, it is truncated silently.
93 +
94 +case ${EAPI:-0} in
95 + 0|1|2|3|4|5)
96 + die "${ECLASS}: EAPI=${EAPI:-0} not supported";;
97 + 6)
98 + ;;
99 + *)
100 + die "${ECLASS}: EAPI=${EAPI} unknown";;
101 +esac
102 +
103 +# @FUNCTION: _ver_parse_range
104 +# @INTERNAL
105 +# @USAGE: <range> <max>
106 +# @DESCRIPTION:
107 +# Parse the range string <range>, setting 'start' and 'end' variables
108 +# to the appropriate bounds. <min> and <max> specify the appropriate
109 +# lower and upper bound for the range; the user-specified value is
110 +# truncated to this range.
111 +_ver_parse_range() {
112 + local range=${1}
113 + local max=${2}
114 +
115 + [[ ${range} == [0-9]* ]] || die "${FUNCNAME}: range must start with a number"
116 + start=${range%-*}
117 + [[ ${range} == *-* ]] && end=${range#*-} || end=${start}
118 + if [[ ${end} ]]; then
119 + [[ ${start} -le ${end} ]] || die "${FUNCNAME}: end of range must be >= start"
120 + [[ ${end} -le ${max} ]] || end=${max}
121 + else
122 + end=${max}
123 + fi
124 +}
125 +
126 +# @FUNCTION: _ver_split
127 +# @INTERNAL
128 +# @USAGE: <version>
129 +# @DESCRIPTION:
130 +# Split the version string <version> into separator-component array.
131 +# Sets 'comp' to an array of the form: ( s_0 c_1 s_1 c_2 s_2 c_3... )
132 +# where s_i are separators and c_i are components.
133 +_ver_split() {
134 + local v=${1} LC_ALL=C
135 +
136 + comp=()
137 +
138 + # get separators and components
139 + local s c
140 + while [[ ${v} ]]; do
141 + # cut the separator
142 + s=${v%%[a-zA-Z0-9]*}
143 + v=${v:${#s}}
144 + # cut the next component; it can be either digits or letters
145 + [[ ${v} == [0-9]* ]] && c=${v%%[^0-9]*} || c=${v%%[^a-zA-Z]*}
146 + v=${v:${#c}}
147 +
148 + comp+=( "${s}" "${c}" )
149 + done
150 +}
151 +
152 +# @FUNCTION: ver_cut
153 +# @USAGE: <range> [<version>]
154 +# @DESCRIPTION:
155 +# Print the substring of the version string containing components
156 +# defined by the <range> and the version separators between them.
157 +# Processes <version> if specified, ${PV} otherwise.
158 +#
159 +# For the syntax of versions and ranges, please see the eclass
160 +# description.
161 +ver_cut() {
162 + local range=${1}
163 + local v=${2:-${PV}}
164 + local start end
165 + local -a comp
166 +
167 + _ver_split "${v}"
168 + local max=$((${#comp[@]}/2))
169 + _ver_parse_range "${range}" "${max}"
170 +
171 + local IFS=
172 + if [[ ${start} -gt 0 ]]; then
173 + start=$(( start*2 - 1 ))
174 + fi
175 + echo "${comp[*]:start:end*2-start}"
176 +}
177 +
178 +# @FUNCTION: ver_rs
179 +# @USAGE: <range> <repl> [<range> <repl>...] [<version>]
180 +# @DESCRIPTION:
181 +# Print the version string after substituting the specified version
182 +# separators at <range> with <repl> (string). Multiple '<range> <repl>'
183 +# pairs can be specified. Processes <version> if specified,
184 +# ${PV} otherwise.
185 +#
186 +# For the syntax of versions and ranges, please see the eclass
187 +# description.
188 +ver_rs() {
189 + local v
190 + (( ${#} & 1 )) && v=${@: -1} || v=${PV}
191 + local start end i
192 + local -a comp
193 +
194 + _ver_split "${v}"
195 + local max=$((${#comp[@]}/2 - 1))
196 +
197 + while [[ ${#} -ge 2 ]]; do
198 + _ver_parse_range "${1}" "${max}"
199 + for (( i = start*2; i <= end*2; i+=2 )); do
200 + [[ ${i} -eq 0 && -z ${comp[i]} ]] && continue
201 + comp[i]=${2}
202 + done
203 + shift 2
204 + done
205 +
206 + local IFS=
207 + echo "${comp[*]}"
208 +}
209 +
210 +# @FUNCTION: ver_test
211 +# @USAGE: [<v1>] <op> <v2>
212 +# @DESCRIPTION:
213 +# Check if the relation <v1> <op> <v2> is true. If <v1> is not specified,
214 +# default to ${PVR}. <op> can be -gt, -ge, -eq, -ne, -le, -lt.
215 +# Both versions must conform to the PMS version syntax (with optional
216 +# revision parts), and the comparison is performed according to
217 +# the algorithm specified in the PMS.
218 +ver_test() {
219 + die "${FUNCNAME}: not implemented"
220 +}
221 diff --git a/eclass/tests/eapi7-ver.sh b/eclass/tests/eapi7-ver.sh
222 new file mode 100755
223 index 000000000000..8a96e4d29b1b
224 --- /dev/null
225 +++ b/eclass/tests/eapi7-ver.sh
226 @@ -0,0 +1,65 @@
227 +#!/bin/bash
228 +# Copyright 1999-2017 Gentoo Foundation
229 +# Distributed under the terms of the GNU General Public License v2
230 +
231 +EAPI=6
232 +
233 +source tests-common.sh
234 +
235 +inherit eapi7-ver
236 +
237 +teq() {
238 + local expected=${1}; shift
239 +
240 + tbegin "${*} -> ${expected}"
241 + local got=$("${@}")
242 + [[ ${got} == ${expected} ]]
243 + tend ${?} "returned: ${got}"
244 +}
245 +
246 +txf() {
247 + tbegin "XFAIL: ${*}"
248 + local got=$("${@}" 2>&1)
249 + [[ ${got} == die:* ]]
250 + tend ${?} "function did not die"
251 +}
252 +
253 +teq 1 ver_cut 1 1.2.3
254 +teq 1 ver_cut 1-1 1.2.3
255 +teq 1.2 ver_cut 1-2 1.2.3
256 +teq 2.3 ver_cut 2- 1.2.3
257 +teq 1.2.3 ver_cut 1- 1.2.3
258 +teq 3b ver_cut 3-4 1.2.3b_alpha4
259 +teq alpha ver_cut 5 1.2.3b_alpha4
260 +teq 1.2 ver_cut 1-2 .1.2.3
261 +teq .1.2 ver_cut 0-2 .1.2.3
262 +teq 2.3 ver_cut 2-3 1.2.3.
263 +teq 2.3. ver_cut 2- 1.2.3.
264 +teq 2.3. ver_cut 2-4 1.2.3.
265 +
266 +teq 1-2.3 ver_rs 1 - 1.2.3
267 +teq 1.2-3 ver_rs 2 - 1.2.3
268 +teq 1-2-3.4 ver_rs 1-2 - 1.2.3.4
269 +teq 1.2-3-4 ver_rs 2- - 1.2.3.4
270 +teq 1.2.3 ver_rs 2 . 1.2-3
271 +teq 1.2.3.a ver_rs 3 . 1.2.3a
272 +teq 1.2-alpha-4 ver_rs 2-3 - 1.2_alpha4
273 +teq 1.23-b_alpha4 ver_rs 3 - 2 "" 1.2.3b_alpha4
274 +teq a1b_2-c-3-d4e5 ver_rs 3-5 _ 4-6 - a1b2c3d4e5
275 +teq .1-2.3 ver_rs 1 - .1.2.3
276 +teq -1.2.3 ver_rs 0 - .1.2.3
277 +
278 +# truncating range
279 +teq 1.2 ver_cut 0-2 1.2.3
280 +teq 2.3 ver_cut 2-5 1.2.3
281 +teq "" ver_cut 4 1.2.3
282 +teq "" ver_cut 0 1.2.3
283 +teq "" ver_cut 4- 1.2.3
284 +teq 1.2.3 ver_rs 0 - 1.2.3
285 +teq 1.2.3 ver_rs 3 . 1.2.3
286 +teq 1.2.3 ver_rs 3- . 1.2.3
287 +teq 1.2.3 ver_rs 3-5 . 1.2.3
288 +
289 +txf ver_cut foo 1.2.3
290 +txf ver_rs -3 _ a1b2c3d4e5
291 +txf ver_rs 5-3 _ a1b2c3d4e5
292 diff --git a/eclass/tests/eapi7-ver:benchmark.sh b/eclass/tests/eapi7-ver:benchmark.sh
293 new file mode 100755
294 index 000000000000..4b262ab6accb
295 --- /dev/null
296 +++ b/eclass/tests/eapi7-ver:benchmark.sh
297 @@ -0,0 +1,76 @@
298 +#!/bin/bash
299 +# Copyright 1999-2017 Gentoo Foundation
300 +# Distributed under the terms of the GNU General Public License v2
301 +
302 +EAPI=6
303 +
304 +source tests-common.sh
305 +
306 +inherit eapi7-ver
307 +
308 +cutting() {
309 + local x
310 + for x in {1..1000}; do
311 + ver_cut 1 1.2.3
312 + ver_cut 1-2 1.2.3
313 + ver_cut 2- 1.2.3
314 + ver_cut 1- 1.2.3
315 + ver_cut 3-4 1.2.3b_alpha4
316 + ver_cut 5 1.2.3b_alpha4
317 + ver_cut 1-2 .1.2.3
318 + ver_cut 0-2 .1.2.3
319 + ver_cut 2-3 1.2.3.
320 + ver_cut 2- 1.2.3.
321 + ver_cut 2-4 1.2.3.
322 + done >/dev/null
323 +}
324 +
325 +replacing() {
326 + local x
327 + for x in {1..1000}; do
328 + ver_rs 1 - 1.2.3
329 + ver_rs 2 - 1.2.3
330 + ver_rs 1-2 - 1.2.3.4
331 + ver_rs 2- - 1.2.3.4
332 + ver_rs 2 . 1.2-3
333 + ver_rs 3 . 1.2.3a
334 + ver_rs 2-3 - 1.2_alpha4
335 + ver_rs 3 - 2 "" 1.2.3b_alpha4
336 + ver_rs 3-5 _ 4-6 - a1b2c3d4e5
337 + ver_rs 1 - .1.2.3
338 + ver_rs 0 - .1.2.3
339 + done >/dev/null
340 +}
341 +
342 +get_times() {
343 + echo "${*}"
344 + local real=()
345 + local user=()
346 +
347 + for x in {1..5}; do
348 + while read tt tv; do
349 + case ${tt} in
350 + real) real+=( ${tv} );;
351 + user) user+=( ${tv} );;
352 + esac
353 + done < <( ( time -p "${@}" ) 2>&1 )
354 + done
355 +
356 + [[ ${#real[@]} == 5 ]] || die "Did not get 5 real times"
357 + [[ ${#user[@]} == 5 ]] || die "Did not get 5 user times"
358 +
359 + local sum
360 + for v in real user; do
361 + vr="${v}[*]"
362 + sum=$(dc -e "${!vr} + + + + 3 k 5 / p")
363 +
364 + vr="${v}[@]"
365 + printf '%s %4.2f %4.2f %4.2f %4.2f %4.2f %4.2f\n' \
366 + "${v}" "${!vr}" "${sum}"
367 + done
368 +}
369 +
370 +export LC_ALL=C
371 +
372 +get_times cutting
373 +get_times replacing
374 --
375 2.14.1

Replies