diff options
| author | Richard Purdie <richard@openedhand.com> | 2006-11-16 15:02:15 +0000 |
|---|---|---|
| committer | Richard Purdie <richard@openedhand.com> | 2006-11-16 15:02:15 +0000 |
| commit | 306b7c7a9757ead077363074e7bbac2e5c03e7c5 (patch) | |
| tree | 6935017a9af749c46816881c86258f514384ba1c /bitbake/lib | |
| parent | 65930a38e415ae4a0182e1cea1be838e0ada50ee (diff) | |
| download | openembedded-core-306b7c7a9757ead077363074e7bbac2e5c03e7c5.tar.gz openembedded-core-306b7c7a9757ead077363074e7bbac2e5c03e7c5.tar.bz2 openembedded-core-306b7c7a9757ead077363074e7bbac2e5c03e7c5.zip | |
bitbake: Upgrade from 1.4 -> 1.7.4ish
git-svn-id: https://svn.o-hand.com/repos/poky/trunk@863 311d38ba-8fff-0310-9ca6-ca027cbcb966
Diffstat (limited to 'bitbake/lib')
33 files changed, 3343 insertions, 1186 deletions
diff --git a/bitbake/lib/bb/COW.py b/bitbake/lib/bb/COW.py new file mode 100644 index 0000000000..826d435f98 --- /dev/null +++ b/bitbake/lib/bb/COW.py @@ -0,0 +1,305 @@ +# ex:ts=4:sw=4:sts=4:et +# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- +""" +This is a copy on write dictionary and set which abuses classes to try and be nice and fast. + +Please Note: + Be careful when using mutable types (ie Dict and Lists) - operations involving these are SLOW. + Assign a file to __warn__ to get warnings about slow operations. +""" + +from inspect import getmro + +import copy +import types, sets +types.ImmutableTypes = tuple([ \ + types.BooleanType, \ + types.ComplexType, \ + types.FloatType, \ + types.IntType, \ + types.LongType, \ + types.NoneType, \ + types.TupleType, \ + sets.ImmutableSet] + \ + list(types.StringTypes)) + +MUTABLE = "__mutable__" + +class COWMeta(type): + pass + +class COWDictMeta(COWMeta): + __warn__ = False + __hasmutable__ = False + __marker__ = tuple() + + def __str__(cls): + # FIXME: I have magic numbers! + return "<COWDict Level: %i Current Keys: %i>" % (cls.__count__, len(cls.__dict__) - 3) + __repr__ = __str__ + + def cow(cls): + class C(cls): + __count__ = cls.__count__ + 1 + return C + copy = cow + __call__ = cow + + def __setitem__(cls, key, value): + if not isinstance(value, types.ImmutableTypes): + if not isinstance(value, COWMeta): + cls.__hasmutable__ = True + key += MUTABLE + setattr(cls, key, value) + + def __getmutable__(cls, key, readonly=False): + nkey = key + MUTABLE + try: + return cls.__dict__[nkey] + except KeyError: + pass + + value = getattr(cls, nkey) + if readonly: + return value + + if not cls.__warn__ is False and not isinstance(value, COWMeta): + print >> cls.__warn__, "Warning: Doing a copy because %s is a mutable type." % key + try: + value = value.copy() + except AttributeError, e: + value = copy.copy(value) + setattr(cls, nkey, value) + return value + + __getmarker__ = [] + def __getreadonly__(cls, key, default=__getmarker__): + """\ + Get a value (even if mutable) which you promise not to change. + """ + return cls.__getitem__(key, default, True) + + def __getitem__(cls, key, default=__getmarker__, readonly=False): + try: + try: + value = getattr(cls, key) + except AttributeError: + value = cls.__getmutable__(key, readonly) + + # This is for values which have been deleted + if value is cls.__marker__: + raise AttributeError("key %s does not exist." % key) + + return value + except AttributeError, e: + if not default is cls.__getmarker__: + return default + + raise KeyError(str(e)) + + def __delitem__(cls, key): + cls.__setitem__(key, cls.__marker__) + + def __revertitem__(cls, key): + if not cls.__dict__.has_key(key): + key += MUTABLE + delattr(cls, key) + + def has_key(cls, key): + value = cls.__getreadonly__(key, cls.__marker__) + if value is cls.__marker__: + return False + return True + + def iter(cls, type, readonly=False): + for key in dir(cls): + if key.startswith("__"): + continue + + if key.endswith(MUTABLE): + key = key[:-len(MUTABLE)] + + if type == "keys": + yield key + + try: + if readonly: + value = cls.__getreadonly__(key) + else: + value = cls[key] + except KeyError: + continue + + if type == "values": + yield value + if type == "items": + yield (key, value) + raise StopIteration() + + def iterkeys(cls): + return cls.iter("keys") + def itervalues(cls, readonly=False): + if not cls.__warn__ is False and cls.__hasmutable__ and readonly is False: + print >> cls.__warn__, "Warning: If you arn't going to change any of the values call with True." + return cls.iter("values", readonly) + def iteritems(cls, readonly=False): + if not cls.__warn__ is False and cls.__hasmutable__ and readonly is False: + print >> cls.__warn__, "Warning: If you arn't going to change any of the values call with True." + return cls.iter("items", readonly) + +class COWSetMeta(COWDictMeta): + def __str__(cls): + # FIXME: I have magic numbers! + return "<COWSet Level: %i Current Keys: %i>" % (cls.__count__, len(cls.__dict__) -3) + __repr__ = __str__ + + def cow(cls): + class C(cls): + __count__ = cls.__count__ + 1 + return C + + def add(cls, value): + COWDictMeta.__setitem__(cls, repr(hash(value)), value) + + def remove(cls, value): + COWDictMeta.__delitem__(cls, repr(hash(value))) + + def __in__(cls, value): + return COWDictMeta.has_key(repr(hash(value))) + + def iterkeys(cls): + raise TypeError("sets don't have keys") + + def iteritems(cls): + raise TypeError("sets don't have 'items'") + +# These are the actual classes you use! +class COWDictBase(object): + __metaclass__ = COWDictMeta + __count__ = 0 + +class COWSetBase(object): + __metaclass__ = COWSetMeta + __count__ = 0 + +if __name__ == "__main__": + import sys + COWDictBase.__warn__ = sys.stderr + a = COWDictBase() + print "a", a + + a['a'] = 'a' + a['b'] = 'b' + a['dict'] = {} + + b = a.copy() + print "b", b + b['c'] = 'b' + + print + + print "a", a + for x in a.iteritems(): + print x + print "--" + print "b", b + for x in b.iteritems(): + print x + print + + b['dict']['a'] = 'b' + b['a'] = 'c' + + print "a", a + for x in a.iteritems(): + print x + print "--" + print "b", b + for x in b.iteritems(): + print x + print + + try: + b['dict2'] + except KeyError, e: + print "Okay!" + + a['set'] = COWSetBase() + a['set'].add("o1") + a['set'].add("o1") + a['set'].add("o2") + + print "a", a + for x in a['set'].itervalues(): + print x + print "--" + print "b", b + for x in b['set'].itervalues(): + print x + print + + b['set'].add('o3') + + print "a", a + for x in a['set'].itervalues(): + print x + print "--" + print "b", b + for x in b['set'].itervalues(): + print x + print + + a['set2'] = set() + a['set2'].add("o1") + a['set2'].add("o1") + a['set2'].add("o2") + + print "a", a + for x in a.iteritems(): + print x + print "--" + print "b", b + for x in b.iteritems(readonly=True): + print x + print + + del b['b'] + try: + print b['b'] + except KeyError: + print "Yay! deleted key raises error" + + if b.has_key('b'): + print "Boo!" + else: + print "Yay - has_key with delete works!" + + print "a", a + for x in a.iteritems(): + print x + print "--" + print "b", b + for x in b.iteritems(readonly=True): + print x + print + + b.__revertitem__('b') + + print "a", a + for x in a.iteritems(): + print x + print "--" + print "b", b + for x in b.iteritems(readonly=True): + print x + print + + b.__revertitem__('dict') + print "a", a + for x in a.iteritems(): + print x + print "--" + print "b", b + for x in b.iteritems(readonly=True): + print x + print diff --git a/bitbake/lib/bb/__init__.py b/bitbake/lib/bb/__init__.py index c3e7a16658..61eb5f3db8 100644 --- a/bitbake/lib/bb/__init__.py +++ b/bitbake/lib/bb/__init__.py @@ -23,7 +23,7 @@ this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. """ -__version__ = "1.4.3" +__version__ = "1.7.4" __all__ = [ @@ -63,24 +63,24 @@ __all__ = [ "manifest", "methodpool", "cache", + "runqueue", + "taskdata", + "providers", ] whitespace = '\t\n\x0b\x0c\r ' lowercase = 'abcdefghijklmnopqrstuvwxyz' -import sys, os, types, re, string +import sys, os, types, re, string, bb +from bb import msg #projectdir = os.path.dirname(os.path.dirname(os.path.abspath(sys.argv[0]))) projectdir = os.getcwd() -debug_level = 0 - if "BBDEBUG" in os.environ: level = int(os.environ["BBDEBUG"]) if level: - debug_level = level - else: - debug_level = 0 + bb.msg.set_debug_level(level) class VarExpandError(Exception): pass @@ -99,22 +99,17 @@ class MalformedUrl(Exception): ####################################################################### ####################################################################### -debug_prepend = '' - - def debug(lvl, *args): - if debug_level >= lvl: - print debug_prepend + 'DEBUG:', ''.join(args) + bb.msg.std_debug(lvl, ''.join(args)) def note(*args): - print debug_prepend + 'NOTE:', ''.join(args) + bb.msg.std_note(''.join(args)) def error(*args): - print debug_prepend + 'ERROR:', ''.join(args) + bb.msg.std_error(''.join(args)) def fatal(*args): - print debug_prepend + 'ERROR:', ''.join(args) - sys.exit(1) + bb.msg.std_fatal(''.join(args)) ####################################################################### diff --git a/bitbake/lib/bb/build.py b/bitbake/lib/bb/build.py index 8e169e002a..942bdc1a39 100644 --- a/bitbake/lib/bb/build.py +++ b/bitbake/lib/bb/build.py @@ -25,18 +25,9 @@ You should have received a copy of the GNU General Public License along with Based on functions from the base bb module, Copyright 2003 Holger Schurig """ -from bb import debug, data, fetch, fatal, error, note, event, mkdirhier, utils +from bb import data, fetch, event, mkdirhier, utils import bb, os -# data holds flags and function name for a given task -_task_data = data.init() - -# graph represents task interdependencies -_task_graph = bb.digraph() - -# stack represents execution order, excepting dependencies -_task_stack = [] - # events class FuncFailed(Exception): """Executed function failed""" @@ -76,13 +67,6 @@ class InvalidTask(TaskBase): # functions -def init(data): - global _task_data, _task_graph, _task_stack - _task_data = data.init() - _task_graph = bb.digraph() - _task_stack = [] - - def exec_func(func, d, dirs = None): """Execute an BB 'function'""" @@ -163,7 +147,7 @@ def exec_func_shell(func, d): f = open(runfile, "w") f.write("#!/bin/sh -e\n") - if bb.debug_level > 0: f.write("set -x\n") + if bb.msg.debug_level['default'] > 0: f.write("set -x\n") data.emit_env(f, d) f.write("cd %s\n" % os.getcwd()) @@ -171,18 +155,18 @@ def exec_func_shell(func, d): f.close() os.chmod(runfile, 0775) if not func: - error("Function not specified") + bb.msg.error(bb.msg.domain.Build, "Function not specified") raise FuncFailed() # open logs si = file('/dev/null', 'r') try: - if bb.debug_level > 0: + if bb.msg.debug_level['default'] > 0: so = os.popen("tee \"%s\"" % logfile, "w") else: so = file(logfile, 'w') except OSError, e: - bb.error("opening log file: %s" % e) + bb.msg.error(bb.msg.domain.Build, "opening log file: %s" % e) pass se = so @@ -205,7 +189,10 @@ def exec_func_shell(func, d): else: maybe_fakeroot = '' ret = os.system('%ssh -e %s' % (maybe_fakeroot, runfile)) - os.chdir(prevdir) + try: + os.chdir(prevdir) + except: + pass if not interact: # restore the backups @@ -224,14 +211,14 @@ def exec_func_shell(func, d): os.close(ose[0]) if ret==0: - if bb.debug_level > 0: + if bb.msg.debug_level['default'] > 0: os.remove(runfile) # os.remove(logfile) return else: - error("function %s failed" % func) + bb.msg.error(bb.msg.domain.Build, "function %s failed" % func) if data.getVar("BBINCLUDELOGS", d): - error("log data follows (%s)" % logfile) + bb.msg.error(bb.msg.domain.Build, "log data follows (%s)" % logfile) f = open(logfile, "r") while True: l = f.readline() @@ -241,7 +228,7 @@ def exec_func_shell(func, d): print '| %s' % l f.close() else: - error("see log in %s" % logfile) + bb.msg.error(bb.msg.domain.Build, "see log in %s" % logfile) raise FuncFailed( logfile ) @@ -281,7 +268,7 @@ def exec_task(task, d): return 1 try: - debug(1, "Executing task %s" % item) + bb.msg.debug(1, bb.msg.domain.Build, "Executing task %s" % item) old_overrides = data.getVar('OVERRIDES', d, 0) localdata = data.createCopy(d) data.setVar('OVERRIDES', 'task_%s:%s' % (item, old_overrides), localdata) @@ -292,21 +279,63 @@ def exec_task(task, d): task_cache.append(item) data.setVar('_task_cache', task_cache, d) except FuncFailed, reason: - note( "Task failed: %s" % reason ) + bb.msg.note(1, bb.msg.domain.Build, "Task failed: %s" % reason ) failedevent = TaskFailed(item, d) event.fire(failedevent) raise EventException("Function failed in task: %s" % reason, failedevent) - # execute - task_graph.walkdown(task, execute) + if data.getVarFlag(task, 'dontrundeps', d): + execute(None, task) + else: + task_graph.walkdown(task, execute) # make stamp, or cause event and raise exception if not data.getVarFlag(task, 'nostamp', d): mkstamp(task, d) +def stamp_is_current_cache(dataCache, file_name, task, checkdeps = 1): + """ + Check status of a given task's stamp. + Returns 0 if it is not current and needs updating. + Same as stamp_is_current but works against the dataCache instead of d + """ + task_graph = dataCache.task_queues[file_name] + + if not dataCache.stamp[file_name]: + return 0 + + stampfile = "%s.%s" % (dataCache.stamp[file_name], task) + if not os.access(stampfile, os.F_OK): + return 0 + + if checkdeps == 0: + return 1 + + import stat + tasktime = os.stat(stampfile)[stat.ST_MTIME] + + _deps = [] + def checkStamp(graph, task): + # check for existance + if 'nostamp' in dataCache.task_deps[file_name] and task in dataCache.task_deps[file_name]['nostamp']: + return 1 + + if not stamp_is_current_cache(dataCache, file_name, task, 0): + return 0 + + depfile = "%s.%s" % (dataCache.stamp[file_name], task) + deptime = os.stat(depfile)[stat.ST_MTIME] + if deptime > tasktime: + return 0 + return 1 + + return task_graph.walkdown(task, checkStamp) def stamp_is_current(task, d, checkdeps = 1): - """Check status of a given task's stamp. returns 0 if it is not current and needs updating.""" + """ + Check status of a given task's stamp. + Returns 0 if it is not current and needs updating. + """ task_graph = data.getVar('_task_graph', d) if not task_graph: task_graph = bb.digraph() @@ -360,7 +389,6 @@ def mkstamp(task, d): f = open(stamp, "w") f.close() - def add_task(task, deps, d): task_graph = data.getVar('_task_graph', d) if not task_graph: @@ -374,6 +402,21 @@ def add_task(task, deps, d): # don't assume holding a reference data.setVar('_task_graph', task_graph, d) + task_deps = data.getVar('_task_deps', d) + if not task_deps: + task_deps = {} + def getTask(name): + deptask = data.getVarFlag(task, name, d) + if deptask: + if not name in task_deps: + task_deps[name] = {} + task_deps[name][task] = deptask + getTask('deptask') + getTask('rdeptask') + getTask('recrdeptask') + getTask('nostamp') + + data.setVar('_task_deps', task_deps, d) def remove_task(task, kill, d): """Remove an BB 'task'. @@ -399,6 +442,3 @@ def task_exists(task, d): task_graph = bb.digraph() data.setVar('_task_graph', task_graph, d) return task_graph.hasnode(task) - -def get_task_data(): - return _task_data diff --git a/bitbake/lib/bb/cache.py b/bitbake/lib/bb/cache.py index 921a9f7589..05c42518a7 100644 --- a/bitbake/lib/bb/cache.py +++ b/bitbake/lib/bb/cache.py @@ -33,15 +33,15 @@ Place, Suite 330, Boston, MA 02111-1307 USA. import os, re import bb.data import bb.utils +from sets import Set try: import cPickle as pickle except ImportError: import pickle - print "NOTE: Importing cPickle failed. Falling back to a very slow implementation." + bb.msg.note(1, bb.msg.domain.Cache, "Importing cPickle failed. Falling back to a very slow implementation.") -# __cache_version__ = "123" -__cache_version__ = "124" # changes the __depends structure +__cache_version__ = "125" class Cache: """ @@ -58,14 +58,12 @@ class Cache: if self.cachedir in [None, '']: self.has_cache = False - if cooker.cb is not None: - print "NOTE: Not using a cache. Set CACHE = <directory> to enable." + bb.msg.note(1, bb.msg.domain.Cache, "Not using a cache. Set CACHE = <directory> to enable.") else: self.has_cache = True self.cachefile = os.path.join(self.cachedir,"bb_cache.dat") - if cooker.cb is not None: - print "NOTE: Using cache in '%s'" % self.cachedir + bb.msg.debug(1, bb.msg.domain.Cache, "Using cache in '%s'" % self.cachedir) try: os.stat( self.cachedir ) except OSError: @@ -80,7 +78,7 @@ class Cache: if version_data['BITBAKE_VER'] != bb.__version__: raise ValueError, 'Bitbake Version Mismatch' except (ValueError, KeyError): - bb.note("Invalid cache found, rebuilding...") + bb.msg.note(1, bb.msg.domain.Cache, "Invalid cache found, rebuilding...") self.depends_cache = {} if self.depends_cache: @@ -108,7 +106,7 @@ class Cache: if fn != self.data_fn: # We're trying to access data in the cache which doesn't exist # yet setData hasn't been called to setup the right access. Very bad. - bb.error("Parsing error data_fn %s and fn %s don't match" % (self.data_fn, fn)) + bb.msg.error(bb.msg.domain.Cache, "Parsing error data_fn %s and fn %s don't match" % (self.data_fn, fn)) result = bb.data.getVar(var, self.data, exp) self.depends_cache[fn][var] = result @@ -127,15 +125,15 @@ class Cache: self.getVar("__depends", fn, True) self.depends_cache[fn]["CACHETIMESTAMP"] = bb.parse.cached_mtime(fn) - def loadDataFull(self, fn, cooker): + def loadDataFull(self, fn, cfgData): """ Return a complete set of data for fn. To do this, we need to parse the file. """ - bb_data, skipped = self.load_bbfile(fn, cooker) + bb_data, skipped = self.load_bbfile(fn, cfgData) return bb_data - def loadData(self, fn, cooker): + def loadData(self, fn, cfgData): """ Load a subset of data for fn. If the cached data is valid we do nothing, @@ -148,7 +146,7 @@ class Cache: return True, True return True, False - bb_data, skipped = self.load_bbfile(fn, cooker) + bb_data, skipped = self.load_bbfile(fn, cfgData) self.setData(fn, bb_data) return False, skipped @@ -175,32 +173,36 @@ class Cache: # Check file still exists if self.mtime(fn) == 0: - bb.debug(2, "Cache: %s not longer exists" % fn) + bb.msg.debug(2, bb.msg.domain.Cache, "Cache: %s not longer exists" % fn) self.remove(fn) return False # File isn't in depends_cache if not fn in self.depends_cache: - bb.debug(2, "Cache: %s is not cached" % fn) + bb.msg.debug(2, bb.msg.domain.Cache, "Cache: %s is not cached" % fn) self.remove(fn) return False # Check the file's timestamp if bb.parse.cached_mtime(fn) > self.getVar("CACHETIMESTAMP", fn, True): - bb.debug(2, "Cache: %s changed" % fn) + bb.msg.debug(2, bb.msg.domain.Cache, "Cache: %s changed" % fn) self.remove(fn) return False # Check dependencies are still valid depends = self.getVar("__depends", fn, True) for f,old_mtime in depends: + # Check if file still exists + if self.mtime(f) == 0: + return False + new_mtime = bb.parse.cached_mtime(f) if (new_mtime > old_mtime): - bb.debug(2, "Cache: %s's dependency %s changed" % (fn, f)) + bb.msg.debug(2, bb.msg.domain.Cache, "Cache: %s's dependency %s changed" % (fn, f)) self.remove(fn) return False - bb.debug(2, "Depends Cache: %s is clean" % fn) + bb.msg.debug(2, bb.msg.domain.Cache, "Depends Cache: %s is clean" % fn) if not fn in self.clean: self.clean[fn] = "" @@ -220,7 +222,7 @@ class Cache: Remove a fn from the cache Called from the parser in error cases """ - bb.debug(1, "Removing %s from cache" % fn) + bb.msg.debug(1, bb.msg.domain.Cache, "Removing %s from cache" % fn) if fn in self.depends_cache: del self.depends_cache[fn] if fn in self.clean: @@ -229,7 +231,7 @@ class Cache: def sync(self): """ Save the cache - Called from the parser when complete (or exitting) + Called from the parser when complete (or exiting) """ if not self.has_cache: @@ -243,12 +245,103 @@ class Cache: p.dump([self.depends_cache, version_data]) def mtime(self, cachefile): - try: - return os.stat(cachefile)[8] - except OSError: - return 0 + return bb.parse.cached_mtime_noerror(cachefile) - def load_bbfile( self, bbfile , cooker): + def handle_data(self, file_name, cacheData): + """ + Save data we need into the cache + """ + + pn = self.getVar('PN', file_name, True) + pv = self.getVar('PV', file_name, True) + pr = self.getVar('PR', file_name, True) + dp = int(self.getVar('DEFAULT_PREFERENCE', file_name, True) or "0") + provides = Set([pn] + (self.getVar("PROVIDES", file_name, True) or "").split()) + depends = bb.utils.explode_deps(self.getVar("DEPENDS", file_name, True) or "") + packages = (self.getVar('PACKAGES', file_name, True) or "").split() + packages_dynamic = (self.getVar('PACKAGES_DYNAMIC', file_name, True) or "").split() + rprovides = (self.getVar("RPROVIDES", file_name, True) or "").split() + + cacheData.task_queues[file_name] = self.getVar("_task_graph", file_name, True) + cacheData.task_deps[file_name] = self.getVar("_task_deps", file_name, True) + + # build PackageName to FileName lookup table + if pn not in cacheData.pkg_pn: + cacheData.pkg_pn[pn] = [] + cacheData.pkg_pn[pn].append(file_name) + + cacheData.stamp[file_name] = self.getVar('STAMP', file_name, True) + + # build FileName to PackageName lookup table + cacheData.pkg_fn[file_name] = pn + cacheData.pkg_pvpr[file_name] = (pv,pr) + cacheData.pkg_dp[file_name] = dp + + # Build forward and reverse provider hashes + # Forward: virtual -> [filenames] + # Reverse: PN -> [virtuals] + if pn not in cacheData.pn_provides: + cacheData.pn_provides[pn] = Set() + cacheData.pn_provides[pn] |= provides + + for provide in provides: + if provide not in cacheData.providers: + cacheData.providers[provide] = [] + cacheData.providers[provide].append(file_name) + + cacheData.deps[file_name] = Set() + for dep in depends: + cacheData.all_depends.add(dep) + cacheData.deps[file_name].add(dep) + + # Build reverse hash f |
