data_smart: Micro optimisation - search for "${" instead of "$"
[vuplus_bitbake] / lib / bb / data_smart.py
index 5bbc09e..d63fdde 100644 (file)
@@ -8,7 +8,7 @@ BitBake build tools.
 
 Copyright (C) 2003, 2004  Chris Larson
 Copyright (C) 2004, 2005  Seb Frankengul
-Copyright (C) 2005        Holger Hans Peter Freyther
+Copyright (C) 2005, 2006  Holger Hans Peter Freyther
 Copyright (C) 2005        Uli Luckas
 Copyright (C) 2005        ROAD GmbH
 
@@ -28,26 +28,30 @@ Place, Suite 330, Boston, MA 02111-1307 USA.
 Based on functions from the base bb module, Copyright 2003 Holger Schurig
 """
 
-import copy, os, re, sys, types
-from   bb import note, debug, fatal
+import copy, os, re, sys, time, types
+import bb
+from bb   import utils, methodpool
+from COW  import COWDictBase
+from sets import Set
+from new  import classobj
 
-try:
-    import cPickle as pickle
-except ImportError:
-    import pickle
-    print "NOTE: Importing cPickle failed. Falling back to a very slow implementation."
 
-
-__setvar_keyword__ = ["_append","_prepend","_delete"]
-__setvar_regexp__ = re.compile('(?P<base>.*?)(?P<keyword>_append|_prepend|_delete)(_(?P<add>.*))?')
+__setvar_keyword__ = ["_append","_prepend"]
+__setvar_regexp__ = re.compile('(?P<base>.*?)(?P<keyword>_append|_prepend)(_(?P<add>.*))?')
 __expand_var_regexp__ = re.compile(r"\${[^{}]+}")
 __expand_python_regexp__ = re.compile(r"\${@.+?}")
 
 
 class DataSmart:
-    def __init__(self):
+    def __init__(self, special = COWDictBase.copy(), seen = COWDictBase.copy() ):
         self.dict = {}
 
+        # cookie monster tribute
+        self._special_values = special
+        self._seen_overrides = seen
+
+        self.expand_cache = {}
+
     def expand(self,s, varname):
         def var_sub(match):
             key = match.group()[2:-1]
@@ -71,59 +75,46 @@ class DataSmart:
         if type(s) is not types.StringType: # sanity check
             return s
 
-        while s.find('$') != -1:
+        if varname and varname in self.expand_cache:
+            return self.expand_cache[varname]
+
+        while s.find('${') != -1:
             olds = s
             try:
                 s = __expand_var_regexp__.sub(var_sub, s)
                 s = __expand_python_regexp__.sub(python_sub, s)
                 if s == olds: break
                 if type(s) is not types.StringType: # sanity check
-                    import bb
-                    bb.error('expansion of %s returned non-string %s' % (olds, s))
+                    bb.msg.error(bb.msg.domain.Data, 'expansion of %s returned non-string %s' % (olds, s))
             except KeyboardInterrupt:
                 raise
             except:
-                note("%s:%s while evaluating:\n%s" % (sys.exc_info()[0], sys.exc_info()[1], s))
+                bb.msg.note(1, bb.msg.domain.Data, "%s:%s while evaluating:\n%s" % (sys.exc_info()[0], sys.exc_info()[1], s))
                 raise
+
+        if varname:
+            self.expand_cache[varname] = s
+
         return s
 
     def initVar(self, var):
+        self.expand_cache = {}
         if not var in self.dict:
             self.dict[var] = {}
 
-    def pickle_prep(self, cfg):
-        if "_data" in self.dict:
-            if self.dict["_data"] == cfg:
-                self.dict["_data"] = "cfg";
-            else: # this is an unknown array for the moment
-                pass
-
-    def unpickle_prep(self, cfg):
-        if "_data" in self.dict:
-            if self.dict["_data"] == "cfg":
-                self.dict["_data"] = cfg;
-
     def _findVar(self,var):
-        _dest = self
+        _dest = self.dict
 
-        while (_dest and var not in _dest.dict):
-            if not "_data" in _dest.dict:
+        while (_dest and var not in _dest):
+            if not "_data" in _dest:
                 _dest = None
                 break
             _dest = _dest["_data"]
 
-        if _dest and var in _dest.dict:
-            return _dest.dict[var]
+        if _dest and var in _dest:
+            return _dest[var]
         return None
 
-    def _copyVar(self,var,name):
-        local_var = self._findVar(var)
-        if local_var:
-            self.dict[name] = copy.copy(local_var)
-        else:
-            debug(1,"Warning, _copyVar %s to %s, %s does not exists" % (var,name,var))
-
-
     def _makeShadowCopy(self, var):
         if var in self.dict:
             return
@@ -136,17 +127,24 @@ class DataSmart:
             self.initVar(var)
 
     def setVar(self,var,value):
+        self.expand_cache = {}
         match  = __setvar_regexp__.match(var)
         if match and match.group("keyword") in __setvar_keyword__:
             base = match.group('base')
             keyword = match.group("keyword")
             override = match.group('add')
             l = self.getVarFlag(base, keyword) or []
-            if override == 'delete':
-                if l.count([value, None]):
-                    del l[l.index([value, None])]
             l.append([value, override])
-            self.setVarFlag(base, match.group("keyword"), l)
+            self.setVarFlag(base, keyword, l)
+
+            # todo make sure keyword is not __doc__ or __module__
+            # pay the cookie monster
+            try:
+                self._special_values[keyword].add( base )
+            except:
+                self._special_values[keyword] = Set()
+                self._special_values[keyword].add( base )
+
             return
 
         if not var in self.dict:
@@ -155,6 +153,13 @@ class DataSmart:
             self.delVarFlag(var, 'matchesenv')
             self.setVarFlag(var, 'export', 1)
 
+        # more cookies for the cookie monster
+        if '_' in var:
+            override = var[var.rfind('_')+1:]
+            if not self._seen_overrides.has_key(override):
+                self._seen_overrides[override] = Set()
+            self._seen_overrides[override].add( var )
+
         # setting var
         self.dict[var]["content"] = value
 
@@ -166,6 +171,7 @@ class DataSmart:
         return value
 
     def delVar(self,var):
+        self.expand_cache = {}
         self.dict[var] = {}
 
     def setVarFlag(self,var,flag,flagvalue):
@@ -235,95 +241,30 @@ class DataSmart:
         Create a copy of self by setting _data to self
         """
         # we really want this to be a DataSmart...
