diff options
author | Julien Stephan <jstephan@baylibre.com> | 2023-09-25 10:04:51 +0200 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2023-10-09 15:58:47 +0100 |
commit | 91db19fc36c2822dc271d572fbbf369963e6632b (patch) | |
tree | 646a0f259b3d78132feedb2fb30a9f3c35476a18 /scripts/bblock | |
parent | 043ca5f64db8d98c30bdc4cb1f3f6976561495e7 (diff) | |
download | poky-91db19fc36c2822dc271d572fbbf369963e6632b.tar.gz |
scripts/bblock: add a script to lock/unlock recipes
bblock script allows to lock/unlock recipes to latest task signatures.
The idea is to prevent some recipes to be rebuilt during development.
For example when working on rust recipe, one may not want rust-native to be
rebuilt.
This tool can be used, with proper environment set up, using the following
command:
bblock <recipe_name>
See help for more details
if a <recipe_name>'s task signature change, this task will not be built again and
sstate cache will be used.
[YOCTO #13425]
(From OE-Core rev: 2d9ab0cfd7f3cacc347954676f1323342a6b286f)
Signed-off-by: Julien Stephan <jstephan@baylibre.com>
Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'scripts/bblock')
-rwxr-xr-x | scripts/bblock | 184 |
1 files changed, 184 insertions, 0 deletions
diff --git a/scripts/bblock b/scripts/bblock new file mode 100755 index 0000000000..0082059af8 --- /dev/null +++ b/scripts/bblock | |||
@@ -0,0 +1,184 @@ | |||
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 | |||
11 | import os | ||
12 | import sys | ||
13 | import logging | ||
14 | |||
15 | scripts_path = os.path.dirname(os.path.realpath(__file__)) | ||
16 | lib_path = scripts_path + "/lib" | ||
17 | sys.path = sys.path + [lib_path] | ||
18 | |||
19 | import scriptpath | ||
20 | |||
21 | scriptpath.add_bitbake_lib_path() | ||
22 | |||
23 | import bb.tinfoil | ||
24 | import bb.msg | ||
25 | |||
26 | import argparse_oe | ||
27 | |||
28 | myname = os.path.basename(sys.argv[0]) | ||
29 | logger = bb.msg.logger_create(myname) | ||
30 | |||
31 | |||
32 | def 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 | |||
61 | def 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 | |||
71 | def 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 | |||
81 | def 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 | |||
105 | def 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 | |||
176 | if __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) | ||