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