diff options
-rw-r--r-- | pym/portage.py | 339 | ||||
-rw-r--r-- | pym/portage_versions.py | 222 |
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) + |