mirror of
https://git.yoctoproject.org/git/poky
synced 2026-01-04 16:10:04 +00:00
It is becomming increasingly clear we need to find a way to show what is/is not an override in our syntax. We need to do this in a way which is clear to users, readable and in a way we can transition to. The most effective way I've found to this is to use the ":" charater to directly replace "_" where an override is being specified. This includes "append", "prepend" and "remove" which are effectively special override directives. This patch simply adds the character to the parser so bitbake accepts the value but maps it back to "_" internally so there is no behaviour change. This change is simple enough it could potentially be backported to older version of bitbake meaning layers using the new syntax/markup could work with older releases. Even if other no other changes are accepted at this time and we don't backport, it does set us on a path where at some point in future we could require a more explict syntax. I've tested this patch by converting oe-core/meta-yocto to the new syntax for overrides (9000+ changes) and then seeing that builds continue to work with this patch. (Bitbake rev: aa9f7b80cfdb1119050af469a07ebd949829026c) Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
434 lines
16 KiB
Python
434 lines
16 KiB
Python
"""
|
|
AbstractSyntaxTree classes for the Bitbake language
|
|
"""
|
|
|
|
# Copyright (C) 2003, 2004 Chris Larson
|
|
# Copyright (C) 2003, 2004 Phil Blundell
|
|
# Copyright (C) 2009 Holger Hans Peter Freyther
|
|
#
|
|
# SPDX-License-Identifier: GPL-2.0-only
|
|
#
|
|
|
|
import bb
|
|
from bb import methodpool
|
|
from bb.parse import logger
|
|
|
|
class StatementGroup(list):
|
|
def eval(self, data):
|
|
for statement in self:
|
|
statement.eval(data)
|
|
|
|
class AstNode(object):
|
|
def __init__(self, filename, lineno):
|
|
self.filename = filename
|
|
self.lineno = lineno
|
|
|
|
class IncludeNode(AstNode):
|
|
def __init__(self, filename, lineno, what_file, force):
|
|
AstNode.__init__(self, filename, lineno)
|
|
self.what_file = what_file
|
|
self.force = force
|
|
|
|
def eval(self, data):
|
|
"""
|
|
Include the file and evaluate the statements
|
|
"""
|
|
s = data.expand(self.what_file)
|
|
logger.debug(2, "CONF %s:%s: including %s", self.filename, self.lineno, s)
|
|
|
|
# TODO: Cache those includes... maybe not here though
|
|
if self.force:
|
|
bb.parse.ConfHandler.include(self.filename, s, self.lineno, data, "include required")
|
|
else:
|
|
bb.parse.ConfHandler.include(self.filename, s, self.lineno, data, False)
|
|
|
|
class ExportNode(AstNode):
|
|
def __init__(self, filename, lineno, var):
|
|
AstNode.__init__(self, filename, lineno)
|
|
self.var = var
|
|
|
|
def eval(self, data):
|
|
data.setVarFlag(self.var, "export", 1, op = 'exported')
|
|
|
|
class UnsetNode(AstNode):
|
|
def __init__(self, filename, lineno, var):
|
|
AstNode.__init__(self, filename, lineno)
|
|
self.var = var
|
|
|
|
def eval(self, data):
|
|
loginfo = {
|
|
'variable': self.var,
|
|
'file': self.filename,
|
|
'line': self.lineno,
|
|
}
|
|
data.delVar(self.var,**loginfo)
|
|
|
|
class UnsetFlagNode(AstNode):
|
|
def __init__(self, filename, lineno, var, flag):
|
|
AstNode.__init__(self, filename, lineno)
|
|
self.var = var
|
|
self.flag = flag
|
|
|
|
def eval(self, data):
|
|
loginfo = {
|
|
'variable': self.var,
|
|
'file': self.filename,
|
|
'line': self.lineno,
|
|
}
|
|
data.delVarFlag(self.var, self.flag, **loginfo)
|
|
|
|
class DataNode(AstNode):
|
|
"""
|
|
Various data related updates. For the sake of sanity
|
|
we have one class doing all this. This means that all
|
|
this need to be re-evaluated... we might be able to do
|
|
that faster with multiple classes.
|
|
"""
|
|
def __init__(self, filename, lineno, groupd):
|
|
AstNode.__init__(self, filename, lineno)
|
|
self.groupd = groupd
|
|
|
|
def getFunc(self, key, data):
|
|
if 'flag' in self.groupd and self.groupd['flag'] is not None:
|
|
return data.getVarFlag(key, self.groupd['flag'], expand=False, noweakdefault=True)
|
|
else:
|
|
return data.getVar(key, False, noweakdefault=True, parsing=True)
|
|
|
|
def eval(self, data):
|
|
groupd = self.groupd
|
|
key = groupd["var"]
|
|
key = key.replace(":", "_")
|
|
loginfo = {
|
|
'variable': key,
|
|
'file': self.filename,
|
|
'line': self.lineno,
|
|
}
|
|
if "exp" in groupd and groupd["exp"] is not None:
|
|
data.setVarFlag(key, "export", 1, op = 'exported', **loginfo)
|
|
|
|
op = "set"
|
|
if "ques" in groupd and groupd["ques"] is not None:
|
|
val = self.getFunc(key, data)
|
|
op = "set?"
|
|
if val is None:
|
|
val = groupd["value"]
|
|
elif "colon" in groupd and groupd["colon"] is not None:
|
|
e = data.createCopy()
|
|
op = "immediate"
|
|
val = e.expand(groupd["value"], key + "[:=]")
|
|
elif "append" in groupd and groupd["append"] is not None:
|
|
op = "append"
|
|
val = "%s %s" % ((self.getFunc(key, data) or ""), groupd["value"])
|
|
elif "prepend" in groupd and groupd["prepend"] is not None:
|
|
op = "prepend"
|
|
val = "%s %s" % (groupd["value"], (self.getFunc(key, data) or ""))
|
|
elif "postdot" in groupd and groupd["postdot"] is not None:
|
|
op = "postdot"
|
|
val = "%s%s" % ((self.getFunc(key, data) or ""), groupd["value"])
|
|
elif "predot" in groupd and groupd["predot"] is not None:
|
|
op = "predot"
|
|
val = "%s%s" % (groupd["value"], (self.getFunc(key, data) or ""))
|
|
else:
|
|
val = groupd["value"]
|
|
|
|
flag = None
|
|
if 'flag' in groupd and groupd['flag'] is not None:
|
|
flag = groupd['flag']
|
|
elif groupd["lazyques"]:
|
|
flag = "_defaultval"
|
|
|
|
loginfo['op'] = op
|
|
loginfo['detail'] = groupd["value"]
|
|
|
|
if flag:
|
|
data.setVarFlag(key, flag, val, **loginfo)
|
|
else:
|
|
data.setVar(key, val, parsing=True, **loginfo)
|
|
|
|
class MethodNode(AstNode):
|
|
tr_tbl = str.maketrans('/.+-@%&', '_______')
|
|
|
|
def __init__(self, filename, lineno, func_name, body, python, fakeroot):
|
|
AstNode.__init__(self, filename, lineno)
|
|
self.func_name = func_name
|
|
self.body = body
|
|
self.python = python
|
|
self.fakeroot = fakeroot
|
|
|
|
def eval(self, data):
|
|
text = '\n'.join(self.body)
|
|
funcname = self.func_name
|
|
if self.func_name == "__anonymous":
|
|
funcname = ("__anon_%s_%s" % (self.lineno, self.filename.translate(MethodNode.tr_tbl)))
|
|
self.python = True
|
|
text = "def %s(d):\n" % (funcname) + text
|
|
bb.methodpool.insert_method(funcname, text, self.filename, self.lineno - len(self.body) - 1)
|
|
anonfuncs = data.getVar('__BBANONFUNCS', False) or []
|
|
anonfuncs.append(funcname)
|
|
data.setVar('__BBANONFUNCS', anonfuncs)
|
|
if data.getVar(funcname, False):
|
|
# clean up old version of this piece of metadata, as its
|
|
# flags could cause problems
|
|
data.delVarFlag(funcname, 'python')
|
|
data.delVarFlag(funcname, 'fakeroot')
|
|
if self.python:
|
|
data.setVarFlag(funcname, "python", "1")
|
|
if self.fakeroot:
|
|
data.setVarFlag(funcname, "fakeroot", "1")
|
|
data.setVarFlag(funcname, "func", 1)
|
|
data.setVar(funcname, text, parsing=True)
|
|
data.setVarFlag(funcname, 'filename', self.filename)
|
|
data.setVarFlag(funcname, 'lineno', str(self.lineno - len(self.body)))
|
|
|
|
class PythonMethodNode(AstNode):
|
|
def __init__(self, filename, lineno, function, modulename, body):
|
|
AstNode.__init__(self, filename, lineno)
|
|
self.function = function
|
|
self.modulename = modulename
|
|
self.body = body
|
|
|
|
def eval(self, data):
|
|
# Note we will add root to parsedmethods after having parse
|
|
# 'this' file. This means we will not parse methods from
|
|
# bb classes twice
|
|
text = '\n'.join(self.body)
|
|
bb.methodpool.insert_method(self.modulename, text, self.filename, self.lineno - len(self.body) - 1)
|
|
data.setVarFlag(self.function, "func", 1)
|
|
data.setVarFlag(self.function, "python", 1)
|
|
data.setVar(self.function, text, parsing=True)
|
|
data.setVarFlag(self.function, 'filename', self.filename)
|
|
data.setVarFlag(self.function, 'lineno', str(self.lineno - len(self.body) - 1))
|
|
|
|
class ExportFuncsNode(AstNode):
|
|
def __init__(self, filename, lineno, fns, classname):
|
|
AstNode.__init__(self, filename, lineno)
|
|
self.n = fns.split()
|
|
self.classname = classname
|
|
|
|
def eval(self, data):
|
|
|
|
for func in self.n:
|
|
func = func.replace(":", "_")
|
|
calledfunc = self.classname + "_" + func
|
|
|
|
if data.getVar(func, False) and not data.getVarFlag(func, 'export_func', False):
|
|
continue
|
|
|
|
if data.getVar(func, False):
|
|
data.setVarFlag(func, 'python', None)
|
|
data.setVarFlag(func, 'func', None)
|
|
|
|
for flag in [ "func", "python" ]:
|
|
if data.getVarFlag(calledfunc, flag, False):
|
|
data.setVarFlag(func, flag, data.getVarFlag(calledfunc, flag, False))
|
|
for flag in [ "dirs" ]:
|
|
if data.getVarFlag(func, flag, False):
|
|
data.setVarFlag(calledfunc, flag, data.getVarFlag(func, flag, False))
|
|
data.setVarFlag(func, "filename", "autogenerated")
|
|
data.setVarFlag(func, "lineno", 1)
|
|
|
|
if data.getVarFlag(calledfunc, "python", False):
|
|
data.setVar(func, " bb.build.exec_func('" + calledfunc + "', d)\n", parsing=True)
|
|
else:
|
|
if "-" in self.classname:
|
|
bb.fatal("The classname %s contains a dash character and is calling an sh function %s using EXPORT_FUNCTIONS. Since a dash is illegal in sh function names, this cannot work, please rename the class or don't use EXPORT_FUNCTIONS." % (self.classname, calledfunc))
|
|
data.setVar(func, " " + calledfunc + "\n", parsing=True)
|
|
data.setVarFlag(func, 'export_func', '1')
|
|
|
|
class AddTaskNode(AstNode):
|
|
def __init__(self, filename, lineno, func, before, after):
|
|
AstNode.__init__(self, filename, lineno)
|
|
self.func = func
|
|
self.before = before
|
|
self.after = after
|
|
|
|
def eval(self, data):
|
|
bb.build.addtask(self.func, self.before, self.after, data)
|
|
|
|
class DelTaskNode(AstNode):
|
|
def __init__(self, filename, lineno, tasks):
|
|
AstNode.__init__(self, filename, lineno)
|
|
self.tasks = tasks
|
|
|
|
def eval(self, data):
|
|
tasks = data.expand(self.tasks).split()
|
|
for task in tasks:
|
|
bb.build.deltask(task, data)
|
|
|
|
class BBHandlerNode(AstNode):
|
|
def __init__(self, filename, lineno, fns):
|
|
AstNode.__init__(self, filename, lineno)
|
|
self.hs = fns.split()
|
|
|
|
def eval(self, data):
|
|
bbhands = data.getVar('__BBHANDLERS', False) or []
|
|
for h in self.hs:
|
|
bbhands.append(h)
|
|
data.setVarFlag(h, "handler", 1)
|
|
data.setVar('__BBHANDLERS', bbhands)
|
|
|
|
class InheritNode(AstNode):
|
|
def __init__(self, filename, lineno, classes):
|
|
AstNode.__init__(self, filename, lineno)
|
|
self.classes = classes
|
|
|
|
def eval(self, data):
|
|
bb.parse.BBHandler.inherit(self.classes, self.filename, self.lineno, data)
|
|
|
|
def handleInclude(statements, filename, lineno, m, force):
|
|
statements.append(IncludeNode(filename, lineno, m.group(1), force))
|
|
|
|
def handleExport(statements, filename, lineno, m):
|
|
statements.append(ExportNode(filename, lineno, m.group(1)))
|
|
|
|
def handleUnset(statements, filename, lineno, m):
|
|
statements.append(UnsetNode(filename, lineno, m.group(1)))
|
|
|
|
def handleUnsetFlag(statements, filename, lineno, m):
|
|
statements.append(UnsetFlagNode(filename, lineno, m.group(1), m.group(2)))
|
|
|
|
def handleData(statements, filename, lineno, groupd):
|
|
statements.append(DataNode(filename, lineno, groupd))
|
|
|
|
def handleMethod(statements, filename, lineno, func_name, body, python, fakeroot):
|
|
statements.append(MethodNode(filename, lineno, func_name, body, python, fakeroot))
|
|
|
|
def handlePythonMethod(statements, filename, lineno, funcname, modulename, body):
|
|
statements.append(PythonMethodNode(filename, lineno, funcname, modulename, body))
|
|
|
|
def handleExportFuncs(statements, filename, lineno, m, classname):
|
|
statements.append(ExportFuncsNode(filename, lineno, m.group(1), classname))
|
|
|
|
def handleAddTask(statements, filename, lineno, m):
|
|
func = m.group("func")
|
|
before = m.group("before")
|
|
after = m.group("after")
|
|
if func is None:
|
|
return
|
|
|
|
statements.append(AddTaskNode(filename, lineno, func, before, after))
|
|
|
|
def handleDelTask(statements, filename, lineno, m):
|
|
func = m.group(1)
|
|
if func is None:
|
|
return
|
|
|
|
statements.append(DelTaskNode(filename, lineno, func))
|
|
|
|
def handleBBHandlers(statements, filename, lineno, m):
|
|
statements.append(BBHandlerNode(filename, lineno, m.group(1)))
|
|
|
|
def handleInherit(statements, filename, lineno, m):
|
|
classes = m.group(1)
|
|
statements.append(InheritNode(filename, lineno, classes))
|
|
|
|
def runAnonFuncs(d):
|
|
code = []
|
|
for funcname in d.getVar("__BBANONFUNCS", False) or []:
|
|
code.append("%s(d)" % funcname)
|
|
bb.utils.better_exec("\n".join(code), {"d": d})
|
|
|
|
def finalize(fn, d, variant = None):
|
|
saved_handlers = bb.event.get_handlers().copy()
|
|
try:
|
|
for var in d.getVar('__BBHANDLERS', False) or []:
|
|
# try to add the handler
|
|
handlerfn = d.getVarFlag(var, "filename", False)
|
|
if not handlerfn:
|
|
bb.fatal("Undefined event handler function '%s'" % var)
|
|
handlerln = int(d.getVarFlag(var, "lineno", False))
|
|
bb.event.register(var, d.getVar(var, False), (d.getVarFlag(var, "eventmask") or "").split(), handlerfn, handlerln)
|
|
|
|
bb.event.fire(bb.event.RecipePreFinalise(fn), d)
|
|
|
|
bb.data.expandKeys(d)
|
|
|
|
bb.event.fire(bb.event.RecipePostKeyExpansion(fn), d)
|
|
|
|
runAnonFuncs(d)
|
|
|
|
tasklist = d.getVar('__BBTASKS', False) or []
|
|
bb.event.fire(bb.event.RecipeTaskPreProcess(fn, list(tasklist)), d)
|
|
bb.build.add_tasks(tasklist, d)
|
|
|
|
bb.parse.siggen.finalise(fn, d, variant)
|
|
|
|
d.setVar('BBINCLUDED', bb.parse.get_file_depends(d))
|
|
|
|
bb.event.fire(bb.event.RecipeParsed(fn), d)
|
|
finally:
|
|
bb.event.set_handlers(saved_handlers)
|
|
|
|
def _create_variants(datastores, names, function, onlyfinalise):
|
|
def create_variant(name, orig_d, arg = None):
|
|
if onlyfinalise and name not in onlyfinalise:
|
|
return
|
|
new_d = bb.data.createCopy(orig_d)
|
|
function(arg or name, new_d)
|
|
datastores[name] = new_d
|
|
|
|
for variant in list(datastores.keys()):
|
|
for name in names:
|
|
if not variant:
|
|
# Based on main recipe
|
|
create_variant(name, datastores[""])
|
|
else:
|
|
create_variant("%s-%s" % (variant, name), datastores[variant], name)
|
|
|
|
def multi_finalize(fn, d):
|
|
appends = (d.getVar("__BBAPPEND") or "").split()
|
|
for append in appends:
|
|
logger.debug(1, "Appending .bbappend file %s to %s", append, fn)
|
|
bb.parse.BBHandler.handle(append, d, True)
|
|
|
|
onlyfinalise = d.getVar("__ONLYFINALISE", False)
|
|
|
|
safe_d = d
|
|
d = bb.data.createCopy(safe_d)
|
|
try:
|
|
finalize(fn, d)
|
|
except bb.parse.SkipRecipe as e:
|
|
d.setVar("__SKIPPED", e.args[0])
|
|
datastores = {"": safe_d}
|
|
|
|
extended = d.getVar("BBCLASSEXTEND") or ""
|
|
if extended:
|
|
# the following is to support bbextends with arguments, for e.g. multilib
|
|
# an example is as follows:
|
|
# BBCLASSEXTEND = "multilib:lib32"
|
|
# it will create foo-lib32, inheriting multilib.bbclass and set
|
|
# BBEXTENDCURR to "multilib" and BBEXTENDVARIANT to "lib32"
|
|
extendedmap = {}
|
|
variantmap = {}
|
|
|
|
for ext in extended.split():
|
|
eext = ext.split(':', 2)
|
|
if len(eext) > 1:
|
|
extendedmap[ext] = eext[0]
|
|
variantmap[ext] = eext[1]
|
|
else:
|
|
extendedmap[ext] = ext
|
|
|
|
pn = d.getVar("PN")
|
|
def extendfunc(name, d):
|
|
if name != extendedmap[name]:
|
|
d.setVar("BBEXTENDCURR", extendedmap[name])
|
|
d.setVar("BBEXTENDVARIANT", variantmap[name])
|
|
else:
|
|
d.setVar("PN", "%s-%s" % (pn, name))
|
|
bb.parse.BBHandler.inherit(extendedmap[name], fn, 0, d)
|
|
|
|
safe_d.setVar("BBCLASSEXTEND", extended)
|
|
_create_variants(datastores, extendedmap.keys(), extendfunc, onlyfinalise)
|
|
|
|
for variant in datastores.keys():
|
|
if variant:
|
|
try:
|
|
if not onlyfinalise or variant in onlyfinalise:
|
|
finalize(fn, datastores[variant], variant)
|
|
except bb.parse.SkipRecipe as e:
|
|
datastores[variant].setVar("__SKIPPED", e.args[0])
|
|
|
|
datastores[""] = d
|
|
return datastores
|