c894b7fd491c3f5a2636432988dee7a450ebf635
[vuplus_bitbake] / lib / bb / data_smart.py
1 # ex:ts=4:sw=4:sts=4:et
2 # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
3 """
4 BitBake Smart Dictionary Implementation
5
6 Functions for interacting with the data structure used by the
7 BitBake build tools.
8
9 Copyright (C) 2003, 2004  Chris Larson
10 Copyright (C) 2004, 2005  Seb Frankengul
11 Copyright (C) 2005, 2006  Holger Hans Peter Freyther
12 Copyright (C) 2005        Uli Luckas
13 Copyright (C) 2005        ROAD GmbH
14
15 This program is free software; you can redistribute it and/or modify it under
16 the terms of the GNU General Public License as published by the Free Software
17 Foundation; either version 2 of the License, or (at your option) any later
18 version.
19
20 This program is distributed in the hope that it will be useful, but WITHOUT
21 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
22 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
23
24 You should have received a copy of the GNU General Public License along with
25 this program; if not, write to the Free Software Foundation, Inc., 59 Temple
26 Place, Suite 330, Boston, MA 02111-1307 USA. 
27
28 Based on functions from the base bb module, Copyright 2003 Holger Schurig
29 """
30
31 import copy, os, re, sys, time, types
32 from bb   import note, debug, error, fatal, utils, methodpool
33 from sets import Set
34
35 try:
36     import cPickle as pickle
37 except ImportError:
38     import pickle
39     print "NOTE: Importing cPickle failed. Falling back to a very slow implementation."
40
41 __setvar_keyword__ = ["_append","_prepend"]
42 __setvar_regexp__ = re.compile('(?P<base>.*?)(?P<keyword>_append|_prepend)(_(?P<add>.*))?')
43 __expand_var_regexp__ = re.compile(r"\${[^{}]+}")
44 __expand_python_regexp__ = re.compile(r"\${@.+?}")
45
46
47 class DataSmart:
48     def __init__(self):
49         self.dict = {}
50
51         # cookie monster tribute
52         self._special_values = {}
53         self._seen_overrides = {}
54
55     def expand(self,s, varname):
56         def var_sub(match):
57             key = match.group()[2:-1]
58             if varname and key:
59                 if varname == key:
60                     raise Exception("variable %s references itself!" % varname)
61             var = self.getVar(key, 1)
62             if var is not None:
63                 return var
64             else:
65                 return match.group()
66
67         def python_sub(match):
68             import bb
69             code = match.group()[3:-1]
70             locals()['d'] = self
71             s = eval(code)
72             if type(s) == types.IntType: s = str(s)
73             return s
74
75         if type(s) is not types.StringType: # sanity check
76             return s
77
78         while s.find('$') != -1:
79             olds = s
80             try:
81                 s = __expand_var_regexp__.sub(var_sub, s)
82                 s = __expand_python_regexp__.sub(python_sub, s)
83                 if s == olds: break
84                 if type(s) is not types.StringType: # sanity check
85                     error('expansion of %s returned non-string %s' % (olds, s))
86             except KeyboardInterrupt:
87                 raise
88             except:
89                 note("%s:%s while evaluating:\n%s" % (sys.exc_info()[0], sys.exc_info()[1], s))
90                 raise
91         return s
92
93     def initVar(self, var):
94         if not var in self.dict:
95             self.dict[var] = {}
96
97     def _findVar(self,var):
98         _dest = self.dict
99
100         while (_dest and var not in _dest):
101             if not "_data" in _dest:
102                 _dest = None
103                 break
104             _dest = _dest["_data"]
105
106         if _dest and var in _dest:
107             return _dest[var]
108         return None
109
110     def _makeShadowCopy(self, var):
111         if var in self.dict:
112             return
113
114         local_var = self._findVar(var)
115
116         if local_var:
117             self.dict[var] = copy.copy(local_var)
118         else:
119             self.initVar(var)
120
121     def setVar(self,var,value):
122         match  = __setvar_regexp__.match(var)
123         if match and match.group("keyword") in __setvar_keyword__:
124             base = match.group('base')
125             keyword = match.group("keyword")
126             override = match.group('add')
127             l = self.getVarFlag(base, keyword) or []
128             l.append([value, override])
129             self.setVarFlag(base, keyword, l)
130
131             # pay the cookie monster
132             try:
133                 self._special_values[keyword].add( base )
134             except:
135                 self._special_values[keyword] = Set()
136                 self._special_values[keyword].add( base )
137
138             # SRC_URI_append_simpad is both a flag and a override
139             #if not override in self._seen_overrides:
140             #    self._seen_overrides[override] = Set()
141             #self._seen_overrides[override].add( base )
142             return
143
144         if not var in self.dict:
145             self._makeShadowCopy(var)
146         if self.getVarFlag(var, 'matchesenv'):
147             self.delVarFlag(var, 'matchesenv')
148             self.setVarFlag(var, 'export', 1)
149
150         # more cookies for the cookie monster
151         if '_' in var:
152             override = var[var.rfind('_')+1:]
153             if not override in self._seen_overrides:
154                 self._seen_overrides[override] = Set()
155             self._seen_overrides[override].add( var )
156
157         # setting var
158         self.dict[var]["content"] = value
159
160     def getVar(self,var,exp):
161         value = self.getVarFlag(var,"content")
162
163         if exp and value:
164             return self.expand(value,var)
165         return value
166
167     def delVar(self,var):
168         if not var in self.dict:
169             self._makeShadowCopy(var)
170         self.dict[var] = {}
171
172     def setVarFlag(self,var,flag,flagvalue):
173         if not var in self.dict:
174             self._makeShadowCopy(var)
175         self.dict[var][flag] = flagvalue
176
177     def getVarFlag(self,var,flag):
178         local_var = self._findVar(var)
179         if local_var:
180             if flag in local_var:
181                 return copy.copy(local_var[flag])
182         return None
183
184     def delVarFlag(self,var,flag):
185         local_var = self._findVar(var)
186         if not local_var:
187             return
188         if not var in self.dict:
189             self._makeShadowCopy(var)
190
191         if var in self.dict and flag in self.dict[var]:
192             del self.dict[var][flag]
193
194     def setVarFlags(self,var,flags):
195         if not var in self.dict:
196             self._makeShadowCopy(var)
197
198         for i in flags.keys():
199             if i == "content":
200                 continue
201             self.dict[var][i] = flags[i]
202
203     def getVarFlags(self,var):
204         local_var = self._findVar(var)
205         flags = {}
206
207         if local_var:
208             for i in self.dict[var].keys():
209                 if i == "content":
210                     continue
211                 flags[i] = self.dict[var][i]
212
213         if len(flags) == 0:
214             return None
215         return flags
216
217
218     def delVarFlags(self,var):
219         if not var in self.dict:
220             self._makeShadowCopy(var)
221
222         if var in self.dict:
223             content = None
224
225             # try to save the content
226             if "content" in self.dict[var]:
227                 content  = self.dict[var]["content"]
228                 self.dict[var]            = {}
229                 self.dict[var]["content"] = content
230             else:
231                 del self.dict[var]
232
233
234     def createCopy(self):
235         """
236         Create a copy of self by setting _data to self
237         """
238         # we really want this to be a DataSmart...
239         data = DataSmart()
240         data.dict["_data"] = self.dict
241         data._seen_overrides = copy.copy(self._seen_overrides)
242         data._special_values = copy.copy(self._special_values)
243
244         return data
245
246     # Dictionary Methods
247     def keys(self):
248         def _keys(d, mykey):
249             if "_data" in d:
250                 _keys(d["_data"],mykey)
251
252             for key in d.keys():
253                 if key != "_data":
254                     mykey[key] = None
255         keytab = {}
256         _keys(self.dict,keytab)
257         return keytab.keys()
258
259     def __getitem__(self,item):
260         start = self.dict
261         while start:
262             if item in start:
263                 return start[item]
264             elif "_data" in start:
265                 start = start["_data"]
266             else:
267                 start = None
268         return None
269
270     def __setitem__(self,var,data):
271         self._makeShadowCopy(var)
272         self.dict[var] = data
273
274
275 class DataSmartPackage(DataSmart):
276     """
277     Persistent Data Storage
278     """
279     def sanitize_filename(bbfile):
280         return bbfile.replace( '/', '_' )
281     sanitize_filename = staticmethod(sanitize_filename)
282
283     def unpickle(self):
284         """
285         Restore the dict from memory
286         """
287         cache_bbfile = self.sanitize_filename(self.bbfile)
288         p = pickle.Unpickler( file("%s/%s"%(self.cache,cache_bbfile),"rb"))
289         self.dict = p.load()
290         self._seen_overrides = p.load()
291         self._special_values = p.load()
292         self.unpickle_prep()
293
294         # compile the functions into global scope
295         funcs = self.getVar('__functions__', 0) or {}
296         for key in funcs.keys():
297             methodpool.check_insert_method( key, funcs[key], self.bbfile )
298             methodpool.parsed_module( key )
299
300         # now add the handlers which were present
301         handlers = self.getVar('__all_handlers__', 0) or {}
302         import bb.event
303         for key in handlers.keys():
304             bb.event.register(key, handlers[key])
305
306
307     def linkDataSet(self):
308         if not self.parent == None:
309             # assume parent is a DataSmartInstance
310             self.dict["_data"] = self.parent.dict
311
312
313     def __init__(self,cache,name,clean,parent):
314         """
315         Construct a persistent data instance
316         """
317         #Initialize the dictionary
318         DataSmart.__init__(self)
319
320         self.cache  = cache
321         self.bbfile = os.path.abspath( name )
322         self.parent = parent
323
324         # Either unpickle the data or do copy on write
325         if clean:
326             self.linkDataSet()
327             self._seen_overrides = copy.copy(parent._seen_overrides)
328             self._special_values = copy.copy(parent._special_values)
329         else:
330             self.unpickle()
331
332     def commit(self, mtime):
333         """
334         Save the package to a permanent storage
335         """
336         self.pickle_prep()
337
338         cache_bbfile = self.sanitize_filename(self.bbfile)
339         p = pickle.Pickler(file("%s/%s" %(self.cache,cache_bbfile), "wb" ), -1 )
340         p.dump( self.dict )
341         p.dump( self._seen_overrides )
342         p.dump( self._special_values )
343
344         self.unpickle_prep()
345
346     def mtime(cache,bbfile):
347         cache_bbfile = DataSmartPackage.sanitize_filename(bbfile)
348         try:
349             return os.stat( "%s/%s" % (cache,cache_bbfile) )[8]
350         except OSError:
351             return 0
352     mtime = staticmethod(mtime)
353
354     def pickle_prep(self):
355         """
356         If self.dict contains a _data key and it is a configuration
357         we will remember we had a configuration instance attached
358         """
359         if "_data" in self.dict and  self.dict["_data"] == self.parent:
360             dest["_data"] = "cfg"
361
362     def unpickle_prep(self):
363         """
364         If we had a configuration instance attached, we will reattach it
365         """
366         if "_data" in self.dict and  self.dict["_data"] == "cfg":
367             self.dict["_data"] = self.parent