bitbake/lib/bb/data_dict.py:
[bitbake.git] / lib / bb / data_dict.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 'Data-Dict' 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) 2005        Holger Hans Peter Freyther
11
12 This program is free software; you can redistribute it and/or modify it under
13 the terms of the GNU General Public License as published by the Free Software
14 Foundation; either version 2 of the License, or (at your option) any later
15 version.
16
17 This program is distributed in the hope that it will be useful, but WITHOUT
18 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
19 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
20
21 You should have received a copy of the GNU General Public License along with
22 this program; if not, write to the Free Software Foundation, Inc., 59 Temple
23 Place, Suite 330, Boston, MA 02111-1307 USA. 
24
25 Based on functions from the base bb module, Copyright 2003 Holger Schurig
26 """
27
28 import os, re, sys, types, copy
29 from   bb import note, debug, fatal
30
31 try:
32     import cPickle as pickle
33 except ImportError:
34     import pickle
35     print "NOTE: Importing cPickle failed. Falling back to a very slow implementation."
36
37
38
39 __setvar_regexp__ = {}
40 __setvar_regexp__["_append"]  = re.compile('(?P<base>.*?)%s(_(?P<add>.*))?' % "_append")
41 __setvar_regexp__["_prepend"] = re.compile('(?P<base>.*?)%s(_(?P<add>.*))?' % "_prepend")
42 __setvar_regexp__["_delete"]  = re.compile('(?P<base>.*?)%s(_(?P<add>.*))?' % "_delete")
43
44 __expand_var_regexp__ = re.compile(r"\${[^{}]+}")
45 __expand_python_regexp__ = re.compile(r"\${@.+?}")
46
47
48 class DataDict:
49     def __init__(self):
50         self.dict = {}
51
52     def expand(self,s, varname):
53         def var_sub(match):
54             key = match.group()[2:-1]
55             if varname and key:
56                 if varname == key:
57                     raise Exception("variable %s references itself!" % varname)
58             var = self.getVar(key, 1)
59             if var is not None:
60                 return var
61             else:
62                 return match.group()
63
64         def python_sub(match):
65             import bb
66             code = match.group()[3:-1]
67             locals()['d'] = self
68             s = eval(code)
69             if type(s) == types.IntType: s = str(s)
70             return s
71
72         if type(s) is not types.StringType: # sanity check
73             return s
74
75         while s.find('$') != -1:
76             olds = s
77             try:
78                 s = __expand_var_regexp__.sub(var_sub, s)
79                 s = __expand_python_regexp__.sub(python_sub, s)
80                 if s == olds: break
81                 if type(s) is not types.StringType: # sanity check
82                     import bb
83                     bb.error('expansion of %s returned non-string %s' % (olds, s))
84             except KeyboardInterrupt:
85                 raise
86             except:
87                 note("%s:%s while evaluating:\n%s" % (sys.exc_info()[0], sys.exc_info()[1], s))
88                 raise
89         return s
90
91     def initVar(self, var):
92         if not var in self.dict:
93             self.dict[var] = {}
94
95         if not "flags" in self.dict[var]:
96             self.dict[var]["flags"] = {}
97
98     def setVar(self,var,value):
99         for v in ["_append", "_prepend", "_delete"]:
100             match = __setvar_regexp__[v].match(var)
101
102             if match:
103                 base = match.group('base')
104                 override = match.group('add')
105                 l = self.getVarFlag(base, v) or []
106                 if override == 'delete':
107                     if l.count([value, None]):
108                         del l[l.index([value, None])]
109                 l.append([value, override])
110                 self.setVarFlag(base, v, l)
111                 return
112
113         self.initVar(var)
114         if self.getVarFlag(var, 'matchesenv'):
115             self.delVarFlag(var, 'matchesenv')
116             self.setVarFlag(var, 'export', 1)
117         self.dict[var]["content"] = value
118
119     def getVar(self,var,exp):
120         if not var in self.dict or not "content" in self.dict[var]:
121             return None
122
123         if exp:
124             return self.expand(self.dict[var]["content"], var)
125         return self.dict[var]["content"]
126
127     def delVar(self,var):
128         if var in self.dict:
129             del self.dict[var]
130
131     def setVarFlag(self,var,flag,flagvalue):
132         self.initVar(var)
133         self.dict[var]["flags"][flag] = flagvalue
134
135     def getVarFlag(self,var,flag):
136         if var in self.dict and "flags" in self.dict[var] and flag in self.dict[var]["flags"]:
137             di = self.dict[var]
138             di = di["flags"]
139             return di[flag]
140         return None
141
142     def delVarFlag(self,var,flag):
143         if var in self.dict and "flags" in self.dict[var] and flag in self.dict[var]["flags"]:
144             del self.dict[var]["flags"][flag]
145
146     def setVarFlags(self,var,flags):
147         self.initVar(var)
148         if flags == None:
149             debug("Setting Null Flag %s" % var)
150
151         self.dict[var]["flags"] = flags
152
153     def getVarFlags(self,var):
154         if var in self.dict and "flags" in self.dict[var]:
155             return self.dict[var]["flags"]
156
157         return None
158
159     def delVarFlags(self,var):
160         if var in self.dict and "flags" in self.dict[var]:
161             del self.dict[var]["flags"]
162
163     def createCopy(self):
164         return copy.deepcopy(self)
165
166     # Dictionary Methods
167     def keys(self):
168         return self.dict.keys()
169
170     def iterkeys(self):
171         return self.dict.iterkeys()
172
173     def iteritems(self):
174         return self.dict.iteritems()
175
176     def items(self):
177         return self.dict.items()
178
179     def __getitem__(self,y):
180         return self.dict.__getitem__(y)
181
182     def __setitem__(self,x,y):
183         self.dict.__setitem__(x,y)
184
185
186
187 class DataDictPackage(DataDict):
188     """
189     Persistent Data Storage
190     """
191     def sanitize_filename(bbfile):
192         return bbfile.replace( '/', '_' )
193     sanitize_filename = staticmethod(sanitize_filename)
194
195     def unpickle(self):
196         """
197         Restore the dict from memory
198         """
199         cache_bbfile = self.sanitize_filename(self.bbfile)
200         p = pickle.Unpickler( file("%s/%s"%(self.cache,cache_bbfile),"rb"))
201         self.dict = p.load()
202         funcstr = self.getVar('__functions__', 0)
203         if funcstr:
204             comp = compile(funcstr, "<pickled>", "exec")
205             exec comp in  __builtins__
206
207     def linkDataSet(self,parent):
208         from copy import deepcopy
209         if not parent == None:
210             self.dict = deepcopy(parent)
211
212
213     def __init__(self,cache,name,clean,parent):
214         """
215         Construct a persistent data instance
216         """
217         #Initialize the dictionary
218         DataDict.__init__(self)
219
220         self.cache  = cache
221         self.bbfile = name
222
223         # Either unpickle the data or do copy on write
224         if clean:
225             self.linkDataSet(parent)
226         else:
227             self.unpickle()
228
229     def commit(self, mtime):
230         """
231         Save the package to a permanent storage
232         """
233         cache_bbfile = self.sanitize_filename(self.bbfile)
234         p = pickle.Pickler(file("%s/%s" %(self.cache,cache_bbfile), "wb" ), -1 )
235         p.dump( self.dict )
236
237     def mtime(cache,bbfile):
238         cache_bbfile = DataDictPackage.sanitize_filename(bbfile)
239         try:
240             return os.stat( "%s/%s" % (cache,cache_bbfile) )[8]
241         except OSError:
242             return 0
243     mtime = staticmethod(mtime)
244