2 # ex:ts=4:sw=4:sts=4:et
3 # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
5 OpenEmbedded Build System Python Library
7 Copyright: (c) 2003 by Holger Schurig
9 Part of this code has been shamelessly stolen from Gentoo's portage.py.
10 This source had GPL-2 as license, so the same goes for this file.
12 Please visit http://www.openembedded.org/phpwiki/ for more info.
14 Try "pydoc ./oe.py" to get some nice output.
58 whitespace = '\t\n\x0b\x0c\r '
59 lowercase = 'abcdefghijklmnopqrstuvwxyz'
61 import sys, os, types, re
64 # Check for the Python version. A lot of stuff needs Python 2.3 or later
66 if sys.version_info[:3] < (2, 3, 0):
67 print "OpenEmbedded needs Python 2.3 or later. Please upgrade."
70 #projectdir = os.path.dirname(os.path.dirname(os.path.abspath(sys.argv[0])))
71 projectdir = os.getcwd()
74 class VarExpandError(Exception):
77 class MalformedUrl(Exception):
78 """Exception raised when encountering an invalid url"""
81 #######################################################################
82 #######################################################################
86 # PURPOSE: little functions to make yourself known
88 #######################################################################
89 #######################################################################
94 def debug(lvl, *args):
95 if 'OEDEBUG' in env and (env['OEDEBUG'] >= str(lvl)):
96 print debug_prepend + 'DEBUG:', ''.join(args)
99 print debug_prepend + 'NOTE:', ''.join(args)
102 print debug_prepend + 'ERROR:', ''.join(args)
105 print debug_prepend + 'ERROR:', ''.join(args)
109 #######################################################################
110 #######################################################################
114 # PURPOSE: Basic file and directory tree related functions
116 #######################################################################
117 #######################################################################
120 """Create a directory like 'mkdir -p', but does not complain if
121 directory already exists like os.makedirs
124 debug(3, "mkdirhier(%s)" % dir)
127 debug(2, "created " + dir)
129 if e.errno != 17: raise e
132 #######################################################################
136 def movefile(src,dest,newmtime=None,sstat=None):
137 """Moves a file from src to dest, preserving all permissions and
138 attributes; mtime will be preserved even when moving across
139 filesystems. Returns true on success and false on failure. Move is
143 #print "movefile("+src+","+dest+","+str(newmtime)+","+str(sstat)+")"
148 print "!!! Stating source file failed... movefile()"
156 dstat=os.lstat(os.path.dirname(dest))
160 if stat.S_ISLNK(dstat[stat.ST_MODE]):
167 if stat.S_ISLNK(sstat[stat.ST_MODE]):
169 target=os.readlink(src)
170 if destexists and not stat.S_ISDIR(dstat[stat.ST_MODE]):
172 os.symlink(target,dest)
173 # os.lchown(dest,sstat[stat.ST_UID],sstat[stat.ST_GID])
175 return os.lstat(dest)
177 print "!!! failed to properly create symlink:"
178 print "!!!",dest,"->",target
183 if sstat[stat.ST_DEV]==dstat[stat.ST_DEV]:
185 ret=os.rename(src,dest)
189 if e[0]!=errno.EXDEV:
191 print "!!! Failed to move",src,"to",dest
194 # Invalid cross-device-link 'bind' mounted or actually Cross-Device
198 if stat.S_ISREG(sstat[stat.ST_MODE]):
199 try: # For safety copy then move it over.
200 shutil.copyfile(src,dest+"#new")
201 os.rename(dest+"#new",dest)
204 print '!!! copy',src,'->',dest,'failed.'
208 #we don't yet handle special, so we need to fall back to /bin/mv
209 a=getstatusoutput("/bin/mv -f "+"'"+src+"' '"+dest+"'")
211 print "!!! Failed to move special file:"
212 print "!!! '"+src+"' to '"+dest+"'"
214 return None # failure
217 missingos.lchown(dest,sstat[stat.ST_UID],sstat[stat.ST_GID])
218 os.chmod(dest, stat.S_IMODE(sstat[stat.ST_MODE])) # Sticky is reset on chown
221 print "!!! Failed to chown/chmod/unlink in movefile()"
227 os.utime(dest,(newmtime,newmtime))
229 os.utime(dest, (sstat[stat.ST_ATIME], sstat[stat.ST_MTIME]))
230 newmtime=sstat[stat.ST_MTIME]
235 #######################################################################
236 #######################################################################
240 # PURPOSE: Download via HTTP, FTP, CVS, BITKEEPER, handling of MD5-signatures
243 #######################################################################
244 #######################################################################
247 """Decodes an URL into the tokens (scheme, network location, path,
248 user, password, parameters).
250 >>> decodeurl("http://www.google.com/index.html")
251 ('http', 'www.google.com', '/index.html', '', '', {})
253 CVS url with username, host and cvsroot. The cvs module to check out is in the
256 >>> decodeurl("cvs://anoncvs@cvs.handhelds.org/cvs;module=familiar/dist/ipkg")
257 ('cvs', 'cvs.handhelds.org', '/cvs', 'anoncvs', '', {'module': 'familiar/dist/ipkg'})
259 Dito, but this time the username has a password part. And we also request a special tag
262 >>> decodeurl("cvs://anoncvs:anonymous@cvs.handhelds.org/cvs;module=familiar/dist/ipkg;tag=V0-99-81")
263 ('cvs', 'cvs.handhelds.org', '/cvs', 'anoncvs', 'anonymous', {'tag': 'V0-99-81', 'module': 'familiar/dist/ipkg'})
266 m = re.compile('(?P<type>[^:]*)://((?P<user>.+)@)?(?P<location>[^;]+)(;(?P<parm>.*))?').match(url)
268 raise MalformedUrl(url)
270 type = m.group('type')
271 location = m.group('location')
273 raise MalformedUrl(url)
274 user = m.group('user')
275 parm = m.group('parm')
276 m = re.compile('(?P<host>[^/;]+)(?P<path>/[^;]+)').match(location)
278 host = m.group('host')
279 path = m.group('path')
284 m = re.compile('(?P<user>[^:]+)(:?(?P<pswd>.*))').match(user)
286 user = m.group('user')
287 pswd = m.group('pswd')
291 note("decodeurl: %s decoded to:" % url)
292 note("decodeurl: type = '%s'" % type)
293 note("decodeurl: host = '%s'" % host)
294 note("decodeurl: path = '%s'" % path)
295 note("decodeurl: parm = '%s'" % parm)
296 note("decodeurl: user = '%s'" % user)
297 note("decodeurl: pswd = '%s'" % pswd)
300 for s in parm.split(';'):
304 return (type, host, path, user, pswd, p)
306 #######################################################################
308 def encodeurl(decoded):
309 """Encodes a URL from tokens (scheme, network location, path,
310 user, password, parameters).
312 >>> encodeurl(['http', 'www.google.com', '/index.html', '', '', {}])
314 "http://www.google.com/index.html"
316 CVS with username, host and cvsroot. The cvs module to check out is in the
319 >>> encodeurl(['cvs', 'cvs.handhelds.org', '/cvs', 'anoncvs', '', {'module': 'familiar/dist/ipkg'}])
321 "cvs://anoncvs@cvs.handhelds.org/cvs;module=familiar/dist/ipkg"
323 Dito, but this time the username has a password part. And we also request a special tag
326 >>> encodeurl(['cvs', 'cvs.handhelds.org', '/cvs', 'anoncvs', 'anonymous', {'tag': 'V0-99-81', 'module': 'familiar/dist/ipkg'}])
328 "cvs://anoncvs:anonymous@cvs.handhelds.org/cvs;module=familiar/dist/ipkg;tag=V0-99-81"
331 (type, host, path, user, pswd, p) = decoded
333 if not type or not path:
334 fatal("invalid or missing parameters for url encoding")
345 for parm in p.keys():
346 url += ";%s=%s" % (parm, p[parm])
350 #######################################################################
352 def which(path, item, direction = 0):
353 """Useful function for locating a file in a PATH"""
355 for p in (path or "").split(':'):
356 if os.path.exists(os.path.join(p, item)):
357 found = os.path.join(p, item)
362 #######################################################################
367 #######################################################################
368 #######################################################################
370 # SECTION: Dependency
372 # PURPOSE: Compare build & run dependencies
374 #######################################################################
375 #######################################################################
377 def tokenize(mystring):
378 """Breaks a string like 'foo? (bar) oni? (blah (blah))' into (possibly embedded) lists:
384 >>> tokenize("(x y)")
386 >>> tokenize("(x y) b c")
387 [['x', 'y'], 'b', 'c']
388 >>> tokenize("foo? (bar) oni? (blah (blah))")
389 ['foo?', ['bar'], 'oni?', ['blah', ['blah']]]
390 >>> tokenize("sys-apps/linux-headers nls? (sys-devel/gettext)")
391 ['sys-apps/linux-headers', 'nls?', ['sys-devel/gettext']]
402 curlist.append(accum)
404 prevlists.append(curlist)
409 curlist.append(accum)
412 print "!!! tokenizer: Unmatched left parenthesis in:\n'"+mystring+"'"
415 curlist=prevlists.pop()
416 curlist.append(newlist)
418 elif x in whitespace:
420 curlist.append(accum)
425 curlist.append(accum)
427 print "!!! tokenizer: Exiting with unterminated parenthesis in:\n'"+mystring+"'"
432 #######################################################################
434 def evaluate(tokens,mydefines,allon=0):
435 """Removes tokens based on whether conditional definitions exist or not.
438 >>> evaluate(['sys-apps/linux-headers', 'nls?', ['sys-devel/gettext']], {})
439 ['sys-apps/linux-headers']
443 >>> evaluate(['sys-apps/linux-headers', '!nls?', ['sys-devel/gettext']], {})
444 ['sys-apps/linux-headers', ['sys-devel/gettext']]
448 >>> evaluate(['sys-apps/linux-headers', 'nls?', ['sys-devel/gettext']], {"nls":1})
449 ['sys-apps/linux-headers', ['sys-devel/gettext']]
453 >>> evaluate(['sys-apps/linux-headers', 'nls?', ['sys-devel/gettext']], {}, True)
454 ['sys-apps/linux-headers', ['sys-devel/gettext']]
459 mytokens = tokens + [] # this copies the list
461 while pos < len(mytokens):
462 if type(mytokens[pos]) == types.ListType:
463 evaluate(mytokens[pos], mydefines)
464 if not len(mytokens[pos]):
467 elif mytokens[pos][-1] == "?":
468 cur = mytokens[pos][:-1]
475 if (cur[1:] in mydefines) and (pos < len(mytokens)):
478 elif (cur not in mydefines) and (pos < len(mytokens)):
485 #######################################################################
487 def flatten(mytokens):
488 """Converts nested arrays into a flat arrays:
490 >>> flatten([1,[2,3]])
492 >>> flatten(['sys-apps/linux-headers', ['sys-devel/gettext']])
493 ['sys-apps/linux-headers', 'sys-devel/gettext']
498 if type(x)==types.ListType:
499 newlist.extend(flatten(x))
505 #######################################################################
507 _package_weights_ = {"pre":-2,"p":0,"alpha":-4,"beta":-3,"rc":-1} # dicts are unordered
508 _package_ends_ = ["pre", "p", "alpha", "beta", "rc", "cvs", "bk", "HEAD" ] # so we need ordered list
511 """Parses the last elements of a version number into a triplet, that can
514 >>> relparse('1.2_pre3')
525 mynewver = myver.split('_')
527 # an _package_weights_
528 number = float(mynewver[0])
530 for x in _package_ends_:
532 if mynewver[1][:elen] == x:
534 p1 = _package_weights_[x]
536 p2 = float(mynewver[1][elen:])
541 # normal number or number with letter at end
542 divider = len(myver)-1
543 if myver[divider:] not in "1234567890":
545 p1 = ord(myver[divider:])
546 number = float(myver[0:divider])
548 number = float(myver)
550 # normal number or number with letter at end
551 divider = len(myver)-1
552 if myver[divider:] not in "1234567890":
554 p1 = ord(myver[divider:])
555 number = float(myver[0:divider])
557 number = float(myver)
558 return [number,p1,p2]
561 #######################################################################
563 __ververify_cache__ = {}
565 def ververify(myorigval,silent=1):
566 """Returns 1 if given a valid version string, els 0. Valid versions are in the format
568 <v1>.<v2>...<vx>[a-z,_{_package_weights_}[vy]]
570 >>> ververify('2.4.20')
572 >>> ververify('2.4..20') # two dots
574 >>> ververify('2.x.20') # 'x' is not numeric
576 >>> ververify('2.4.20a')
578 >>> ververify('2.4.20cvs') # only one trailing letter
582 >>> ververify('test_a') # no version at all
584 >>> ververify('2.4.20_beta1')
586 >>> ververify('2.4.20_beta')
588 >>> ververify('2.4.20_wrongext') # _wrongext is no valid trailer
592 # Lookup the cache first
594 return __ververify_cache__[myorigval]
598 if len(myorigval) == 0:
600 error("package version is empty")
601 __ververify_cache__[myorigval] = 0
603 myval = myorigval.split('.')
606 error("package name has empty version string")
607 __ververify_cache__[myorigval] = 0
609 # all but the last version must be a numeric
613 error("package version has two points in a row")
614 __ververify_cache__[myorigval] = 0
620 error("package version contains non-numeric '"+x+"'")
621 __ververify_cache__[myorigval] = 0
623 if not len(myval[-1]):
625 error("package version has trailing dot")
626 __ververify_cache__[myorigval] = 0
630 __ververify_cache__[myorigval] = 1
635 # ok, our last component is not a plain number or blank, let's continue
636 if myval[-1][-1] in lowercase:
638 foo = int(myval[-1][:-1])
640 __ververify_cache__[myorigval] = 1
644 # ok, maybe we have a 1_alpha or 1_beta2; let's see
645 ep=string.split(myval[-1],"_")
648 error("package version has more than one letter at then end")
649 __ververify_cache__[myorigval] = 0
652 foo = string.atoi(ep[0])
654 # this needs to be numeric, i.e. the "1" in "1_alpha"
656 error("package version must have numeric part before the '_'")
657 __ververify_cache__[myorigval] = 0
660 for mye in _package_ends_:
661 if ep[1][0:len(mye)] == mye:
662 if len(mye) == len(ep[1]):
663 # no trailing numeric is ok
664 __ververify_cache__[myorigval] = 1
668 foo = string.atoi(ep[1][len(mye):])
669 __ververify_cache__[myorigval] = 1
672 # if no _package_weights_ work, *then* we return 0
675 error("package version extension after '_' is invalid")
676 __ververify_cache__[myorigval] = 0
680 def isjustname(mypkg):
681 myparts = string.split(mypkg,'-')
688 _isspecific_cache_={}
690 def isspecific(mypkg):
691 "now supports packages with no category"
693 return __isspecific_cache__[mypkg]
697 mysplit = string.split(mypkg,"/")
698 if not isjustname(mysplit[-1]):
699 __isspecific_cache__[mypkg] = 1
701 __isspecific_cache__[mypkg] = 0
705 #######################################################################
707 __pkgsplit_cache__={}
709 def pkgsplit(mypkg, silent=1):
711 """This function can be used as a package verification function. If
712 it is a valid name, pkgsplit will return a list containing:
713 [pkgname, pkgversion(norev), pkgrev ].
719 >>> pkgsplit('glibc-1.2-8.9-r7')
720 >>> pkgsplit('glibc-2.2.5-r7')
721 ['glibc', '2.2.5', 'r7']
722 >>> pkgsplit('foo-1.2-1')
723 >>> pkgsplit('Mesa-3.0')
724 ['Mesa', '3.0', 'r0']
728 return __pkgsplit_cache__[mypkg]
732 myparts = string.split(mypkg,'-')
735 error("package name without name or version part")
736 __pkgsplit_cache__[mypkg] = None
741 error("package name with empty name or version part")
742 __pkgsplit_cache__[mypkg] = None
748 if len(myrev) and myrev[0] == "r":
750 string.atoi(myrev[1:])
755 if ververify(myparts[-2]):
756 if len(myparts) == 2:
757 __pkgsplit_cache__[mypkg] = None
760 for x in myparts[:-2]:
762 __pkgsplit_cache__[mypkg]=None
764 # names can't have versiony looking parts
765 myval=[string.join(myparts[:-2],"-"),myparts[-2],myparts[-1]]
766 __pkgsplit_cache__[mypkg]=myval
769 __pkgsplit_cache__[mypkg] = None
772 elif ververify(myparts[-1],silent):
775 print "!!! Name error in",mypkg+": missing name part."
776 __pkgsplit_cache__[mypkg]=None
779 for x in myparts[:-1]:
781 if not silent: error("package name has multiple version parts")
782 __pkgsplit_cache__[mypkg] = None
784 myval = [string.join(myparts[:-1],"-"), myparts[-1],"r0"]
785 __pkgsplit_cache__[mypkg] = myval
788 __pkgsplit_cache__[mypkg] = None
792 #######################################################################
794 __catpkgsplit_cache__ = {}
796 def catpkgsplit(mydata,silent=1):
797 """returns [cat, pkgname, version, rev ]
799 >>> catpkgsplit('sys-libs/glibc-1.2-r7')
800 ['sys-libs', 'glibc', '1.2', 'r7']
801 >>> catpkgsplit('glibc-1.2-r7')
802 ['null', 'glibc', '1.2', 'r7']
806 return __catpkgsplit_cache__[mydata]
810 cat = os.path.basename(os.path.dirname(mydata))
811 mydata = os.path.join(cat, os.path.basename(mydata))
812 # if mydata[:len(projectdir)] == projectdir:
813 # mydata = mydata[len(projectdir)+1:]
814 if mydata[-3:] == '.oe':
817 mysplit = mydata.split("/")
819 splitlen = len(mysplit)
822 p_split = pkgsplit(mydata,silent)
824 retval = [mysplit[splitlen - 2]]
825 p_split = pkgsplit(mysplit[splitlen - 1],silent)
827 __catpkgsplit_cache__[mydata] = None
829 retval.extend(p_split)
830 __catpkgsplit_cache__[mydata] = retval
834 #######################################################################
836 __vercmp_cache__ = {}
838 def vercmp(val1,val2):
839 """This takes two version strings and returns an integer to tell you whether
840 the versions are the same, val1>val2 or val2>val1.
846 >>> vercmp('1', '1.0')
848 >>> vercmp('1', '1.1')
850 >>> vercmp('1.1', '1_p2')
854 # quick short-circuit
857 valkey = val1+" "+val2
861 return __vercmp_cache__[valkey]
863 return - __vercmp_cache__[val2+" "+val1]
869 # consider 1_p2 vc 1.1
870 # after expansion will become (1_p2,0) vc (1,1)
871 # then 1_p2 is compared with 1 before 0 is compared with 1
872 # to solve the bug we need to convert it to (1,0_p2)
873 # by splitting _prepart part and adding it back _after_expansion
875 val1_prepart = val2_prepart = ''
877 val1, val1_prepart = val1.split('_', 1)
879 val2, val2_prepart = val2.split('_', 1)
882 # FIXME: Is it needed? can val1/2 contain '-'?
884 val1 = string.split(val1,'-')
886 val1[0] = val1[0] +"."+ val1[1]
887 val2 = string.split(val2,'-')
889 val2[0] = val2[0] +"."+ val2[1]
891 val1 = string.split(val1[0],'.')
892 val2 = string.split(val2[0],'.')
894 # add back decimal point so that .03 does not become "3" !
895 for x in range(1,len(val1)):
896 if val1[x][0] == '0' :
897 val1[x] = '.' + val1[x]
898 for x in range(1,len(val2)):
899 if val2[x][0] == '0' :
900 val2[x] = '.' + val2[x]
902 # extend varion numbers
903 if len(val2) < len(val1):
904 val2.extend(["0"]*(len(val1)-len(val2)))
905 elif len(val1) < len(val2):
906 val1.extend(["0"]*(len(val2)-len(val1)))
908 # add back _prepart tails
910 val1[-1] += '_' + val1_prepart
912 val2[-1] += '_' + val2_prepart
913 # The above code will extend version numbers out so they
914 # have the same number of digits.
915 for x in range(0,len(val1)):
916 cmp1 = relparse(val1[x])
917 cmp2 = relparse(val2[x])
919 myret = cmp1[y] - cmp2[y]
921 __vercmp_cache__[valkey] = myret
923 __vercmp_cache__[valkey] = 0
927 #######################################################################
929 def pkgcmp(pkg1,pkg2):
930 """ Compares two packages, which should have been split via
931 pkgsplit(). if the return value val is less than zero, then pkg2 is
932 newer than pkg1, zero if equal and positive if older.
934 >>> pkgcmp(['glibc', '2.2.5', 'r7'], ['glibc', '2.2.5', 'r7'])
936 >>> pkgcmp(['glibc', '2.2.5', 'r4'], ['glibc', '2.2.5', 'r7'])
938 >>> pkgcmp(['glibc', '2.2.5', 'r7'], ['glibc', '2.2.5', 'r2'])
942 mycmp = vercmp(pkg1[1],pkg2[1])
947 r1=string.atoi(pkg1[2][1:])
948 r2=string.atoi(pkg2[2][1:])
956 #######################################################################
958 def dep_parenreduce(mysplit, mypos=0):
959 """Accepts a list of strings, and converts '(' and ')' surrounded items to sub-lists:
961 >>> dep_parenreduce([''])
963 >>> dep_parenreduce(['1', '2', '3'])
965 >>> dep_parenreduce(['1', '(', '2', '3', ')', '4'])
966 ['1', ['2', '3'], '4']
969 while mypos < len(mysplit):
970 if mysplit[mypos] == "(":
973 while mypos < len(mysplit):
974 if mysplit[mypos] == ")":
975 mysplit[firstpos:mypos+1] = [mysplit[firstpos+1:mypos]]
978 elif mysplit[mypos] == "(":
980 mysplit = dep_parenreduce(mysplit,mypos)
986 def dep_opconvert(mysplit, myuse):
987 "Does dependency operator conversion"
991 while mypos < len(mysplit):
992 if type(mysplit[mypos]) == types.ListType:
993 newsplit.append(dep_opconvert(mysplit[mypos],myuse))
995 elif mysplit[mypos] == ")":
996 # mismatched paren, error
998 elif mysplit[mypos]=="||":
999 if ((mypos+1)>=len(mysplit)) or (type(mysplit[mypos+1])!=types.ListType):
1000 # || must be followed by paren'd list
1003 mynew = dep_opconvert(mysplit[mypos+1],myuse)
1004 except Exception, e:
1005 error("unable to satisfy OR dependancy: " + string.join(mysplit," || "))
1008 newsplit.append(mynew)
1010 elif mysplit[mypos][-1] == "?":
1011 # use clause, i.e "gnome? ( foo bar )"
1012 # this is a quick and dirty hack so that repoman can enable all USE vars:
1013 if (len(myuse) == 1) and (myuse[0] == "*"):
1014 # enable it even if it's ! (for repoman) but kill it if it's
1015 # an arch variable that isn't for this arch. XXX Sparc64?
1016 if (mysplit[mypos][:-1] not in settings.usemask) or \
1017 (mysplit[mypos][:-1]==settings["ARCH"]):
1022 if mysplit[mypos][0] == "!":
1023 myusevar = mysplit[mypos][1:-1]
1024 enabled = not myusevar in myuse
1025 #if myusevar in myuse:
1030 myusevar=mysplit[mypos][:-1]
1031 enabled = myusevar in myuse
1032 #if myusevar in myuse:
1036 if (mypos +2 < len(mysplit)) and (mysplit[mypos+2] == ":"):
1039 # choose the first option
1040 if type(mysplit[mypos+1]) == types.ListType:
1041 newsplit.append(dep_opconvert(mysplit[mypos+1],myuse))
1043 newsplit.append(mysplit[mypos+1])
1045 # choose the alternate option
1046 if type(mysplit[mypos+1]) == types.ListType:
1047 newsplit.append(dep_opconvert(mysplit[mypos+3],myuse))
1049 newsplit.append(mysplit[mypos+3])
1054 if type(mysplit[mypos+1]) == types.ListType:
1055 newsplit.append(dep_opconvert(mysplit[mypos+1],myuse))
1057 newsplit.append(mysplit[mypos+1])
1058 # otherwise, continue
1062 newsplit.append(mysplit[mypos])
1067 """beautiful directed graph object"""
1071 #okeys = keys, in order they were added (to optimize firstzero() ordering)
1073 self.__callback_cache=[]
1077 for key in self.okeys:
1078 str += "%s:\t%s\n" % (key, self.dict[key][1])
1081 def addnode(self,mykey,myparent):
1082 if not mykey in self.dict:
1083 self.okeys.append(mykey)
1085 self.dict[mykey]=[0,[]]
1087 self.dict[mykey]=[0,[myparent]]
1088 self.dict[myparent][0]=self.dict[myparent][0]+1
1090 if myparent and (not myparent in self.dict[mykey][1]):
1091 self.dict[mykey][1].append(myparent)
1092 self.dict[myparent][0]=self.dict[myparent][0]+1
1094 def delnode(self,mykey, ref = 1):
1097 If ref is 1, remove references to this node from other nodes.
1098 If ref is 2, remove nodes that reference this node."""
1099 if not mykey in self.dict:
1101 for x in self.dict[mykey][1]:
1102 self.dict[x][0]=self.dict[x][0]-1
1103 del self.dict[mykey]
1106 self.okeys.remove(mykey)
1111 for k in self.okeys:
1112 if mykey in self.dict[k][1]:
1113 if ref == 1 or ref == 2:
1114 self.dict[k][1].remove(mykey)
1118 self.delnode(l, ref)
1121 "returns all nodes in the dictionary"
1122 return self.dict.keys()
1124 def firstzero(self):
1125 "returns first node with zero references, or NULL if no such node exists"
1126 for x in self.okeys:
1127 if self.dict[x][0]==0:
1131 def firstnonzero(self):
1132 "returns first node with nonzero references, or NULL if no such node exists"
1133 for x in self.okeys:
1134 if self.dict[x][0]!=0:
1140 "returns all nodes with zero references, or NULL if no such node exists"
1142 for x in self.dict.keys():
1143 if self.dict[x][0]==0:
1147 def hasallzeros(self):
1148 "returns 0/1, Are all nodes zeros? 1 : 0"
1150 for x in self.dict.keys():
1151 if self.dict[x][0]!=0:
1156 if len(self.dict)==0:
1160 def hasnode(self,mynode):
1161 return mynode in self.dict
1163 def getparents(self, item):
1164 if not self.hasnode(item):
1166 return self.dict[item][1]
1168 def getchildren(self, item):
1169 if not self.hasnode(item):
1171 children = [i for i in self.okeys if item in self.getparents(i)]
1174 def walkdown(self, item, callback, debug = None, usecache = False):
1175 if not self.hasnode(item):
1179 if self.__callback_cache.count(item):
1181 print "hit cache for item: %s" % item
1184 parents = self.getparents(item)
1185 children = self.getchildren(item)
1188 # print "%s is both parent and child of %s" % (p, item)
1190 self.__callback_cache.append(p)
1191 ret = callback(self, p)
1196 print "eek, i'm my own parent!"
1199 print "item: %s, p: %s" % (item, p)
1200 ret = self.walkdown(p, callback, debug, usecache)
1204 self.__callback_cache.append(item)
1205 return callback(self, item)
1207 def walkup(self, item, callback):
1208 if not self.hasnode(item):
1211 parents = self.getparents(item)
1212 children = self.getchildren(item)
1215 ret = callback(self, item)
1220 print "eek, i'm my own child!"
1222 ret = self.walkup(c, callback)
1225 return callback(self, item)
1229 for x in self.dict.keys():
1230 mygraph.dict[x]=self.dict[x][:]
1231 mygraph.okeys=self.okeys[:]
1234 #######################################################################
1235 #######################################################################
1239 # PURPOSE: Reading and handling of system/target-specific/local configuration
1240 # reading of package configuration
1242 #######################################################################
1243 #######################################################################
1245 def reader(cfgfile, feeder):
1246 """Generic configuration file reader that opens a file, reads the lines,
1247 handles continuation lines, comments, empty lines and feed all read lines
1248 into the function feeder(lineno, line).
1251 f = open(cfgfile,'r')
1258 if not w: continue # skip empty lines
1260 if s[0] == '#': continue # skip comments
1261 while s[-1] == '\\':
1262 s2 = f.readline()[:-1].strip()
1266 if __name__ == "__main__":