# # Copyright (c) 2023 BayLibre, SAS # Author: Julien Stepahn # # SPDX-License-Identifier: GPL-2.0-only # import os import re import bb.tinfoil import oeqa.utils.ftools as ftools from oeqa.utils.commands import runCmd, get_bb_var, get_bb_vars, bitbake from oeqa.selftest.case import OESelftestTestCase class BBLock(OESelftestTestCase): @classmethod def setUpClass(cls): super(BBLock, cls).setUpClass() cls.lockfile = cls.builddir + "/conf/bblock.conf" def unlock_recipes(self, recipes=None, tasks=None): cmd = "bblock -r " if recipes: cmd += " ".join(recipes) if tasks: cmd += " -t " + ",".join(tasks) result = runCmd(cmd) if recipes: # ensure all signatures are removed from lockfile contents = ftools.read_file(self.lockfile) for recipe in recipes: for task in tasks: find_in_contents = re.search( 'SIGGEN_LOCKEDSIGS_.+\s\+=\s"%s:%s:.*"' % (recipe, task), contents, ) self.assertFalse( find_in_contents, msg="%s:%s should not be present into bblock.conf anymore" % (recipe, task), ) self.assertExists(self.lockfile) else: self.assertNotExists(self.lockfile) def lock_recipes(self, recipes, tasks=None): cmd = "bblock " + " ".join(recipes) if tasks: cmd += " -t " + ",".join(tasks) result = runCmd(cmd) self.assertExists(self.lockfile) # ensure all signatures are added to lockfile contents = ftools.read_file(self.lockfile) for recipe in recipes: if tasks: for task in tasks: find_in_contents = re.search( 'SIGGEN_LOCKEDSIGS_.+\s\+=\s"%s:%s:.*"' % (recipe, task), contents, ) self.assertTrue( find_in_contents, msg="%s:%s was not added into bblock.conf. bblock output: %s" % (recipe, task, result.output), ) def modify_tasks(self, recipes, tasks): task_append = "" for recipe in recipes: bb_vars = get_bb_vars(["PV"], recipe) recipe_pv = bb_vars["PV"] recipe_append_file = recipe + "_" + recipe_pv + ".bbappend" os.mkdir(os.path.join(self.testlayer_path, "recipes-test", recipe)) recipe_append_path = os.path.join( self.testlayer_path, "recipes-test", recipe, recipe_append_file ) for task in tasks: task_append += "%s:append() {\n#modify task hash \n}\n" % task ftools.write_file(recipe_append_path, task_append) self.add_command_to_tearDown( "rm -rf %s" % os.path.join(self.testlayer_path, "recipes-test", recipe) ) def test_lock_single_recipe_single_task(self): recipes = ["quilt"] tasks = ["do_compile"] self._run_test(recipes, tasks) def test_lock_single_recipe_multiple_tasks(self): recipes = ["quilt"] tasks = ["do_compile", "do_install"] self._run_test(recipes, tasks) def test_lock_single_recipe_all_tasks(self): recipes = ["quilt"] self._run_test(recipes, None) def test_lock_multiple_recipe_single_task(self): recipes = ["quilt", "bc"] tasks = ["do_compile"] self._run_test(recipes, tasks) def test_lock_architecture_specific(self): # unlock all recipes and ensure no bblock.conf file exist self.unlock_recipes() recipes = ["quilt"] tasks = ["do_compile"] # lock quilt's do_compile task for another machine if self.td["MACHINE"] == "qemux86-64": machine = "qemuarm" else: machine = "qemux86-64" self.write_config('MACHINE = "%s"\n' % machine) self.lock_recipes(recipes, tasks) self.write_config('MACHINE = "%s"\n' % self.td["MACHINE"]) # modify quilt's do_compile task self.modify_tasks(recipes, tasks) # build quilt using the default machine # No Note/Warning should be emitted since sig is locked for another machine # (quilt package is architecture dependant) info_message = "NOTE: The following recipes have locked tasks: " + recipes[0] warn_message = "The %s:%s sig is computed to be" % (recipes[0], tasks[0]) result = bitbake(recipes[0] + " -n") self.assertNotIn(info_message, result.output) self.assertNotIn(warn_message, result.output) # unlock all recipes self.unlock_recipes() def _run_test(self, recipes, tasks=None): # unlock all recipes and ensure no bblock.conf file exist self.unlock_recipes() self.write_config('BB_SIGNATURE_HANDLER = "OEBasicHash"') # lock tasks for recipes result = self.lock_recipes(recipes, tasks) if not tasks: tasks = [] result = bitbake("-c listtasks " + recipes[0]) with bb.tinfoil.Tinfoil() as tinfoil: tinfoil.prepare(config_only=False, quiet=2) d = tinfoil.parse_recipe(recipes[0]) for line in result.output.splitlines(): if line.startswith("do_"): task = line.split()[0] if "setscene" in task: continue if d.getVarFlag(task, "nostamp"): continue tasks.append(task) # build recipes. At this stage we should have a Note about recipes # having locked task's sig, but no warning since sig still match info_message = "NOTE: The following recipes have locked tasks: " + " ".join( recipes ) for recipe in recipes: result = bitbake(recipe + " -n") self.assertIn(info_message, result.output) for task in tasks: warn_message = "The %s:%s sig is computed to be" % (recipe, task) self.assertNotIn(warn_message, result.output) # modify all tasks that are locked to trigger a sig change then build the recipes # at this stage we should have a Note as before, but also a Warning for all # locked tasks indicating the sig mismatch self.modify_tasks(recipes, tasks) for recipe in recipes: result = bitbake(recipe + " -n") self.assertIn(info_message, result.output) for task in tasks: warn_message = "The %s:%s sig is computed to be" % (recipe, task) self.assertIn(warn_message, result.output) # unlock all tasks and rebuild, no more Note/Warning should remain self.unlock_recipes(recipes, tasks) for recipe in recipes: result = bitbake(recipe + " -n") self.assertNotIn(info_message, result.output) for task in tasks: warn_message = "The %s:%s sig is computed to be" % (recipe, task) self.assertNotIn(warn_message, result.output) # unlock all recipes self.unlock_recipes()