741790502f070dcddbebc2553e0d4af91b40cedb
[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        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, fatal
33
34 try:
35     import cPickle as pickle
36 except ImportError:
37     import pickle
38     print "NOTE: Importing cPickle failed. Falling back to a very slow implementation."
39
40
41 __setvar_keyword__ = ["_append","_prepend","_delete"]
42 __setvar_regexp__ = re.compile('(?P<base>.*?)(?P<keyword>_append|_prepend|_delete)(_(?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     def expand(self,s, varname):
52         def var_sub(match):
53             key = match.group()[2:-1]
54             if varname and key:
55                 if varname == key:
56                     raise Exception("variable %s references itself!" % varname)
57             var = self.getVar(key, 1)
58             if var is not None:
59                 return var
60             else:
61                 return match.group()
62
63         def python_sub(match):
64             import bb
65             code = match.group()[3:-1]
66             locals()['d'] = self
67             s = eval(code)
68             if type(s) == types.IntType: s = str(s)
69             return s
70
71         if type(s) is not types.StringType: # sanity check
72             return s
73
74         while s.find('$') != -1:
75             olds = s
76             try:
77                 s = __expand_var_regexp__.sub(var_sub, s)
78                 s = __expand_python_regexp__.sub(python_sub, s)
79                 if s == olds: break
80                 if type(s) is not types.StringType: # sanity check
81                     import bb
82                     bb.error('expansion of %s returned non-string %s' % (olds, s))
83             except KeyboardInterrupt:
84                 raise
85             except:
86                 note("%s:%s while evaluating:\n%s" % (sys.exc_info()[0], sys.exc_info()[1], s))
87                 raise
88         return s
89
90     def initVar(self, var):
91         if not var in self.dict:
92             self.dict[var] = {}
93
94     def pickle_prep(self, cfg):
95         if "_data" in self.dict:
96             if self.dict["_data"] == cfg:
97                 self.dict["_data"] = "cfg";
98             else: # this is an unknown array for the moment
99                 pass
100
101     def unpickle_prep(self, cfg):
102         if "_data" in self.dict:
103             if self.dict["_data"] == "cfg":
104                 self.dict["_data"] = cfg;
105
106     def _findVar(self,var):
107         _dest = self.dict
108
109         while (_dest and var not in _dest):
110             if not "_data" in _dest:
111                 _dest = None
112                 break
113             _dest = _dest["_data"]
114
115         if _dest and var in _dest:
116             return _dest[var]
117         return None
118
119     def _copyVar(self,var,name):
120         local_var = self._findVar(var)
121         if local_var:
122             self.dict[name] = copy.copy(local_var)
123         else:
124             debug(1,"Warning, _copyVar %s to %s, %s does not exists" % (var,name,var))
125
126
127     def _makeShadowCopy(self, var):
128         if var in self.dict:
129             return
130
131         local_var = self._findVar(var)
132
133         if local_var:
134             self.dict[var] = copy.copy(local_var)
135         else:
136             self.initVar(var)
137
138     def setVar(self,var,value):
139         match  = __setvar_regexp__.match(var)
140         if match and match.group("keyword") in __setvar_keyword__:
141             base = match.group('base')
142             keyword = match.group("keyword")
143             override = match.group('add')
144             l = self.getVarFlag(base, keyword) or []
145             if override == 'delete':
146                 if l.count([value, None]):
147                     del l[l.index([value, None])]
148             l.append([value, override])
149             self.setVarFlag(base, match.group("keyword"), l)
150             return
151
152         if not var in self.dict:
153             self._makeShadowCopy(var)
154         if self.getVarFlag(var, 'matchesenv'):
155             self.delVarFlag(var, 'matchesenv')
156             self.setVarFlag(var, 'export', 1)
157
158         # setting var
159         self.dict[var]["content"] = value
160
161     def getVar(self,var,exp):
162         value = self.getVarFlag(var,"content")
163
164         if exp and value:
165             return self.expand(value,var)
166         return value
167
168     def delVar(self,var):
169         self.dict[var] = {}
170
171     def setVarFlag(self,var,flag,flagvalue):
172         if not var in self.dict:
173             self._makeShadowCopy(var)
174         self.dict[var][flag] = flagvalue
175
176     def getVarFlag(self,var,flag):
177         local_var = self._findVar(var)
178         if local_var:
179             if flag in local_var:
180                 return copy.copy(local_var[flag])
181         return None
182
183     def delVarFlag(self,var,flag):
184         local_var = self._findVar(var)
185         if not local_var:
186             return
187         if not var in self.dict:
188             self._makeShadowCopy(var)
189
190         if var in self.dict and flag in self.dict[var]:
191             del self.dict[var][flag]
192
193     def setVarFlags(self,var,flags):
194         if not var in self.dict:
195             self._makeShadowCopy(var)
196
197         for i in flags.keys():
198             if i == "content":
199                 continue
200             self.dict[var][i] = flags[i]
201
202     def getVarFlags(self,var):
203         local_var = self._findVar(var)
204         flags = {}
205
206         if local_var:
207             for i in self.dict[var].keys():
208                 if i == "content":
209                     continue
210                 flags[i] = self.dict[var][i]
211
212         if len(flags) == 0:
213             return None
214         return flags
215
216
217     def delVarFlags(self,var):
218         if not var in self.dict:
219             self._makeShadowCopy(var)
220
221         if var in self.dict:
222             content = None
223
224             # try to save the content
225             if "content" in self.dict[var]:
226                 content  = self.dict[var]["content"]
227                 self.dict[var]            = {}
228                 self.dict[var]["content"] = content
229             else:
230                 del self.dict[var]
231
232
233     def createCopy(self):
234         """
235         Create a copy of self by setting _data to self
236         """
237         # we really want this to be a DataSmart...
238         data = DataSmart()
239         data.dict["_data"] = self.dict
240
241         return data
242
243     # Dictionary Methods
244     def keys(self):
245         def _keys(d, mykey):
246             if "_data" in d:
247                 _keys(d["_data"],mykey)
248
249             for key in d.keys():
250                 if key != "_data":
251                     mykey[key] = None
252         keytab = {}
253         _keys(self.dict,keytab)
254         return keytab.keys()
255
256     def __getitem__(self,item):
257         start = self.dict
258         while start:
259             if item in start:
260                 return start[item]
261             elif "_data" in start:
262                 start = start["_data"]
263             else:
264                 start = None
265         return None
266
267     def __setitem__(self,var,data):
268         self._makeShadowCopy(var)
269         self.dict[var] = data
270
271
272 class DataSmartPackage(DataSmart):
273     """
274     Persistent Data Storage
275     """
276     def sanitize_filename(bbfile):
277         return bbfile.replace( '/', '_' )
278     sanitize_filename = staticmethod(sanitize_filename)
279
280     def unpickle(self):
281         """
282         Restore the dict from memory
283         """
284         cache_bbfile = self.sanitize_filename(self.bbfile)
285         p = pickle.Unpickler( file("%s/%s"%(self.cache,cache_bbfile),"rb"))
286         self.dict = p.load()
287         self.unpickle_prep()
288         funcstr = self.getVar('__functions__', 0)
289         if funcstr:
290             comp = compile(funcstr, "<pickled>", "exec")
291             exec comp in  __builtins__
292
293     def linkDataSet(self):
294         if not self.parent == None:
295             # assume parent is a DataSmartInstance
296             self.dict["_data"] = self.parent.dict
297
298
299     def __init__(self,cache,name,clean,parent):
300         """
301         Construct a persistent data instance
302         """
303         #Initialize the dictionary
304         DataSmart.__init__(self)
305
306         self.cache  = cache
307         self.bbfile = os.path.abspath( name )
308         self.parent = parent
309
310         # Either unpickle the data or do copy on write
311         if clean:
312             self.linkDataSet()
313         else:
314             self.unpickle()
315
316     def commit(self, mtime):
317         """
318         Save the package to a permanent storage
319         """
320         self.pickle_prep()
321
322         cache_bbfile = self.sanitize_filename(self.bbfile)
323         p = pickle.Pickler(file("%s/%s" %(self.cache,cache_bbfile), "wb" ), -1 )
324         p.dump( self.dict )
325
326         self.unpickle_prep()
327
328     def mtime(cache,bbfile):
329         cache_bbfile = DataSmartPackage.sanitize_filename(bbfile)
330         try:
331             return os.stat( "%s/%s" % (cache,cache_bbfile) )[8]
332         except OSError:
333             return 0
334     mtime = staticmethod(mtime)
335
336     def pickle_prep(self):
337         """
338         If self.dict contains a _data key and it is a configuration
339         we will remember we had a configuration instance attached
340         """
341         if "_data" in self.dict:
342             if self.dict["_data"] == self.parent:
343                 dest["_data"] = "cfg"
344
345     def unpickle_prep(self):
346         """
347         If we had a configuration instance attached, we will reattach it
348         """
349         if "_data" in self.dict:
350             if self.dict["_data"] == "cfg":
351                 self.dict["_data"] = self.parent