#!/usr/bin/env python
# ex:ts=4:sw=4:sts=4:et
# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
"""
BitBake 'Data' implementations
Functions for interacting with the data structure used by the
BitBake build tools.
Copyright (C) 2003, 2004 Chris Larson
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place, Suite 330, Boston, MA 02111-1307 USA.
Based on functions from the base bb module, Copyright 2003 Holger Schurig
"""
import sys, os, re, time, types
if sys.argv[0][-5:] == "pydoc":
path = os.path.dirname(os.path.dirname(sys.argv[1]))
else:
path = os.path.dirname(os.path.dirname(sys.argv[0]))
sys.path.append(path)
from bb import note, debug
def init():
return {}
_data = init()
def initVar(var, d = _data):
"""Non-destructive var init for data structure"""
if not var in d:
d[var] = {}
if not "flags" in d[var]:
d[var]["flags"] = {}
__setvar_regexp__ = {}
__setvar_regexp__["_append"] = re.compile('(?P.*?)%s(_(?P.*))?' % "_append")
__setvar_regexp__["_prepend"] = re.compile('(?P.*?)%s(_(?P.*))?' % "_prepend")
__setvar_regexp__["_delete"] = re.compile('(?P.*?)%s(_(?P.*))?' % "_delete")
def setVar(var, value, d = _data):
"""Set a variable to a given value
Example:
>>> setVar('TEST', 'testcontents')
>>> print getVar('TEST')
testcontents
"""
for v in ["_append", "_prepend", "_delete"]:
match = __setvar_regexp__[v].match(var)
if match:
base = match.group('base')
override = match.group('add')
l = getVarFlag(base, v, d) or []
if override == 'delete':
if l.count([value, None]):
del l[l.index([value, None])]
l.append([value, override])
setVarFlag(base, v, l, d)
return
if not var in d:
initVar(var, d)
if getVarFlag(var, 'matchesenv', d):
delVarFlag(var, 'matchesenv', d)
setVarFlag(var, 'export', 1, d)
d[var]["content"] = value
def getVar(var, d = _data, exp = 0):
"""Gets the value of a variable
Example:
>>> setVar('TEST', 'testcontents')
>>> print getVar('TEST')
testcontents
"""
if not var in d or not "content" in d[var]:
return None
if exp:
return expand(d[var]["content"], d, var)
return d[var]["content"]
def delVar(var, d = _data):
"""Removes a variable from the data set
Example:
>>> setVar('TEST', 'testcontents')
>>> print getVar('TEST')
testcontents
>>> delVar('TEST')
>>> print getVar('TEST')
None
"""
if var in d:
del d[var]
def setVarFlag(var, flag, flagvalue, d = _data):
"""Set a flag for a given variable to a given value
Example:
>>> setVarFlag('TEST', 'python', 1)
>>> print getVarFlag('TEST', 'python')
1
"""
# print "d[%s][\"flags\"][%s] = %s" % (var, flag, flagvalue)
if not var in d:
initVar(var, d)
d[var]["flags"][flag] = flagvalue
def getVarFlag(var, flag, d = _data):
"""Gets given flag from given var
Example:
>>> setVarFlag('TEST', 'python', 1)
>>> print getVarFlag('TEST', 'python')
1
"""
if var in d and "flags" in d[var] and flag in d[var]["flags"]:
return d[var]["flags"][flag]
return None
def delVarFlag(var, flag, d = _data):
"""Removes a given flag from the variable's flags
Example:
>>> setVarFlag('TEST', 'testflag', 1)
>>> print getVarFlag('TEST', 'testflag')
1
>>> delVarFlag('TEST', 'testflag')
>>> print getVarFlag('TEST', 'testflag')
None
"""
if var in d and "flags" in d[var] and flag in d[var]["flags"]:
del d[var]["flags"][flag]
def setVarFlags(var, flags, d = _data):
"""Set the flags for a given variable
Example:
>>> myflags = {}
>>> myflags['test'] = 'blah'
>>> setVarFlags('TEST', myflags)
>>> print getVarFlag('TEST', 'test')
blah
"""
if not var in d:
initVar(var, d)
d[var]["flags"] = flags
def getVarFlags(var, d = _data):
"""Gets a variable's flags
Example:
>>> setVarFlag('TEST', 'test', 'blah')
>>> print getVarFlags('TEST')['test']
blah
"""
if var in d and "flags" in d[var]:
return d[var]["flags"]
return None
def delVarFlags(var, d = _data):
"""Removes a variable's flags
Example:
>>> setVarFlag('TEST', 'testflag', 1)
>>> print getVarFlag('TEST', 'testflag')
1
>>> delVarFlags('TEST')
>>> print getVarFlags('TEST')
None
"""
if var in d and "flags" in d[var]:
del d[var]["flags"]
def getData(d = _data):
"""Returns the data object used"""
return d
def setData(newData, d = _data):
"""Sets the data object to the supplied value"""
d = newData
__expand_var_regexp__ = re.compile(r"\${[^{}]+}")
__expand_python_regexp__ = re.compile(r"\${@.+?}")
def expand(s, d = _data, varname = None):
"""Variable expansion using the data store.
Example:
Standard expansion:
>>> setVar('A', 'sshd')
>>> print expand('/usr/bin/${A}')
/usr/bin/sshd
Python expansion:
>>> print expand('result: ${@37 * 72}')
result: 2664
"""
def var_sub(match):
key = match.group()[2:-1]
if varname and key:
if varname == key:
raise Exception("variable %s references itself!" % varname)
var = getVar(key, d, 1)
if var is not None:
return var
else:
return match.group()
def python_sub(match):
import bb
code = match.group()[3:-1]
locals()['d'] = d
s = eval(code)
if type(s) == types.IntType: s = str(s)
return s
if type(s) is not types.StringType: # sanity check
return s
while s.find('$') != -1:
olds = s
try:
s = __expand_var_regexp__.sub(var_sub, s)
s = __expand_python_regexp__.sub(python_sub, s)
if s == olds: break
if type(s) is not types.StringType: # sanity check
import bb
bb.error('expansion of %s returned non-string %s' % (olds, s))
except:
import bb
bb.note("%s:%s while evaluating:\n%s" % (sys.exc_info()[0], sys.exc_info()[1], s))
raise
return s
def expandKeys(alterdata = _data, readdata = None):
if readdata == None:
readdata = alterdata
for key in alterdata.keys():
ekey = expand(key, readdata)
if key == ekey:
continue
val = getVar(key, alterdata)
if val is None:
continue
# import copy
# setVarFlags(ekey, copy.copy(getVarFlags(key, readdata)), alterdata)
setVar(ekey, val, alterdata)
for i in ('_append', '_prepend', '_delete'):
dest = getVarFlag(ekey, i, alterdata) or []
src = getVarFlag(key, i, readdata) or []
dest.extend(src)
setVarFlag(ekey, i, dest, alterdata)
delVar(key, alterdata)
def expandData(alterdata = _data, readdata = None):
"""For each variable in alterdata, expand it, and update the var contents.
Replacements use data from readdata.
Example:
>>> a=init()
>>> b=init()
>>> setVar("dlmsg", "dl_dir is ${DL_DIR}", a)
>>> setVar("DL_DIR", "/path/to/whatever", b)
>>> expandData(a, b)
>>> print getVar("dlmsg", a)
dl_dir is /path/to/whatever
"""
if readdata == None:
readdata = alterdata
for key in alterdata.keys():
val = getVar(key, alterdata)
if type(val) is not types.StringType:
continue
expanded = expand(val, readdata)
# print "key is %s, val is %s, expanded is %s" % (key, val, expanded)
if val != expanded:
setVar(key, expanded, alterdata)
import os
def inheritFromOS(d = _data):
"""Inherit variables from the environment."""
# fakeroot needs to be able to set these
non_inherit_vars = [ "LD_LIBRARY_PATH", "LD_PRELOAD" ]
for s in os.environ.keys():
if not s in non_inherit_vars:
try:
setVar(s, os.environ[s], d)
setVarFlag(s, 'matchesenv', '1', d)
except TypeError:
pass
import sys
def emit_var(var, o=sys.__stdout__, d = _data, all=False):
"""Emit a variable to be sourced by a shell."""
if getVarFlag(var, "python", d):
return 0
try:
if all:
oval = getVar(var, d, 0)
val = getVar(var, d, 1)
except KeyboardInterrupt:
raise
except:
o.write('# expansion of %s threw %s\n' % (var, sys.exc_info()[0]))
return 0
if all:
o.write('# %s=%s\n' % (var, oval))
if type(val) is not types.StringType:
return 0
if getVarFlag(var, 'matchesenv', d):
return 0
if var.find("-") != -1 or var.find(".") != -1 or var.find('{') != -1 or var.find('}') != -1 or var.find('+') != -1:
return 0
val.rstrip()
if not val:
return 0
if getVarFlag(var, "func", d):
# NOTE: should probably check for unbalanced {} within the var
o.write("%s() {\n%s\n}\n" % (var, val))
else:
if getVarFlag(var, "export", d):
o.write('export ')
else:
if not all:
return 0
# if we're going to output this within doublequotes,
# to a shell, we need to escape the quotes in the var
alter = re.sub('"', '\\"', val.strip())
o.write('%s="%s"\n' % (var, alter))
return 1
def emit_env(o=sys.__stdout__, d = _data, all=False):
"""Emits all items in the data store in a format such that it can be sourced by a shell."""
env = d.keys()
for e in env:
if getVarFlag(e, "func", d):
continue
emit_var(e, o, d, all) and o.write('\n')
for e in env:
if not getVarFlag(e, "func", d):
continue
emit_var(e, o, d) and o.write('\n')
def update_data(d = _data):
"""Modifies the environment vars according to local overrides and commands.
Examples:
Appending to a variable:
>>> setVar('TEST', 'this is a')
>>> setVar('TEST_append', ' test')
>>> setVar('TEST_append', ' of the emergency broadcast system.')
>>> update_data()
>>> print getVar('TEST')
this is a test of the emergency broadcast system.
Prepending to a variable:
>>> setVar('TEST', 'virtual/libc')
>>> setVar('TEST_prepend', 'virtual/tmake ')
>>> setVar('TEST_prepend', 'virtual/patcher ')
>>> update_data()
>>> print getVar('TEST')
virtual/patcher virtual/tmake virtual/libc
Overrides:
>>> setVar('TEST_arm', 'target')
>>> setVar('TEST_ramses', 'machine')
>>> setVar('TEST_local', 'local')
>>> setVar('OVERRIDES', 'arm')
>>> setVar('TEST', 'original')
>>> update_data()
>>> print getVar('TEST')
target
>>> setVar('OVERRIDES', 'arm:ramses:local')
>>> setVar('TEST', 'original')
>>> update_data()
>>> print getVar('TEST')
local
"""
debug(2, "update_data()")
# can't do delete env[...] while iterating over the dictionary, so remember them
dodel = []
overrides = (getVar('OVERRIDES', d, 1) or "").split(':') or []
def applyOverrides(var, d = _data):
if not overrides:
debug(1, "OVERRIDES not defined, nothing to do")
return
val = getVar(var, d)
for o in overrides:
if var.endswith("_" + o):
l = len(o)+1
name = var[:-l]
d[name] = d[var]
for s in d.keys():
applyOverrides(s, d)
sval = getVar(s, d) or ""
# Handle line appends:
for (a, o) in getVarFlag(s, '_append', d) or []:
# maybe the OVERRIDE was not yet added so keep the append
if (o and o in overrides) or not o:
delVarFlag(s, '_append', d)
if o:
if not o in overrides:
continue
sval+=a
setVar(s, sval, d)
# Handle line prepends
for (a, o) in getVarFlag(s, '_prepend', d) or []:
# maybe the OVERRIDE was not yet added so keep the append
if (o and o in overrides) or not o:
delVarFlag(s, '_prepend', d)
if o:
if not o in overrides:
continue
sval=a+sval
setVar(s, sval, d)
# Handle line deletions
name = s + "_delete"
nameval = getVar(name, d)
if nameval:
sval = getVar(s, d)
if sval:
new = ''
pattern = nameval.replace('\n','').strip()
for line in sval.split('\n'):
if line.find(pattern) == -1:
new = new + '\n' + line
setVar(s, new, d)
dodel.append(name)
# delete all environment vars no longer needed
for s in dodel:
delVar(s, d)
def inherits_class(klass, d):
val = getVar('__inherit_cache', d) or ""
if os.path.join('classes', '%s.bbclass' % klass) in val.split():
return True
return False
def _test():
"""Start a doctest run on this module"""
import doctest
from bb import data
doctest.testmod(data)
if __name__ == "__main__":
_test()