From c665a2c93337fc372207af604c21bdea5babb4e5 Mon Sep 17 00:00:00 2001 From: Richard Purdie Date: Tue, 9 Jan 2024 13:47:36 +0000 Subject: [PATCH] bitbake: ast: Fix EXPORT_FUNCTIONS bug If you have two classes, both of which set EXPORT_FUNCTIONS for the same funciton and a standard funciton definition for the function that is exported, the export function can sometimes overwrite the standard one. The issue is that the internal flag the code uses isn't ovweritten if the variable is giving a new value. Fix the issue by using a comment in the code that is injected so that we know if it is ours or not. Also add some testing for EXPORT_FUNCTIONS, not perfect but a start. (Bitbake rev: 66306d5151acb0a26a171c338d8f60eb9eb16c6b) Signed-off-by: Richard Purdie --- bitbake/lib/bb/parse/ast.py | 9 ++-- bitbake/lib/bb/tests/parse.py | 98 +++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+), 4 deletions(-) diff --git a/bitbake/lib/bb/parse/ast.py b/bitbake/lib/bb/parse/ast.py index 6441c5cf7c..d30b688965 100644 --- a/bitbake/lib/bb/parse/ast.py +++ b/bitbake/lib/bb/parse/ast.py @@ -211,10 +211,12 @@ class ExportFuncsNode(AstNode): def eval(self, data): + sentinel = " # Export function set\n" for func in self.n: calledfunc = self.classname + "_" + func - if data.getVar(func, False) and not data.getVarFlag(func, 'export_func', False): + basevar = data.getVar(func, False) + if basevar and sentinel not in basevar: continue if data.getVar(func, False): @@ -231,12 +233,11 @@ class ExportFuncsNode(AstNode): data.setVarFlag(func, "lineno", 1) if data.getVarFlag(calledfunc, "python", False): - data.setVar(func, " bb.build.exec_func('" + calledfunc + "', d)\n", parsing=True) + data.setVar(func, sentinel + " 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') + data.setVar(func, sentinel + " " + calledfunc + "\n", parsing=True) class AddTaskNode(AstNode): def __init__(self, filename, lineno, func, before, after): diff --git a/bitbake/lib/bb/tests/parse.py b/bitbake/lib/bb/tests/parse.py index 304bbbe222..72d1962e7e 100644 --- a/bitbake/lib/bb/tests/parse.py +++ b/bitbake/lib/bb/tests/parse.py @@ -243,3 +243,101 @@ unset A[flag@.service] with self.assertRaises(bb.parse.ParseError): d = bb.parse.handle(f.name, self.d)[''] + export_function_recipe = """ +inherit someclass +""" + + export_function_recipe2 = """ +inherit someclass + +do_compile () { + false +} + +python do_compilepython () { + bb.note("Something else") +} + +""" + export_function_class = """ +someclass_do_compile() { + true +} + +python someclass_do_compilepython () { + bb.note("Something") +} + +EXPORT_FUNCTIONS do_compile do_compilepython +""" + + export_function_class2 = """ +secondclass_do_compile() { + true +} + +python secondclass_do_compilepython () { + bb.note("Something") +} + +EXPORT_FUNCTIONS do_compile do_compilepython +""" + + def test_parse_export_functions(self): + def check_function_flags(d): + self.assertEqual(d.getVarFlag("do_compile", "func"), 1) + self.assertEqual(d.getVarFlag("do_compilepython", "func"), 1) + self.assertEqual(d.getVarFlag("do_compile", "python"), None) + self.assertEqual(d.getVarFlag("do_compilepython", "python"), "1") + + with tempfile.TemporaryDirectory() as tempdir: + self.d.setVar("__bbclasstype", "recipe") + recipename = tempdir + "/recipe.bb" + os.makedirs(tempdir + "/classes") + with open(tempdir + "/classes/someclass.bbclass", "w") as f: + f.write(self.export_function_class) + f.flush() + with open(tempdir + "/classes/secondclass.bbclass", "w") as f: + f.write(self.export_function_class2) + f.flush() + + with open(recipename, "w") as f: + f.write(self.export_function_recipe) + f.flush() + os.chdir(tempdir) + d = bb.parse.handle(recipename, bb.data.createCopy(self.d))[''] + self.assertIn("someclass_do_compile", d.getVar("do_compile")) + self.assertIn("someclass_do_compilepython", d.getVar("do_compilepython")) + check_function_flags(d) + + recipename2 = tempdir + "/recipe2.bb" + with open(recipename2, "w") as f: + f.write(self.export_function_recipe2) + f.flush() + + d = bb.parse.handle(recipename2, bb.data.createCopy(self.d))[''] + self.assertNotIn("someclass_do_compile", d.getVar("do_compile")) + self.assertNotIn("someclass_do_compilepython", d.getVar("do_compilepython")) + self.assertIn("false", d.getVar("do_compile")) + self.assertIn("else", d.getVar("do_compilepython")) + check_function_flags(d) + + with open(recipename, "a+") as f: + f.write("\ninherit secondclass\n") + f.flush() + with open(recipename2, "a+") as f: + f.write("\ninherit secondclass\n") + f.flush() + + d = bb.parse.handle(recipename, bb.data.createCopy(self.d))[''] + self.assertIn("secondclass_do_compile", d.getVar("do_compile")) + self.assertIn("secondclass_do_compilepython", d.getVar("do_compilepython")) + check_function_flags(d) + + d = bb.parse.handle(recipename2, bb.data.createCopy(self.d))[''] + self.assertNotIn("someclass_do_compile", d.getVar("do_compile")) + self.assertNotIn("someclass_do_compilepython", d.getVar("do_compilepython")) + self.assertIn("false", d.getVar("do_compile")) + self.assertIn("else", d.getVar("do_compilepython")) + check_function_flags(d) +