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