data.py: Fix inherits_class() so it doesn't split lists. Add unexport handling to...
[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 The expandData and update_data are the most expensive
11 operations. At night the cookie monster came by and
12 suggested 'give me cookies on setting the variables and
13 things will work out'. Taking this suggestion into account
14 applying the skills from the not yet passed 'Entwurf und
15 Analyse von Algorithmen' lecture and the cookie 
16 monster seems to be right. We will track setVar more carefully
17 to have faster update_data and expandKeys operations.
18
19 This is a treade-off between speed and memory again but
20 the speed is more critical here.
21
22 Copyright (C) 2003, 2004  Chris Larson
23 Copyright (C) 2005        Holger Hans Peter Freyther
24
25 This program is free software; you can redistribute it and/or modify it under
26 the terms of the GNU General Public License as published by the Free Software
27 Foundation; either version 2 of the License, or (at your option) any later
28 version.
29
30 This program is distributed in the hope that it will be useful, but WITHOUT
31 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
32 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
33
34 You should have received a copy of the GNU General Public License along with
35 this program; if not, write to the Free Software Foundation, Inc., 59 Temple
36 Place, Suite 330, Boston, MA 02111-1307 USA. 
37
38 Based on functions from the base bb module, Copyright 2003 Holger Schurig
39 """
40
41 import sys, os, re, time, types
42 if sys.argv[0][-5:] == "pydoc":
43     path = os.path.dirname(os.path.dirname(sys.argv[1]))
44 else:
45     path = os.path.dirname(os.path.dirname(sys.argv[0]))
46 sys.path.insert(0,path)
47
48 from bb import data_smart
49 import bb
50
51 _dict_type = data_smart.DataSmart
52
53 def init():
54     return _dict_type()
55
56 def init_db(parent = None):
57     if parent:
58         return parent.createCopy()
59     else:
60         return _dict_type()
61
62 def createCopy(source):
63      """Link the source set to the destination
64      If one does not find the value in the destination set,
65      search will go on to the source set to get the value.
66      Value from source are copy-on-write. i.e. any try to
67      modify one of them will end up putting the modified value
68      in the destination set.
69      """
70      return source.createCopy()
71
72 def initVar(var, d):
73     """Non-destructive var init for data structure"""
74     d.initVar(var)
75
76
77 def setVar(var, value, d):
78     """Set a variable to a given value
79
80     Example:
81         >>> d = init()
82         >>> setVar('TEST', 'testcontents', d)
83         >>> print getVar('TEST', d)
84         testcontents
85     """
86     d.setVar(var,value)
87
88
89 def getVar(var, d, exp = 0):
90     """Gets the value of a variable
91
92     Example:
93         >>> d = init()
94         >>> setVar('TEST', 'testcontents', d)
95         >>> print getVar('TEST', d)
96         testcontents
97     """
98     return d.getVar(var,exp)
99
100 def delVar(var, d):
101     """Removes a variable from the data set
102
103     Example:
104         >>> d = init()
105         >>> setVar('TEST', 'testcontents', d)
106         >>> print getVar('TEST', d)
107         testcontents
108         >>> delVar('TEST', d)
109         >>> print getVar('TEST', d)
110         None
111     """
112     d.delVar(var)
113
114 def setVarFlag(var, flag, flagvalue, d):
115     """Set a flag for a given variable to a given value
116
117     Example:
118         >>> d = init()
119         >>> setVarFlag('TEST', 'python', 1, d)
120         >>> print getVarFlag('TEST', 'python', d)
121         1
122     """
123     d.setVarFlag(var,flag,flagvalue)
124
125 def getVarFlag(var, flag, d):
126     """Gets given flag from given var
127
128     Example:
129         >>> d = init()
130         >>> setVarFlag('TEST', 'python', 1, d)
131         >>> print getVarFlag('TEST', 'python', d)
132         1
133     """
134     return d.getVarFlag(var,flag)
135
136 def delVarFlag(var, flag, d):
137     """Removes a given flag from the variable's flags
138
139     Example:
140         >>> d = init()
141         >>> setVarFlag('TEST', 'testflag', 1, d)
142         >>> print getVarFlag('TEST', 'testflag', d)
143         1
144         >>> delVarFlag('TEST', 'testflag', d)
145         >>> print getVarFlag('TEST', 'testflag', d)
146         None
147
148     """
149     d.delVarFlag(var,flag)
150
151 def setVarFlags(var, flags, d):
152     """Set the flags for a given variable
153
154     Note:
155         setVarFlags will not clear previous
156         flags. Think of this method as
157         addVarFlags
158
159     Example:
160         >>> d = init()
161         >>> myflags = {}
162         >>> myflags['test'] = 'blah'
163         >>> setVarFlags('TEST', myflags, d)
164         >>> print getVarFlag('TEST', 'test', d)
165         blah
166     """
167     d.setVarFlags(var,flags)
168
169 def getVarFlags(var, d):
170     """Gets a variable's flags
171
172     Example:
173         >>> d = init()
174         >>> setVarFlag('TEST', 'test', 'blah', d)
175         >>> print getVarFlags('TEST', d)['test']
176         blah
177     """
178     return d.getVarFlags(var)
179
180 def delVarFlags(var, d):
181     """Removes a variable's flags
182
183     Example:
184         >>> data = init()
185         >>> setVarFlag('TEST', 'testflag', 1, data)
186         >>> print getVarFlag('TEST', 'testflag', data)
187         1
188         >>> delVarFlags('TEST', data)
189         >>> print getVarFlags('TEST', data)
190         None
191
192     """
193     d.delVarFlags(var)
194
195 def keys(d):
196     """Return a list of keys in d
197
198     Example:
199         >>> d = init()
200         >>> setVar('TEST',  1, d)
201         >>> setVar('MOO' ,  2, d)
202         >>> setVarFlag('TEST', 'test', 1, d)
203         >>> keys(d)
204         ['TEST', 'MOO']
205     """
206     return d.keys()
207
208 def getData(d):
209     """Returns the data object used"""
210     return d
211
212 def setData(newData, d):
213     """Sets the data object to the supplied value"""
214     d = newData
215
216
217 ##
218 ## Cookie Monsters' query functions
219 ##
220 def _get_override_vars(d, override):
221     """
222     Internal!!!
223
224     Get the Names of Variables that have a specific
225     override. This function returns a iterable
226     Set or an empty list
227     """
228     return []
229
230 def _get_var_flags_triple(d):
231     """
232     Internal!!!
233
234     """
235     return []
236
237 __expand_var_regexp__ = re.compile(r"\${[^{}]+}")
238 __expand_python_regexp__ = re.compile(r"\${@.+?}")
239
240 def expand(s, d, varname = None):
241     """Variable expansion using the data store.
242
243     Example:
244         Standard expansion:
245         >>> d = init()
246         >>> setVar('A', 'sshd', d)
247         >>> print expand('/usr/bin/${A}', d)
248         /usr/bin/sshd
249
250         Python expansion:
251         >>> d = init()
252         >>> print expand('result: ${@37 * 72}', d)
253         result: 2664
254
255         Shell expansion:
256         >>> d = init()
257         >>> print expand('${TARGET_MOO}', d)
258         ${TARGET_MOO}
259         >>> setVar('TARGET_MOO', 'yupp', d)
260         >>> print expand('${TARGET_MOO}',d)
261         yupp
262         >>> setVar('SRC_URI', 'http://somebug.${TARGET_MOO}', d)
263         >>> delVar('TARGET_MOO', d)
264         >>> print expand('${SRC_URI}', d)
265         http://somebug.${TARGET_MOO}
266     """
267     return d.expand(s, varname)
268
269 def expandKeys(alterdata, readdata = None):
270     if readdata == None:
271         readdata = alterdata
272
273     for key in keys(alterdata):
274         if not '${' in key:
275             continue
276
277         ekey = expand(key, readdata)
278         if key == ekey:
279             continue
280         val = getVar(key, alterdata)
281         if val is None:
282             continue
283 #        import copy
284 #        setVarFlags(ekey, copy.copy(getVarFlags(key, readdata)), alterdata)
285         setVar(ekey, val, alterdata)
286
287         for i in ('_append', '_prepend'):
288             dest = getVarFlag(ekey, i, alterdata) or []
289             src = getVarFlag(key, i, readdata) or []
290             dest.extend(src)
291             setVarFlag(ekey, i, dest, alterdata)
292
293         delVar(key, alterdata)
294
295 def expandData(alterdata, readdata = None):
296     """For each variable in alterdata, expand it, and update the var contents.
297        Replacements use data from readdata.
298
299     Example:
300         >>> a=init()
301         >>> b=init()
302         >>> setVar("dlmsg", "dl_dir is ${DL_DIR}", a)
303         >>> setVar("DL_DIR", "/path/to/whatever", b)
304         >>> expandData(a, b)
305         >>> print getVar("dlmsg", a)
306         dl_dir is /path/to/whatever
307        """
308     if readdata == None:
309         readdata = alterdata
310
311     for key in keys(alterdata):
312         val = getVar(key, alterdata)
313         if type(val) is not types.StringType:
314             continue
315         expanded = expand(val, readdata)
316 #       print "key is %s, val is %s, expanded is %s" % (key, val, expanded)
317         if val != expanded:
318             setVar(key, expanded, alterdata)
319
320 import os
321
322 def inheritFromOS(d):
323     """Inherit variables from the environment."""
324 #   fakeroot needs to be able to set these
325     non_inherit_vars = [ "LD_LIBRARY_PATH", "LD_PRELOAD" ]
326     for s in os.environ.keys():
327         if not s in non_inherit_vars:
328             try:
329                 setVar(s, os.environ[s], d)
330                 setVarFlag(s, 'matchesenv', '1', d)
331             except TypeError:
332                 pass
333
334 import sys
335
336 def emit_var(var, o=sys.__stdout__, d = init(), all=False):
337     """Emit a variable to be sourced by a shell."""
338     if getVarFlag(var, "python", d):
339         return 0
340
341     try:
342         if all:
343             oval = getVar(var, d, 0)
344         val = getVar(var, d, 1)
345     except KeyboardInterrupt:
346         raise
347     except:
348         excname = str(sys.exc_info()[0])
349         if excname == "bb.build.FuncFailed":
350             raise
351         o.write('# expansion of %s threw %s\n' % (var, excname))
352         return 0
353
354     if all:
355         o.write('# %s=%s\n' % (var, oval))
356
357     if type(val) is not types.StringType:
358         return 0
359
360     if getVarFlag(var, 'matchesenv', d):
361         return 0
362
363     if (var.find("-") != -1 or var.find(".") != -1 or var.find('{') != -1 or var.find('}') != -1 or var.find('+') != -1) and not all:
364         return 0
365
366     val.rstrip()
367     if not val:
368         return 0
369         
370     varExpanded = expand(var, d)
371
372     if getVarFlag(var, "func", d):
373 #       NOTE: should probably check for unbalanced {} within the var
374         o.write("%s() {\n%s\n}\n" % (varExpanded, val))
375     else:
376         if getVarFlag(var, "unexport", d):
377             o.write('unset %s\n' % varExpanded)
378             return 1
379         if getVarFlag(var, "export", d):
380             o.write('export ')
381         else:
382             if not all:
383                 return 0
384 #       if we're going to output this within doublequotes,
385 #       to a shell, we need to escape the quotes in the var
386         alter = re.sub('"', '\\"', val.strip())
387         o.write('%s="%s"\n' % (varExpanded, alter))
388     return 1
389
390
391 def emit_env(o=sys.__stdout__, d = init(), all=False):
392     """Emits all items in the data store in a format such that it can be sourced by a shell."""
393
394     env = keys(d)
395
396     for e in env:
397         if getVarFlag(e, "func", d):
398             continue
399         emit_var(e, o, d, all) and o.write('\n')
400
401     for e in env:
402         if not getVarFlag(e, "func", d):
403             continue
404         emit_var(e, o, d) and o.write('\n')
405
406 def update_data(d):
407     """Modifies the environment vars according to local overrides and commands.
408     Examples:
409         Appending to a variable:
410         >>> d = init()
411         >>> setVar('TEST', 'this is a', d)
412         >>> setVar('TEST_append', ' test', d)
413         >>> setVar('TEST_append', ' of the emergency broadcast system.', d)
414         >>> update_data(d)
415         >>> print getVar('TEST', d)
416         this is a test of the emergency broadcast system.
417
418         Prepending to a variable:
419         >>> setVar('TEST', 'virtual/libc', d)
420         >>> setVar('TEST_prepend', 'virtual/tmake ', d)
421         >>> setVar('TEST_prepend', 'virtual/patcher ', d)
422         >>> update_data(d)
423         >>> print getVar('TEST', d)
424         virtual/patcher virtual/tmake virtual/libc
425
426         Overrides:
427         >>> setVar('TEST_arm', 'target', d)
428         >>> setVar('TEST_ramses', 'machine', d)
429         >>> setVar('TEST_local', 'local', d)
430         >>> setVar('OVERRIDES', 'arm', d)
431
432         >>> setVar('TEST', 'original', d)
433         >>> update_data(d)
434         >>> print getVar('TEST', d)
435         target
436
437         >>> setVar('OVERRIDES', 'arm:ramses:local', d)
438         >>> setVar('TEST', 'original', d)
439         >>> update_data(d)
440         >>> print getVar('TEST', d)
441         local
442
443         CopyMonster:
444         >>> e = d.createCopy()
445         >>> setVar('TEST_foo', 'foo', e)
446         >>> update_data(e)
447         >>> print getVar('TEST', e)
448         local
449
450         >>> setVar('OVERRIDES', 'arm:ramses:local:foo', e)
451         >>> update_data(e)
452         >>> print getVar('TEST', e)
453         foo
454
455         >>> f = d.createCopy()
456         >>> setVar('TEST_moo', 'something', f)
457         >>> setVar('OVERRIDES', 'moo:arm:ramses:local:foo', e)
458         >>> update_data(e)
459         >>> print getVar('TEST', e)
460         foo
461
462
463         >>> h = init()
464         >>> setVar('SRC_URI', 'file://append.foo;patch=1 ', h)
465         >>> g = h.createCopy()
466         >>> setVar('SRC_URI_append_arm', 'file://other.foo;patch=1', g)
467         >>> setVar('OVERRIDES', 'arm:moo', g)
468         >>> update_data(g)
469         >>> print getVar('SRC_URI', g)
470         file://append.foo;patch=1 file://other.foo;patch=1
471
472     """
473     bb.msg.debug(2, bb.msg.domain.Data, "update_data()")
474
475     # now ask the cookie monster for help
476     #print "Cookie Monster"
477     #print "Append/Prepend %s" % d._special_values
478     #print "Overrides      %s" % d._seen_overrides
479
480     overrides = (getVar('OVERRIDES', d, 1) or "").split(':') or []
481
482     #
483     # Well let us see what breaks here. We used to iterate
484     # over each variable and apply the override and then
485     # do the line expanding.
486     # If we have bad luck - which we will have - the keys
487     # where in some order that is so important for this
488     # method which we don't have anymore.
489     # Anyway we will fix that and write test cases this
490     # time.
491
492     #
493     # First we apply all overrides
494     # Then  we will handle _append and _prepend
495     #
496
497     for o in overrides:
498         # calculate '_'+override
499         l    = len(o)+1
500
501         # see if one should even try
502         if not d._seen_overrides.has_key(o):
503             continue
504
505         vars = d._seen_overrides[o]
506         for var in vars:
507             name = var[:-l]
508             try:
509                 d[name] = d[var]
510             except:
511                 bb.msg.note(1, bb.msg.domain.Data, "Untracked delVar")
512
513     # now on to the appends and prepends
514     if d._special_values.has_key('_append'):
515         appends = d._special_values['_append'] or []
516         for append in appends:
517             for (a, o) in getVarFlag(append, '_append', d) or []:
518                 # maybe the OVERRIDE was not yet added so keep the append
519                 if (o and o in overrides) or not o:
520                     delVarFlag(append, '_append', d)
521                 if o and not o in overrides:
522                     continue
523
524                 sval = getVar(append,d) or ""
525                 sval+=a
526                 setVar(append, sval, d)
527
528
529     if d._special_values.has_key('_prepend'):
530         prepends = d._special_values['_prepend'] or []
531
532         for prepend in prepends:
533             for (a, o) in getVarFlag(prepend, '_prepend', d) or []:
534                 # maybe the OVERRIDE was not yet added so keep the prepend
535                 if (o and o in overrides) or not o:
536                     delVarFlag(prepend, '_prepend', d)
537                 if o and not o in overrides:
538                     continue
539
540                 sval = a + (getVar(prepend,d) or "")
541                 setVar(prepend, sval, d)
542
543
544 def inherits_class(klass, d):
545     val = getVar('__inherit_cache', d) or []
546     if os.path.join('classes', '%s.bbclass' % klass) in val.split():
547         return True
548     return False
549
550 def _test():
551     """Start a doctest run on this module"""
552     import doctest
553     from bb import data
554     doctest.testmod(data)
555
556 if __name__ == "__main__":
557     _test()