>>>>> 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

Attachment: pgpVs4KDfC9qE.pgp
Description: PGP signature

Reply via email to