Replacing B.has_key(A) calls with A in B.
[bitbake.git] / bin / oe / data.py
1 #!/usr/bin/python
2 """
3 OpenEmbedded 'Data' implementations
4
5 Functions for interacting with the data structure used by the
6 OpenEmbedded (http://openembedded.org) build infrastructure.
7
8 Copyright: (c) 2003 Chris Larson
9
10 Based on functions from the base oe module, Copyright 2003 Holger Schurig
11 """
12
13 import sys, os, re, time, types
14 if sys.argv[0][-5:] == "pydoc":
15         path = os.path.dirname(os.path.dirname(sys.argv[1]))
16 else:
17         path = os.path.dirname(os.path.dirname(sys.argv[0]))
18 sys.path.append(path)
19
20 from oe import note, debug
21
22 def init():
23         return {}
24
25 _data = init()
26
27 def initVar(var, d = _data):
28         """Non-destructive var init for data structure"""
29         if not var in d:
30                 d[var] = {}
31
32         if not "flags" in d[var]:
33                 d[var]["flags"] = {}
34
35 __setvar_regexp__ = {}
36 __setvar_regexp__["_append"]  = re.compile('(?P<base>.*?)%s(_(?P<add>.*))?' % "_append")
37 __setvar_regexp__["_prepend"] = re.compile('(?P<base>.*?)%s(_(?P<add>.*))?' % "_prepend")
38 __setvar_regexp__["_delete"]  = re.compile('(?P<base>.*?)%s(_(?P<add>.*))?' % "_delete")
39
40 def setVar(var, value, d = _data):
41         """Set a variable to a given value
42
43         Example:
44                 >>> setVar('TEST', 'testcontents')
45                 >>> print getVar('TEST')
46                 testcontents
47         """
48         for v in ["_append", "_prepend", "_delete"]:
49                 match = __setvar_regexp__[v].match(var)
50                 if match:
51                         base = match.group('base')
52                         override = match.group('add')
53                         l = getVarFlag(base, v, d) or []
54                         if override == 'delete':
55                                 if l.count([value, None]):
56                                         del l[l.index([value, None])]
57                         l.append([value, override])
58                         setVarFlag(base, v, l, d)
59                         return
60                 
61         if not var in d:
62                 initVar(var, d)
63         d[var]["content"] = value 
64
65 def getVar(var, d = _data, exp = 0):
66         """Gets the value of a variable
67
68         Example:
69                 >>> setVar('TEST', 'testcontents')
70                 >>> print getVar('TEST')
71                 testcontents
72         """
73         if not var in d or not "content" in d[var]:
74                 return None
75         if exp:
76                 return expand(d[var]["content"], d)
77         return d[var]["content"]
78
79 def delVar(var, d = _data):
80         """Removes a variable from the data set
81
82         Example:
83                 >>> setVar('TEST', 'testcontents')
84                 >>> print getVar('TEST')
85                 testcontents
86                 >>> delVar('TEST')
87                 >>> print getVar('TEST')
88                 None
89         """
90         if var in d:
91                 del d[var]
92
93 def setVarFlag(var, flag, flagvalue, d = _data):
94         """Set a flag for a given variable to a given value
95
96         Example:
97                 >>> setVarFlag('TEST', 'python', 1)
98                 >>> print getVarFlag('TEST', 'python')
99                 1
100         """
101 #       print "d[%s][\"flags\"][%s] = %s" % (var, flag, flagvalue)
102         if not var in d:
103                 initVar(var, d)
104         d[var]["flags"][flag] = flagvalue
105
106 def getVarFlag(var, flag, d = _data):
107         """Gets given flag from given var
108
109         Example:
110                 >>> setVarFlag('TEST', 'python', 1)
111                 >>> print getVarFlag('TEST', 'python')
112                 1
113         """
114         if var in d and "flags" in d[var] and flag in d[var]["flags"]:
115                 return d[var]["flags"][flag]
116         return None
117
118 def delVarFlag(var, flag, d = _data):
119         """Removes a given flag from the variable's flags
120
121         Example:
122                 >>> setVarFlag('TEST', 'testflag', 1)
123                 >>> print getVarFlag('TEST', 'testflag')
124                 1
125                 >>> delVarFlag('TEST', 'testflag')
126                 >>> print getVarFlag('TEST', 'testflag')
127                 None
128                 
129         """
130         if var in d and "flags" in d[var] and flag in d[var]["flags"]:
131                 del d[var]["flags"][flag]
132
133 def setVarFlags(var, flags, d = _data):
134         """Set the flags for a given variable
135         
136         Example:
137                 >>> myflags = {}
138                 >>> myflags['test'] = 'blah'
139                 >>> setVarFlags('TEST', myflags)
140                 >>> print getVarFlag('TEST', 'test')
141                 blah
142         """
143         if not var in d:
144                 initVar(var, d)
145         d[var]["flags"] = flags
146
147 def getVarFlags(var, d = _data):
148         """Gets a variable's flags
149
150         Example:
151                 >>> setVarFlag('TEST', 'test', 'blah')
152                 >>> print getVarFlags('TEST')['test']
153                 blah
154         """
155         if var in d and "flags" in d[var]:
156                 return d[var]["flags"]
157         return None
158
159 def delVarFlags(var, d = _data):
160         """Removes a variable's flags
161
162         Example:
163                 >>> setVarFlag('TEST', 'testflag', 1)
164                 >>> print getVarFlag('TEST', 'testflag')
165                 1
166                 >>> delVarFlags('TEST')
167                 >>> print getVarFlags('TEST')
168                 None
169                 
170         """
171         if var in d and "flags" in d[var]:
172                 del d[var]["flags"]
173
174 def getData(d = _data):
175         """Returns the data object used"""
176         return d
177
178 def setData(newData, d = _data):
179         """Sets the data object to the supplied value"""
180         d = newData
181
182 __expand_var_regexp__ = re.compile(r"\${[^{}]+}")
183 __expand_python_regexp__ = re.compile(r"\${@.+?}")
184
185 def expand(s, d = _data):
186         """Variable expansion using the data store.
187
188         Example:
189                 Standard expansion:
190                 >>> setVar('A', 'sshd')
191                 >>> print expand('/usr/bin/${A}')
192                 /usr/bin/sshd
193
194                 Python expansion:
195                 >>> print expand('result: ${@37 * 72}')
196                 result: 2664
197         """
198         def var_sub(match):
199                 key = match.group()[2:-1]
200                 #print "got key:", key
201                 var = getVar(key, d)
202                 if var is not None:
203                         return var
204                 else:
205                         return match.group()
206
207         def python_sub(match):
208                 code = match.group()[3:-1]
209                 import oe
210                 locals()['d'] = d
211                 s = eval(code)
212                 if type(s) == types.IntType: s = str(s)
213                 return s
214
215         if type(s) is not types.StringType: # sanity check
216                 return s
217
218         while s.find('$') != -1:
219                 olds = s
220                 s = __expand_var_regexp__.sub(var_sub, s)
221                 s = __expand_python_regexp__.sub(python_sub, s)
222                 if len(s)>2048:
223                         debug(1, "expanded string too long")
224                         return s
225                 if s == olds: break
226         return s
227
228 def expandKeys(alterdata = _data, readdata = None):
229         if readdata == None:
230                 readdata = alterdata
231
232         for key in alterdata.keys():
233                 ekey = expand(key, readdata)
234                 if key == ekey:
235                         continue
236                 val = getVar(key, alterdata)
237                 if val is None:
238                         continue
239                 setVar(ekey, val, alterdata)
240
241 def expandData(alterdata = _data, readdata = None):
242         """For each variable in alterdata, expand it, and update the var contents.
243            Replacements use data from readdata.
244
245         Example:
246                 >>> a=init()
247                 >>> b=init()
248                 >>> setVar("dlmsg", "dl_dir is ${DL_DIR}", a)
249                 >>> setVar("DL_DIR", "/path/to/whatever", b)
250                 >>> expandData(a, b)
251                 >>> print getVar("dlmsg", a)
252                 dl_dir is /path/to/whatever
253            """
254         if readdata == None:
255                 readdata = alterdata
256
257         for key in alterdata.keys():
258                 val = getVar(key, alterdata)
259                 if type(val) is not types.StringType:
260                         continue
261                 expanded = expand(val, readdata)
262 #               print "key is %s, val is %s, expanded is %s" % (key, val, expanded)
263                 if val != expanded:
264                         setVar(key, expanded, alterdata)
265
266 import os
267
268 def inheritFromOS(d = _data):
269         """Inherit variables from the environment."""
270         for s in os.environ.keys():
271                 try:
272                         setVar(s, os.environ[s], d)
273                 except TypeError:
274                         pass
275
276 import sys, string
277
278 def emit_var(var, o=sys.__stdout__, d = _data):
279         """Emit a variable to be sourced by a shell."""
280         if getVarFlag(var, "python", d):
281                 return 0
282
283         val = getVar(var, d, 1)
284         if type(val) is not types.StringType:
285                 debug(2, "Warning, %s variable is not a string, not emitting" % var)
286                 return 0
287
288         if var.find("-") != -1 or var.find(".") != -1 or var.find('{') != -1 or var.find('}') != -1 or var.find('+') != -1:
289                 debug(2, "Warning, %s variable name contains an invalid char, not emitting to shell" % var)
290                 return 0
291
292         if getVarFlag(var, "func", d):
293                 # NOTE: should probably check for unbalanced {} within the var
294                 o.write("%s() {\n%s\n}\n" % (var, val))
295         else:   
296                 if getVarFlag(var, "export", d):
297                         o.write('export ')
298                 # if we're going to output this within doublequotes,
299                 # to a shell, we need to escape the quotes in the var
300                 alter = re.sub('"', '\\"', val.strip())
301                 o.write('%s="%s"\n' % (var, alter))
302         return 1
303
304
305 def emit_env(o=sys.__stdout__, d = _data):
306         """Emits all items in the data store in a format such that it can be sourced by a shell."""
307
308         expandData(d)
309         env = d.keys()
310
311         for e in env:
312                 if getVarFlag(e, "func", d):
313                         continue
314                 emit_var(e, o, d) and o.write('\n')
315
316         for e in env:
317                 if not getVarFlag(e, "func", d):
318                         continue
319                 emit_var(e, o, d) and o.write('\n')
320
321 def update_data(d = _data):
322         """Modifies the environment vars according to local overrides and commands.
323         Examples:
324                 Appending to a variable:
325                 >>> setVar('TEST', 'this is a')
326                 >>> setVar('TEST_append', ' test')
327                 >>> setVar('TEST_append', ' of the emergency broadcast system.')
328                 >>> update_data()
329                 >>> print getVar('TEST')
330                 this is a test of the emergency broadcast system.
331
332                 Prepending to a variable:
333                 >>> setVar('TEST', 'virtual/libc')
334                 >>> setVar('TEST_prepend', 'virtual/tmake ')
335                 >>> setVar('TEST_prepend', 'virtual/patcher ')
336                 >>> update_data()
337                 >>> print getVar('TEST')
338                 virtual/patcher virtual/tmake virtual/libc
339
340                 Overrides:
341                 >>> setVar('TEST_arm', 'target')
342                 >>> setVar('TEST_ramses', 'machine')
343                 >>> setVar('TEST_local', 'local')
344                 >>> setVar('OVERRIDES', 'arm')
345         
346                 >>> setVar('TEST', 'original')
347                 >>> update_data()
348                 >>> print getVar('TEST')
349                 target
350         
351                 >>> setVar('OVERRIDES', 'arm:ramses:local')
352                 >>> setVar('TEST', 'original')
353                 >>> update_data()
354                 >>> print getVar('TEST')
355                 local
356         """
357
358         debug(2, "update_data()")
359
360         # can't do delete env[...] while iterating over the dictionary, so remember them
361         dodel = []
362         overrides = string.split(getVar('OVERRIDES', d, 1) or "", ":") or []
363
364         def applyOverrides(var, d = _data):
365                 if not overrides:
366                         debug(1, "OVERRIDES not defined, nothing to do")
367                         return
368                 val = getVar(var, d)
369                 for o in overrides:
370                         if var.endswith("_" + o):
371                                 l = len(o)+1
372                                 name = var[:-l]
373                                 d[name] = d[var]
374
375         for s in d.keys():
376                 applyOverrides(s, d)
377                 sval = getVar(s, d) or ""
378
379                 # Handle line appends:
380                 for (a, o) in getVarFlag(s, '_append', d) or []:
381                         delVarFlag(s, '_append', d)
382                         if o:
383                                 if not o in overrides:
384                                         break
385                         sval+=a
386                         setVar(s, sval, d)
387                 
388                 # Handle line prepends
389                 for (a, o) in getVarFlag(s, '_prepend', d) or []:
390                         delVarFlag(s, '_prepend', d)
391                         if o:
392                                 if not o in overrides:
393                                         break
394                         sval=a+sval
395                         setVar(s, sval, d)
396
397                 # Handle line deletions
398                 name = s + "_delete"
399                 nameval = getVar(name, d)
400                 if nameval:
401                         sval = getVar(s, d)
402                         if sval:
403                                 new = ''
404                                 pattern = string.replace(nameval,"\n","").strip()
405                                 for line in string.split(sval,"\n"):
406                                         if line.find(pattern) == -1:
407                                                 new = new + '\n' + line
408                                 setVar(s, new, d)
409                                 dodel.append(name)
410
411         # delete all environment vars no longer needed
412         for s in dodel:
413                 delVar(s, d)
414
415 def _test():
416         """Start a doctest run on this module"""
417         import doctest
418         from oe import data
419         doctest.testmod(data)
420
421 if __name__ == "__main__":
422         _test()