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). --- eclass/eapi7-ver.eclass | 181 ++++++++++++++++++++++++++++++++++++ eclass/tests/eapi7-ver.sh | 65 +++++++++++++ eclass/tests/eapi7-ver:benchmark.sh | 76 +++++++++++++++ 3 files changed, 322 insertions(+) create mode 100644 eclass/eapi7-ver.eclass create mode 100755 eclass/tests/eapi7-ver.sh create mode 100755 eclass/tests/eapi7-ver:benchmark.sh diff --git a/eclass/eapi7-ver.eclass b/eclass/eapi7-ver.eclass new file mode 100644 index 000000000000..c82da6192c94 --- /dev/null +++ b/eclass/eapi7-ver.eclass @@ -0,0 +1,181 @@ +# Copyright 1999-2017 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 + +# @ECLASS: eapi7-ver.eclass +# @MAINTAINER: +# PMS team <p...@gentoo.org> +# @AUTHOR: +# Ulrich Müller <u...@gentoo.org> +# Michał Górny <mgo...@gentoo.org> +# @BLURB: Testing implementation of EAPI 7 version manipulators +# @DESCRIPTION: +# A stand-alone implementation of the version manipulation functions +# aimed for EAPI 7. Intended to be used for wider testing of +# the proposed functions and to allow ebuilds to switch to the new +# model early, with minimal change needed for actual EAPI 7. +# +# https://bugs.gentoo.org/482170 +# +# Note: version comparison function is not included currently. +# +# Version strings +# =============== +# The functions support arbitrary version strings consisting of version +# components interspersed with (possibly empty) version separators. +# +# A version component can either consist purely of digits ([0-9]+) or +# purely of uppercase and lowercase letters ([a-zA-Z]+). Any other +# character is treated as a version separator. +# +# The version is processed left-to-right, and each successive component +# is assigned numbers starting with 1. The components are either split +# on version separators or on boundaries between digits and letters +# (in which case the separator between the components is empty). +# Version separators are assigned numbers starting with 1 for +# the separator between 1st and 2nd components. As a special case, +# if the version string starts with a separator, it is assigned index 0. +# +# Examples: +# +# 1.2b-alpha4 -> 1 . 2 '' b - alpha '' 4 +# c s c s c s c s c +# 1 1 2 2 3 3 4 4 5 +# +# .11. -> . 11 . +# s c s +# 0 1 1 +# +# Ranges +# ====== +# A range can be specified as 'm' for m-th version component, 'm-' +# for all components starting with m-th or 'm-n' for components starting +# at m-th and ending at n-th (inclusive). If the range spans outside +# the version string, it is truncated silently. + +case ${EAPI:-0} in + 0|1|2|3|4|5) + die "${ECLASS}: EAPI=${EAPI:-0} not supported";; + 6) + ;; + *) + die "${ECLASS}: EAPI=${EAPI} unknown";; +esac + +# @FUNCTION: _ver_parse_range +# @INTERNAL +# @USAGE: <range> <max> +# @DESCRIPTION: +# Parse the range string <range>, setting 'start' and 'end' variables +# to the appropriate bounds. <min> and <max> specify the appropriate +# lower and upper bound for the range; the user-specified value is +# truncated to this range. +_ver_parse_range() { + local range=${1} + local max=${2} + + [[ ${range} == [0-9]* ]] || die "${FUNCNAME}: range must start with a number" + start=${range%-*} + [[ ${range} == *-* ]] && end=${range#*-} || end=${start} + if [[ ${end} ]]; then + [[ ${start} -le ${end} ]] || die "${FUNCNAME}: end of range must be >= start" + [[ ${end} -le ${max} ]] || end=${max} + else + end=${max} + fi +} + +# @FUNCTION: _ver_split +# @INTERNAL +# @USAGE: <version> +# @DESCRIPTION: +# Split the version string <version> into separator-component array. +# Sets 'comp' to an array of the form: ( s_0 c_1 s_1 c_2 s_2 c_3... ) +# where s_i are separators and c_i are components. +_ver_split() { + local v=${1} LC_ALL=C + + comp=() + + # get separators and components + local s c + while [[ ${v} ]]; do + # cut the separator + s=${v%%[a-zA-Z0-9]*} + v=${v:${#s}} + # cut the next component; it can be either digits or letters + [[ ${v} == [0-9]* ]] && c=${v%%[^0-9]*} || c=${v%%[^a-zA-Z]*} + v=${v:${#c}} + + comp+=( "${s}" "${c}" ) + done +} + +# @FUNCTION: ver_cut +# @USAGE: <range> [<version>] +# @DESCRIPTION: +# Print the substring of the version string containing components +# defined by the <range> and the version separators between them. +# Processes <version> if specified, ${PV} otherwise. +# +# For the syntax of versions and ranges, please see the eclass +# description. +ver_cut() { + local range=${1} + local v=${2:-${PV}} + local start end + local -a comp + + _ver_split "${v}" + local max=$((${#comp[@]}/2)) + _ver_parse_range "${range}" "${max}" + + local IFS= + if [[ ${start} -gt 0 ]]; then + start=$(( start*2 - 1 )) + fi + echo "${comp[*]:start:end*2-start}" +} + +# @FUNCTION: ver_rs +# @USAGE: <range> <repl> [<range> <repl>...] [<version>] +# @DESCRIPTION: +# Print the version string after substituting the specified version +# separators at <range> with <repl> (string). Multiple '<range> <repl>' +# pairs can be specified. Processes <version> if specified, +# ${PV} otherwise. +# +# For the syntax of versions and ranges, please see the eclass +# description. +ver_rs() { + local v + (( ${#} & 1 )) && v=${@: -1} || v=${PV} + local start end i + local -a comp + + _ver_split "${v}" + local max=$((${#comp[@]}/2 - 1)) + + while [[ ${#} -ge 2 ]]; do + _ver_parse_range "${1}" "${max}" + for (( i = start*2; i <= end*2; i+=2 )); do + [[ ${i} -eq 0 && -z ${comp[i]} ]] && continue + comp[i]=${2} + done + shift 2 + done + + local IFS= + echo "${comp[*]}" +} + +# @FUNCTION: ver_test +# @USAGE: [<v1>] <op> <v2> +# @DESCRIPTION: +# Check if the relation <v1> <op> <v2> is true. If <v1> is not specified, +# default to ${PVR}. <op> can be -gt, -ge, -eq, -ne, -le, -lt. +# Both versions must conform to the PMS version syntax (with optional +# revision parts), and the comparison is performed according to +# the algorithm specified in the PMS. +ver_test() { + die "${FUNCNAME}: not implemented" +} diff --git a/eclass/tests/eapi7-ver.sh b/eclass/tests/eapi7-ver.sh new file mode 100755 index 000000000000..8a96e4d29b1b --- /dev/null +++ b/eclass/tests/eapi7-ver.sh @@ -0,0 +1,65 @@ +#!/bin/bash +# Copyright 1999-2017 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 + +EAPI=6 + +source tests-common.sh + +inherit eapi7-ver + +teq() { + local expected=${1}; shift + + tbegin "${*} -> ${expected}" + local got=$("${@}") + [[ ${got} == ${expected} ]] + tend ${?} "returned: ${got}" +} + +txf() { + tbegin "XFAIL: ${*}" + local got=$("${@}" 2>&1) + [[ ${got} == die:* ]] + tend ${?} "function did not die" +} + +teq 1 ver_cut 1 1.2.3 +teq 1 ver_cut 1-1 1.2.3 +teq 1.2 ver_cut 1-2 1.2.3 +teq 2.3 ver_cut 2- 1.2.3 +teq 1.2.3 ver_cut 1- 1.2.3 +teq 3b ver_cut 3-4 1.2.3b_alpha4 +teq alpha ver_cut 5 1.2.3b_alpha4 +teq 1.2 ver_cut 1-2 .1.2.3 +teq .1.2 ver_cut 0-2 .1.2.3 +teq 2.3 ver_cut 2-3 1.2.3. +teq 2.3. ver_cut 2- 1.2.3. +teq 2.3. ver_cut 2-4 1.2.3. + +teq 1-2.3 ver_rs 1 - 1.2.3 +teq 1.2-3 ver_rs 2 - 1.2.3 +teq 1-2-3.4 ver_rs 1-2 - 1.2.3.4 +teq 1.2-3-4 ver_rs 2- - 1.2.3.4 +teq 1.2.3 ver_rs 2 . 1.2-3 +teq 1.2.3.a ver_rs 3 . 1.2.3a +teq 1.2-alpha-4 ver_rs 2-3 - 1.2_alpha4 +teq 1.23-b_alpha4 ver_rs 3 - 2 "" 1.2.3b_alpha4 +teq a1b_2-c-3-d4e5 ver_rs 3-5 _ 4-6 - a1b2c3d4e5 +teq .1-2.3 ver_rs 1 - .1.2.3 +teq -1.2.3 ver_rs 0 - .1.2.3 + +# truncating range +teq 1.2 ver_cut 0-2 1.2.3 +teq 2.3 ver_cut 2-5 1.2.3 +teq "" ver_cut 4 1.2.3 +teq "" ver_cut 0 1.2.3 +teq "" ver_cut 4- 1.2.3 +teq 1.2.3 ver_rs 0 - 1.2.3 +teq 1.2.3 ver_rs 3 . 1.2.3 +teq 1.2.3 ver_rs 3- . 1.2.3 +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 diff --git a/eclass/tests/eapi7-ver:benchmark.sh b/eclass/tests/eapi7-ver:benchmark.sh new file mode 100755 index 000000000000..4b262ab6accb --- /dev/null +++ b/eclass/tests/eapi7-ver:benchmark.sh @@ -0,0 +1,76 @@ +#!/bin/bash +# Copyright 1999-2017 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 + +EAPI=6 + +source tests-common.sh + +inherit eapi7-ver + +cutting() { + local x + for x in {1..1000}; do + ver_cut 1 1.2.3 + ver_cut 1-2 1.2.3 + ver_cut 2- 1.2.3 + ver_cut 1- 1.2.3 + ver_cut 3-4 1.2.3b_alpha4 + ver_cut 5 1.2.3b_alpha4 + ver_cut 1-2 .1.2.3 + ver_cut 0-2 .1.2.3 + ver_cut 2-3 1.2.3. + ver_cut 2- 1.2.3. + ver_cut 2-4 1.2.3. + done >/dev/null +} + +replacing() { + local x + for x in {1..1000}; do + ver_rs 1 - 1.2.3 + ver_rs 2 - 1.2.3 + ver_rs 1-2 - 1.2.3.4 + ver_rs 2- - 1.2.3.4 + ver_rs 2 . 1.2-3 + ver_rs 3 . 1.2.3a + ver_rs 2-3 - 1.2_alpha4 + ver_rs 3 - 2 "" 1.2.3b_alpha4 + ver_rs 3-5 _ 4-6 - a1b2c3d4e5 + ver_rs 1 - .1.2.3 + ver_rs 0 - .1.2.3 + done >/dev/null +} + +get_times() { + echo "${*}" + local real=() + local user=() + + for x in {1..5}; do + while read tt tv; do + case ${tt} in + real) real+=( ${tv} );; + user) user+=( ${tv} );; + esac + done < <( ( time -p "${@}" ) 2>&1 ) + done + + [[ ${#real[@]} == 5 ]] || die "Did not get 5 real times" + [[ ${#user[@]} == 5 ]] || die "Did not get 5 user times" + + local sum + for v in real user; do + vr="${v}[*]" + sum=$(dc -e "${!vr} + + + + 3 k 5 / p") + + vr="${v}[@]" + printf '%s %4.2f %4.2f %4.2f %4.2f %4.2f %4.2f\n' \ + "${v}" "${!vr}" "${sum}" + done +} + +export LC_ALL=C + +get_times cutting +get_times replacing -- 2.14.1