1 # versions.py -- core Portage functionality
2 # Copyright 1998-2006 Gentoo Foundation
3 # Distributed under the terms of the GNU General Public License v2
11 # 2.1.1 A category name may contain any of the characters [A-Za-z0-9+_.-].
12 # It must not begin with a hyphen or a dot.
13 _cat = r'[\w+][\w+.-]*'
15 # 2.1.2 A package name may contain any of the characters [A-Za-z0-9+_-].
16 # It must not begin with a hyphen,
17 # and must not end in a hyphen followed by one or more digits.
18 _pkg = r'[\w+][\w+-]*?'
20 _v = r'(cvs\.)?(\d+)((\.\d+)*)([a-z]?)((_(pre|p|beta|alpha|rc)\d*)*)'
22 _vr = _v + '(-r(' + _rev + '))?'
24 _cp = '(' + _cat + '/' + _pkg + '(-' + _vr + ')?)'
25 _cpv = '(' + _cp + '-' + _vr + ')'
26 _pv = '(?P<pn>' + _pkg + '(?P<pn_inval>-' + _vr + ')?)' + '-(?P<ver>' + _v + ')(-r(?P<rev>' + _rev + '))?'
28 ver_regexp = re.compile("^" + _vr + "$")
29 suffix_regexp = re.compile("^(alpha|beta|rc|pre|p)(\\d*)$")
30 suffix_value = {"pre": -2, "p": 0, "alpha": -4, "beta": -3, "rc": -1}
31 endversion_keys = ["pre", "p", "alpha", "beta", "rc"]
33 from portage.exception import InvalidData
34 from portage.localization import _
36 def ververify(myver, silent=1):
37 if ver_regexp.match(myver):
41 print(_("!!! syntax error in version: %s") % myver)
45 def vercmp(ver1, ver2, silent=1):
49 >>> from portage.versions import vercmp
50 >>> vercmp('1.0-r1','1.2-r3')
52 >>> vercmp('1.3','1.2-r3')
54 >>> vercmp('1.0_p3','1.0_p3')
57 @param pkg1: version to compare with (see ver_regexp in portage.versions.py)
58 @type pkg1: string (example: "2.1.2-r3")
59 @param pkg2: version to compare againts (see ver_regexp in portage.versions.py)
60 @type pkg2: string (example: "2.1.2_rc5")
63 1. positive if ver1 is greater than ver2
64 2. negative if ver1 is less than ver2
65 3. 0 if ver1 equals ver2
66 4. None if ver1 or ver2 are invalid (see ver_regexp in portage.versions.py)
73 return vercmp_cache[mykey]
76 match1 = ver_regexp.match(ver1)
77 match2 = ver_regexp.match(ver2)
79 # checking that the versions are valid
80 if not match1 or not match1.groups():
82 print(_("!!! syntax error in version: %s") % ver1)
84 if not match2 or not match2.groups():
86 print(_("!!! syntax error in version: %s") % ver2)
89 # shortcut for cvs ebuilds (new style)
90 if match1.group(1) and not match2.group(1):
91 vercmp_cache[mykey] = 1
93 elif match2.group(1) and not match1.group(1):
94 vercmp_cache[mykey] = -1
97 # building lists of the version parts before the suffix
98 # first part is simple
99 list1 = [int(match1.group(2))]
100 list2 = [int(match2.group(2))]
102 # this part would greatly benefit from a fixed-length version pattern
103 if len(match1.group(3)) or len(match2.group(3)):
104 vlist1 = match1.group(3)[1:].split(".")
105 vlist2 = match2.group(3)[1:].split(".")
106 for i in range(0, max(len(vlist1), len(vlist2))):
107 # Implcit .0 is given a value of -1, so that 1.0.0 > 1.0, since it
108 # would be ambiguous if two versions that aren't literally equal
109 # are given the same value (in sorting, for example).
110 if len(vlist1) <= i or len(vlist1[i]) == 0:
112 list2.append(int(vlist2[i]))
113 elif len(vlist2) <= i or len(vlist2[i]) == 0:
114 list1.append(int(vlist1[i]))
116 # Let's make life easy and use integers unless we're forced to use floats
117 elif (vlist1[i][0] != "0" and vlist2[i][0] != "0"):
118 list1.append(int(vlist1[i]))
119 list2.append(int(vlist2[i]))
120 # now we have to use floats so 1.02 compares correctly against 1.1
122 # list1.append(float("0."+vlist1[i]))
123 # list2.append(float("0."+vlist2[i]))
124 # Since python floats have limited range, we multiply both
125 # floating point representations by a constant so that they are
126 # transformed into whole numbers. This allows the practically
127 # infinite range of a python int to be exploited. The
128 # multiplication is done by padding both literal strings with
129 # zeros as necessary to ensure equal length.
130 max_len = max(len(vlist1[i]), len(vlist2[i]))
131 list1.append(int(vlist1[i].ljust(max_len, "0")))
132 list2.append(int(vlist2[i].ljust(max_len, "0")))
134 # and now the final letter
135 if len(match1.group(5)):
136 list1.append(ord(match1.group(5)))
137 if len(match2.group(5)):
138 list2.append(ord(match2.group(5)))
140 for i in range(0, max(len(list1), len(list2))):
142 vercmp_cache[mykey] = -1
144 elif len(list2) <= i:
145 vercmp_cache[mykey] = 1
147 elif list1[i] != list2[i]:
150 rval = (a > b) - (a < b)
151 vercmp_cache[mykey] = rval
154 # main version is equal, so now compare the _suffix part
155 list1 = match1.group(6).split("_")[1:]
156 list2 = match2.group(6).split("_")[1:]
158 for i in range(0, max(len(list1), len(list2))):
159 # Implicit _p0 is given a value of -1, so that 1 < 1_p0
163 s1 = suffix_regexp.match(list1[i]).groups()
167 s2 = suffix_regexp.match(list2[i]).groups()
169 a = suffix_value[s1[0]]
170 b = suffix_value[s2[0]]
171 rval = (a > b) - (a < b)
172 vercmp_cache[mykey] = rval
175 # it's possible that the s(1|2)[1] == ''
176 # in such a case, fudge it.
185 rval = (r1 > r2) - (r1 < r2)
187 vercmp_cache[mykey] = rval
190 # the suffix part is equal to, so finally check the revision
192 r1 = int(match1.group(10))
196 r2 = int(match2.group(10))
199 rval = (r1 > r2) - (r1 < r2)
200 vercmp_cache[mykey] = rval
203 def pkgcmp(pkg1, pkg2):
205 Compare 2 package versions created in pkgsplit format.
208 >>> from portage.versions import *
209 >>> pkgcmp(pkgsplit('test-1.0-r1'),pkgsplit('test-1.2-r3'))
211 >>> pkgcmp(pkgsplit('test-1.3'),pkgsplit('test-1.2-r3'))
214 @param pkg1: package to compare with
215 @type pkg1: list (example: ['test', '1.0', 'r1'])
216 @param pkg2: package to compare againts
217 @type pkg2: list (example: ['test', '1.0', 'r1'])
218 @rtype: None or integer
220 1. None if package names are not the same
221 2. 1 if pkg1 is greater than pkg2
222 3. -1 if pkg1 is less than pkg2
223 4. 0 if pkg1 equals pkg2
225 if pkg1[0] != pkg2[0]:
227 return vercmp("-".join(pkg1[1:]), "-".join(pkg2[1:]))
229 _pv_re = re.compile('^' + _pv + '$', re.VERBOSE)
231 def _pkgsplit(mypkg):
233 m = _pv_re.match(mypkg)
237 if m.group('pn_inval') is not None:
238 # package name appears to have a version-like suffix
246 return (m.group('pn'), m.group('ver'), rev)
249 def catpkgsplit(mydata,silent=1):
251 Takes a Category/Package-Version-Rev and returns a list of each.
253 @param mydata: Data to split
255 @param silent: suppress error messages
256 @type silent: Boolean (integer)
259 1. If each exists, it returns [cat, pkgname, version, rev]
260 2. If cat is not specificed in mydata, cat will be "null"
261 3. if rev does not exist it will be '-r0'
265 return catcache[mydata]
268 mysplit = mydata.split('/', 1)
272 p_split = _pkgsplit(mydata)
273 elif len(mysplit)==2:
275 p_split = _pkgsplit(mysplit[1])
277 catcache[mydata]=None
279 retval = (cat, p_split[0], p_split[1], p_split[2])
280 catcache[mydata]=retval
283 def pkgsplit(mypkg, silent=1):
285 @param mypkg: either a pv or cpv
287 1. None if input is invalid.
288 2. (pn, ver, rev) if input is pv
289 3. (cp, ver, rev) if input is a cpv
291 catpsplit = catpkgsplit(mypkg)
292 if catpsplit is None:
294 cat, pn, ver, rev = catpsplit
296 return (pn, ver, rev)
298 return (cat + '/' + pn, ver, rev)
301 return mydep.split("/", 1)
304 """Accepts None arguments; assumes matches are valid."""
307 if len(mymatches) == 1:
309 bestmatch = mymatches[0]
310 p2 = catpkgsplit(bestmatch)[1:]
311 for x in mymatches[1:]:
312 p1 = catpkgsplit(x)[1:]
313 if pkgcmp(p1, p2) > 0:
315 p2 = catpkgsplit(bestmatch)[1:]