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
73 # Check for the Python version. A lot of stuff needs Python 2.3 or later
75 if sys.version_info[:3] < (2, 3, 0):
76 print "BitBake needs Python 2.3 or later. Please upgrade."
79 #projectdir = os.path.dirname(os.path.dirname(os.path.abspath(sys.argv[0])))
80 projectdir = os.getcwd()
83 class VarExpandError(Exception):
86 class MalformedUrl(Exception):
87 """Exception raised when encountering an invalid url"""
90 #######################################################################
91 #######################################################################
95 # PURPOSE: little functions to make yourself known
97 #######################################################################
98 #######################################################################
103 def debug(lvl, *args):
104 if 'BBDEBUG' in env and (env['BBDEBUG'] >= str(lvl)):
105 print debug_prepend + 'DEBUG:', ''.join(args)
108 print debug_prepend + 'NOTE:', ''.join(args)
111 print debug_prepend + 'ERROR:', ''.join(args)
114 print debug_prepend + 'ERROR:', ''.join(args)
118 #######################################################################
119 #######################################################################
123 # PURPOSE: Basic file and directory tree related functions
125 #######################################################################
126 #######################################################################
129 """Create a directory like 'mkdir -p', but does not complain if
130 directory already exists like os.makedirs
133 debug(3, "mkdirhier(%s)" % dir)
136 debug(2, "created " + dir)
138 if e.errno != 17: raise e
141 #######################################################################
145 def movefile(src,dest,newmtime=None,sstat=None):
146 """Moves a file from src to dest, preserving all permissions and
147 attributes; mtime will be preserved even when moving across
148 filesystems. Returns true on success and false on failure. Move is
152 #print "movefile("+src+","+dest+","+str(newmtime)+","+str(sstat)+")"
157 print "!!! Stating source file failed... movefile()"
165 dstat=os.lstat(os.path.dirname(dest))
169 if stat.S_ISLNK(dstat[stat.ST_MODE]):
176 if stat.S_ISLNK(sstat[stat.ST_MODE]):
178 target=os.readlink(src)
179 if destexists and not stat.S_ISDIR(dstat[stat.ST_MODE]):
181 os.symlink(target,dest)
182 # os.lchown(dest,sstat[stat.ST_UID],sstat[stat.ST_GID])
184 return os.lstat(dest)
186 print "!!! failed to properly create symlink:"
187 print "!!!",dest,"->",target
192 if sstat[stat.ST_DEV]==dstat[stat.ST_DEV]:
194 ret=os.rename(src,dest)
198 if e[0]!=errno.EXDEV:
200 print "!!! Failed to move",src,"to",dest
203 # Invalid cross-device-link 'bind' mounted or actually Cross-Device
207 if stat.S_ISREG(sstat[stat.ST_MODE]):
208 try: # For safety copy then move it over.
209 shutil.copyfile(src,dest+"#new")
210 os.rename(dest+"#new",dest)
213 print '!!! copy',src,'->',dest,'failed.'
217 #we don't yet handle special, so we need to fall back to /bin/mv
218 a=getstatusoutput("/bin/mv -f "+"'"+src+"' '"+dest+"'")
220 print "!!! Failed to move special file:"
221 print "!!! '"+src+"' to '"+dest+"'"
223 return None # failure
226 missingos.lchown(dest,sstat[stat.ST_UID],sstat[stat.ST_GID])
227 os.chmod(dest, stat.S_IMODE(sstat[stat.ST_MODE])) # Sticky is reset on chown
230 print "!!! Failed to chown/chmod/unlink in movefile()"
236 os.utime(dest,(newmtime,newmtime))
238 os.utime(dest, (sstat[stat.ST_ATIME], sstat[stat.ST_MTIME]))
239 newmtime=sstat[stat.ST_MTIME]
244 #######################################################################
245 #######################################################################
249 # PURPOSE: Download via HTTP, FTP, CVS, BITKEEPER, handling of MD5-signatures
252 #######################################################################
253 #######################################################################
256 """Decodes an URL into the tokens (scheme, network location, path,
257 user, password, parameters).
259 >>> decodeurl("http://www.google.com/index.html")
260 ('http', 'www.google.com', '/index.html', '', '', {})
262 CVS url with username, host and cvsroot. The cvs module to check out is in the
265 >>> decodeurl("cvs://anoncvs@cvs.handhelds.org/cvs;module=familiar/dist/ipkg")
266 ('cvs', 'cvs.handhelds.org', '/cvs', 'anoncvs', '', {'module': 'familiar/dist/ipkg'})
268 Dito, but this time the username has a password part. And we also request a special tag
271 >>> decodeurl("cvs://anoncvs:anonymous@cvs.handhelds.org/cvs;module=familiar/dist/ipkg;tag=V0-99-81")
272 ('cvs', 'cvs.handhelds.org', '/cvs', 'anoncvs', 'anonymous', {'tag': 'V0-99-81', 'module': 'familiar/dist/ipkg'})
275 m = re.compile('(?P<type>[^:]*)://((?P<user>.+)@)?(?P<location>[^;]+)(;(?P<parm>.*))?').match(url)
277 raise MalformedUrl(url)
279 type = m.group('type')
280 location = m.group('location')
282 raise MalformedUrl(url)
283 user = m.group('user')
284 parm = m.group('parm')
285 m = re.compile('(?P<host>[^/;]+)(?P<path>/[^;]+)').match(location)
287 host = m.group('host')
288 path = m.group('path')
293 m = re.compile('(?P<user>[^:]+)(:?(?P<pswd>.*))').match(user)
295 user = m.group('user')
296 pswd = m.group('pswd')
300 #note("decodeurl: %s decoded to:" % url)
301 #note("decodeurl: type = '%s'" % type)
302 #note("decodeurl: host = '%s'" % host)
303 #note("decodeurl: path = '%s'" % path)
304 #note("decodeurl: parm = '%s'" % parm)
305 #note("decodeurl: user = '%s'" % user)
306 #note("decodeurl: pswd = '%s'" % pswd)
309 for s in parm.split(';'):
313 return (type, host, path, user, pswd, p)
315 #######################################################################
317 def encodeurl(decoded):
318 """Encodes a URL from tokens (scheme, network location, path,
319 user, password, parameters).
321 >>> encodeurl(['http', 'www.google.com', '/index.html', '', '', {}])
323 "http://www.google.com/index.html"
325 CVS with username, host and cvsroot. The cvs module to check out is in the
328 >>> encodeurl(['cvs', 'cvs.handhelds.org', '/cvs', 'anoncvs', '', {'module': 'familiar/dist/ipkg'}])
330 "cvs://anoncvs@cvs.handhelds.org/cvs;module=familiar/dist/ipkg"
332 Dito, but this time the username has a password part. And we also request a special tag
335 >>> encodeurl(['cvs', 'cvs.handhelds.org', '/cvs', 'anoncvs', 'anonymous', {'tag': 'V0-99-81', 'module': 'familiar/dist/ipkg'}])
337 "cvs://anoncvs:anonymous@cvs.handhelds.org/cvs;module=familiar/dist/ipkg;tag=V0-99-81"
340 (type, host, path, user, pswd, p) = decoded
342 if not type or not path:
343 fatal("invalid or missing parameters for url encoding")
354 for parm in p.keys():
355 url += ";%s=%s" % (parm, p[parm])
359 #######################################################################
361 def which(path, item, direction = 0):
362 """Useful function for locating a file in a PATH"""
364 for p in (path or "").split(':'):
365 if os.path.exists(os.path.join(p, item)):
366 found = os.path.join(p, item)
371 #######################################################################
376 #######################################################################
377 #######################################################################
379 # SECTION: Dependency
381 # PURPOSE: Compare build & run dependencies
383 #######################################################################
384 #######################################################################
386 def tokenize(mystring):
387 """Breaks a string like 'foo? (bar) oni? (blah (blah))' into (possibly embedded) lists:
393 >>> tokenize("(x y)")
395 >>> tokenize("(x y) b c")
396 [['x', 'y'], 'b', 'c']
397 >>> tokenize("foo? (bar) oni? (blah (blah))")
398 ['foo?', ['bar'], 'oni?', ['blah', ['blah']]]
399 >>> tokenize("sys-apps/linux-headers nls? (sys-devel/gettext)")
400 ['sys-apps/linux-headers', 'nls?', ['sys-devel/gettext']]
411 curlist.append(accum)
413 prevlists.append(curlist)
418 curlist.append(accum)
421 print "!!! tokenizer: Unmatched left parenthesis in:\n'"+mystring+"'"
424 curlist=prevlists.pop()
425 curlist.append(newlist)
427 elif x in whitespace:
429 curlist.append(accum)
434 curlist.append(accum)
436 print "!!! tokenizer: Exiting with unterminated parenthesis in:\n'"+mystring+"'"
441 #######################################################################
443 def evaluate(tokens,mydefines,allon=0):
444 """Removes tokens based on whether conditional definitions exist or not.
447 >>> evaluate(['sys-apps/linux-headers', 'nls?', ['sys-devel/gettext']], {})
448 ['sys-apps/linux-headers']
452 >>> evaluate(['sys-apps/linux-headers', '!nls?', ['sys-devel/gettext']], {})
453 ['sys-apps/linux-headers', ['sys-devel/gettext']]
457 >>> evaluate(['sys-apps/linux-headers', 'nls?', ['sys-devel/gettext']], {"nls":1})
458 ['sys-apps/linux-headers', ['sys-devel/gettext']]
462 >>> evaluate(['sys-apps/linux-headers', 'nls?', ['sys-devel/gettext']], {}, True)
463 ['sys-apps/linux-headers', ['sys-devel/gettext']]
468 mytokens = tokens + [] # this copies the list
470 while pos < len(mytokens):
471 if type(mytokens[pos]) == types.ListType:
472 evaluate(mytokens[pos], mydefines)
473 if not len(mytokens[pos]):
476 elif mytokens[pos][-1] == "?":
477 cur = mytokens[pos][:-1]
484 if (cur[1:] in mydefines) and (pos < len(mytokens)):
487 elif (cur not in mydefines) and (pos < len(mytokens)):
494 #######################################################################
496 def flatten(mytokens):
497 """Converts nested arrays into a flat arrays:
499 >>> flatten([1,[2,3]])
501 >>> flatten(['sys-apps/linux-headers', ['sys-devel/gettext']])
502 ['sys-apps/linux-headers', 'sys-devel/gettext']
507 if type(x)==types.ListType:
508 newlist.extend(flatten(x))
514 #######################################################################
516 _package_weights_ = {"pre":-2,"p":0,"alpha":-4,"beta":-3,"rc":-1} # dicts are unordered
517 _package_ends_ = ["pre", "p", "alpha", "beta", "rc", "cvs", "bk", "HEAD" ] # so we need ordered list
520 """Parses the last elements of a version number into a triplet, that can
523 >>> relparse('1.2_pre3')
534 mynewver = myver.split('_')
536 # an _package_weights_
537 number = float(mynewver[0])
539 for x in _package_ends_:
541 if mynewver[1][:elen] == x:
543 p1 = _package_weights_[x]
545 p2 = float(mynewver[1][elen:])
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)
559 # normal number or number with letter at end
560 divider = len(myver)-1
561 if myver[divider:] not in "1234567890":
563 p1 = ord(myver[divider:])
564 number = float(myver[0:divider])
566 number = float(myver)
567 return [number,p1,p2]
570 #######################################################################
572 __ververify_cache__ = {}
574 def ververify(myorigval,silent=1):
575 """Returns 1 if given a valid version string, els 0. Valid versions are in the format
577 <v1>.<v2>...<vx>[a-z,_{_package_weights_}[vy]]
579 >>> ververify('2.4.20')
581 >>> ververify('2.4..20') # two dots
583 >>> ververify('2.x.20') # 'x' is not numeric
585 >>> ververify('2.4.20a')
587 >>> ververify('2.4.20cvs') # only one trailing letter
591 >>> ververify('test_a') # no version at all
593 >>> ververify('2.4.20_beta1')
595 >>> ververify('2.4.20_beta')
597 >>> ververify('2.4.20_wrongext') # _wrongext is no valid trailer
601 # Lookup the cache first
603 return __ververify_cache__[myorigval]
607 if len(myorigval) == 0:
609 error("package version is empty")
610 __ververify_cache__[myorigval] = 0
612 myval = myorigval.split('.')
615 error("package name has empty version string")
616 __ververify_cache__[myorigval] = 0
618 # all but the last version must be a numeric
622 error("package version has two points in a row")
623 __ververify_cache__[myorigval] = 0
629 error("package version contains non-numeric '"+x+"'")
630 __ververify_cache__[myorigval] = 0
632 if not len(myval[-1]):
634 error("package version has trailing dot")
635 __ververify_cache__[myorigval] = 0
639 __ververify_cache__[myorigval] = 1
644 # ok, our last component is not a plain number or blank, let's continue
645 if myval[-1][-1] in lowercase:
647 foo = int(myval[-1][:-1])
649 __ververify_cache__[myorigval] = 1
653 # ok, maybe we have a 1_alpha or 1_beta2; let's see
654 ep=string.split(myval[-1],"_")
657 error("package version has more than one letter at then end")
658 __ververify_cache__[myorigval] = 0
661 foo = string.atoi(ep[0])
663 # this needs to be numeric, i.e. the "1" in "1_alpha"
665 error("package version must have numeric part before the '_'")
666 __ververify_cache__[myorigval] = 0
669 for mye in _package_ends_:
670 if ep[1][0:len(mye)] == mye:
671 if len(mye) == len(ep[1]):
672 # no trailing numeric is ok
673 __ververify_cache__[myorigval] = 1
677 foo = string.atoi(ep[1][len(mye):])
678 __ververify_cache__[myorigval] = 1
681 # if no _package_weights_ work, *then* we return 0
684 error("package version extension after '_' is invalid")
685 __ververify_cache__[myorigval] = 0
689 def isjustname(mypkg):
690 myparts = string.split(mypkg,'-')
697 _isspecific_cache_={}
699 def isspecific(mypkg):
700 "now supports packages with no category"
702 return __isspecific_cache__[mypkg]
706 mysplit = string.split(mypkg,"/")
707 if not isjustname(mysplit[-1]):
708 __isspecific_cache__[mypkg] = 1
710 __isspecific_cache__[mypkg] = 0
714 #######################################################################
716 __pkgsplit_cache__={}
718 def pkgsplit(mypkg, silent=1):
720 """This function can be used as a package verification function. If
721 it is a valid name, pkgsplit will return a list containing:
722 [pkgname, pkgversion(norev), pkgrev ].
728 >>> pkgsplit('glibc-1.2-8.9-r7')
729 >>> pkgsplit('glibc-2.2.5-r7')
730 ['glibc', '2.2.5', 'r7']
731 >>> pkgsplit('foo-1.2-1')
732 >>> pkgsplit('Mesa-3.0')
733 ['Mesa', '3.0', 'r0']
737 return __pkgsplit_cache__[mypkg]
741 myparts = string.split(mypkg,'-')
744 error("package name without name or version part")
745 __pkgsplit_cache__[mypkg] = None
750 error("package name with empty name or version part")
751 __pkgsplit_cache__[mypkg] = None
757 if len(myrev) and myrev[0] == "r":
759 string.atoi(myrev[1:])
764 if ververify(myparts[-2]):
765 if len(myparts) == 2:
766 __pkgsplit_cache__[mypkg] = None
769 for x in myparts[:-2]:
771 __pkgsplit_cache__[mypkg]=None
773 # names can't have versiony looking parts
774 myval=[string.join(myparts[:-2],"-"),myparts[-2],myparts[-1]]
775 __pkgsplit_cache__[mypkg]=myval
778 __pkgsplit_cache__[mypkg] = None
781 elif ververify(myparts[-1],silent):
784 print "!!! Name error in",mypkg+": missing name part."
785 __pkgsplit_cache__[mypkg]=None
788 for x in myparts[:-1]:
790 if not silent: error("package name has multiple version parts")
791 __pkgsplit_cache__[mypkg] = None
793 myval = [string.join(myparts[:-1],"-"), myparts[-1],"r0"]
794 __pkgsplit_cache__[mypkg] = myval
797 __pkgsplit_cache__[mypkg] = None
801 #######################################################################
803 __catpkgsplit_cache__ = {}
805 def catpkgsplit(mydata,silent=1):
806 """returns [cat, pkgname, version, rev ]
808 >>> catpkgsplit('sys-libs/glibc-1.2-r7')
809 ['sys-libs', 'glibc', '1.2', 'r7']
810 >>> catpkgsplit('glibc-1.2-r7')
811 ['null', 'glibc', '1.2', 'r7']
815 return __catpkgsplit_cache__[mydata]
819 cat = os.path.basename(os.path.dirname(mydata))
820 mydata = os.path.join(cat, os.path.basename(mydata))
821 # if mydata[:len(projectdir)] == projectdir:
822 # mydata = mydata[len(projectdir)+1:]
823 if mydata[-3:] == '.bb':
826 mysplit = mydata.split("/")
828 splitlen = len(mysplit)
831 p_split = pkgsplit(mydata,silent)
833 retval = [mysplit[splitlen - 2]]
834 p_split = pkgsplit(mysplit[splitlen - 1],silent)
836 __catpkgsplit_cache__[mydata] = None
838 retval.extend(p_split)
839 __catpkgsplit_cache__[mydata] = retval
843 #######################################################################
845 __vercmp_cache__ = {}
847 def vercmp(val1,val2):
848 """This takes two version strings and returns an integer to tell you whether
849 the versions are the same, val1>val2 or val2>val1.
855 >>> vercmp('1', '1.0')
857 >>> vercmp('1', '1.1')
859 >>> vercmp('1.1', '1_p2')
863 # quick short-circuit
866 valkey = val1+" "+val2
870 return __vercmp_cache__[valkey]
872 return - __vercmp_cache__[val2+" "+val1]
878 # consider 1_p2 vc 1.1
879 # after expansion will become (1_p2,0) vc (1,1)
880 # then 1_p2 is compared with 1 before 0 is compared with 1
881 # to solve the bug we need to convert it to (1,0_p2)
882 # by splitting _prepart part and adding it back _after_expansion
884 val1_prepart = val2_prepart = ''
886 val1, val1_prepart = val1.split('_', 1)
888 val2, val2_prepart = val2.split('_', 1)
891 # FIXME: Is it needed? can val1/2 contain '-'?
893 val1 = string.split(val1,'-')
895 val1[0] = val1[0] +"."+ val1[1]
896 val2 = string.split(val2,'-')
898 val2[0] = val2[0] +"."+ val2[1]
900 val1 = string.split(val1[0],'.')
901 val2 = string.split(val2[0],'.')
903 # add back decimal point so that .03 does not become "3" !
904 for x in range(1,len(val1)):
905 if val1[x][0] == '0' :
906 val1[x] = '.' + val1[x]
907 for x in range(1,len(val2)):
908 if val2[x][0] == '0' :
909 val2[x] = '.' + val2[x]
911 # extend varion numbers
912 if len(val2) < len(val1):
913 val2.extend(["0"]*(len(val1)-len(val2)))
914 elif len(val1) < len(val2):
915 val1.extend(["0"]*(len(val2)-len(val1)))
917 # add back _prepart tails
919 val1[-1] += '_' + val1_prepart
921 val2[-1] += '_' + val2_prepart
922 # The above code will extend version numbers out so they
923 # have the same number of digits.
924 for x in range(0,len(val1)):
925 cmp1 = relparse(val1[x])
926 cmp2 = relparse(val2[x])
928 myret = cmp1[y] - cmp2[y]
930 __vercmp_cache__[valkey] = myret
932 __vercmp_cache__[valkey] = 0
936 #######################################################################
938 def pkgcmp(pkg1,pkg2):
939 """ Compares two packages, which should have been split via
940 pkgsplit(). if the return value val is less than zero, then pkg2 is
941 newer than pkg1, zero if equal and positive if older.
943 >>> pkgcmp(['glibc', '2.2.5', 'r7'], ['glibc', '2.2.5', 'r7'])
945 >>> pkgcmp(['glibc', '2.2.5', 'r4'], ['glibc', '2.2.5', 'r7'])
947 >>> pkgcmp(['glibc', '2.2.5', 'r7'], ['glibc', '2.2.5', 'r2'])
951 mycmp = vercmp(pkg1[1],pkg2[1])
956 r1=string.atoi(pkg1[2][1:])
957 r2=string.atoi(pkg2[2][1:])
965 #######################################################################
967 def dep_parenreduce(mysplit, mypos=0):
968 """Accepts a list of strings, and converts '(' and ')' surrounded items to sub-lists:
970 >>> dep_parenreduce([''])
972 >>> dep_parenreduce(['1', '2', '3'])
974 >>> dep_parenreduce(['1', '(', '2', '3', ')', '4'])
975 ['1', ['2', '3'], '4']
978 while mypos < len(mysplit):
979 if mysplit[mypos] == "(":
982 while mypos < len(mysplit):
983 if mysplit[mypos] == ")":
984 mysplit[firstpos:mypos+1] = [mysplit[firstpos+1:mypos]]
987 elif mysplit[mypos] == "(":
989 mysplit = dep_parenreduce(mysplit,mypos)
995 def dep_opconvert(mysplit, myuse):
996 "Does dependency operator conversion"
1000 while mypos < len(mysplit):
1001 if type(mysplit[mypos]) == types.ListType:
1002 newsplit.append(dep_opconvert(mysplit[mypos],myuse))
1004 elif mysplit[mypos] == ")":
1005 # mismatched paren, error
1007 elif mysplit[mypos]=="||":
1008 if ((mypos+1)>=len(mysplit)) or (type(mysplit[mypos+1])!=types.ListType):
1009 # || must be followed by paren'd list
1012 mynew = dep_opconvert(mysplit[mypos+1],myuse)
1013 except Exception, e:
1014 error("unable to satisfy OR dependancy: " + string.join(mysplit," || "))
1017 newsplit.append(mynew)
1019 elif mysplit[mypos][-1] == "?":
1020 # use clause, i.e "gnome? ( foo bar )"
1021 # this is a quick and dirty hack so that repoman can enable all USE vars:
1022 if (len(myuse) == 1) and (myuse[0] == "*"):
1023 # enable it even if it's ! (for repoman) but kill it if it's
1024 # an arch variable that isn't for this arch. XXX Sparc64?
1025 if (mysplit[mypos][:-1] not in settings.usemask) or \
1026 (mysplit[mypos][:-1]==settings["ARCH"]):
1031 if mysplit[mypos][0] == "!":
1032 myusevar = mysplit[mypos][1:-1]
1033 enabled = not myusevar in myuse
1034 #if myusevar in myuse:
1039 myusevar=mysplit[mypos][:-1]
1040 enabled = myusevar in myuse
1041 #if myusevar in myuse:
1045 if (mypos +2 < len(mysplit)) and (mysplit[mypos+2] == ":"):
1048 # choose the first option
1049 if type(mysplit[mypos+1]) == types.ListType:
1050 newsplit.append(dep_opconvert(mysplit[mypos+1],myuse))
1052 newsplit.append(mysplit[mypos+1])
1054 # choose the alternate option
1055 if type(mysplit[mypos+1]) == types.ListType:
1056 newsplit.append(dep_opconvert(mysplit[mypos+3],myuse))
1058 newsplit.append(mysplit[mypos+3])
1063 if type(mysplit[mypos+1]) == types.ListType:
1064 newsplit.append(dep_opconvert(mysplit[mypos+1],myuse))
1066 newsplit.append(mysplit[mypos+1])
1067 # otherwise, continue
1071 newsplit.append(mysplit[mypos])
1076 """beautiful directed graph object"""
1080 #okeys = keys, in order they were added (to optimize firstzero() ordering)
1082 self.__callback_cache=[]
1086 for key in self.okeys:
1087 str += "%s:\t%s\n" % (key, self.dict[key][1])
1090 def addnode(self,mykey,myparent):
1091 if not mykey in self.dict:
1092 self.okeys.append(mykey)
1094 self.dict[mykey]=[0,[]]
1096 self.dict[mykey]=[0,[myparent]]
1097 self.dict[myparent][0]=self.dict[myparent][0]+1
1099 if myparent and (not myparent in self.dict[mykey][1]):
1100 self.dict[mykey][1].append(myparent)
1101 self.dict[myparent][0]=self.dict[myparent][0]+1
1103 def delnode(self,mykey, ref = 1):
1106 If ref is 1, remove references to this node from other nodes.
1107 If ref is 2, remove nodes that reference this node."""
1108 if not mykey in self.dict:
1110 for x in self.dict[mykey][1]:
1111 self.dict[x][0]=self.dict[x][0]-1
1112 del self.dict[mykey]
1115 self.okeys.remove(mykey)
1120 for k in self.okeys:
1121 if mykey in self.dict[k][1]:
1122 if ref == 1 or ref == 2:
1123 self.dict[k][1].remove(mykey)
1127 self.delnode(l, ref)
1130 "returns all nodes in the dictionary"
1131 return self.dict.keys()
1133 def firstzero(self):
1134 "returns first node with zero references, or NULL if no such node exists"
1135 for x in self.okeys:
1136 if self.dict[x][0]==0:
1140 def firstnonzero(self):
1141 "returns first node with nonzero references, or NULL if no such node exists"
1142 for x in self.okeys:
1143 if self.dict[x][0]!=0:
1149 "returns all nodes with zero references, or NULL if no such node exists"
1151 for x in self.dict.keys():
1152 if self.dict[x][0]==0:
1156 def hasallzeros(self):
1157 "returns 0/1, Are all nodes zeros? 1 : 0"
1159 for x in self.dict.keys():
1160 if self.dict[x][0]!=0:
1165 if len(self.dict)==0:
1169 def hasnode(self,mynode):
1170 return mynode in self.dict
1172 def getparents(self, item):
1173 if not self.hasnode(item):
1175 return self.dict[item][1]
1177 def getchildren(self, item):
1178 if not self.hasnode(item):
1180 children = [i for i in self.okeys if item in self.getparents(i)]
1183 def walkdown(self, item, callback, debug = None, usecache = False):
1184 if not self.hasnode(item):
1188 if self.__callback_cache.count(item):
1190 print "hit cache for item: %s" % item
1193 parents = self.getparents(item)
1194 children = self.getchildren(item)
1197 # print "%s is both parent and child of %s" % (p, item)
1199 self.__callback_cache.append(p)
1200 ret = callback(self, p)
1205 print "eek, i'm my own parent!"
1208 print "item: %s, p: %s" % (item, p)
1209 ret = self.walkdown(p, callback, debug, usecache)
1213 self.__callback_cache.append(item)
1214 return callback(self, item)
1216 def walkup(self, item, callback):
1217 if not self.hasnode(item):
1220 parents = self.getparents(item)
1221 children = self.getchildren(item)
1224 ret = callback(self, item)
1229 print "eek, i'm my own child!"
1231 ret = self.walkup(c, callback)
1234 return callback(self, item)
1238 for x in self.dict.keys():
1239 mygraph.dict[x]=self.dict[x][:]
1240 mygraph.okeys=self.okeys[:]
1243 #######################################################################
1244 #######################################################################
1248 # PURPOSE: Reading and handling of system/target-specific/local configuration
1249 # reading of package configuration
1251 #######################################################################
1252 #######################################################################
1254 def reader(cfgfile, feeder):
1255 """Generic configuration file reader that opens a file, reads the lines,
1256 handles continuation lines, comments, empty lines and feed all read lines
1257 into the function feeder(lineno, line).
1260 f = open(cfgfile,'r')
1267 if not w: continue # skip empty lines
1269 if s[0] == '#': continue # skip comments
1270 while s[-1] == '\\':
1271 s2 = f.readline()[:-1].strip()
1275 if __name__ == "__main__":