summaryrefslogtreecommitdiffstats
path: root/pym
diff options
context:
space:
mode:
authorMarius Mauch <genone@gentoo.org>2005-11-13 16:07:28 +0000
committerMarius Mauch <genone@gentoo.org>2005-11-13 16:07:28 +0000
commit9f3a46665c40b48206c90fabc5befaaeff6b43d0 (patch)
treea38156cbc22ffed23d28baca9eff7625e2f3e611 /pym
parent1eaf47d9ddb16a30fce01869c877bcd1abb58937 (diff)
downloadportage-9f3a46665c40b48206c90fabc5befaaeff6b43d0.tar.gz
portage-9f3a46665c40b48206c90fabc5befaaeff6b43d0.tar.bz2
portage-9f3a46665c40b48206c90fabc5befaaeff6b43d0.zip
Backport of version code rewrite (bug 37406), should be completely backwards compatible (and the algorithm has been tested on the whole tree multiple times already).
Also adds some new features in version syntax like cvs version prefixes (in the same package) and multiple suffixes. svn path=/main/trunk/; revision=2309
Diffstat (limited to 'pym')
-rw-r--r--pym/portage.py339
-rw-r--r--pym/portage_versions.py222
2 files changed, 226 insertions, 335 deletions
diff --git a/pym/portage.py b/pym/portage.py
index 60ab80e4b..63a67f6e8 100644
--- a/pym/portage.py
+++ b/pym/portage.py
@@ -100,6 +100,10 @@ try:
from portage_checksum import perform_md5,perform_checksum,prelink_capable
import eclass_cache
from portage_localization import _
+
+ # Need these functions directly in portage namespace to not break every external tool in existence
+ from portage_versions import ververify,vercmp,catsplit,catpkgsplit,pkgsplit,pkgcmp
+
except SystemExit, e:
raise
except Exception, e:
@@ -3009,150 +3013,6 @@ def unmerge(cat,pkg,myroot,mysettings,mytrimworld=1):
mylink.unmerge(trimworld=mytrimworld,cleanup=1)
mylink.delete()
-def relparse(myver):
- "converts last version part into three components"
- number=0
- suffix=0
- endtype=0
- endnumber=0
-
- mynewver=string.split(myver,"_")
- myver=mynewver[0]
-
- #normal number or number with letter at end
- divider=len(myver)-1
- if myver[divider:] not in "1234567890":
- #letter at end
- suffix=ord(myver[divider:])
- number=string.atof(myver[0:divider])
- else:
- number=string.atof(myver)
-
- if len(mynewver)==2:
- #an endversion
- for x in endversion_keys:
- elen=len(x)
- if mynewver[1][:elen] == x:
- match=1
- endtype=endversion[x]
- try:
- endnumber=string.atof(mynewver[1][elen:])
- except SystemExit, e:
- raise
- except:
- endnumber=0
- break
- return [number,suffix,endtype,endnumber]
-
-#returns 1 if valid version string, else 0
-# valid string in format: <v1>.<v2>...<vx>[a-z,_{endversion}[vy]]
-# ververify doesn't do package rev.
-
-vercache={}
-def ververify(myorigval,silent=1):
- try:
- return vercache[myorigval]
- except KeyError:
- pass
- if len(myorigval)==0:
- if not silent:
- print "!!! Name error: package contains empty \"-\" part."
- return 0
- myval=string.split(myorigval,'.')
- if len(myval)==0:
- if not silent:
- print "!!! Name error: empty version string."
- vercache[myorigval]=0
- return 0
- #all but the last version must be a numeric
- for x in myval[:-1]:
- if not len(x):
- if not silent:
- print "!!! Name error in",myorigval+": two decimal points in a row"
- vercache[myorigval]=0
- return 0
- try:
- foo=int(x)
- except SystemExit, e:
- raise
- except:
- if not silent:
- print "!!! Name error in",myorigval+": \""+x+"\" is not a valid version component."
- vercache[myorigval]=0
- return 0
- if not len(myval[-1]):
- if not silent:
- print "!!! Name error in",myorigval+": two decimal points in a row"
- vercache[myorigval]=0
- return 0
- try:
- foo=int(myval[-1])
- vercache[myorigval]=1
- return 1
- except SystemExit, e:
- raise
- except:
- pass
- #ok, our last component is not a plain number or blank, let's continue
- if myval[-1][-1] in string.lowercase:
- try:
- foo=int(myval[-1][:-1])
- vercache[myorigval]=1
- return 1
- # 1a, 2.0b, etc.
- except SystemExit, e:
- raise
- except:
- pass
- #ok, maybe we have a 1_alpha or 1_beta2; let's see
- #ep="endpart"
- ep=string.split(myval[-1],"_")
- if len(ep)!=2:
- if not silent:
- print "!!! Name error in",myorigval
- vercache[myorigval]=0
- return 0
- try:
- foo=int(ep[0][-1])
- chk=ep[0]
- except SystemExit, e:
- raise
- except:
- # because it's ok last char is not numeric. example: foo-1.0.0a_pre1
- chk=ep[0][:-1]
-
- try:
- foo=int(chk)
- except SystemExit, e:
- raise
- except:
- #this needs to be numeric or numeric+single letter,
- #i.e. the "1" in "1_alpha" or "1a_alpha"
- if not silent:
- print "!!! Name error in",myorigval+": characters before _ must be numeric or numeric+single letter"
- vercache[myorigval]=0
- return 0
- for mye in endversion_keys:
- if ep[1][0:len(mye)]==mye:
- if len(mye)==len(ep[1]):
- #no trailing numeric; ok
- vercache[myorigval]=1
- return 1
- else:
- try:
- foo=int(ep[1][len(mye):])
- vercache[myorigval]=1
- return 1
- except SystemExit, e:
- raise
- except:
- #if no endversions work, *then* we return 0
- pass
- if not silent:
- print "!!! Name error in",myorigval
- vercache[myorigval]=0
- return 0
-
def isvalidatom(atom):
mycpv_cps = catpkgsplit(dep_getcpv(atom))
operator = get_operator(atom)
@@ -3198,201 +3058,10 @@ def isspecific(mypkg):
iscache[mypkg]=0
return 0
-# This function can be used as a package verification function, i.e.
-# "pkgsplit("foo-1.2-1") will return None if foo-1.2-1 isn't a valid
-# package (with version) name. If it is a valid name, pkgsplit will
-# return a list containing: [ pkgname, pkgversion(norev), pkgrev ].
-# For foo-1.2-1, this list would be [ "foo", "1.2", "1" ]. For
-# Mesa-3.0, this list would be [ "Mesa", "3.0", "0" ].
-pkgcache={}
-
-def pkgsplit(mypkg,silent=1):
- try:
- if not pkgcache[mypkg]:
- return None
- return pkgcache[mypkg][:]
- except KeyError:
- pass
- myparts=string.split(mypkg,'-')
- if len(myparts)<2:
- if not silent:
- print "!!! Name error in",mypkg+": missing a version or name part."
- pkgcache[mypkg]=None
- return None
- for x in myparts:
- if len(x)==0:
- if not silent:
- print "!!! Name error in",mypkg+": empty \"-\" part."
- pkgcache[mypkg]=None
- return None
- #verify rev
- revok=0
- myrev=myparts[-1]
- if len(myrev) and myrev[0]=="r":
- try:
- int(myrev[1:])
- revok=1
- except SystemExit, e:
- raise
- except:
- pass
- if revok:
- if ververify(myparts[-2]):
- if len(myparts)==2:
- pkgcache[mypkg]=None
- return None
- else:
- for x in myparts[:-2]:
- if ververify(x):
- pkgcache[mypkg]=None
- return None
- #names can't have versiony looking parts
- myval=[string.join(myparts[:-2],"-"),myparts[-2],myparts[-1]]
- pkgcache[mypkg]=myval
- return myval
- else:
- pkgcache[mypkg]=None
- return None
-
- elif ververify(myparts[-1],silent=silent):
- if len(myparts)==1:
- if not silent:
- print "!!! Name error in",mypkg+": missing name part."
- pkgcache[mypkg]=None
- return None
- else:
- for x in myparts[:-1]:
- if ververify(x):
- if not silent:
- print "!!! Name error in",mypkg+": multiple version parts."
- pkgcache[mypkg]=None
- return None
- myval=[string.join(myparts[:-1],"-"),myparts[-1],"r0"]
- pkgcache[mypkg]=myval[:]
- return myval
- else:
- pkgcache[mypkg]=None
- return None
-
def getCPFromCPV(mycpv):
"""Calls pkgsplit on a cpv and returns only the cp."""
return pkgsplit(mycpv)[0]
-catcache={}
-def catpkgsplit(mydata,silent=1):
- "returns [cat, pkgname, version, rev ]"
- try:
- if not catcache[mydata]:
- return None
- return catcache[mydata][:]
- except KeyError:
- pass
- mysplit=mydata.split("/")
- p_split=None
- if len(mysplit)==1:
- retval=["null"]
- p_split=pkgsplit(mydata,silent=silent)
- elif len(mysplit)==2:
- retval=[mysplit[0]]
- p_split=pkgsplit(mysplit[1],silent=silent)
- if not p_split:
- catcache[mydata]=None
- return None
- retval.extend(p_split)
- catcache[mydata]=retval
- return retval
-
-# vercmp:
-# This takes two version strings and returns an integer to tell you whether
-# the versions are the same, val1>val2 or val2>val1.
-vcmpcache={}
-def vercmp(val1,val2):
- if val1==val2:
- #quick short-circuit
- return 0
- valkey=val1+" "+val2
- try:
- return vcmpcache[valkey]
- try:
- return -vcmpcache[val2+" "+val1]
- except KeyError:
- pass
- except KeyError:
- pass
-
- # consider 1_p2 vc 1.1
- # after expansion will become (1_p2,0) vc (1,1)
- # then 1_p2 is compared with 1 before 0 is compared with 1
- # to solve the bug we need to convert it to (1,0_p2)
- # by splitting _prepart part and adding it back _after_expansion
- val1_prepart = val2_prepart = ''
- if val1.count('_'):
- val1, val1_prepart = val1.split('_', 1)
- if val2.count('_'):
- val2, val2_prepart = val2.split('_', 1)
-
- # replace '-' by '.'
- # FIXME: Is it needed? can val1/2 contain '-'?
- val1=string.split(val1,'-')
- if len(val1)==2:
- val1[0]=val1[0]+"."+val1[1]
- val2=string.split(val2,'-')
- if len(val2)==2:
- val2[0]=val2[0]+"."+val2[1]
-
- val1=string.split(val1[0],'.')
- val2=string.split(val2[0],'.')
-
- #add back decimal point so that .03 does not become "3" !
- for x in range(1,len(val1)):
- if val1[x][0] == '0' :
- val1[x]='.' + val1[x]
- for x in range(1,len(val2)):
- if val2[x][0] == '0' :
- val2[x]='.' + val2[x]
-
- # extend version numbers
- if len(val2)<len(val1):
- val2.extend(["0"]*(len(val1)-len(val2)))
- elif len(val1)<len(val2):
- val1.extend(["0"]*(len(val2)-len(val1)))
-
- # add back _prepart tails
- if val1_prepart:
- val1[-1] += '_' + val1_prepart
- if val2_prepart:
- val2[-1] += '_' + val2_prepart
- #The above code will extend version numbers out so they
- #have the same number of digits.
- for x in range(0,len(val1)):
- cmp1=relparse(val1[x])
- cmp2=relparse(val2[x])
- for y in range(0,4):
- myret=cmp1[y]-cmp2[y]
- if myret != 0:
- vcmpcache[valkey]=myret
- return myret
- vcmpcache[valkey]=0
- return 0
-
-
-def pkgcmp(pkg1,pkg2):
- """if returnval is less than zero, then pkg2 is newer than pkg1, zero if equal and positive if older."""
- if pkg1[0] != pkg2[0]:
- return None
- mycmp=vercmp(pkg1[1],pkg2[1])
- if mycmp>0:
- return 1
- if mycmp<0:
- return -1
- r1=int(pkg1[2][1:])
- r2=int(pkg2[2][1:])
- if r1>r2:
- return 1
- if r2>r1:
- return -1
- return 0
-
def dep_parenreduce(mysplit,mypos=0):
"Accepts a list of strings, and converts '(' and ')' surrounded items to sub-lists"
while (mypos<len(mysplit)):
diff --git a/pym/portage_versions.py b/pym/portage_versions.py
new file mode 100644
index 000000000..ddb35a38c
--- /dev/null
+++ b/pym/portage_versions.py
@@ -0,0 +1,222 @@
+import re,string
+
+ver_regexp = re.compile("^(cvs\\.)?(\\d+)((\\.\\d+)*)([a-z]?)((_(pre|p|beta|alpha|rc)\\d*)*)(-r(\\d+))?$")
+suffix_regexp = re.compile("^(alpha|beta|rc|pre|p)(\\d*)$")
+suffix_value = {"pre": -2, "p": 0, "alpha": -4, "beta": -3, "rc": -1}
+
+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):
+ 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 = [string.atoi(match1.group(2))]
+ list2 = [string.atoi(match2.group(2))]
+
+ # this part would greatly benefit from a fixed-length version pattern
+ if len(match1.group(3)) or len(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))):
+ if len(vlist1) <= i or len(vlist1[i]) == 0:
+ list1.append(0)
+ list2.append(string.atoi(vlist2[i]))
+ elif len(vlist2) <= i or len(vlist2[i]) == 0:
+ list1.append(string.atoi(vlist1[i]))
+ list2.append(0)
+ # 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(string.atoi(vlist1[i]))
+ list2.append(string.atoi(vlist2[i]))
+ # now we have to use floats so 1.02 compares correctly against 1.1
+ else:
+ list1.append(string.atof("0."+vlist1[i]))
+ list2.append(string.atof("0."+vlist2[i]))
+
+ # and now the final letter
+ 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]:
+ vercmp_cache[mykey] = list1[i] - list2[i]
+ return list1[i] - list2[i]
+
+ # 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))):
+ if len(list1) <= i:
+ s1 = ("p","0")
+ else:
+ s1 = suffix_regexp.match(list1[i]).groups()
+ if len(list2) <= i:
+ s2 = ("p","0")
+ else:
+ s2 = suffix_regexp.match(list2[i]).groups()
+ if s1[0] != s2[0]:
+ return suffix_value[s1[0]] - suffix_value[s2[0]]
+ if s1[1] != s2[1]:
+ # it's possible that the s(1|2)[1] == ''
+ # in such a case, fudge it.
+ try: r1 = string.atoi(s1[1])
+ except ValueError: r1 = 0
+ try: r2 = string.atoi(s2[1])
+ except ValueError: r2 = 0
+ return r1 - r2
+
+ # the suffix part is equal to, so finally check the revision
+ if match1.group(10):
+ r1 = string.atoi(match1.group(10))
+ else:
+ r1 = 0
+ if match2.group(10):
+ r2 = string.atoi(match2.group(10))
+ else:
+ r2 = 0
+ vercmp_cache[mykey] = r1 - r2
+ return r1 - r2
+
+def pkgcmp(pkg1, pkg2):
+ if pkg1[0] != pkg2[0]:
+ return None
+ mycmp=vercmp(pkg1[1],pkg2[1])
+ if mycmp>0:
+ return 1
+ if mycmp<0:
+ return -1
+ r1=string.atof(pkg1[2][1:])
+ r2=string.atof(pkg2[2][1:])
+ if r1>r2:
+ return 1
+ if r2>r1:
+ return -1
+ return 0
+
+
+pkgcache={}
+
+def pkgsplit(mypkg,silent=1):
+ try:
+ if not pkgcache[mypkg]:
+ return None
+ return pkgcache[mypkg][:]
+ except KeyError:
+ pass
+ myparts=string.split(mypkg,'-')
+
+ if len(myparts)<2:
+ if not silent:
+ print "!!! Name error in",mypkg+": missing a version or name part."
+ pkgcache[mypkg]=None
+ return None
+ for x in myparts:
+ if len(x)==0:
+ if not silent:
+ print "!!! Name error in",mypkg+": empty \"-\" part."
+ pkgcache[mypkg]=None
+ return None
+
+ #verify rev
+ revok=0
+ myrev=myparts[-1]
+ if len(myrev) and myrev[0]=="r":
+ try:
+ string.atoi(myrev[1:])
+ revok=1
+ except:
+ pass
+ if revok:
+ verPos = -2
+ revision = myparts[-1]
+ else:
+ verPos = -1
+ revision = "r0"
+
+ if ververify(myparts[verPos]):
+ if len(myparts)== (-1*verPos):
+ pkgcache[mypkg]=None
+ return None
+ else:
+ for x in myparts[:verPos]:
+ if ververify(x):
+ pkgcache[mypkg]=None
+ return None
+ #names can't have versiony looking parts
+ myval=[string.join(myparts[:verPos],"-"),myparts[verPos],revision]
+ pkgcache[mypkg]=myval
+ return myval
+ else:
+ pkgcache[mypkg]=None
+ return None
+
+catcache={}
+def catpkgsplit(mydata,silent=1):
+ "returns [cat, pkgname, version, rev ]"
+ try:
+ if not catcache[mydata]:
+ return None
+ return catcache[mydata][:]
+ except KeyError:
+ pass
+ mysplit=mydata.split("/")
+ p_split=None
+ if len(mysplit)==1:
+ retval=["null"]
+ p_split=pkgsplit(mydata,silent=silent)
+ elif len(mysplit)==2:
+ retval=[mysplit[0]]
+ p_split=pkgsplit(mysplit[1],silent=silent)
+ if not p_split:
+ catcache[mydata]=None
+ return None
+ retval.extend(p_split)
+ catcache[mydata]=retval
+ return retval
+
+def catsplit(mydep):
+ return mydep.split("/", 1)
+