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