1#!/usr/bin/env python3 2# bblock 3# lock/unlock task to latest signature 4# 5# Copyright (c) 2023 BayLibre, SAS 6# Author: Julien Stepahn <jstephan@baylibre.com> 7# 8# SPDX-License-Identifier: GPL-2.0-only 9# 10 11import os 12import sys 13import logging 14 15scripts_path = os.path.dirname(os.path.realpath(__file__)) 16lib_path = scripts_path + "/lib" 17sys.path = sys.path + [lib_path] 18 19import scriptpath 20 21scriptpath.add_bitbake_lib_path() 22 23import bb.tinfoil 24import bb.msg 25 26import argparse_oe 27 28myname = os.path.basename(sys.argv[0]) 29logger = bb.msg.logger_create(myname) 30 31 32def getTaskSignatures(tinfoil, pn, tasks): 33 tinfoil.set_event_mask( 34 [ 35 "bb.event.GetTaskSignatureResult", 36 "logging.LogRecord", 37 "bb.command.CommandCompleted", 38 "bb.command.CommandFailed", 39 ] 40 ) 41 ret = tinfoil.run_command("getTaskSignatures", pn, tasks) 42 if ret: 43 while True: 44 event = tinfoil.wait_event(1) 45 if event: 46 if isinstance(event, bb.command.CommandCompleted): 47 break 48 elif isinstance(event, bb.command.CommandFailed): 49 logger.error(str(event)) 50 sys.exit(2) 51 elif isinstance(event, bb.event.GetTaskSignatureResult): 52 sig = event.sig 53 elif isinstance(event, logging.LogRecord): 54 logger.handle(event) 55 else: 56 logger.error("No result returned from getTaskSignatures command") 57 sys.exit(2) 58 return sig 59 60 61def parseRecipe(tinfoil, recipe): 62 try: 63 tinfoil.parse_recipes() 64 d = tinfoil.parse_recipe(recipe) 65 except Exception: 66 logger.error("Failed to get recipe info for: %s" % recipe) 67 sys.exit(1) 68 return d 69 70 71def bblockDump(lockfile): 72 try: 73 with open(lockfile, "r") as lockfile: 74 for line in lockfile: 75 print(line.strip()) 76 except IOError: 77 return 1 78 return 0 79 80 81def bblockReset(lockfile, pns, package_archs, tasks): 82 if not pns: 83 logger.info("Unlocking all recipes") 84 try: 85 os.remove(lockfile) 86 except FileNotFoundError: 87 pass 88 else: 89 logger.info("Unlocking {pns}".format(pns=pns)) 90 tmp_lockfile = lockfile + ".tmp" 91 with open(lockfile, "r") as infile, open(tmp_lockfile, "w") as outfile: 92 for line in infile: 93 if not ( 94 any(element in line for element in pns) 95 and any(element in line for element in package_archs.split()) 96 ): 97 outfile.write(line) 98 else: 99 if tasks and not any(element in line for element in tasks): 100 outfile.write(line) 101 os.remove(lockfile) 102 os.rename(tmp_lockfile, lockfile) 103 104 105def main(): 106 parser = argparse_oe.ArgumentParser(description="Lock and unlock a recipe") 107 parser.add_argument("pn", nargs="*", help="Space separated list of recipe to lock") 108 parser.add_argument( 109 "-t", 110 "--tasks", 111 help="Comma separated list of tasks", 112 type=lambda s: [ 113 task if task.startswith("do_") else "do_" + task for task in s.split(",") 114 ], 115 ) 116 parser.add_argument( 117 "-r", 118 "--reset", 119 action="store_true", 120 help="Unlock pn recipes, or all recipes if pn is empty", 121 ) 122 parser.add_argument( 123 "-d", 124 "--dump", 125 action="store_true", 126 help="Dump generated bblock.conf file", 127 ) 128 129 global_args, unparsed_args = parser.parse_known_args() 130 131 with bb.tinfoil.Tinfoil() as tinfoil: 132 tinfoil.prepare(config_only=True) 133 134 package_archs = tinfoil.config_data.getVar("PACKAGE_ARCHS") 135 builddir = tinfoil.config_data.getVar("TOPDIR") 136 lockfile = "{builddir}/conf/bblock.conf".format(builddir=builddir) 137 138 if global_args.dump: 139 bblockDump(lockfile) 140 return 0 141 142 if global_args.reset: 143 bblockReset(lockfile, global_args.pn, package_archs, global_args.tasks) 144 return 0 145 146 with open(lockfile, "a") as lockfile: 147 s = "" 148 if lockfile.tell() == 0: 149 s = "# Generated by bblock\n" 150 s += 'SIGGEN_LOCKEDSIGS_TASKSIG_CHECK = "info"\n' 151 s += 'SIGGEN_LOCKEDSIGS_TYPES += "${PACKAGE_ARCHS}"\n' 152 s += "\n" 153 154 for pn in global_args.pn: 155 d = parseRecipe(tinfoil, pn) 156 package_arch = d.getVar("PACKAGE_ARCH") 157 siggen_locked_sigs_package_arch = d.getVar( 158 "SIGGEN_LOCKEDSIGS_{package_arch}".format(package_arch=package_arch) 159 ) 160 sigs = getTaskSignatures(tinfoil, [pn], global_args.tasks) 161 for sig in sigs: 162 new_entry = "{pn}:{taskname}:{sig}".format( 163 pn=sig[0], taskname=sig[1], sig=sig[2] 164 ) 165 if ( 166 siggen_locked_sigs_package_arch 167 and not new_entry in siggen_locked_sigs_package_arch 168 ) or not siggen_locked_sigs_package_arch: 169 s += 'SIGGEN_LOCKEDSIGS_{package_arch} += "{new_entry}"\n'.format( 170 package_arch=package_arch, new_entry=new_entry 171 ) 172 lockfile.write(s) 173 return 0 174 175 176if __name__ == "__main__": 177 try: 178 ret = main() 179 except Exception: 180 ret = 1 181 import traceback 182 183 traceback.print_exc() 184 sys.exit(ret) 185