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 |