>>>>> On Tue, 19 Sep 2017, Michał Górny wrote: >> EAPI 7 is introducing new version manipulation and comparison functions >> that aim to replace versionator.eclass. This eclass provides an 'early >> adopter' versions of those routines. >> >> It serves two goals: >> >> a. getting wider review and some real-life testing before >> the specification is set in stone, and >> >> b. making it possible to adapt ebuilds to the new routines early, >> reducing the future work of EAPI 7 porting. >> >> For more details on the new logic, please see the eclass documentation. >> Long story short, we are introducing three functions: >> >> 1. ver_cut -- to get substrings of the version string, >> >> 2. ver_rs -- to replace version separators via indices, >> >> 3. ver_test -- to compare two version numbers. >> >> The third function is not implemented in the eclass. It's meant to reuse >> the algorithms from the package manager, and the final implementation >> will most likely reuse the code from the package manager (e.g. via IPC).
Meanwhile the ver_test function has been added to the eclass in the eapi7-ver branch. Please review the patch included below. > Merged now, with some documentation fixes and additional benchmark to > compare it with versionator.eclass. [...] For completeness, here are benchmark results for ver_test(), in comparison with version_is_at_least(). comparing real 6.78 6.80 6.81 6.76 6.75 => 6.78 avg user 6.73 6.75 6.75 6.71 6.69 => 6.73 avg comparing_versionator real 196.60 197.80 197.40 199.10 198.60 => 197.90 avg user 80.10 80.10 80.00 80.40 80.60 => 80.24 avg Ulrich -- 8< -- From 9c4953e2d2bdef1244cff025672489bf9c9ff72b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ulrich=20M=C3=BCller?= <u...@gentoo.org> Date: Wed, 20 Sep 2017 21:28:22 +0200 Subject: [PATCH] eapi7-ver.eclass: Implement ver_test(). MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This should strictly follow Algorithms 3.1 to 3.7 specified in PMS: https://projects.gentoo.org/pms/6/pms.html#x1-310003.3 Thanks to Michał Górny for many optimizations, and for pointing out how much my initial implementation sucked. ;-) --- eclass/eapi7-ver.eclass | 111 +++++++++++++++++++++++++++++++++++- eclass/tests/eapi7-ver.sh | 107 ++++++++++++++++++++++++++++++++++ eclass/tests/eapi7-ver_benchmark.sh | 34 +++++++++++ 3 files changed, 251 insertions(+), 1 deletion(-) diff --git a/eclass/eapi7-ver.eclass b/eclass/eapi7-ver.eclass index 6f8f0c0a1c37..ead9fac5e807 100644 --- a/eclass/eapi7-ver.eclass +++ b/eclass/eapi7-ver.eclass @@ -176,6 +176,94 @@ ver_rs() { echo "${comp[*]}" } +# @FUNCTION: _ver_compare +# @USAGE: <va> <vb> +# @RETURN: 1 if <va> < <vb>, 2 if <va> = <vb>, 3 if <va> > <vb> +# @INTERNAL +# @DESCRIPTION: +# Compare two versions <va> and <vb>. If <va> is less than, equal to, +# or greater than <vb>, return 1, 2, or 3 as exit status, respectively. +_ver_compare() { + local va=${1} vb=${2} a an al as ar b bn bl bs br re + + re="^([0-9]+(\.[0-9]+)*)([a-z]?)((_(alpha|beta|pre|rc|p)[0-9]*)*)(-r[0-9]+)?$" + + [[ ${va} =~ ${re} ]] || die "${FUNCNAME}: invalid version: ${va}" + an=${BASH_REMATCH[1]} + al=${BASH_REMATCH[3]} + as=${BASH_REMATCH[4]} + ar=${BASH_REMATCH[7]} + + [[ ${vb} =~ ${re} ]] || die "${FUNCNAME}: invalid version: ${vb}" + bn=${BASH_REMATCH[1]} + bl=${BASH_REMATCH[3]} + bs=${BASH_REMATCH[4]} + br=${BASH_REMATCH[7]} + + # Compare numeric components (PMS algorithm 3.2) + # First component + a=${an%%.*} + b=${bn%%.*} + [[ 10#${a} -gt 10#${b} ]] && return 3 + [[ 10#${a} -lt 10#${b} ]] && return 1 + + while [[ ${an} == *.* && ${bn} == *.* ]]; do + # Other components (PMS algorithm 3.3) + an=${an#*.} + bn=${bn#*.} + a=${an%%.*} + b=${bn%%.*} + if [[ ${a} == 0* || ${b} == 0* ]]; then + # Remove trailing zeros + while [[ ${a} == *0 ]]; do a=${a::-1}; done + while [[ ${b} == *0 ]]; do b=${b::-1}; done + [[ ${a} > ${b} ]] && return 3 + [[ ${a} < ${b} ]] && return 1 + else + [[ ${a} -gt ${b} ]] && return 3 + [[ ${a} -lt ${b} ]] && return 1 + fi + done + [[ ${an} == *.* ]] && return 3 + [[ ${bn} == *.* ]] && return 1 + + # Compare letter components (PMS algorithm 3.4) + [[ ${al} > ${bl} ]] && return 3 + [[ ${al} < ${bl} ]] && return 1 + + # Compare suffixes (PMS algorithm 3.5) + as=${as#_}${as:+_} + bs=${bs#_}${bs:+_} + while [[ -n ${as} && -n ${bs} ]]; do + # Compare each suffix (PMS algorithm 3.6) + a=${as%%_*} + b=${bs%%_*} + if [[ ${a%%[0-9]*} == "${b%%[0-9]*}" ]]; then + [[ 10#${a##*[a-z]} -gt 10#${b##*[a-z]} ]] && return 3 + [[ 10#${a##*[a-z]} -lt 10#${b##*[a-z]} ]] && return 1 + else + # Check for p first + [[ ${a%%[0-9]*} == p ]] && return 3 + [[ ${b%%[0-9]*} == p ]] && return 1 + # Hack: Use that alpha < beta < pre < rc alphabetically + [[ ${a} > ${b} ]] && return 3 || return 1 + fi + as=${as#*_} + bs=${bs#*_} + done + if [[ -n ${as} ]]; then + [[ ${as} == p[_0-9]* ]] && return 3 || return 1 + elif [[ -n ${bs} ]]; then + [[ ${bs} == p[_0-9]* ]] && return 1 || return 3 + fi + + # Compare revision components (PMS algorithm 3.7) + [[ 10#${ar#-r} -gt 10#${br#-r} ]] && return 3 + [[ 10#${ar#-r} -lt 10#${br#-r} ]] && return 1 + + return 2 +} + # @FUNCTION: ver_test # @USAGE: [<v1>] <op> <v2> # @DESCRIPTION: @@ -185,5 +273,26 @@ ver_rs() { # revision parts), and the comparison is performed according to # the algorithm specified in the PMS. ver_test() { - die "${FUNCNAME}: not implemented" + local LC_ALL=C + local va op vb + + if [[ $# -eq 3 ]]; then + va=${1} + shift + else + va=${PVR} + fi + + [[ $# -eq 2 ]] || die "${FUNCNAME}: bad number of arguments" + + op=${1} + vb=${2} + + case ${op} in + -eq|-ne|-lt|-le|-gt|-ge) ;; + *) die "${FUNCNAME}: invalid operator: ${op}" ;; + esac + + _ver_compare "${va}" "${vb}" + test $? "${op}" 2 } diff --git a/eclass/tests/eapi7-ver.sh b/eclass/tests/eapi7-ver.sh index 8a96e4d29b1b..144bb2bddc3e 100755 --- a/eclass/tests/eapi7-ver.sh +++ b/eclass/tests/eapi7-ver.sh @@ -17,6 +17,15 @@ teq() { tend ${?} "returned: ${got}" } +teqr() { + local expected=$1; shift + tbegin "$* -> ${expected}" + "$@" + local ret=$? + [[ ${ret} -eq ${expected} ]] + tend $? "returned: ${ret}" +} + txf() { tbegin "XFAIL: ${*}" local got=$("${@}" 2>&1) @@ -63,3 +72,101 @@ teq 1.2.3 ver_rs 3-5 . 1.2.3 txf ver_cut foo 1.2.3 txf ver_rs -3 _ a1b2c3d4e5 txf ver_rs 5-3 _ a1b2c3d4e5 + +# Tests from Portage's test_vercmp.py +teqr 0 ver_test 6.0 -gt 5.0 +teqr 0 ver_test 5.0 -gt 5 +teqr 0 ver_test 1.0-r1 -gt 1.0-r0 +teqr 0 ver_test 999999999999999999 -gt 999999999999999998 # 18 digits +teqr 0 ver_test 1.0.0 -gt 1.0 +teqr 0 ver_test 1.0.0 -gt 1.0b +teqr 0 ver_test 1b -gt 1 +teqr 0 ver_test 1b_p1 -gt 1_p1 +teqr 0 ver_test 1.1b -gt 1.1 +teqr 0 ver_test 12.2.5 -gt 12.2b +teqr 0 ver_test 4.0 -lt 5.0 +teqr 0 ver_test 5 -lt 5.0 +teqr 0 ver_test 1.0_pre2 -lt 1.0_p2 +teqr 0 ver_test 1.0_alpha2 -lt 1.0_p2 +teqr 0 ver_test 1.0_alpha1 -lt 1.0_beta1 +teqr 0 ver_test 1.0_beta3 -lt 1.0_rc3 +teqr 0 ver_test 1.001000000000000001 -lt 1.001000000000000002 +teqr 0 ver_test 1.00100000000 -lt 1.001000000000000001 +teqr 0 ver_test 999999999999999998 -lt 999999999999999999 +teqr 0 ver_test 1.01 -lt 1.1 +teqr 0 ver_test 1.0-r0 -lt 1.0-r1 +teqr 0 ver_test 1.0 -lt 1.0-r1 +teqr 0 ver_test 1.0 -lt 1.0.0 +teqr 0 ver_test 1.0b -lt 1.0.0 +teqr 0 ver_test 1_p1 -lt 1b_p1 +teqr 0 ver_test 1 -lt 1b +teqr 0 ver_test 1.1 -lt 1.1b +teqr 0 ver_test 12.2b -lt 12.2.5 +teqr 0 ver_test 4.0 -eq 4.0 +teqr 0 ver_test 1.0 -eq 1.0 +teqr 0 ver_test 1.0-r0 -eq 1.0 +teqr 0 ver_test 1.0 -eq 1.0-r0 +teqr 0 ver_test 1.0-r0 -eq 1.0-r0 +teqr 0 ver_test 1.0-r1 -eq 1.0-r1 +teqr 1 ver_test 1 -eq 2 +teqr 1 ver_test 1.0_alpha -eq 1.0_pre +teqr 1 ver_test 1.0_beta -eq 1.0_alpha +teqr 1 ver_test 1 -eq 0.0 +teqr 1 ver_test 1.0-r0 -eq 1.0-r1 +teqr 1 ver_test 1.0-r1 -eq 1.0-r0 +teqr 1 ver_test 1.0 -eq 1.0-r1 +teqr 1 ver_test 1.0-r1 -eq 1.0 +teqr 1 ver_test 1.0 -eq 1.0.0 +teqr 1 ver_test 1_p1 -eq 1b_p1 +teqr 1 ver_test 1b -eq 1 +teqr 1 ver_test 1.1b -eq 1.1 +teqr 1 ver_test 12.2b -eq 12.2 + +# A subset of tests from Paludis +teqr 0 ver_test 1.0_alpha -gt 1_alpha +teqr 0 ver_test 1.0_alpha -gt 1 +teqr 0 ver_test 1.0_alpha -lt 1.0 +teqr 0 ver_test 1.2.0.0_alpha7-r4 -gt 1.2_alpha7-r4 +teqr 0 ver_test 0001 -eq 1 +teqr 0 ver_test 01 -eq 001 +teqr 0 ver_test 0001.1 -eq 1.1 +teqr 0 ver_test 01.01 -eq 1.01 +teqr 0 ver_test 1.010 -eq 1.01 +teqr 0 ver_test 1.00 -eq 1.0 +teqr 0 ver_test 1.0100 -eq 1.010 +teqr 0 ver_test 1-r00 -eq 1-r0 + +# Additional tests +teqr 0 ver_test 0_rc99 -lt 0 +teqr 0 ver_test 011 -eq 11 +teqr 0 ver_test 019 -eq 19 +teqr 0 ver_test 1.2 -eq 001.2 +teqr 0 ver_test 1.2 -gt 1.02 +teqr 0 ver_test 1.2a -lt 1.2b +teqr 0 ver_test 1.2_pre1 -gt 1.2_pre1_beta2 +teqr 0 ver_test 1.2_pre1 -lt 1.2_pre1_p2 +teqr 0 ver_test 1.00 -lt 1.0.0 +teqr 0 ver_test 1.010 -eq 1.01 +teqr 0 ver_test 1.01 -lt 1.1 +teqr 0 ver_test 1.2_pre08-r09 -eq 1.2_pre8-r9 + +# Bad number or ordering of arguments +txf ver_test 1 +txf ver_test 1 -lt 2 3 +txf ver_test -lt 1 2 + +# Bad operators +txf ver_test 1 "<" 2 +txf ver_test 1 lt 2 +txf ver_test 1 -foo 2 + +# Malformed versions +txf ver_test "" -ne 1 +txf ver_test 1. -ne 1 +txf ver_test 1ab -ne 1 +txf ver_test b -ne 1 +txf ver_test 1-r1_pre -ne 1 +txf ver_test 1-pre1 -ne 1 +txf ver_test 1_foo -ne 1 +txf ver_test 1_pre1.1 -ne 1 +txf ver_test 1-r1.0 -ne 1 diff --git a/eclass/tests/eapi7-ver_benchmark.sh b/eclass/tests/eapi7-ver_benchmark.sh index 1de26444c9b3..c46713713368 100755 --- a/eclass/tests/eapi7-ver_benchmark.sh +++ b/eclass/tests/eapi7-ver_benchmark.sh @@ -76,6 +76,38 @@ replacing_versionator() { done >/dev/null } +comparing() { + local x + for x in {1..1000}; do + ver_test 1b_p1 -le 1_p1 + ver_test 1.1b -le 1.1 + ver_test 12.2.5 -le 12.2b + ver_test 4.0 -le 5.0 + ver_test 5 -le 5.0 + ver_test 1.0_pre2 -le 1.0_p2 + ver_test 1.0_alpha2 -le 1.0_p2 + ver_test 1.0_alpha1 -le 1.0_beta1 + ver_test 1.0_beta3 -le 1.0_rc3 + ver_test 1.001000000000000001 -le 1.001000000000000002 + done +} + +comparing_versionator() { + local x + for x in {1..100}; do + version_is_at_least 1b_p1 1_p1 + version_is_at_least 1.1b 1.1 + version_is_at_least 12.2.5 12.2b + version_is_at_least 4.0 5.0 + version_is_at_least 5 5.0 + version_is_at_least 1.0_pre2 1.0_p2 + version_is_at_least 1.0_alpha2 1.0_p2 + version_is_at_least 1.0_alpha1 1.0_beta1 + version_is_at_least 1.0_beta3 1.0_rc3 + version_is_at_least 1.001000000000000001 1.001000000000000002 + done +} + get_times() { local factor=${1}; shift echo "${*}" @@ -111,3 +143,5 @@ get_times 1 cutting get_times 10 cutting_versionator get_times 1 replacing get_times 10 replacing_versionator +get_times 1 comparing +get_times 10 comparing_versionator -- 2.14.1
pgpVs4KDfC9qE.pgp
Description: PGP signature