2 # ex:ts=4:sw=4:sts=4:et
3 # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
5 BitBake Build System Python Library
7 Copyright (C) 2003 Holger Schurig
8 Copyright (C) 2003, 2004 Chris Larson
10 Based on Gentoo's portage.py.
12 This program is free software; you can redistribute it and/or modify it under
13 the terms of the GNU General Public License as published by the Free Software
14 Foundation; either version 2 of the License, or (at your option) any later
17 This program is distributed in the hope that it will be useful, but WITHOUT
18 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
19 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License along with
22 this program; if not, write to the Free Software Foundation, Inc., 59 Temple
23 Place, Suite 330, Boston, MA 02111-1307 USA.
67 whitespace = '\t\n\x0b\x0c\r '
68 lowercase = 'abcdefghijklmnopqrstuvwxyz'
70 import sys, os, types, re, string
72 #projectdir = os.path.dirname(os.path.dirname(os.path.abspath(sys.argv[0])))
73 projectdir = os.getcwd()
77 if "BBDEBUG" in os.environ:
78 level = int(os.environ["BBDEBUG"])
84 class VarExpandError(Exception):
87 class MalformedUrl(Exception):
88 """Exception raised when encountering an invalid url"""
91 #######################################################################
92 #######################################################################
96 # PURPOSE: little functions to make yourself known
98 #######################################################################
99 #######################################################################
104 def debug(lvl, *args):
105 if debug_level >= lvl:
106 print debug_prepend + 'DEBUG:', ''.join(args)
109 print debug_prepend + 'NOTE:', ''.join(args)
112 print debug_prepend + 'ERROR:', ''.join(args)
115 print debug_prepend + 'ERROR:', ''.join(args)
119 #######################################################################
120 #######################################################################
124 # PURPOSE: Basic file and directory tree related functions
126 #######################################################################
127 #######################################################################
130 """Create a directory like 'mkdir -p', but does not complain if
131 directory already exists like os.makedirs
134 debug(3, "mkdirhier(%s)" % dir)
137 debug(2, "created " + dir)
139 if e.errno != 17: raise e
142 #######################################################################
146 def movefile(src,dest,newmtime=None,sstat=None):
147 """Moves a file from src to dest, preserving all permissions and
148 attributes; mtime will be preserved even when moving across
149 filesystems. Returns true on success and false on failure. Move is
153 #print "movefile("+src+","+dest+","+str(newmtime)+","+str(sstat)+")"
158 print "!!! Stating source file failed... movefile()"
166 dstat=os.lstat(os.path.dirname(dest))
170 if stat.S_ISLNK(dstat[stat.ST_MODE]):
177 if stat.S_ISLNK(sstat[stat.ST_MODE]):
179 target=os.readlink(src)
180 if destexists and not stat.S_ISDIR(dstat[stat.ST_MODE]):
182 os.symlink(target,dest)
183 # os.lchown(dest,sstat[stat.ST_UID],sstat[stat.ST_GID])
185 return os.lstat(dest)
187 print "!!! failed to properly create symlink:"
188 print "!!!",dest,"->",target
193 if sstat[stat.ST_DEV]==dstat[stat.ST_DEV]:
195 ret=os.rename(src,dest)
199 if e[0]!=errno.EXDEV:
201 print "!!! Failed to move",src,"to",dest
204 # Invalid cross-device-link 'bind' mounted or actually Cross-Device
208 if stat.S_ISREG(sstat[stat.ST_MODE]):
209 try: # For safety copy then move it over.
210 shutil.copyfile(src,dest+"#new")
211 os.rename(dest+"#new",dest)
214 print '!!! copy',src,'->',dest,'failed.'
218 #we don't yet handle special, so we need to fall back to /bin/mv
219 a=getstatusoutput("/bin/mv -f "+"'"+src+"' '"+dest+"'")
221 print "!!! Failed to move special file:"
222 print "!!! '"+src+"' to '"+dest+"'"
224 return None # failure
227 missingos.lchown(dest,sstat[stat.ST_UID],sstat[stat.ST_GID])
228 os.chmod(dest, stat.S_IMODE(sstat[stat.ST_MODE])) # Sticky is reset on chown
231 print "!!! Failed to chown/chmod/unlink in movefile()"
237 os.utime(dest,(newmtime,newmtime))
239 os.utime(dest, (sstat[stat.ST_ATIME], sstat[stat.ST_MTIME]))
240 newmtime=sstat[stat.ST_MTIME]
245 #######################################################################
246 #######################################################################
250 # PURPOSE: Download via HTTP, FTP, CVS, BITKEEPER, handling of MD5-signatures
253 #######################################################################
254 #######################################################################
257 """Decodes an URL into the tokens (scheme, network location, path,
258 user, password, parameters).
260 >>> decodeurl("http://www.google.com/index.html")
261 ('http', 'www.google.com', '/index.html', '', '', {})
263 CVS url with username, host and cvsroot. The cvs module to check out is in the
266 >>> decodeurl("cvs://anoncvs@cvs.handhelds.org/cvs;module=familiar/dist/ipkg")
267 ('cvs', 'cvs.handhelds.org', '/cvs', 'anoncvs', '', {'module': 'familiar/dist/ipkg'})
269 Dito, but this time the username has a password part. And we also request a special tag
272 >>> decodeurl("cvs://anoncvs:anonymous@cvs.handhelds.org/cvs;module=familiar/dist/ipkg;tag=V0-99-81")
273 ('cvs', 'cvs.handhelds.org', '/cvs', 'anoncvs', 'anonymous', {'tag': 'V0-99-81', 'module': 'familiar/dist/ipkg'})
276 m = re.compile('(?P<type>[^:]*)://((?P<user>.+)@)?(?P<location>[^;]+)(;(?P<parm>.*))?').match(url)
278 raise MalformedUrl(url)
280 type = m.group('type')
281 location = m.group('location')
283 raise MalformedUrl(url)
284 user = m.group('user')
285 parm = m.group('parm')
286 m = re.compile('(?P<host>[^/;]+)(?P<path>/[^;]+)').match(location)
288 host = m.group('host')
289 path = m.group('path')
294 m = re.compile('(?P<user>[^:]+)(:?(?P<pswd>.*))').match(user)
296 user = m.group('user')
297 pswd = m.group('pswd')
304 for s in parm.split(';'):
308 return (type, host, path, user, pswd, p)
310 #######################################################################
312 def encodeurl(decoded):
313 """Encodes a URL from tokens (scheme, network location, path,
314 user, password, parameters).
316 >>> encodeurl(['http', 'www.google.com', '/index.html', '', '', {}])
317 'http://www.google.com/index.html'
319 CVS with username, host and cvsroot. The cvs module to check out is in the
322 >>> encodeurl(['cvs', 'cvs.handhelds.org', '/cvs', 'anoncvs', '', {'module': 'familiar/dist/ipkg'}])
323 'cvs://anoncvs@cvs.handhelds.org/cvs;module=familiar/dist/ipkg'
325 Dito, but this time the username has a password part. And we also request a special tag
328 >>> encodeurl(['cvs', 'cvs.handhelds.org', '/cvs', 'anoncvs', 'anonymous', {'tag': 'V0-99-81', 'module': 'familiar/dist/ipkg'}])
329 'cvs://anoncvs:anonymous@cvs.handhelds.org/cvs;tag=V0-99-81;module=familiar/dist/ipkg'
332 (type, host, path, user, pswd, p) = decoded
334 if not type or not path:
335 fatal("invalid or missing parameters for url encoding")
346 for parm in p.keys():
347 url += ";%s=%s" % (parm, p[parm])
351 #######################################################################
353 def which(path, item, direction = 0):
354 """Useful function for locating a file in a PATH"""
356 for p in (path or "").split(':'):
357 if os.path.exists(os.path.join(p, item)):
358 found = os.path.join(p, item)
363 #######################################################################
368 #######################################################################
369 #######################################################################
371 # SECTION: Dependency
373 # PURPOSE: Compare build & run dependencies
375 #######################################################################
376 #######################################################################
378 def tokenize(mystring):
379 """Breaks a string like 'foo? (bar) oni? (blah (blah))' into (possibly embedded) lists:
385 >>> tokenize("(x y)")
387 >>> tokenize("(x y) b c")
388 [['x', 'y'], 'b', 'c']
389 >>> tokenize("foo? (bar) oni? (blah (blah))")
390 ['foo?', ['bar'], 'oni?', ['blah', ['blah']]]
391 >>> tokenize("sys-apps/linux-headers nls? (sys-devel/gettext)")
392 ['sys-apps/linux-headers', 'nls?', ['sys-devel/gettext']]
403 curlist.append(accum)
405 prevlists.append(curlist)
410 curlist.append(accum)
413 print "!!! tokenizer: Unmatched left parenthesis in:\n'"+mystring+"'"
416 curlist=prevlists.pop()
417 curlist.append(newlist)
419 elif x in whitespace:
421 curlist.append(accum)
426 curlist.append(accum)
428 print "!!! tokenizer: Exiting with unterminated parenthesis in:\n'"+mystring+"'"
433 #######################################################################
435 def evaluate(tokens,mydefines,allon=0):
436 """Removes tokens based on whether conditional definitions exist or not.
439 >>> evaluate(['sys-apps/linux-headers', 'nls?', ['sys-devel/gettext']], {})
440 ['sys-apps/linux-headers']
444 >>> evaluate(['sys-apps/linux-headers', '!nls?', ['sys-devel/gettext']], {})
445 ['sys-apps/linux-headers', ['sys-devel/gettext']]
449 >>> evaluate(['sys-apps/linux-headers', 'nls?', ['sys-devel/gettext']], {"nls":1})
450 ['sys-apps/linux-headers', ['sys-devel/gettext']]
454 >>> evaluate(['sys-apps/linux-headers', 'nls?', ['sys-devel/gettext']], {}, True)
455 ['sys-apps/linux-headers', ['sys-devel/gettext']]
460 mytokens = tokens + [] # this copies the list
462 while pos < len(mytokens):
463 if type(mytokens[pos]) == types.ListType:
464 evaluate(mytokens[pos], mydefines)
465 if not len(mytokens[pos]):
468 elif mytokens[pos][-1] == "?":
469 cur = mytokens[pos][:-1]
476 if (cur[1:] in mydefines) and (pos < len(mytokens)):
479 elif (cur not in mydefines) and (pos < len(mytokens)):
486 #######################################################################
488 def flatten(mytokens):
489 """Converts nested arrays into a flat arrays:
491 >>> flatten([1,[2,3]])
493 >>> flatten(['sys-apps/linux-headers', ['sys-devel/gettext']])
494 ['sys-apps/linux-headers', 'sys-devel/gettext']
499 if type(x)==types.ListType:
500 newlist.extend(flatten(x))
506 #######################################################################
508 _package_weights_ = {"pre":-2,"p":0,"alpha":-4,"beta":-3,"rc":-1} # dicts are unordered
509 _package_ends_ = ["pre", "p", "alpha", "beta", "rc", "cvs", "bk", "HEAD" ] # so we need ordered list
512 """Parses the last elements of a version number into a triplet, that can
515 >>> relparse('1.2_pre3')
526 mynewver = myver.split('_')
528 # an _package_weights_
529 number = float(mynewver[0])
531 for x in _package_ends_:
533 if mynewver[1][:elen] == x:
535 p1 = _package_weights_[x]
537 p2 = float(mynewver[1][elen:])
542 # normal number or number with letter at end
543 divider = len(myver)-1
544 if myver[divider:] not in "1234567890":
546 p1 = ord(myver[divider:])
547 number = float(myver[0:divider])
549 number = float(myver)
551 # normal number or number with letter at end
552 divider = len(myver)-1
553 if myver[divider:] not in "1234567890":
555 p1 = ord(myver[divider:])
556 number = float(myver[0:divider])
558 number = float(myver)
559 return [number,p1,p2]
562 #######################################################################
564 __ververify_cache__ = {}
566 def ververify(myorigval,silent=1):
567 """Returns 1 if given a valid version string, els 0. Valid versions are in the format
569 <v1>.<v2>...<vx>[a-z,_{_package_weights_}[vy]]
571 >>> ververify('2.4.20')
573 >>> ververify('2.4..20') # two dots
575 >>> ververify('2.x.20') # 'x' is not numeric
577 >>> ververify('2.4.20a')
579 >>> ververify('2.4.20cvs') # only one trailing letter
583 >>> ververify('test_a') # no version at all
585 >>> ververify('2.4.20_beta1')
587 >>> ververify('2.4.20_beta')
589 >>> ververify('2.4.20_wrongext') # _wrongext is no valid trailer
593 # Lookup the cache first
595 return __ververify_cache__[myorigval]
599 if len(myorigval) == 0:
601 error("package version is empty")
602 __ververify_cache__[myorigval] = 0
604 myval = myorigval.split('.')
607 error("package name has empty version string")
608 __ververify_cache__[myorigval] = 0
610 # all but the last version must be a numeric
614 error("package version has two points in a row")
615 __ververify_cache__[myorigval] = 0
621 error("package version contains non-numeric '"+x+"'")
622 __ververify_cache__[myorigval] = 0
624 if not len(myval[-1]):
626 error("package version has trailing dot")
627 __ververify_cache__[myorigval] = 0
631 __ververify_cache__[myorigval] = 1
636 # ok, our last component is not a plain number or blank, let's continue
637 if myval[-1][-1] in lowercase:
639 foo = int(myval[-1][:-1])
641 __ververify_cache__[myorigval] = 1
645 # ok, maybe we have a 1_alpha or 1_beta2; let's see
646 ep=string.split(myval[-1],"_")
649 error("package version has more than one letter at then end")
650 __ververify_cache__[myorigval] = 0
653 foo = string.atoi(ep[0])
655 # this needs to be numeric, i.e. the "1" in "1_alpha"
657 error("package version must have numeric part before the '_'")
658 __ververify_cache__[myorigval] = 0
661 for mye in _package_ends_:
662 if ep[1][0:len(mye)] == mye:
663 if len(mye) == len(ep[1]):
664 # no trailing numeric is ok
665 __ververify_cache__[myorigval] = 1
669 foo = string.atoi(ep[1][len(mye):])
670 __ververify_cache__[myorigval] = 1
673 # if no _package_weights_ work, *then* we return 0
676 error("package version extension after '_' is invalid")
677 __ververify_cache__[myorigval] = 0
681 def isjustname(mypkg):
682 myparts = string.split(mypkg,'-')
689 _isspecific_cache_={}
691 def isspecific(mypkg):
692 "now supports packages with no category"
694 return __isspecific_cache__[mypkg]
698 mysplit = string.split(mypkg,"/")
699 if not isjustname(mysplit[-1]):
700 __isspecific_cache__[mypkg] = 1
702 __isspecific_cache__[mypkg] = 0
706 #######################################################################
708 __pkgsplit_cache__={}
710 def pkgsplit(mypkg, silent=1):
712 """This function can be used as a package verification function. If
713 it is a valid name, pkgsplit will return a list containing:
714 [pkgname, pkgversion(norev), pkgrev ].
720 >>> pkgsplit('glibc-1.2-8.9-r7')
721 >>> pkgsplit('glibc-2.2.5-r7')
722 ['glibc', '2.2.5', 'r7']
723 >>> pkgsplit('foo-1.2-1')
724 >>> pkgsplit('Mesa-3.0')
725 ['Mesa', '3.0', 'r0']
729 return __pkgsplit_cache__[mypkg]
733 myparts = string.split(mypkg,'-')
736 error("package name without name or version part")
737 __pkgsplit_cache__[mypkg] = None
742 error("package name with empty name or version part")
743 __pkgsplit_cache__[mypkg] = None
749 if len(myrev) and myrev[0] == "r":
751 string.atoi(myrev[1:])
756 if ververify(myparts[-2]):
757 if len(myparts) == 2:
758 __pkgsplit_cache__[mypkg] = None
761 for x in myparts[:-2]:
763 __pkgsplit_cache__[mypkg]=None
765 # names can't have versiony looking parts
766 myval=[string.join(myparts[:-2],"-"),myparts[-2],myparts[-1]]
767 __pkgsplit_cache__[mypkg]=myval
770 __pkgsplit_cache__[mypkg] = None
773 elif ververify(myparts[-1],silent):
776 print "!!! Name error in",mypkg+": missing name part."
777 __pkgsplit_cache__[mypkg]=None
780 for x in myparts[:-1]:
782 if not silent: error("package name has multiple version parts")
783 __pkgsplit_cache__[mypkg] = None
785 myval = [string.join(myparts[:-1],"-"), myparts[-1],"r0"]
786 __pkgsplit_cache__[mypkg] = myval
789 __pkgsplit_cache__[mypkg] = None
793 #######################################################################
795 __catpkgsplit_cache__ = {}
797 def catpkgsplit(mydata,silent=1):
798 """returns [cat, pkgname, version, rev ]
800 >>> catpkgsplit('sys-libs/glibc-1.2-r7')
801 ['sys-libs', 'glibc', '1.2', 'r7']
802 >>> catpkgsplit('glibc-1.2-r7')
803 [None, 'glibc', '1.2', 'r7']
807 return __catpkgsplit_cache__[mydata]
811 cat = os.path.basename(os.path.dirname(mydata))
812 mydata = os.path.join(cat, os.path.basename(mydata))
813 if mydata[-3:] == '.bb':
816 mysplit = mydata.split("/")
818 splitlen = len(mysplit)
821 p_split = pkgsplit(mydata,silent)
823 retval = [mysplit[splitlen - 2]]
824 p_split = pkgsplit(mysplit[splitlen - 1],silent)
826 __catpkgsplit_cache__[mydata] = None
828 retval.extend(p_split)
829 __catpkgsplit_cache__[mydata] = retval
833 #######################################################################
835 __vercmp_cache__ = {}
837 def vercmp(val1,val2):
838 """This takes two version strings and returns an integer to tell you whether
839 the versions are the same, val1>val2 or val2>val1.
845 >>> vercmp('1', '1.0')
847 >>> vercmp('1', '1.1')
849 >>> vercmp('1.1', '1_p2')
853 # quick short-circuit
856 valkey = val1+" "+val2
860 return __vercmp_cache__[valkey]
862 return - __vercmp_cache__[val2+" "+val1]
868 # consider 1_p2 vc 1.1
869 # after expansion will become (1_p2,0) vc (1,1)
870 # then 1_p2 is compared with 1 before 0 is compared with 1
871 # to solve the bug we need to convert it to (1,0_p2)
872 # by splitting _prepart part and adding it back _after_expansion
874 val1_prepart = val2_prepart = ''
876 val1, val1_prepart = val1.split('_', 1)
878 val2, val2_prepart = val2.split('_', 1)
881 # FIXME: Is it needed? can val1/2 contain '-'?
883 val1 = string.split(val1,'-')
885 val1[0] = val1[0] +"."+ val1[1]
886 val2 = string.split(val2,'-')
888 val2[0] = val2[0] +"."+ val2[1]
890 val1 = string.split(val1[0],'.')
891 val2 = string.split(val2[0],'.')
893 # add back decimal point so that .03 does not become "3" !
894 for x in range(1,len(val1)):
895 if val1[x][0] == '0' :
896 val1[x] = '.' + val1[x]
897 for x in range(1,len(val2)):
898 if val2[x][0] == '0' :
899 val2[x] = '.' + val2[x]
901 # extend varion numbers
902 if len(val2) < len(val1):
903 val2.extend(["0"]*(len(val1)-len(val2)))
904 elif len(val1) < len(val2):
905 val1.extend(["0"]*(len(val2)-len(val1)))
907 # add back _prepart tails
909 val1[-1] += '_' + val1_prepart
911 val2[-1] += '_' + val2_prepart
912 # The above code will extend version numbers out so they
913 # have the same number of digits.
914 for x in range(0,len(val1)):
915 cmp1 = relparse(val1[x])
916 cmp2 = relparse(val2[x])
918 myret = cmp1[y] - cmp2[y]
920 __vercmp_cache__[valkey] = myret
922 __vercmp_cache__[valkey] = 0
926 #######################################################################
928 def pkgcmp(pkg1,pkg2):
929 """ Compares two packages, which should have been split via
930 pkgsplit(). if the return value val is less than zero, then pkg2 is
931 newer than pkg1, zero if equal and positive if older.
933 >>> pkgcmp(['glibc', '2.2.5', 'r7'], ['glibc', '2.2.5', 'r7'])
935 >>> pkgcmp(['glibc', '2.2.5', 'r4'], ['glibc', '2.2.5', 'r7'])
937 >>> pkgcmp(['glibc', '2.2.5', 'r7'], ['glibc', '2.2.5', 'r2'])
941 mycmp = vercmp(pkg1[1],pkg2[1])
946 r1=string.atoi(pkg1[2][1:])
947 r2=string.atoi(pkg2[2][1:])
955 #######################################################################
957 def dep_parenreduce(mysplit, mypos=0):
958 """Accepts a list of strings, and converts '(' and ')' surrounded items to sub-lists:
960 >>> dep_parenreduce([''])
962 >>> dep_parenreduce(['1', '2', '3'])
964 >>> dep_parenreduce(['1', '(', '2', '3', ')', '4'])
965 ['1', ['2', '3'], '4']
968 while mypos < len(mysplit):
969 if mysplit[mypos] == "(":
972 while mypos < len(mysplit):
973 if mysplit[mypos] == ")":
974 mysplit[firstpos:mypos+1] = [mysplit[firstpos+1:mypos]]
977 elif mysplit[mypos] == "(":
979 mysplit = dep_parenreduce(mysplit,mypos)
985 def dep_opconvert(mysplit, myuse):
986 "Does dependency operator conversion"
990 while mypos < len(mysplit):
991 if type(mysplit[mypos]) == types.ListType:
992 newsplit.append(dep_opconvert(mysplit[mypos],myuse))
994 elif mysplit[mypos] == ")":
995 # mismatched paren, error
997 elif mysplit[mypos]=="||":
998 if ((mypos+1)>=len(mysplit)) or (type(mysplit[mypos+1])!=types.ListType):
999 # || must be followed by paren'd list
1002 mynew = dep_opconvert(mysplit[mypos+1],myuse)
1003 except Exception, e:
1004 error("unable to satisfy OR dependancy: " + string.join(mysplit," || "))
1007 newsplit.append(mynew)
1009 elif mysplit[mypos][-1] == "?":
1010 # use clause, i.e "gnome? ( foo bar )"
1011 # this is a quick and dirty hack so that repoman can enable all USE vars:
1012 if (len(myuse) == 1) and (myuse[0] == "*"):
1013 # enable it even if it's ! (for repoman) but kill it if it's
1014 # an arch variable that isn't for this arch. XXX Sparc64?
1015 if (mysplit[mypos][:-1] not in settings.usemask) or \
1016 (mysplit[mypos][:-1]==settings["ARCH"]):
1021 if mysplit[mypos][0] == "!":
1022 myusevar = mysplit[mypos][1:-1]
1023 enabled = not myusevar in myuse
1024 #if myusevar in myuse:
1029 myusevar=mysplit[mypos][:-1]
1030 enabled = myusevar in myuse
1031 #if myusevar in myuse:
1035 if (mypos +2 < len(mysplit)) and (mysplit[mypos+2] == ":"):
1038 # choose the first option
1039 if type(mysplit[mypos+1]) == types.ListType:
1040 newsplit.append(dep_opconvert(mysplit[mypos+1],myuse))
1042 newsplit.append(mysplit[mypos+1])
1044 # choose the alternate option
1045 if type(mysplit[mypos+1]) == types.ListType:
1046 newsplit.append(dep_opconvert(mysplit[mypos+3],myuse))
1048 newsplit.append(mysplit[mypos+3])
1053 if type(mysplit[mypos+1]) == types.ListType:
1054 newsplit.append(dep_opconvert(mysplit[mypos+1],myuse))
1056 newsplit.append(mysplit[mypos+1])
1057 # otherwise, continue
1061 newsplit.append(mysplit[mypos])
1066 """beautiful directed graph object"""
1070 #okeys = keys, in order they were added (to optimize firstzero() ordering)
1072 self.__callback_cache=[]
1076 for key in self.okeys:
1077 str += "%s:\t%s\n" % (key, self.dict[key][1])
1080 def addnode(self,mykey,myparent):
1081 if not mykey in self.dict:
1082 self.okeys.append(mykey)
1084 self.dict[mykey]=[0,[]]
1086 self.dict[mykey]=[0,[myparent]]
1087 self.dict[myparent][0]=self.dict[myparent][0]+1
1089 if myparent and (not myparent in self.dict[mykey][1]):
1090 self.dict[mykey][1].append(myparent)
1091 self.dict[myparent][0]=self.dict[myparent][0]+1
1093 def delnode(self,mykey, ref = 1):
1096 If ref is 1, remove references to this node from other nodes.
1097 If ref is 2, remove nodes that reference this node."""
1098 if not mykey in self.dict:
1100 for x in self.dict[mykey][1]:
1101 self.dict[x][0]=self.dict[x][0]-1
1102 del self.dict[mykey]
1105 self.okeys.remove(mykey)
1110 for k in self.okeys:
1111 if mykey in self.dict[k][1]:
1112 if ref == 1 or ref == 2:
1113 self.dict[k][1].remove(mykey)
1117 self.delnode(l, ref)
1120 "returns all nodes in the dictionary"
1121 return self.dict.keys()
1123 def firstzero(self):
1124 "returns first node with zero references, or NULL if no such node exists"
1125 for x in self.okeys:
1126 if self.dict[x][0]==0:
1130 def firstnonzero(self):
1131 "returns first node with nonzero references, or NULL if no such node exists"
1132 for x in self.okeys:
1133 if self.dict[x][0]!=0:
1139 "returns all nodes with zero references, or NULL if no such node exists"
1141 for x in self.dict.keys():
1142 if self.dict[x][0]==0:
1146 def hasallzeros(self):
1147 "returns 0/1, Are all nodes zeros? 1 : 0"
1149 for x in self.dict.keys():
1150 if self.dict[x][0]!=0:
1155 if len(self.dict)==0:
1159 def hasnode(self,mynode):
1160 return mynode in self.dict
1162 def getparents(self, item):
1163 if not self.hasnode(item):
1165 return self.dict[item][1]
1167 def getchildren(self, item):
1168 if not self.hasnode(item):
1170 children = [i for i in self.okeys if item in self.getparents(i)]
1173 def walkdown(self, item, callback, debug = None, usecache = False):
1174 if not self.hasnode(item):
1178 if self.__callback_cache.count(item):
1180 print "hit cache for item: %s" % item
1183 parents = self.getparents(item)
1184 children = self.getchildren(item)
1187 # print "%s is both parent and child of %s" % (p, item)
1189 self.__callback_cache.append(p)
1190 ret = callback(self, p)
1195 print "eek, i'm my own parent!"
1198 print "item: %s, p: %s" % (item, p)
1199 ret = self.walkdown(p, callback, debug, usecache)
1203 self.__callback_cache.append(item)
1204 return callback(self, item)
1206 def walkup(self, item, callback):
1207 if not self.hasnode(item):
1210 parents = self.getparents(item)
1211 children = self.getchildren(item)
1214 ret = callback(self, item)
1219 print "eek, i'm my own child!"
1221 ret = self.walkup(c, callback)
1224 return callback(self, item)
1228 for x in self.dict.keys():
1229 mygraph.dict[x]=self.dict[x][:]
1230 mygraph.okeys=self.okeys[:]
1233 #######################################################################
1234 #######################################################################
1238 # PURPOSE: Reading and handling of system/target-specific/local configuration
1239 # reading of package configuration
1241 #######################################################################
1242 #######################################################################
1244 def reader(cfgfile, feeder):
1245 """Generic configuration file reader that opens a file, reads the lines,
1246 handles continuation lines, comments, empty lines and feed all read lines
1247 into the function feeder(lineno, line).
1250 f = open(cfgfile,'r')
1257 if not w: continue # skip empty lines
1259 if s[0] == '#': continue # skip comments
1260 while s[-1] == '\\':
1261 s2 = f.readline()[:-1].strip()
1265 if __name__ == "__main__":