have bb.parse.handle() throw ParseError if the input file is not
[bitbake.git] / 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, 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         funcstr = self.getVar('__functions__', 0)
288         if funcstr:
289             comp = compile(funcstr, "<pickled>", "exec")
290             exec comp in  __builtins__
291
292     def linkDataSet(self,parent):
293         if not parent == None:
294             # assume parent is a DataSmartInstance
295             self.dict = copy.deepcopy(parent.dict)
296
297
298     def __init__(self,cache,name,clean,parent):
299         """
300         Construct a persistent data instance
301         """
302         #Initialize the dictionary
303         DataSmart.__init__(self)
304
305         self.cache  = cache
306         self.bbfile = name
307
308         # Either unpickle the data or do copy on write
309         if clean:
310             self.linkDataSet(parent)
311         else:
312             self.unpickle()
313
314     def commit(self, mtime):
315         """
316         Save the package to a permanent storage
317         """
318         cache_bbfile = self.sanitize_filename(self.bbfile)
319         p = pickle.Pickler(file("%s/%s" %(self.cache,cache_bbfile), "wb" ), -1 )
320         p.dump( self.dict )
321
322     def mtime(cache,bbfile):
323         cache_bbfile = DataSmartPackage.sanitize_filename(bbfile)
324         try:
325             return os.stat( "%s/%s" % (cache,cache_bbfile) )[8]
326         except OSError:
327             return 0
328     mtime = staticmethod(mtime)