dev-python/pytest: arm64 stable (bug #723996)
[gentoo.git] / eclass / eapi7-ver.eclass
1 # Copyright 1999-2020 Gentoo Authors
2 # Distributed under the terms of the GNU General Public License v2
3
4 # @ECLASS: eapi7-ver.eclass
5 # @MAINTAINER:
6 # PMS team <pms@gentoo.org>
7 # @AUTHOR:
8 # Ulrich Müller <ulm@gentoo.org>
9 # Michał Górny <mgorny@gentoo.org>
10 # @SUPPORTED_EAPIS: 0 1 2 3 4 5 6
11 # @BLURB: Testing implementation of EAPI 7 version manipulators
12 # @DESCRIPTION:
13 # A stand-alone implementation of the version manipulation functions
14 # aimed for EAPI 7.  Intended to be used for wider testing of
15 # the proposed functions and to allow ebuilds to switch to the new
16 # model early, with minimal change needed for actual EAPI 7.
17 #
18 # https://bugs.gentoo.org/482170
19 #
20 # @SUBSECTION Version strings
21 #
22 # The functions support arbitrary version strings consisting of version
23 # components interspersed with (possibly empty) version separators.
24 #
25 # A version component can either consist purely of digits ([0-9]+)
26 # or purely of uppercase and lowercase letters ([A-Za-z]+).  A version
27 # separator is either a string of any other characters ([^A-Za-z0-9]+),
28 # or it occurs at the transition between a sequence of letters
29 # and a sequence of digits, or vice versa.  In the latter case,
30 # the version separator is an empty string.
31 #
32 # The version is processed left-to-right, and each successive component
33 # is assigned numbers starting with 1.  The components are either split
34 # on version separators or on boundaries between digits and letters
35 # (in which case the separator between the components is empty).
36 # Version separators are assigned numbers starting with 1 for
37 # the separator between 1st and 2nd components.  As a special case,
38 # if the version string starts with a separator, it is assigned index 0.
39 #
40 # Examples:
41 #
42 # @CODE
43 #   1.2b-alpha4 -> 1 . 2 '' b - alpha '' 4
44 #                  c s c s  c s c     s  c
45 #                  1 1 2 2  3 3 4     4  5
46 #
47 #   .11.        -> . 11 .
48 #                  s c  s
49 #                  0 1  1
50 # @CODE
51 #
52 # @SUBSECTION Ranges
53 #
54 # A range can be specified as 'm' for m-th version component, 'm-'
55 # for all components starting with m-th or 'm-n' for components starting
56 # at m-th and ending at n-th (inclusive).  If the range spans outside
57 # the version string, it is truncated silently.
58
59 case ${EAPI:-0} in
60         0|1|2|3|4|5|6) ;;
61         7) die "${ECLASS}: EAPI=${EAPI} includes all functions from this eclass" ;;
62         *) die "${ECLASS}: EAPI=${EAPI} unknown" ;;
63 esac
64
65 # @FUNCTION: _ver_parse_range
66 # @USAGE: <range> <max>
67 # @INTERNAL
68 # @DESCRIPTION:
69 # Parse the range string <range>, setting 'start' and 'end' variables
70 # to the appropriate bounds.  <max> specifies the appropriate upper
71 # bound for the range; the user-specified value is truncated to this.
72 _ver_parse_range() {
73         local range=${1}
74         local max=${2}
75
76         [[ ${range} == [0-9]* ]] \
77                 || die "${FUNCNAME}: range must start with a number"
78         start=${range%-*}
79         [[ ${range} == *-* ]] && end=${range#*-} || end=${start}
80         if [[ ${end} ]]; then
81                 [[ ${start} -le ${end} ]] \
82                         || die "${FUNCNAME}: end of range must be >= start"
83                 [[ ${end} -le ${max} ]] || end=${max}
84         else
85                 end=${max}
86         fi
87 }
88
89 # @FUNCTION: _ver_split
90 # @USAGE: <version>
91 # @INTERNAL
92 # @DESCRIPTION:
93 # Split the version string <version> into separator-component array.
94 # Sets 'comp' to an array of the form: ( s_0 c_1 s_1 c_2 s_2 c_3... )
95 # where s_i are separators and c_i are components.
96 _ver_split() {
97         local v=${1} LC_ALL=C
98
99         comp=()
100
101         # get separators and components
102         local s c
103         while [[ ${v} ]]; do
104                 # cut the separator
105                 s=${v%%[a-zA-Z0-9]*}
106                 v=${v:${#s}}
107                 # cut the next component; it can be either digits or letters
108                 [[ ${v} == [0-9]* ]] && c=${v%%[^0-9]*} || c=${v%%[^a-zA-Z]*}
109                 v=${v:${#c}}
110
111                 comp+=( "${s}" "${c}" )
112         done
113 }
114
115 # @FUNCTION: ver_cut
116 # @USAGE: <range> [<version>]
117 # @DESCRIPTION:
118 # Print the substring of the version string containing components
119 # defined by the <range> and the version separators between them.
120 # Processes <version> if specified, ${PV} otherwise.
121 #
122 # For the syntax of versions and ranges, please see the eclass
123 # description.
124 ver_cut() {
125         local range=${1}
126         local v=${2:-${PV}}
127         local start end
128         local -a comp
129
130         _ver_split "${v}"
131         local max=$((${#comp[@]}/2))
132         _ver_parse_range "${range}" "${max}"
133
134         if [[ ${start} -gt 0 ]]; then
135                 start=$(( start*2 - 1 ))
136         fi
137         # Work around a bug in bash-3.2, where "${comp[*]:start:end*2-start}"
138         # inserts stray 0x7f characters for empty array elements
139         printf "%s" "${comp[@]:start:end*2-start}" $'\n'
140 }
141
142 # @FUNCTION: ver_rs
143 # @USAGE: <range> <repl> [<range> <repl>...] [<version>]
144 # @DESCRIPTION:
145 # Print the version string after substituting the specified version
146 # separators at <range> with <repl> (string).  Multiple '<range> <repl>'
147 # pairs can be specified.  Processes <version> if specified,
148 # ${PV} otherwise.
149 #
150 # For the syntax of versions and ranges, please see the eclass
151 # description.
152 ver_rs() {
153         local v
154         (( ${#} & 1 )) && v=${@: -1} || v=${PV}
155         local start end i
156         local -a comp
157
158         _ver_split "${v}"
159         local max=$((${#comp[@]}/2 - 1))
160
161         while [[ ${#} -ge 2 ]]; do
162                 _ver_parse_range "${1}" "${max}"
163                 for (( i = start*2; i <= end*2; i+=2 )); do
164                         [[ ${i} -eq 0 && -z ${comp[i]} ]] && continue
165                         comp[i]=${2}
166                 done
167                 shift 2
168         done
169
170         local IFS=
171         echo "${comp[*]}"
172 }
173
174 # @FUNCTION: _ver_compare_int
175 # @USAGE: <a> <b>
176 # @RETURN: 0 if <a> -eq <b>, 1 if <a> -lt <b>, 3 if <a> -gt <b>
177 # @INTERNAL
178 # @DESCRIPTION:
179 # Compare two non-negative integers <a> and <b>, of arbitrary length.
180 # If <a> is equal to, less than, or greater than <b>, return 0, 1, or 3
181 # as exit status, respectively.
182 _ver_compare_int() {
183         local a=$1 b=$2 d=$(( ${#1}-${#2} ))
184
185         # Zero-pad to equal length if necessary.
186         if [[ ${d} -gt 0 ]]; then
187                 printf -v b "%0${d}d%s" 0 "${b}"
188         elif [[ ${d} -lt 0 ]]; then
189                 printf -v a "%0$(( -d ))d%s" 0 "${a}"
190         fi
191
192         [[ ${a} > ${b} ]] && return 3
193         [[ ${a} == "${b}" ]]
194 }
195
196 # @FUNCTION: _ver_compare
197 # @USAGE: <va> <vb>
198 # @RETURN: 1 if <va> < <vb>, 2 if <va> = <vb>, 3 if <va> > <vb>
199 # @INTERNAL
200 # @DESCRIPTION:
201 # Compare two versions <va> and <vb>.  If <va> is less than, equal to,
202 # or greater than <vb>, return 1, 2, or 3 as exit status, respectively.
203 _ver_compare() {
204         local va=${1} vb=${2} a an al as ar b bn bl bs br re LC_ALL=C
205
206         re="^([0-9]+(\.[0-9]+)*)([a-z]?)((_(alpha|beta|pre|rc|p)[0-9]*)*)(-r[0-9]+)?$"
207
208         [[ ${va} =~ ${re} ]] || die "${FUNCNAME}: invalid version: ${va}"
209         an=${BASH_REMATCH[1]}
210         al=${BASH_REMATCH[3]}
211         as=${BASH_REMATCH[4]}
212         ar=${BASH_REMATCH[7]}
213
214         [[ ${vb} =~ ${re} ]] || die "${FUNCNAME}: invalid version: ${vb}"
215         bn=${BASH_REMATCH[1]}
216         bl=${BASH_REMATCH[3]}
217         bs=${BASH_REMATCH[4]}
218         br=${BASH_REMATCH[7]}
219
220         # Compare numeric components (PMS algorithm 3.2)
221         # First component
222         _ver_compare_int "${an%%.*}" "${bn%%.*}" || return
223
224         while [[ ${an} == *.* && ${bn} == *.* ]]; do
225                 # Other components (PMS algorithm 3.3)
226                 an=${an#*.}
227                 bn=${bn#*.}
228                 a=${an%%.*}
229                 b=${bn%%.*}
230                 if [[ ${a} == 0* || ${b} == 0* ]]; then
231                         # Remove any trailing zeros
232                         [[ ${a} =~ 0+$ ]] && a=${a%"${BASH_REMATCH[0]}"}
233                         [[ ${b} =~ 0+$ ]] && b=${b%"${BASH_REMATCH[0]}"}
234                         [[ ${a} > ${b} ]] && return 3
235                         [[ ${a} < ${b} ]] && return 1
236                 else
237                         _ver_compare_int "${a}" "${b}" || return
238                 fi
239         done
240         [[ ${an} == *.* ]] && return 3
241         [[ ${bn} == *.* ]] && return 1
242
243         # Compare letter components (PMS algorithm 3.4)
244         [[ ${al} > ${bl} ]] && return 3
245         [[ ${al} < ${bl} ]] && return 1
246
247         # Compare suffixes (PMS algorithm 3.5)
248         as=${as#_}${as:+_}
249         bs=${bs#_}${bs:+_}
250         while [[ -n ${as} && -n ${bs} ]]; do
251                 # Compare each suffix (PMS algorithm 3.6)
252                 a=${as%%_*}
253                 b=${bs%%_*}
254                 if [[ ${a%%[0-9]*} == "${b%%[0-9]*}" ]]; then
255                         _ver_compare_int "${a##*[a-z]}" "${b##*[a-z]}" || return
256                 else
257                         # Check for p first
258                         [[ ${a%%[0-9]*} == p ]] && return 3
259                         [[ ${b%%[0-9]*} == p ]] && return 1
260                         # Hack: Use that alpha < beta < pre < rc alphabetically
261                         [[ ${a} > ${b} ]] && return 3 || return 1
262                 fi
263                 as=${as#*_}
264                 bs=${bs#*_}
265         done
266         if [[ -n ${as} ]]; then
267                 [[ ${as} == p[_0-9]* ]] && return 3 || return 1
268         elif [[ -n ${bs} ]]; then
269                 [[ ${bs} == p[_0-9]* ]] && return 1 || return 3
270         fi
271
272         # Compare revision components (PMS algorithm 3.7)
273         _ver_compare_int "${ar#-r}" "${br#-r}" || return
274
275         return 2
276 }
277
278 # @FUNCTION: ver_test
279 # @USAGE: [<v1>] <op> <v2>
280 # @DESCRIPTION:
281 # Check if the relation <v1> <op> <v2> is true.  If <v1> is not specified,
282 # default to ${PVR}.  <op> can be -gt, -ge, -eq, -ne, -le, -lt.
283 # Both versions must conform to the PMS version syntax (with optional
284 # revision parts), and the comparison is performed according to
285 # the algorithm specified in the PMS.
286 ver_test() {
287         local va op vb
288
289         if [[ $# -eq 3 ]]; then
290                 va=${1}
291                 shift
292         else
293                 va=${PVR}
294         fi
295
296         [[ $# -eq 2 ]] || die "${FUNCNAME}: bad number of arguments"
297
298         op=${1}
299         vb=${2}
300
301         case ${op} in
302                 -eq|-ne|-lt|-le|-gt|-ge) ;;
303                 *) die "${FUNCNAME}: invalid operator: ${op}" ;;
304         esac
305
306         _ver_compare "${va}" "${vb}"
307         test $? "${op}" 2
308 }