| Index: lib/cros_portage_versions.py
|
| diff --git a/lib/cros_portage_versions.py b/lib/cros_portage_versions.py
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..6dd5b464d531caca09bd3ca2b4fad609eab0c9eb
|
| --- /dev/null
|
| +++ b/lib/cros_portage_versions.py
|
| @@ -0,0 +1,325 @@
|
| +# versions.py -- core Portage functionality
|
| +# Copyright 1998-2006 Gentoo Foundation
|
| +# Distributed under the terms of the GNU General Public License v2
|
| +# $Id: versions.py 15234 2010-01-29 18:45:23Z zmedico $
|
| +
|
| +import re
|
| +
|
| +
|
| +# \w is [a-zA-Z0-9_]
|
| +
|
| +# 2.1.1 A category name may contain any of the characters [A-Za-z0-9+_.-].
|
| +# It must not begin with a hyphen or a dot.
|
| +_cat = r'[\w+][\w+.-]*'
|
| +
|
| +# 2.1.2 A package name may contain any of the characters [A-Za-z0-9+_-].
|
| +# It must not begin with a hyphen,
|
| +# and must not end in a hyphen followed by one or more digits.
|
| +_pkg = r'[\w+][\w+-]*?'
|
| +
|
| +_v = r'(cvs\.)?(\d+)((\.\d+)*)([a-z]?)((_(pre|p|beta|alpha|rc)\d*)*)'
|
| +_rev = r'\d+'
|
| +_vr = _v + '(-r(' + _rev + '))?'
|
| +
|
| +_cp = '(' + _cat + '/' + _pkg + '(-' + _vr + ')?)'
|
| +_cpv = '(' + _cp + '-' + _vr + ')'
|
| +_pv = '(?P<pn>' + _pkg + '(?P<pn_inval>-' + _vr + ')?)' + '-(?P<ver>' + _v + ')(-r(?P<rev>' + _rev + '))?'
|
| +
|
| +ver_regexp = re.compile("^" + _vr + "$")
|
| +suffix_regexp = re.compile("^(alpha|beta|rc|pre|p)(\\d*)$")
|
| +suffix_value = {"pre": -2, "p": 0, "alpha": -4, "beta": -3, "rc": -1}
|
| +endversion_keys = ["pre", "p", "alpha", "beta", "rc"]
|
| +
|
| +def ververify(myver, silent=1):
|
| + if ver_regexp.match(myver):
|
| + return 1
|
| + else:
|
| + if not silent:
|
| + print("!!! syntax error in version: %s" % myver)
|
| + return 0
|
| +
|
| +vercmp_cache = {}
|
| +def vercmp(ver1, ver2, silent=1):
|
| + """
|
| + Compare two versions
|
| + Example usage:
|
| + >>> from portage.versions import vercmp
|
| + >>> vercmp('1.0-r1','1.2-r3')
|
| + negative number
|
| + >>> vercmp('1.3','1.2-r3')
|
| + positive number
|
| + >>> vercmp('1.0_p3','1.0_p3')
|
| + 0
|
| +
|
| + @param pkg1: version to compare with (see ver_regexp in portage.versions.py)
|
| + @type pkg1: string (example: "2.1.2-r3")
|
| + @param pkg2: version to compare againts (see ver_regexp in portage.versions.py)
|
| + @type pkg2: string (example: "2.1.2_rc5")
|
| + @rtype: None or float
|
| + @return:
|
| + 1. positive if ver1 is greater than ver2
|
| + 2. negative if ver1 is less than ver2
|
| + 3. 0 if ver1 equals ver2
|
| + 4. None if ver1 or ver2 are invalid (see ver_regexp in portage.versions.py)
|
| + """
|
| +
|
| + if ver1 == ver2:
|
| + return 0
|
| + mykey=ver1+":"+ver2
|
| + try:
|
| + return vercmp_cache[mykey]
|
| + except KeyError:
|
| + pass
|
| + match1 = ver_regexp.match(ver1)
|
| + match2 = ver_regexp.match(ver2)
|
| +
|
| + # checking that the versions are valid
|
| + if not match1 or not match1.groups():
|
| + if not silent:
|
| + print("!!! syntax error in version: %s" % ver1)
|
| + return None
|
| + if not match2 or not match2.groups():
|
| + if not silent:
|
| + print("!!! syntax error in version: %s" % ver2)
|
| + return None
|
| +
|
| + # shortcut for cvs ebuilds (new style)
|
| + if match1.group(1) and not match2.group(1):
|
| + vercmp_cache[mykey] = 1
|
| + return 1
|
| + elif match2.group(1) and not match1.group(1):
|
| + vercmp_cache[mykey] = -1
|
| + return -1
|
| +
|
| + # building lists of the version parts before the suffix
|
| + # first part is simple
|
| + list1 = [int(match1.group(2))]
|
| + list2 = [int(match2.group(2))]
|
| +
|
| + # this part would greatly benefit from a fixed-length version pattern
|
| + if match1.group(3) or match2.group(3):
|
| + vlist1 = match1.group(3)[1:].split(".")
|
| + vlist2 = match2.group(3)[1:].split(".")
|
| +
|
| + for i in range(0, max(len(vlist1), len(vlist2))):
|
| + # Implcit .0 is given a value of -1, so that 1.0.0 > 1.0, since it
|
| + # would be ambiguous if two versions that aren't literally equal
|
| + # are given the same value (in sorting, for example).
|
| + if len(vlist1) <= i or len(vlist1[i]) == 0:
|
| + list1.append(-1)
|
| + list2.append(int(vlist2[i]))
|
| + elif len(vlist2) <= i or len(vlist2[i]) == 0:
|
| + list1.append(int(vlist1[i]))
|
| + list2.append(-1)
|
| + # Let's make life easy and use integers unless we're forced to use floats
|
| + elif (vlist1[i][0] != "0" and vlist2[i][0] != "0"):
|
| + list1.append(int(vlist1[i]))
|
| + list2.append(int(vlist2[i]))
|
| + # now we have to use floats so 1.02 compares correctly against 1.1
|
| + else:
|
| + # list1.append(float("0."+vlist1[i]))
|
| + # list2.append(float("0."+vlist2[i]))
|
| + # Since python floats have limited range, we multiply both
|
| + # floating point representations by a constant so that they are
|
| + # transformed into whole numbers. This allows the practically
|
| + # infinite range of a python int to be exploited. The
|
| + # multiplication is done by padding both literal strings with
|
| + # zeros as necessary to ensure equal length.
|
| + max_len = max(len(vlist1[i]), len(vlist2[i]))
|
| + list1.append(int(vlist1[i].ljust(max_len, "0")))
|
| + list2.append(int(vlist2[i].ljust(max_len, "0")))
|
| +
|
| + # and now the final letter
|
| + # NOTE: Behavior changed in r2309 (between portage-2.0.x and portage-2.1).
|
| + # The new behavior is 12.2.5 > 12.2b which, depending on how you look at,
|
| + # may seem counter-intuitive. However, if you really think about it, it
|
| + # seems like it's probably safe to assume that this is the behavior that
|
| + # is intended by anyone who would use versions such as these.
|
| + if len(match1.group(5)):
|
| + list1.append(ord(match1.group(5)))
|
| + if len(match2.group(5)):
|
| + list2.append(ord(match2.group(5)))
|
| +
|
| + for i in range(0, max(len(list1), len(list2))):
|
| + if len(list1) <= i:
|
| + vercmp_cache[mykey] = -1
|
| + return -1
|
| + elif len(list2) <= i:
|
| + vercmp_cache[mykey] = 1
|
| + return 1
|
| + elif list1[i] != list2[i]:
|
| + a = list1[i]
|
| + b = list2[i]
|
| + rval = (a > b) - (a < b)
|
| + vercmp_cache[mykey] = rval
|
| + return rval
|
| +
|
| + # main version is equal, so now compare the _suffix part
|
| + list1 = match1.group(6).split("_")[1:]
|
| + list2 = match2.group(6).split("_")[1:]
|
| +
|
| + for i in range(0, max(len(list1), len(list2))):
|
| + # Implicit _p0 is given a value of -1, so that 1 < 1_p0
|
| + if len(list1) <= i:
|
| + s1 = ("p","-1")
|
| + else:
|
| + s1 = suffix_regexp.match(list1[i]).groups()
|
| + if len(list2) <= i:
|
| + s2 = ("p","-1")
|
| + else:
|
| + s2 = suffix_regexp.match(list2[i]).groups()
|
| + if s1[0] != s2[0]:
|
| + a = suffix_value[s1[0]]
|
| + b = suffix_value[s2[0]]
|
| + rval = (a > b) - (a < b)
|
| + vercmp_cache[mykey] = rval
|
| + return rval
|
| + if s1[1] != s2[1]:
|
| + # it's possible that the s(1|2)[1] == ''
|
| + # in such a case, fudge it.
|
| + try:
|
| + r1 = int(s1[1])
|
| + except ValueError:
|
| + r1 = 0
|
| + try:
|
| + r2 = int(s2[1])
|
| + except ValueError:
|
| + r2 = 0
|
| + rval = (r1 > r2) - (r1 < r2)
|
| + if rval:
|
| + vercmp_cache[mykey] = rval
|
| + return rval
|
| +
|
| + # the suffix part is equal to, so finally check the revision
|
| + if match1.group(10):
|
| + r1 = int(match1.group(10))
|
| + else:
|
| + r1 = 0
|
| + if match2.group(10):
|
| + r2 = int(match2.group(10))
|
| + else:
|
| + r2 = 0
|
| + rval = (r1 > r2) - (r1 < r2)
|
| + vercmp_cache[mykey] = rval
|
| + return rval
|
| +
|
| +def pkgcmp(pkg1, pkg2):
|
| + """
|
| + Compare 2 package versions created in pkgsplit format.
|
| +
|
| + Example usage:
|
| + >>> from portage.versions import *
|
| + >>> pkgcmp(pkgsplit('test-1.0-r1'),pkgsplit('test-1.2-r3'))
|
| + -1
|
| + >>> pkgcmp(pkgsplit('test-1.3'),pkgsplit('test-1.2-r3'))
|
| + 1
|
| +
|
| + @param pkg1: package to compare with
|
| + @type pkg1: list (example: ['test', '1.0', 'r1'])
|
| + @param pkg2: package to compare againts
|
| + @type pkg2: list (example: ['test', '1.0', 'r1'])
|
| + @rtype: None or integer
|
| + @return:
|
| + 1. None if package names are not the same
|
| + 2. 1 if pkg1 is greater than pkg2
|
| + 3. -1 if pkg1 is less than pkg2
|
| + 4. 0 if pkg1 equals pkg2
|
| + """
|
| + if pkg1[0] != pkg2[0]:
|
| + return None
|
| + return vercmp("-".join(pkg1[1:]), "-".join(pkg2[1:]))
|
| +
|
| +_pv_re = re.compile('^' + _pv + '$', re.VERBOSE)
|
| +
|
| +def _pkgsplit(mypkg):
|
| + """
|
| + @param mypkg: pv
|
| + @return:
|
| + 1. None if input is invalid.
|
| + 2. (pn, ver, rev) if input is pv
|
| + """
|
| + m = _pv_re.match(mypkg)
|
| + if m is None:
|
| + return None
|
| +
|
| + if m.group('pn_inval') is not None:
|
| + # package name appears to have a version-like suffix
|
| + return None
|
| +
|
| + rev = m.group('rev')
|
| + if rev is None:
|
| + rev = '0'
|
| + rev = 'r' + rev
|
| +
|
| + return (m.group('pn'), m.group('ver'), rev)
|
| +
|
| +_missing_cat = 'null'
|
| +catcache={}
|
| +def catpkgsplit(mydata,silent=1):
|
| + """
|
| + Takes a Category/Package-Version-Rev and returns a list of each.
|
| +
|
| + @param mydata: Data to split
|
| + @type mydata: string
|
| + @param silent: suppress error messages
|
| + @type silent: Boolean (integer)
|
| + @rype: list
|
| + @return:
|
| + 1. If each exists, it returns [cat, pkgname, version, rev]
|
| + 2. If cat is not specificed in mydata, cat will be "null"
|
| + 3. if rev does not exist it will be '-r0'
|
| + """
|
| +
|
| + try:
|
| + return catcache[mydata]
|
| + except KeyError:
|
| + pass
|
| + mysplit = mydata.split('/', 1)
|
| + p_split=None
|
| + if len(mysplit)==1:
|
| + cat = _missing_cat
|
| + p_split = _pkgsplit(mydata)
|
| + elif len(mysplit)==2:
|
| + cat = mysplit[0]
|
| + p_split = _pkgsplit(mysplit[1])
|
| + if not p_split:
|
| + catcache[mydata]=None
|
| + return None
|
| + retval = (cat, p_split[0], p_split[1], p_split[2])
|
| + catcache[mydata]=retval
|
| + return retval
|
| +
|
| +def pkgsplit(mypkg, silent=1):
|
| + """
|
| + @param mypkg: either a pv or cpv
|
| + @return:
|
| + 1. None if input is invalid.
|
| + 2. (pn, ver, rev) if input is pv
|
| + 3. (cp, ver, rev) if input is a cpv
|
| + """
|
| + catpsplit = catpkgsplit(mypkg)
|
| + if catpsplit is None:
|
| + return None
|
| + cat, pn, ver, rev = catpsplit
|
| + if cat is _missing_cat and '/' not in mypkg:
|
| + return (pn, ver, rev)
|
| + else:
|
| + return (cat + '/' + pn, ver, rev)
|
| +
|
| +def catsplit(mydep):
|
| + return mydep.split("/", 1)
|
| +
|
| +def best(mymatches):
|
| + """Accepts None arguments; assumes matches are valid."""
|
| + if not mymatches:
|
| + return ""
|
| + if len(mymatches) == 1:
|
| + return mymatches[0]
|
| + bestmatch = mymatches[0]
|
| + p2 = catpkgsplit(bestmatch)[1:]
|
| + for x in mymatches[1:]:
|
| + p1 = catpkgsplit(x)[1:]
|
| + if pkgcmp(p1, p2) > 0:
|
| + bestmatch = x
|
| + p2 = catpkgsplit(bestmatch)[1:]
|
| + return bestmatch
|
|
|