-        data = DataSmart()
-        data.initVar("_data")
-        data.dict["_data"] = self
+        data = DataSmart(seen=self._seen_overrides.copy(), special=self._special_values.copy())
+        data.dict["_data"] = self.dict
 
         return data
 
     # Dictionary Methods
     def keys(self):
         def _keys(d, mykey):
-            if "_data" in d.dict:
-                _keys(d.dict["_data"],mykey)
+            if "_data" in d:
+                _keys(d["_data"],mykey)
 
-            for key in d.dict.keys():
+            for key in d.keys():
                 if key != "_data":
                     mykey[key] = None
         keytab = {}
-        _keys(self,keytab)
+        _keys(self.dict,keytab)
         return keytab.keys()
 
     def __getitem__(self,item):
-        start = self
-        while start:
-            if item in start.dict:
-                return start.dict[item]
-            elif "_data" in start.dict:
-                start = start.dict["_data"]
-            else:
-                start = None
-        return None
+        #print "Warning deprecated"
+        return self.getVar(item, False)
 
     def __setitem__(self,var,data):
-        self._makeShadowCopy(var)
-        self.dict[var] = data
-
-
-class DataSmartPackage(DataSmart):
-    """
-    Persistent Data Storage
-    """
-    def sanitize_filename(bbfile):
-        return bbfile.replace( '/', '_' )
-    sanitize_filename = staticmethod(sanitize_filename)
-
-    def unpickle(self):
-        """
-        Restore the dict from memory
-        """
-        cache_bbfile = self.sanitize_filename(self.bbfile)
-        p = pickle.Unpickler( file("%s/%s"%(self.cache,cache_bbfile),"rb"))
-        self.dict = p.load()
-        funcstr = self.getVar('__functions__', 0)
-        if funcstr:
-            comp = compile(funcstr, "<pickled>", "exec")
-            exec comp in  __builtins__
-
-    def linkDataSet(self,parent):
-        if not parent == None:
-            self.initVar("_data")
-            self.dict["_data"] = parent
+        #print "Warning deprecated"
+        self.setVar(var,data)
 
 
-    def __init__(self,cache,name,clean,parent):
-        """
-        Construct a persistent data instance
-        """
-        #Initialize the dictionary
-        DataSmart.__init__(self)
-
-        self.cache  = cache
-        self.bbfile = name
-
-        # Either unpickle the data or do copy on write
-        if clean:
-            self.linkDataSet(parent)
-        else:
-            self.unpickle()
-
-    def commit(self, mtime):
-        """
-        Save the package to a permanent storage
-        """
-        cache_bbfile = self.sanitize_filename(self.bbfile)
-        p = pickle.Pickler(file("%s/%s" %(self.cache,cache_bbfile), "wb" ), -1 )
-        p.dump( self.dict )
-
-    def mtime(cache,bbfile):
-        cache_bbfile = DataSmartPackage.sanitize_filename(bbfile)
-        try:
-            return os.stat( "%s/%s" % (cache,cache_bbfile) )[8]
-        except OSError:
-            return 0
-    mtime = staticmethod(mtime)