bblock 5.5 KB

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