data_smart.py: Add expand_cache, caching expanded data (but wiped on any write to...
[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 import bb
33 from bb   import utils, methodpool
34 from COW  import COWDictBase
35 from sets import Set
36 from new  import classobj
37
38
39 __setvar_keyword__ = ["_append","_prepend"]
40 __setvar_regexp__ = re.compile('(?P<base>.*?)(?P<keyword>_append|_prepend)(_(?P<add>.*))?')
41 __expand_var_regexp__ = re.compile(r"\${[^{}]+}")
42 __expand_python_regexp__ = re.compile(r"\${@.+?}")
43
44
45 class DataSmart:
46     def __init__(self, special = COWDictBase.copy(), seen = COWDictBase.copy() ):
47         self.dict = {}
48
49         # cookie monster tribute
50         self._special_values = special
51         self._seen_overrides = seen
52
53         self.expand_cache = {}
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         if varname and varname in self.expand_cache:
79             return self.expand_cache[varname]
80
81         while s.find('$') != -1:
82             olds = s
83             try:
84                 s = __expand_var_regexp__.sub(var_sub, s)
85                 s = __expand_python_regexp__.sub(python_sub, s)
86                 if s == olds: break
87                 if type(s) is not types.StringType: # sanity check
88                     bb.msg.error(bb.msg.domain.Data, 'expansion of %s returned non-string %s' % (olds, s))
89             except KeyboardInterrupt:
90                 raise
91             except:
92                 bb.msg.note(1, bb.msg.domain.Data, "%s:%s while evaluating:\n%s" % (sys.exc_info()[0], sys.exc_info()[1], s))
93                 raise
94
95         if varname:
96             self.expand_cache[varname] = s
97
98         return s
99
100     def initVar(self, var):
101         self.expand_cache = {}
102         if not var in self.dict:
103             self.dict[var] = {}
104
105     def _findVar(self,var):
106         _dest = self.dict
107
108         while (_dest and var not in _dest):
109             if not "_data" in _dest:
110                 _dest = None
111                 break
112             _dest = _dest["_data"]
113
114         if _dest and var in _dest:
115             return _dest[var]
116         return None
117
118     def _makeShadowCopy(self, var):
119         if var in self.dict:
120             return
121
122         local_var = self._findVar(var)
123
124         if local_var:
125             self.dict[var] = copy.copy(local_var)
126         else:
127             self.initVar(var)
128
129     def setVar(self,var,value):
130         self.expand_cache = {}
131         match  = __setvar_regexp__.match(var)
132         if match and match.group("keyword") in __setvar_keyword__:
133             base = match.group('base')
134             keyword = match.group("keyword")
135             override = match.group('add')
136             l = self.getVarFlag(base, keyword) or []
137             l.append([value, override])
138             self.setVarFlag(base, keyword, l)
139
140             # todo make sure keyword is not __doc__ or __module__
141             # pay the cookie monster
142             try:
143                 self._special_values[keyword].add( base )
144             except:
145                 self._special_values[keyword] = Set()
146                 self._special_values[keyword].add( base )
147
148             return
149
150         if not var in self.dict:
151             self._makeShadowCopy(var)
152         if self.getVarFlag(var, 'matchesenv'):
153             self.delVarFlag(var, 'matchesenv')
154             self.setVarFlag(var, 'export', 1)
155
156         # more cookies for the cookie monster
157         if '_' in var:
158             override = var[var.rfind('_')+1:]
159             if not self._seen_overrides.has_key(override):
160                 self._seen_overrides[override] = Set()
161             self._seen_overrides[override].add( var )
162
163         # setting var
164         self.dict[var]["content"] = value
165
166     def getVar(self,var,exp):
167         value = self.getVarFlag(var,"content")
168
169         if exp and value:
170             return self.expand(value,var)
171         return value
172
173     def delVar(self,var):
174         self.expand_cache = {}
175         self.dict[var] = {}
176
177     def setVarFlag(self,var,flag,flagvalue):
178         if not var in self.dict:
179             self._makeShadowCopy(var)
180         self.dict[var][flag] = flagvalue
181
182     def getVarFlag(self,var,flag):
183         local_var = self._findVar(var)
184         if local_var:
185             if flag in local_var:
186                 return copy.copy(local_var[flag])
187         return None
188
189     def delVarFlag(self,var,flag):
190         local_var = self._findVar(var)
191         if not local_var:
192             return
193         if not var in self.dict:
194             self._makeShadowCopy(var)
195
196         if var in self.dict and flag in self.dict[var]:
197             del self.dict[var][flag]
198
199     def setVarFlags(self,var,flags):
200         if not var in self.dict:
201             self._makeShadowCopy(var)
202
203         for i in flags.keys():
204             if i == "content":
205                 continue
206             self.dict[var][i] = flags[i]
207
208     def getVarFlags(self,var):
209         local_var = self._findVar(var)
210         flags = {}
211
212         if local_var:
213             for i in self.dict[var].keys():
214                 if i == "content":
215                     continue
216                 flags[i] = self.dict[var][i]
217
218         if len(flags) == 0:
219             return None
220         return flags
221
222
223     def delVarFlags(self,var):
224         if not var in self.dict:
225             self._makeShadowCopy(var)
226
227         if var in self.dict:
228             content = None
229
230             # try to save the content
231             if "content" in self.dict[var]:
232                 content  = self.dict[var]["content"]
233                 self.dict[var]            = {}
234                 self.dict[var]["content"] = content
235             else:
236                 del self.dict[var]
237
238
239     def createCopy(self):
240         """
241         Create a copy of self by setting _data to self
242         """
243         # we really want this to be a DataSmart...
244         data = DataSmart(seen=self._seen_overrides.copy(), special=self._special_values.copy())
245         data.dict["_data"] = self.dict
246
247         return data
248
249     # Dictionary Methods
250     def keys(self):
251         def _keys(d, mykey):
252             if "_data" in d:
253                 _keys(d["_data"],mykey)
254
255             for key in d.keys():
256                 if key != "_data":
257                     mykey[key] = None
258         keytab = {}
259         _keys(self.dict,keytab)
260         return keytab.keys()
261
262     def __getitem__(self,item):
263         #print "Warning deprecated"
264         return self.getVar(item, False)
265
266     def __setitem__(self,var,data):
267         #print "Warning deprecated"
268         self.setVar(var,data)
269
270