aboutsummaryrefslogtreecommitdiff
path: root/mesonbuild/mesonlib.py
diff options
context:
space:
mode:
authorJon Turney <jon.turney@dronecode.org.uk>2018-08-13 15:18:16 +0100
committerJon Turney <jon.turney@dronecode.org.uk>2018-09-11 16:30:15 +0100
commit47d0a13482e98323621192ebc27b74871af81c62 (patch)
treedbf35f9963938744b0a123447a0f4cf83f5fc957 /mesonbuild/mesonlib.py
parent9a02340afd835c2556e9b5f40cd8e633c98cdb7b (diff)
downloadmeson-47d0a13482e98323621192ebc27b74871af81c62.zip
meson-47d0a13482e98323621192ebc27b74871af81c62.tar.gz
meson-47d0a13482e98323621192ebc27b74871af81c62.tar.bz2
Re-implement version_compare using RPM-style version comparison
Diffstat (limited to 'mesonbuild/mesonlib.py')
-rw-r--r--mesonbuild/mesonlib.py72
1 files changed, 60 insertions, 12 deletions
diff --git a/mesonbuild/mesonlib.py b/mesonbuild/mesonlib.py
index 1b9cb42..723a81d 100644
--- a/mesonbuild/mesonlib.py
+++ b/mesonbuild/mesonlib.py
@@ -14,6 +14,7 @@
"""A library of random helper functionality."""
+import functools
import sys
import stat
import time
@@ -409,14 +410,59 @@ def make_same_len(listA, listB):
for n in range(len(i), maxlen):
i.append(0)
-numpart = re.compile('[0-9.]+')
+# a helper class which implements the same version ordering as RPM
+@functools.total_ordering
+class Version:
+ def __init__(self, s):
+ self._s = s
-def version_compare(vstr1, vstr2, strict=False):
- match = numpart.match(vstr1.strip())
- if match is None:
- msg = 'Uncomparable version string {!r}.'
- raise MesonException(msg.format(vstr1))
- vstr1 = match.group(0)
+ # split into numeric, alphabetic and non-alphanumeric sequences
+ sequences = re.finditer(r'(\d+|[a-zA-Z]+|[^a-zA-Z\d]+)', s)
+ # non-alphanumeric separators are discarded
+ sequences = [m for m in sequences if not re.match(r'[^a-zA-Z\d]+', m.group(1))]
+ # numeric sequences have leading zeroes discarded
+ sequences = [re.sub(r'^0+(\d)', r'\1', m.group(1), 1) for m in sequences]
+
+ self._v = sequences
+
+ def __str__(self):
+ return '%s (V=%s)' % (self._s, str(self._v))
+
+ def __lt__(self, other):
+ return self.__cmp__(other) == -1
+
+ def __eq__(self, other):
+ return self.__cmp__(other) == 0
+
+ def __cmp__(self, other):
+ def cmp(a, b):
+ return (a > b) - (a < b)
+
+ # compare each sequence in order
+ for i in range(0, min(len(self._v), len(other._v))):
+ # sort a non-digit sequence before a digit sequence
+ if self._v[i].isdigit() != other._v[i].isdigit():
+ return 1 if self._v[i].isdigit() else -1
+
+ # compare as numbers
+ if self._v[i].isdigit():
+ # because leading zeros have already been removed, if one number
+ # has more digits, it is greater
+ c = cmp(len(self._v[i]), len(other._v[i]))
+ if c != 0:
+ return c
+ # fallthrough
+
+ # compare lexicographically
+ c = cmp(self._v[i], other._v[i])
+ if c != 0:
+ return c
+
+ # if equal length, all components have matched, so equal
+ # otherwise, the version with a suffix remaining is greater
+ return cmp(len(self._v), len(other._v))
+
+def _version_extract_cmpop(vstr2):
if vstr2.startswith('>='):
cmpop = operator.ge
vstr2 = vstr2[2:]
@@ -440,10 +486,12 @@ def version_compare(vstr1, vstr2, strict=False):
vstr2 = vstr2[1:]
else:
cmpop = operator.eq
- varr1 = grab_leading_numbers(vstr1, strict)
- varr2 = grab_leading_numbers(vstr2, strict)
- make_same_len(varr1, varr2)
- return cmpop(varr1, varr2)
+
+ return (cmpop, vstr2)
+
+def version_compare(vstr1, vstr2):
+ (cmpop, vstr2) = _version_extract_cmpop(vstr2)
+ return cmpop(Version(vstr1), Version(vstr2))
def version_compare_many(vstr1, conditions):
if not isinstance(conditions, (list, tuple, frozenset)):
@@ -451,7 +499,7 @@ def version_compare_many(vstr1, conditions):
found = []
not_found = []
for req in conditions:
- if not version_compare(vstr1, req, strict=True):
+ if not version_compare(vstr1, req):
not_found.append(req)
else:
found.append(req)