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