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