Skip to content

Commit

Permalink
eapi7-ver.eclass: 'Early adopter' version of EAPI 7 version manip
Browse files Browse the repository at this point in the history
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,

b. making it possible to adapt ebuilds to the new routines early,
reducing the future work of EAPI 7 porting,

c. improving cache generation speed (the rountines are roughly 15-20
times than versionator.eclass).

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

The code has been initially written by Ulrich Müller.
  • Loading branch information
mgorny committed Sep 19, 2017
1 parent 99142c6 commit 59a1a0d
Show file tree
Hide file tree
Showing 3 changed files with 368 additions and 0 deletions.
190 changes: 190 additions & 0 deletions eclass/eapi7-ver.eclass
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
# Copyright 1999-2017 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2

# @ECLASS: eapi7-ver.eclass
# @MAINTAINER:
# PMS team <[email protected]>
# @AUTHOR:
# Ulrich Müller <[email protected]>
# Michał Górny <[email protected]>
# @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.
#
# @ROFF .SS
# 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]+). A version
# separator is either a string of any other characters ([^A-Za-z0-9]+),
# or it occurs at the transition between a sequence of letters
# and a sequence of digits, or vice versa. In the latter case,
# the version separator is an empty string.
#
# 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:
#
# @CODE
# 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
# @CODE
#
# @ROFF .SS
# 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
# @USAGE: <range> <max>
# @INTERNAL
# @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
# @USAGE: <version>
# @INTERNAL
# @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"
}
65 changes: 65 additions & 0 deletions eclass/tests/eapi7-ver.sh
Original file line number Diff line number Diff line change
@@ -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
113 changes: 113 additions & 0 deletions eclass/tests/eapi7-ver_benchmark.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
#!/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 versionator

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
}

cutting_versionator() {
local x
for x in {1..100}; do
get_version_component_range 1 1.2.3
get_version_component_range 1-2 1.2.3
get_version_component_range 2- 1.2.3
get_version_component_range 1- 1.2.3
get_version_component_range 3-4 1.2.3b_alpha4
get_version_component_range 5 1.2.3b_alpha4
get_version_component_range 1-2 .1.2.3
get_version_component_range 0-2 .1.2.3
get_version_component_range 2-3 1.2.3.
get_version_component_range 2- 1.2.3.
get_version_component_range 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
}

replacing_versionator() {
local x
for x in {1..100}; do
replace_version_separator 1 - 1.2.3
replace_version_separator 2 - 1.2.3
replace_version_separator 1-2 - 1.2.3.4
replace_version_separator 2- - 1.2.3.4
replace_version_separator 2 . 1.2-3
replace_version_separator 3 . 1.2.3a
replace_version_separator 2-3 - 1.2_alpha4
#replace_version_separator 3 - 2 "" 1.2.3b_alpha4
#replace_version_separator 3-5 _ 4-6 - a1b2c3d4e5
replace_version_separator 1 - .1.2.3
replace_version_separator 0 - .1.2.3
done >/dev/null
}

get_times() {
local factor=${1}; shift
echo "${*}"
local real=()
local user=()

for x in {1..5}; do
while read tt tv; do
case ${tt} in
real) real+=( $(dc -e "${tv} ${factor} * p") );;
user) user+=( $(dc -e "${tv} ${factor} * p") );;
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 1 cutting
get_times 10 cutting_versionator
get_times 1 replacing
get_times 10 replacing_versionator

0 comments on commit 59a1a0d

Please sign in to comment.