createCopy:
[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 Copyright (C) 2005        Holger Hans Peter Freyther
12
13 This program is free software; you can redistribute it and/or modify it under
14 the terms of the GNU General Public License as published by the Free Software
15 Foundation; either version 2 of the License, or (at your option) any later
16 version.
17
18 This program is distributed in the hope that it will be useful, but WITHOUT
19 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
20 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
21
22 You should have received a copy of the GNU General Public License along with
23 this program; if not, write to the Free Software Foundation, Inc., 59 Temple
24 Place, Suite 330, Boston, MA 02111-1307 USA. 
25
26 Based on functions from the base bb module, Copyright 2003 Holger Schurig
27 """
28
29 import sys, os, re, time, types
30 if sys.argv[0][-5:] == "pydoc":
31     path = os.path.dirname(os.path.dirname(sys.argv[1]))
32 else:
33     path = os.path.dirname(os.path.dirname(sys.argv[0]))
34 sys.path.append(path)
35
36 from bb import note, debug, data_dict
37
38 _dict_type = data_dict.DataDict
39
40
41 def init():
42     return _dict_type()
43
44 _data_dict = init()
45
46 def createCopy(source):
47      """Link the source set to the destination
48      If one does not find the value in the destination set,
49      search will go on to the source set to get the value.
50      Value from source are copy-on-write. i.e. any try to
51      modify one of them will end up putting the modified value
52      in the destination set.
53      """
54      return source.createCopy()
55
56 def initVar(var, d = _data_dict):
57     """Non-destructive var init for data structure"""
58     d.initVar(var)
59
60
61 def setVar(var, value, d = _data_dict):
62     """Set a variable to a given value
63
64     Example:
65         >>> setVar('TEST', 'testcontents')
66         >>> print getVar('TEST')
67         testcontents
68     """
69     d.setVar(var,value)
70
71
72 def getVar(var, d = _data_dict, exp = 0):
73     """Gets the value of a variable
74
75     Example:
76         >>> setVar('TEST', 'testcontents')
77         >>> print getVar('TEST')
78         testcontents
79     """
80     return d.getVar(var,exp)
81
82 def delVar(var, d = _data_dict):
83     """Removes a variable from the data set
84
85     Example:
86         >>> setVar('TEST', 'testcontents')
87         >>> print getVar('TEST')
88         testcontents
89         >>> delVar('TEST')
90         >>> print getVar('TEST')
91         None
92     """
93     d.delVar(var)
94
95 def setVarFlag(var, flag, flagvalue, d = _data_dict):
96     """Set a flag for a given variable to a given value
97
98     Example:
99         >>> setVarFlag('TEST', 'python', 1)
100         >>> print getVarFlag('TEST', 'python')
101         1
102     """
103     d.setVarFlag(var,flag,flagvalue)
104
105 def getVarFlag(var, flag, d = _data_dict):
106     """Gets given flag from given var
107
108     Example:
109         >>> setVarFlag('TEST', 'python', 1)
110         >>> print getVarFlag('TEST', 'python')
111         1
112     """
113     return d.getVarFlag(var,flag)
114
115 def delVarFlag(var, flag, d = _data_dict):
116     """Removes a given flag from the variable's flags
117
118     Example:
119         >>> setVarFlag('TEST', 'testflag', 1)
120         >>> print getVarFlag('TEST', 'testflag')
121         1
122         >>> delVarFlag('TEST', 'testflag')
123         >>> print getVarFlag('TEST', 'testflag')
124         None
125
126     """
127     d.delVarFlag(var,flag)
128
129 def setVarFlags(var, flags, d = _data_dict):
130     """Set the flags for a given variable
131
132     Example:
133         >>> myflags = {}
134         >>> myflags['test'] = 'blah'
135         >>> setVarFlags('TEST', myflags)
136         >>> print getVarFlag('TEST', 'test')
137         blah
138     """
139     d.setVarFlags(var,flags)
140
141 def getVarFlags(var, d = _data_dict):
142     """Gets a variable's flags
143
144     Example:
145         >>> setVarFlag('TEST', 'test', 'blah')
146         >>> print getVarFlags('TEST')['test']
147         blah
148     """
149     return d.getVarFlags(var)
150
151 def delVarFlags(var, d = _data_dict):
152     """Removes a variable's flags
153
154     Example:
155         >>> data = init()
156         >>> setVarFlag('TEST', 'testflag', 1, data)
157         >>> print getVarFlag('TEST', 'testflag', data)
158         1
159         >>> delVarFlags('TEST', data)
160         >>> print getVarFlags('TEST', data)
161         None
162
163     """
164     d.delVarFlags(var)
165
166 def keys(d = _data_dict):
167     """Return a list of keys in d
168
169     Example:
170         >>> d = init()
171         >>> setVar('TEST',  1, d)
172         >>> setVar('MOO' ,  2, d)
173         >>> setVarFlag('TEST', 'test', 1, d)
174         >>> keys(d)
175         ['TEST', 'MOO']
176     """
177     return d.keys()
178
179 def getData(d = _data_dict):
180     """Returns the data object used"""
181     return d
182
183 def setData(newData, d = _data_dict):
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_dict, 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 bb
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 bb
233                 bb.error('expansion of %s returned non-string %s' % (olds, s))
234         except KeyboardInterrupt:
235             raise
236         except:
237             note("%s:%s while evaluating:\n%s" % (sys.exc_info()[0], sys.exc_info()[1], s))
238             raise
239     return s
240
241 def expandKeys(alterdata = _data_dict, readdata = None):
242     if readdata == None:
243         readdata = alterdata
244
245     for key in keys(alterdata):
246         ekey = expand(key, readdata)
247         if key == ekey:
248             continue
249         val = getVar(key, alterdata)
250         if val is None:
251             continue
252 #        import copy
253 #        setVarFlags(ekey, copy.copy(getVarFlags(key, readdata)), alterdata)
254         setVar(ekey, val, alterdata)
255
256         for i in ('_append', '_prepend', '_delete'):
257             dest = getVarFlag(ekey, i, alterdata) or []
258             src = getVarFlag(key, i, readdata) or []
259             dest.extend(src)
260             setVarFlag(ekey, i, dest, alterdata)
261
262         delVar(key, alterdata)
263
264 def expandData(alterdata = _data_dict, readdata = None):
265     """For each variable in alterdata, expand it, and update the var contents.
266        Replacements use data from readdata.
267
268     Example:
269         >>> a=init()
270         >>> b=init()
271         >>> setVar("dlmsg", "dl_dir is ${DL_DIR}", a)
272         >>> setVar("DL_DIR", "/path/to/whatever", b)
273         >>> expandData(a, b)
274         >>> print getVar("dlmsg", a)
275         dl_dir is /path/to/whatever
276        """
277     if readdata == None:
278         readdata = alterdata
279
280     for key in keys(alterdata):
281         val = getVar(key, alterdata)
282         if type(val) is not types.StringType:
283             continue
284         expanded = expand(val, readdata)
285 #       print "key is %s, val is %s, expanded is %s" % (key, val, expanded)
286         if val != expanded:
287             setVar(key, expanded, alterdata)
288
289 import os
290
291 def inheritFromOS(d = _data_dict):
292     """Inherit variables from the environment."""
293 #   fakeroot needs to be able to set these
294     non_inherit_vars = [ "LD_LIBRARY_PATH", "LD_PRELOAD" ]
295     for s in os.environ.keys():
296         if not s in non_inherit_vars:
297             try:
298                 setVar(s, os.environ[s], d)
299                 setVarFlag(s, 'matchesenv', '1', d)
300             except TypeError:
301                 pass
302
303 import sys
304
305 def emit_var(var, o=sys.__stdout__, d = _data_dict, all=False):
306     """Emit a variable to be sourced by a shell."""
307     if getVarFlag(var, "python", d):
308         return 0
309
310     try:
311         if all:
312             oval = getVar(var, d, 0)
313         val = getVar(var, d, 1)
314     except KeyboardInterrupt:
315         raise
316     except:
317         excname = str(sys.exc_info()[0])
318         if excname == "bb.build.FuncFailed":
319             raise
320         o.write('# expansion of %s threw %s\n' % (var, excname))
321         return 0
322
323     if all:
324         o.write('# %s=%s\n' % (var, oval))
325
326     if type(val) is not types.StringType:
327         return 0
328
329     if getVarFlag(var, 'matchesenv', d):
330         return 0
331
332     if (var.find("-") != -1 or var.find(".") != -1 or var.find('{') != -1 or var.find('}') != -1 or var.find('+') != -1) and not all:
333         return 0
334
335     val.rstrip()
336     if not val:
337         return 0
338
339     if getVarFlag(var, "func", d):
340 #       NOTE: should probably check for unbalanced {} within the var
341         o.write("%s() {\n%s\n}\n" % (var, val))
342     else:
343         if getVarFlag(var, "export", d):
344             o.write('export ')
345         else:
346             if not all:
347                 return 0
348 #       if we're going to output this within doublequotes,
349 #       to a shell, we need to escape the quotes in the var
350         alter = re.sub('"', '\\"', val.strip())
351         o.write('%s="%s"\n' % (var, alter))
352     return 1
353
354
355 def emit_env(o=sys.__stdout__, d = _data_dict, all=False):
356     """Emits all items in the data store in a format such that it can be sourced by a shell."""
357
358     env = keys(d)
359
360     for e in env:
361         if getVarFlag(e, "func", d):
362             continue
363         emit_var(e, o, d, all) and o.write('\n')
364
365     for e in env:
366         if not getVarFlag(e, "func", d):
367             continue
368         emit_var(e, o, d) and o.write('\n')
369
370 def update_data(d = _data_dict):
371     """Modifies the environment vars according to local overrides and commands.
372     Examples:
373         Appending to a variable:
374         >>> setVar('TEST', 'this is a')
375         >>> setVar('TEST_append', ' test')
376         >>> setVar('TEST_append', ' of the emergency broadcast system.')
377         >>> update_data()
378         >>> print getVar('TEST')
379         this is a test of the emergency broadcast system.
380
381         Prepending to a variable:
382         >>> setVar('TEST', 'virtual/libc')
383         >>> setVar('TEST_prepend', 'virtual/tmake ')
384         >>> setVar('TEST_prepend', 'virtual/patcher ')
385         >>> update_data()
386         >>> print getVar('TEST')
387         virtual/patcher virtual/tmake virtual/libc
388
389         Overrides:
390         >>> setVar('TEST_arm', 'target')
391         >>> setVar('TEST_ramses', 'machine')
392         >>> setVar('TEST_local', 'local')
393         >>> setVar('OVERRIDES', 'arm')
394
395         >>> setVar('TEST', 'original')
396         >>> update_data()
397         >>> print getVar('TEST')
398         target
399
400         >>> setVar('OVERRIDES', 'arm:ramses:local')
401         >>> setVar('TEST', 'original')
402         >>> update_data()
403         >>> print getVar('TEST')
404         local
405     """
406
407     debug(2, "update_data()")
408
409 #   can't do delete env[...] while iterating over the dictionary, so remember them
410     dodel = []
411     overrides = (getVar('OVERRIDES', d, 1) or "").split(':') or []
412
413     def applyOverrides(var, d = _data_dict):
414         if not overrides:
415             debug(1, "OVERRIDES not defined, nothing to do")
416             return
417         val = getVar(var, d)
418         for o in overrides:
419             if var.endswith("_" + o):
420                 l = len(o)+1
421                 name = var[:-l]
422                 d[name] = d[var]
423
424     for s in keys(d):
425         applyOverrides(s, d)
426         sval = getVar(s, d) or ""
427
428 #       Handle line appends:
429         for (a, o) in getVarFlag(s, '_append', d) or []:
430             # maybe the OVERRIDE was not yet added so keep the append
431             if (o and o in overrides) or not o:
432                 delVarFlag(s, '_append', d)
433             if o:
434                 if not o in overrides:
435                     continue
436             sval+=a
437             setVar(s, sval, d)
438
439 #       Handle line prepends
440         for (a, o) in getVarFlag(s, '_prepend', d) or []:
441             # maybe the OVERRIDE was not yet added so keep the append
442             if (o and o in overrides) or not o:
443                 delVarFlag(s, '_prepend', d)
444             if o:
445                 if not o in overrides:
446                     continue
447             sval=a+sval
448             setVar(s, sval, d)
449
450 #       Handle line deletions
451         name = s + "_delete"
452         nameval = getVar(name, d)
453         if nameval:
454             sval = getVar(s, d)
455             if sval:
456                 new = ''
457                 pattern = nameval.replace('\n','').strip()
458                 for line in sval.split('\n'):
459                     if line.find(pattern) == -1:
460                         new = new + '\n' + line
461                 setVar(s, new, d)
462                 dodel.append(name)
463
464 #   delete all environment vars no longer needed
465     for s in dodel:
466         delVar(s, d)
467
468 def inherits_class(klass, d):
469     val = getVar('__inherit_cache', d) or ""
470     if os.path.join('classes', '%s.bbclass' % klass) in val.split():
471         return True
472     return False
473
474 def _test():
475     """Start a doctest run on this module"""
476     import doctest
477     from bb import data
478     doctest.testmod(data)
479
480 if __name__ == "__main__":
481     _test()