1# 2# Copyright (c) 2023 BayLibre, SAS 3# Author: Julien Stepahn <jstephan@baylibre.com> 4# 5# SPDX-License-Identifier: GPL-2.0-only 6# 7 8import os 9import re 10import bb.tinfoil 11 12import oeqa.utils.ftools as ftools 13from oeqa.utils.commands import runCmd, get_bb_var, get_bb_vars, bitbake 14 15from oeqa.selftest.case import OESelftestTestCase 16 17 18class BBLock(OESelftestTestCase): 19 @classmethod 20 def setUpClass(cls): 21 super(BBLock, cls).setUpClass() 22 cls.lockfile = cls.builddir + "/conf/bblock.conf" 23 24 def unlock_recipes(self, recipes=None, tasks=None): 25 cmd = "bblock -r " 26 if recipes: 27 cmd += " ".join(recipes) 28 if tasks: 29 cmd += " -t " + ",".join(tasks) 30 result = runCmd(cmd) 31 32 if recipes: 33 # ensure all signatures are removed from lockfile 34 contents = ftools.read_file(self.lockfile) 35 for recipe in recipes: 36 for task in tasks: 37 find_in_contents = re.search( 38 'SIGGEN_LOCKEDSIGS_.+\s\+=\s"%s:%s:.*"' % (recipe, task), 39 contents, 40 ) 41 self.assertFalse( 42 find_in_contents, 43 msg="%s:%s should not be present into bblock.conf anymore" 44 % (recipe, task), 45 ) 46 self.assertExists(self.lockfile) 47 else: 48 self.assertNotExists(self.lockfile) 49 50 def lock_recipes(self, recipes, tasks=None): 51 cmd = "bblock " + " ".join(recipes) 52 if tasks: 53 cmd += " -t " + ",".join(tasks) 54 55 result = runCmd(cmd) 56 57 self.assertExists(self.lockfile) 58 59 # ensure all signatures are added to lockfile 60 contents = ftools.read_file(self.lockfile) 61 for recipe in recipes: 62 if tasks: 63 for task in tasks: 64 find_in_contents = re.search( 65 'SIGGEN_LOCKEDSIGS_.+\s\+=\s"%s:%s:.*"' % (recipe, task), 66 contents, 67 ) 68 self.assertTrue( 69 find_in_contents, 70 msg="%s:%s was not added into bblock.conf. bblock output: %s" 71 % (recipe, task, result.output), 72 ) 73 74 def modify_tasks(self, recipes, tasks): 75 task_append = "" 76 for recipe in recipes: 77 bb_vars = get_bb_vars(["PV"], recipe) 78 recipe_pv = bb_vars["PV"] 79 recipe_append_file = recipe + "_" + recipe_pv + ".bbappend" 80 81 os.mkdir(os.path.join(self.testlayer_path, "recipes-test", recipe)) 82 recipe_append_path = os.path.join( 83 self.testlayer_path, "recipes-test", recipe, recipe_append_file 84 ) 85 86 for task in tasks: 87 task_append += "%s:append() {\n#modify task hash \n}\n" % task 88 ftools.write_file(recipe_append_path, task_append) 89 self.add_command_to_tearDown( 90 "rm -rf %s" % os.path.join(self.testlayer_path, "recipes-test", recipe) 91 ) 92 93 def test_lock_single_recipe_single_task(self): 94 recipes = ["quilt"] 95 tasks = ["do_compile"] 96 self._run_test(recipes, tasks) 97 98 def test_lock_single_recipe_multiple_tasks(self): 99 recipes = ["quilt"] 100 tasks = ["do_compile", "do_install"] 101 self._run_test(recipes, tasks) 102 103 def test_lock_single_recipe_all_tasks(self): 104 recipes = ["quilt"] 105 self._run_test(recipes, None) 106 107 def test_lock_multiple_recipe_single_task(self): 108 recipes = ["quilt", "bc"] 109 tasks = ["do_compile"] 110 self._run_test(recipes, tasks) 111 112 def test_lock_architecture_specific(self): 113 # unlock all recipes and ensure no bblock.conf file exist 114 self.unlock_recipes() 115 116 recipes = ["quilt"] 117 tasks = ["do_compile"] 118 119 # lock quilt's do_compile task for another machine 120 if self.td["MACHINE"] == "qemux86-64": 121 machine = "qemuarm" 122 else: 123 machine = "qemux86-64" 124 125 self.write_config('MACHINE = "%s"\n' % machine) 126 127 self.lock_recipes(recipes, tasks) 128 129 self.write_config('MACHINE = "%s"\n' % self.td["MACHINE"]) 130 # modify quilt's do_compile task 131 self.modify_tasks(recipes, tasks) 132 133 # build quilt using the default machine 134 # No Note/Warning should be emitted since sig is locked for another machine 135 # (quilt package is architecture dependant) 136 info_message = "NOTE: The following recipes have locked tasks: " + recipes[0] 137 warn_message = "The %s:%s sig is computed to be" % (recipes[0], tasks[0]) 138 result = bitbake(recipes[0] + " -n") 139 self.assertNotIn(info_message, result.output) 140 self.assertNotIn(warn_message, result.output) 141 142 # unlock all recipes 143 self.unlock_recipes() 144 145 def _run_test(self, recipes, tasks=None): 146 # unlock all recipes and ensure no bblock.conf file exist 147 self.unlock_recipes() 148 149 self.write_config('BB_SIGNATURE_HANDLER = "OEBasicHash"') 150 151 # lock tasks for recipes 152 result = self.lock_recipes(recipes, tasks) 153 154 if not tasks: 155 tasks = [] 156 result = bitbake("-c listtasks " + recipes[0]) 157 with bb.tinfoil.Tinfoil() as tinfoil: 158 tinfoil.prepare(config_only=False, quiet=2) 159 d = tinfoil.parse_recipe(recipes[0]) 160 161 for line in result.output.splitlines(): 162 if line.startswith("do_"): 163 task = line.split()[0] 164 if "setscene" in task: 165 continue 166 if d.getVarFlag(task, "nostamp"): 167 continue 168 tasks.append(task) 169 170 # build recipes. At this stage we should have a Note about recipes 171 # having locked task's sig, but no warning since sig still match 172 info_message = "NOTE: The following recipes have locked tasks: " + " ".join( 173 recipes 174 ) 175 for recipe in recipes: 176 result = bitbake(recipe + " -n") 177 self.assertIn(info_message, result.output) 178 for task in tasks: 179 warn_message = "The %s:%s sig is computed to be" % (recipe, task) 180 self.assertNotIn(warn_message, result.output) 181 182 # modify all tasks that are locked to trigger a sig change then build the recipes 183 # at this stage we should have a Note as before, but also a Warning for all 184 # locked tasks indicating the sig mismatch 185 self.modify_tasks(recipes, tasks) 186 for recipe in recipes: 187 result = bitbake(recipe + " -n") 188 self.assertIn(info_message, result.output) 189 for task in tasks: 190 warn_message = "The %s:%s sig is computed to be" % (recipe, task) 191 self.assertIn(warn_message, result.output) 192 193 # unlock all tasks and rebuild, no more Note/Warning should remain 194 self.unlock_recipes(recipes, tasks) 195 for recipe in recipes: 196 result = bitbake(recipe + " -n") 197 self.assertNotIn(info_message, result.output) 198 for task in tasks: 199 warn_message = "The %s:%s sig is computed to be" % (recipe, task) 200 self.assertNotIn(warn_message, result.output) 201 202 # unlock all recipes 203 self.unlock_recipes() 204