improve diagnostics when bad things happen during expansion
[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         import oe
216         code = match.group()[3:-1]
217         locals()['d'] = d
218         s = eval(code)
219         if type(s) == types.IntType: s = str(s)
220         return s
221
222     if type(s) is not types.StringType: # sanity check
223         return s
224
225     while s.find('$') != -1:
226         olds = s
227         try:
228             s = __expand_var_regexp__.sub(var_sub, s)
229             s = __expand_python_regexp__.sub(python_sub, s)
230             if s == olds: break
231             if type(s) is not types.StringType: # sanity check
232                 import oe
233                 oe.error('expansion of %s returned non-string %s' % (olds, s))
234         except:
235             import oe
236             oe.note("%s:%s while evaluating:\n%s" % (sys.exc_info()[0], sys.exc_info()[1], s))
237             raise
238     return s
239
240 def expandKeys(alterdata = _data, readdata = None):
241     if readdata == None:
242         readdata = alterdata
243
244     for key in alterdata.keys():
245         ekey = expand(key, readdata)
246         if key == ekey:
247             continue
248         val = getVar(key, alterdata)
249         if val is None:
250             continue
251 #        import copy
252 #        setVarFlags(ekey, copy.copy(getVarFlags(key, readdata)), alterdata)
253         setVar(ekey, val, alterdata)
254
255         for i in ('_append', '_prepend', '_delete'):
256             dest = getVarFlag(ekey, i, alterdata) or []
257             src = getVarFlag(key, i, readdata) or []
258             dest.extend(src)
259             setVarFlag(ekey, i, dest, alterdata)
260
261         delVar(key, alterdata)
262
263 def expandData(alterdata = _data, readdata = None):
264     """For each variable in alterdata, expand it, and update the var contents.
265        Replacements use data from readdata.
266
267     Example:
268         >>> a=init()
269         >>> b=init()
270         >>> setVar("dlmsg", "dl_dir is ${DL_DIR}", a)
271         >>> setVar("DL_DIR", "/path/to/whatever", b)
272         >>> expandData(a, b)
273         >>> print getVar("dlmsg", a)
274         dl_dir is /path/to/whatever
275        """
276     if readdata == None:
277         readdata = alterdata
278
279     for key in alterdata.keys():
280         val = getVar(key, alterdata)
281         if type(val) is not types.StringType:
282             continue
283         expanded = expand(val, readdata)
284 #       print "key is %s, val is %s, expanded is %s" % (key, val, expanded)
285         if val != expanded:
286             setVar(key, expanded, alterdata)
287
288 import os
289
290 def inheritFromOS(d = _data):
291     """Inherit variables from the environment."""
292 #   fakeroot needs to be able to set these
293     non_inherit_vars = [ "LD_LIBRARY_PATH", "LD_PRELOAD" ]
294     for s in os.environ.keys():
295         if not s in non_inherit_vars:
296             try:
297                 setVar(s, os.environ[s], d)
298                 setVarFlag(s, 'matchesenv', '1', d)
299             except TypeError:
300                 pass
301
302 import sys
303
304 def emit_var(var, o=sys.__stdout__, d = _data, all=False):
305     """Emit a variable to be sourced by a shell."""
306     if getVarFlag(var, "python", d):
307         return 0
308
309     try:
310         if all:
311             oval = getVar(var, d, 0)
312         val = getVar(var, d, 1)
313     except KeyboardInterrupt:
314         raise
315     except:
316         o.write('# expansion of %s threw %s\n' % (var, sys.exc_info()[0]))
317         return 0
318
319     if all:
320         o.write('# %s=%s\n' % (var, oval))
321
322     if type(val) is not types.StringType:
323         return 0
324
325     if getVarFlag(var, 'matchesenv', d):
326         return 0
327
328     if var.find("-") != -1 or var.find(".") != -1 or var.find('{') != -1 or var.find('}') != -1 or var.find('+') != -1:
329         return 0
330
331     val.rstrip()
332     if not val:
333         return 0
334
335     if getVarFlag(var, "func", d):
336 #       NOTE: should probably check for unbalanced {} within the var
337         o.write("%s() {\n%s\n}\n" % (var, val))
338     else:
339         if getVarFlag(var, "export", d):
340             o.write('export ')
341         else:
342             if not all:
343                 return 0
344 #       if we're going to output this within doublequotes,
345 #       to a shell, we need to escape the quotes in the var
346         alter = re.sub('"', '\\"', val.strip())
347         o.write('%s="%s"\n' % (var, alter))
348     return 1
349
350
351 def emit_env(o=sys.__stdout__, d = _data, all=False):
352     """Emits all items in the data store in a format such that it can be sourced by a shell."""
353
354     env = d.keys()
355
356     for e in env:
357         if getVarFlag(e, "func", d):
358             continue
359         emit_var(e, o, d, all) and o.write('\n')
360
361     for e in env:
362         if not getVarFlag(e, "func", d):
363             continue
364         emit_var(e, o, d) and o.write('\n')
365
366 def update_data(d = _data):
367     """Modifies the environment vars according to local overrides and commands.
368     Examples:
369         Appending to a variable:
370         >>> setVar('TEST', 'this is a')
371         >>> setVar('TEST_append', ' test')
372         >>> setVar('TEST_append', ' of the emergency broadcast system.')
373         >>> update_data()
374         >>> print getVar('TEST')
375         this is a test of the emergency broadcast system.
376
377         Prepending to a variable:
378         >>> setVar('TEST', 'virtual/libc')
379         >>> setVar('TEST_prepend', 'virtual/tmake ')
380         >>> setVar('TEST_prepend', 'virtual/patcher ')
381         >>> update_data()
382         >>> print getVar('TEST')
383         virtual/patcher virtual/tmake virtual/libc
384
385         Overrides:
386         >>> setVar('TEST_arm', 'target')
387         >>> setVar('TEST_ramses', 'machine')
388         >>> setVar('TEST_local', 'local')
389         >>> setVar('OVERRIDES', 'arm')
390
391         >>> setVar('TEST', 'original')
392         >>> update_data()
393         >>> print getVar('TEST')
394         target
395
396         >>> setVar('OVERRIDES', 'arm:ramses:local')
397         >>> setVar('TEST', 'original')
398         >>> update_data()
399         >>> print getVar('TEST')
400         local
401     """
402
403     debug(2, "update_data()")
404
405 #   can't do delete env[...] while iterating over the dictionary, so remember them
406     dodel = []
407     overrides = (getVar('OVERRIDES', d, 1) or "").split(':') or []
408
409     def applyOverrides(var, d = _data):
410         if not overrides:
411             debug(1, "OVERRIDES not defined, nothing to do")
412             return
413         val = getVar(var, d)
414         for o in overrides:
415             if var.endswith("_" + o):
416                 l = len(o)+1
417                 name = var[:-l]
418                 d[name] = d[var]
419
420     for s in d.keys():
421         applyOverrides(s, d)
422         sval = getVar(s, d) or ""
423
424 #       Handle line appends:
425         for (a, o) in getVarFlag(s, '_append', d) or []:
426             # maybe the OVERRIDE was not yet added so keep the append
427             if (o and o in overrides) or not o:
428                 delVarFlag(s, '_append', d)
429             if o:
430                 if not o in overrides:
431                     continue
432             sval+=a
433             setVar(s, sval, d)
434
435 #       Handle line prepends
436         for (a, o) in getVarFlag(s, '_prepend', d) or []:
437             # maybe the OVERRIDE was not yet added so keep the append
438             if (o and o in overrides) or not o:
439                 delVarFlag(s, '_prepend', d)
440             if o:
441                 if not o in overrides:
442                     continue
443             sval=a+sval
444             setVar(s, sval, d)
445
446 #       Handle line deletions
447         name = s + "_delete"
448         nameval = getVar(name, d)
449         if nameval:
450             sval = getVar(s, d)
451             if sval:
452                 new = ''
453                 pattern = nameval.replace('\n','').strip()
454                 for line in sval.split('\n'):
455                     if line.find(pattern) == -1:
456                         new = new + '\n' + line
457                 setVar(s, new, d)
458                 dodel.append(name)
459
460 #   delete all environment vars no longer needed
461     for s in dodel:
462         delVar(s, d)
463
464 def inherits_class(klass, d):
465     val = getVar('__inherit_cache', d) or ""
466     if ("classes/%s.oeclass" % klass) in val.split():
467         return True
468     return False
469
470 def _test():
471     """Start a doctest run on this module"""
472     import doctest
473     from oe import data
474     doctest.testmod(data)
475
476 if __name__ == "__main__":
477     _test()