diff options
Diffstat (limited to 'scripts/lib/bsp/kernel.py')
-rw-r--r-- | scripts/lib/bsp/kernel.py | 1069 |
1 files changed, 0 insertions, 1069 deletions
diff --git a/scripts/lib/bsp/kernel.py b/scripts/lib/bsp/kernel.py deleted file mode 100644 index a3ee325a8f..0000000000 --- a/scripts/lib/bsp/kernel.py +++ /dev/null | |||
@@ -1,1069 +0,0 @@ | |||
1 | # ex:ts=4:sw=4:sts=4:et | ||
2 | # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- | ||
3 | # | ||
4 | # Copyright (c) 2012, Intel Corporation. | ||
5 | # All rights reserved. | ||
6 | # | ||
7 | # This program is free software; you can redistribute it and/or modify | ||
8 | # it under the terms of the GNU General Public License version 2 as | ||
9 | # published by the Free Software Foundation. | ||
10 | # | ||
11 | # This program is distributed in the hope that it will be useful, | ||
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | # GNU General Public License for more details. | ||
15 | # | ||
16 | # You should have received a copy of the GNU General Public License along | ||
17 | # with this program; if not, write to the Free Software Foundation, Inc., | ||
18 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
19 | # | ||
20 | # DESCRIPTION | ||
21 | # This module implements the kernel-related functions used by | ||
22 | # 'yocto-kernel' to manage kernel config items and patches for Yocto | ||
23 | # BSPs. | ||
24 | # | ||
25 | # AUTHORS | ||
26 | # Tom Zanussi <tom.zanussi (at] intel.com> | ||
27 | # | ||
28 | |||
29 | import sys | ||
30 | import os | ||
31 | import shutil | ||
32 | from .tags import * | ||
33 | import glob | ||
34 | import subprocess | ||
35 | from .engine import create_context | ||
36 | |||
37 | def find_bblayers(): | ||
38 | """ | ||
39 | Find and return a sanitized list of the layers found in BBLAYERS. | ||
40 | """ | ||
41 | try: | ||
42 | builddir = os.environ["BUILDDIR"] | ||
43 | except KeyError: | ||
44 | print("BUILDDIR not found, exiting. (Did you forget to source oe-init-build-env?)") | ||
45 | sys.exit(1) | ||
46 | bblayers_conf = os.path.join(builddir, "conf/bblayers.conf") | ||
47 | |||
48 | layers = [] | ||
49 | |||
50 | bitbake_env_cmd = "bitbake -e" | ||
51 | bitbake_env_lines = subprocess.Popen(bitbake_env_cmd, shell=True, | ||
52 | stdout=subprocess.PIPE).stdout.read().decode('utf-8') | ||
53 | |||
54 | if not bitbake_env_lines: | ||
55 | print("Couldn't get '%s' output, exiting." % bitbake_env_cmd) | ||
56 | sys.exit(1) | ||
57 | |||
58 | for line in bitbake_env_lines.split('\n'): | ||
59 | bblayers = get_line_val(line, "BBLAYERS") | ||
60 | if (bblayers): | ||
61 | break | ||
62 | |||
63 | if not bblayers: | ||
64 | print("Couldn't find BBLAYERS in %s output, exiting." % bitbake_env_cmd) | ||
65 | sys.exit(1) | ||
66 | |||
67 | raw_layers = bblayers.split() | ||
68 | |||
69 | for layer in raw_layers: | ||
70 | if layer == 'BBLAYERS' or '=' in layer: | ||
71 | continue | ||
72 | layers.append(layer) | ||
73 | |||
74 | return layers | ||
75 | |||
76 | |||
77 | def get_line_val(line, key): | ||
78 | """ | ||
79 | Extract the value from the VAR="val" string | ||
80 | """ | ||
81 | if line.startswith(key + "="): | ||
82 | stripped_line = line.split('=')[1] | ||
83 | stripped_line = stripped_line.replace('\"', '') | ||
84 | return stripped_line | ||
85 | return None | ||
86 | |||
87 | |||
88 | def find_meta_layer(): | ||
89 | """ | ||
90 | Find and return the meta layer in BBLAYERS. | ||
91 | """ | ||
92 | layers = find_bblayers() | ||
93 | |||
94 | for layer in layers: | ||
95 | if layer.endswith("meta"): | ||
96 | return layer | ||
97 | |||
98 | return None | ||
99 | |||
100 | |||
101 | def find_bsp_layer(machine): | ||
102 | """ | ||
103 | Find and return a machine's BSP layer in BBLAYERS. | ||
104 | """ | ||
105 | layers = find_bblayers() | ||
106 | |||
107 | for layer in layers: | ||
108 | if layer.endswith(machine): | ||
109 | return layer | ||
110 | |||
111 | print("Unable to find the BSP layer for machine %s." % machine) | ||
112 | print("Please make sure it is listed in bblayers.conf") | ||
113 | sys.exit(1) | ||
114 | |||
115 | |||
116 | def gen_choices_str(choices): | ||
117 | """ | ||
118 | Generate a numbered list of choices from a list of choices for | ||
119 | display to the user. | ||
120 | """ | ||
121 | choices_str = "" | ||
122 | |||
123 | for i, choice in enumerate(choices): | ||
124 | choices_str += "\t" + str(i + 1) + ") " + choice + "\n" | ||
125 | |||
126 | return choices_str | ||
127 | |||
128 | |||
129 | def open_user_file(scripts_path, machine, userfile, mode): | ||
130 | """ | ||
131 | Find one of the user files (user-config.cfg, user-patches.scc) | ||
132 | associated with the machine (could be in files/, | ||
133 | linux-yocto-custom/, etc). Returns the open file if found, None | ||
134 | otherwise. | ||
135 | |||
136 | The caller is responsible for closing the file returned. | ||
137 | """ | ||
138 | layer = find_bsp_layer(machine) | ||
139 | linuxdir = os.path.join(layer, "recipes-kernel/linux") | ||
140 | linuxdir_list = os.listdir(linuxdir) | ||
141 | for fileobj in linuxdir_list: | ||
142 | fileobj_path = os.path.join(linuxdir, fileobj) | ||
143 | if os.path.isdir(fileobj_path): | ||
144 | userfile_name = os.path.join(fileobj_path, userfile) | ||
145 | try: | ||
146 | f = open(userfile_name, mode) | ||
147 | return f | ||
148 | except IOError: | ||
149 | continue | ||
150 | return None | ||
151 | |||
152 | |||
153 | def read_config_items(scripts_path, machine): | ||
154 | """ | ||
155 | Find and return a list of config items (CONFIG_XXX) in a machine's | ||
156 | user-defined config fragment [${machine}-user-config.cfg]. | ||
157 | """ | ||
158 | config_items = [] | ||
159 | |||
160 | f = open_user_file(scripts_path, machine, machine+"-user-config.cfg", "r") | ||
161 | lines = f.readlines() | ||
162 | for line in lines: | ||
163 | s = line.strip() | ||
164 | if s and not s.startswith("#"): | ||
165 | config_items.append(s) | ||
166 | f.close() | ||
167 | |||
168 | return config_items | ||
169 | |||
170 | |||
171 | def write_config_items(scripts_path, machine, config_items): | ||
172 | """ | ||
173 | Write (replace) the list of config items (CONFIG_XXX) in a | ||
174 | machine's user-defined config fragment [${machine}=user-config.cfg]. | ||
175 | """ | ||
176 | f = open_user_file(scripts_path, machine, machine+"-user-config.cfg", "w") | ||
177 | for item in config_items: | ||
178 | f.write(item + "\n") | ||
179 | f.close() | ||
180 | |||
181 | kernel_contents_changed(scripts_path, machine) | ||
182 | |||
183 | |||
184 | def yocto_kernel_config_list(scripts_path, machine): | ||
185 | """ | ||
186 | Display the list of config items (CONFIG_XXX) in a machine's | ||
187 | user-defined config fragment [${machine}-user-config.cfg]. | ||
188 | """ | ||
189 | config_items = read_config_items(scripts_path, machine) | ||
190 | |||
191 | print("The current set of machine-specific kernel config items for %s is:" % machine) | ||
192 | print(gen_choices_str(config_items)) | ||
193 | |||
194 | |||
195 | def yocto_kernel_config_rm(scripts_path, machine): | ||
196 | """ | ||
197 | Display the list of config items (CONFIG_XXX) in a machine's | ||
198 | user-defined config fragment [${machine}-user-config.cfg], prompt the user | ||
199 | for one or more to remove, and remove them. | ||
200 | """ | ||
201 | config_items = read_config_items(scripts_path, machine) | ||
202 | |||
203 | print("Specify the kernel config items to remove:") | ||
204 | inp = input(gen_choices_str(config_items)) | ||
205 | rm_choices = inp.split() | ||
206 | rm_choices.sort() | ||
207 | |||
208 | removed = [] | ||
209 | |||
210 | for choice in reversed(rm_choices): | ||
211 | try: | ||
212 | idx = int(choice) - 1 | ||
213 | except ValueError: | ||
214 | print("Invalid choice (%s), exiting" % choice) | ||
215 | sys.exit(1) | ||
216 | if idx < 0 or idx >= len(config_items): | ||
217 | print("Invalid choice (%d), exiting" % (idx + 1)) | ||
218 | sys.exit(1) | ||
219 | removed.append(config_items.pop(idx)) | ||
220 | |||
221 | write_config_items(scripts_path, machine, config_items) | ||
222 | |||
223 | print("Removed items:") | ||
224 | for r in removed: | ||
225 | print("\t%s" % r) | ||
226 | |||
227 | |||
228 | def yocto_kernel_config_add(scripts_path, machine, config_items): | ||
229 | """ | ||
230 | Add one or more config items (CONFIG_XXX) to a machine's | ||
231 | user-defined config fragment [${machine}-user-config.cfg]. | ||
232 | """ | ||
233 | new_items = [] | ||
234 | dup_items = [] | ||
235 | |||
236 | cur_items = read_config_items(scripts_path, machine) | ||
237 | |||
238 | for item in config_items: | ||
239 | if not item.startswith("CONFIG") or (not "=y" in item and not "=m" in item): | ||
240 | print("Invalid config item (%s), exiting" % item) | ||
241 | sys.exit(1) | ||
242 | if item not in cur_items and item not in new_items: | ||
243 | new_items.append(item) | ||
244 | else: | ||
245 | dup_items.append(item) | ||
246 | |||
247 | if len(new_items) > 0: | ||
248 | cur_items.extend(new_items) | ||
249 | write_config_items(scripts_path, machine, cur_items) | ||
250 | print("Added item%s:" % ("" if len(new_items)==1 else "s")) | ||
251 | for n in new_items: | ||
252 | print("\t%s" % n) | ||
253 | |||
254 | if len(dup_items) > 0: | ||
255 | output="The following item%s already exist%s in the current configuration, ignoring %s:" % \ | ||
256 | (("","s", "it") if len(dup_items)==1 else ("s", "", "them" )) | ||
257 | print(output) | ||
258 | for n in dup_items: | ||
259 | print("\t%s" % n) | ||
260 | |||
261 | def find_current_kernel(bsp_layer, machine): | ||
262 | """ | ||
263 | Determine the kernel and version currently being used in the BSP. | ||
264 | """ | ||
265 | machine_conf = os.path.join(bsp_layer, "conf/machine/" + machine + ".conf") | ||
266 | |||
267 | preferred_kernel = preferred_kernel_version = preferred_version_varname = None | ||
268 | |||
269 | f = open(machine_conf, "r") | ||
270 | lines = f.readlines() | ||
271 | for line in lines: | ||
272 | if line.strip().startswith("PREFERRED_PROVIDER_virtual/kernel"): | ||
273 | preferred_kernel = line.split()[-1] | ||
274 | preferred_kernel = preferred_kernel.replace('\"','') | ||
275 | preferred_version_varname = "PREFERRED_VERSION_" + preferred_kernel | ||
276 | if preferred_version_varname and line.strip().startswith(preferred_version_varname): | ||
277 | preferred_kernel_version = line.split()[-1] | ||
278 | preferred_kernel_version = preferred_kernel_version.replace('\"','') | ||
279 | preferred_kernel_version = preferred_kernel_version.replace('%','') | ||
280 | |||
281 | if preferred_kernel and preferred_kernel_version: | ||
282 | return preferred_kernel + "_" + preferred_kernel_version | ||
283 | elif preferred_kernel: | ||
284 | return preferred_kernel | ||
285 | |||
286 | |||
287 | def find_filesdir(scripts_path, machine): | ||
288 | """ | ||
289 | Find the name of the 'files' dir associated with the machine | ||
290 | (could be in files/, linux-yocto-custom/, etc). Returns the name | ||
291 | of the files dir if found, None otherwise. | ||
292 | """ | ||
293 | layer = find_bsp_layer(machine) | ||
294 | filesdir = None | ||
295 | linuxdir = os.path.join(layer, "recipes-kernel/linux") | ||
296 | linuxdir_list = os.listdir(linuxdir) | ||
297 | for fileobj in linuxdir_list: | ||
298 | fileobj_path = os.path.join(linuxdir, fileobj) | ||
299 | if os.path.isdir(fileobj_path): | ||
300 | # this could be files/ or linux-yocto-custom/, we have no way of distinguishing | ||
301 | # so we take the first (and normally only) dir we find as the 'filesdir' | ||
302 | filesdir = fileobj_path | ||
303 | |||
304 | return filesdir | ||
305 | |||
306 | |||
307 | def read_patch_items(scripts_path, machine): | ||
308 | """ | ||
309 | Find and return a list of patch items in a machine's user-defined | ||
310 | patch list [${machine}-user-patches.scc]. | ||
311 | """ | ||
312 | patch_items = [] | ||
313 | |||
314 | f = open_user_file(scripts_path, machine, machine+"-user-patches.scc", "r") | ||
315 | lines = f.readlines() | ||
316 | for line in lines: | ||
317 | s = line.strip() | ||
318 | if s and not s.startswith("#"): | ||
319 | fields = s.split() | ||
320 | if not fields[0] == "patch": | ||
321 | continue | ||
322 | patch_items.append(fields[1]) | ||
323 | f.close() | ||
324 | |||
325 | return patch_items | ||
326 | |||
327 | |||
328 | def write_patch_items(scripts_path, machine, patch_items): | ||
329 | """ | ||
330 | Write (replace) the list of patches in a machine's user-defined | ||
331 | patch list [${machine}-user-patches.scc]. | ||
332 | """ | ||
333 | f = open_user_file(scripts_path, machine, machine+"-user-patches.scc", "w") | ||
334 | for item in patch_items: | ||
335 | f.write("patch " + item + "\n") | ||
336 | f.close() | ||
337 | |||
338 | kernel_contents_changed(scripts_path, machine) | ||
339 | |||
340 | |||
341 | def yocto_kernel_patch_list(scripts_path, machine): | ||
342 | """ | ||
343 | Display the list of patches in a machine's user-defined patch list | ||
344 | [${machine}-user-patches.scc]. | ||
345 | """ | ||
346 | patches = read_patch_items(scripts_path, machine) | ||
347 | |||
348 | print("The current set of machine-specific patches for %s is:" % machine) | ||
349 | print(gen_choices_str(patches)) | ||
350 | |||
351 | |||
352 | def yocto_kernel_patch_rm(scripts_path, machine): | ||
353 | """ | ||
354 | Remove one or more patches from a machine's user-defined patch | ||
355 | list [${machine}-user-patches.scc]. | ||
356 | """ | ||
357 | patches = read_patch_items(scripts_path, machine) | ||
358 | |||
359 | print("Specify the patches to remove:") | ||
360 | inp = input(gen_choices_str(patches)) | ||
361 | rm_choices = inp.split() | ||
362 | rm_choices.sort() | ||
363 | |||
364 | removed = [] | ||
365 | |||
366 | filesdir = find_filesdir(scripts_path, machine) | ||
367 | if not filesdir: | ||
368 | print("Couldn't rm patch(es) since we couldn't find a 'files' dir") | ||
369 | sys.exit(1) | ||
370 | |||
371 | for choice in reversed(rm_choices): | ||
372 | try: | ||
373 | idx = int(choice) - 1 | ||
374 | except ValueError: | ||
375 | print("Invalid choice (%s), exiting" % choice) | ||
376 | sys.exit(1) | ||
377 | if idx < 0 or idx >= len(patches): | ||
378 | print("Invalid choice (%d), exiting" % (idx + 1)) | ||
379 | sys.exit(1) | ||
380 | filesdir_patch = os.path.join(filesdir, patches[idx]) | ||
381 | if os.path.isfile(filesdir_patch): | ||
382 | os.remove(filesdir_patch) | ||
383 | removed.append(patches[idx]) | ||
384 | patches.pop(idx) | ||
385 | |||
386 | write_patch_items(scripts_path, machine, patches) | ||
387 | |||
388 | print("Removed patches:") | ||
389 | for r in removed: | ||
390 | print("\t%s" % r) | ||
391 | |||
392 | |||
393 | def yocto_kernel_patch_add(scripts_path, machine, patches): | ||
394 | """ | ||
395 | Add one or more patches to a machine's user-defined patch list | ||
396 | [${machine}-user-patches.scc]. | ||
397 | """ | ||
398 | existing_patches = read_patch_items(scripts_path, machine) | ||
399 | |||
400 | for patch in patches: | ||
401 | if os.path.basename(patch) in existing_patches: | ||
402 | print("Couldn't add patch (%s) since it's already been added" % os.path.basename(patch)) | ||
403 | sys.exit(1) | ||
404 | |||
405 | filesdir = find_filesdir(scripts_path, machine) | ||
406 | if not filesdir: | ||
407 | print("Couldn't add patch (%s) since we couldn't find a 'files' dir to add it to" % os.path.basename(patch)) | ||
408 | sys.exit(1) | ||
409 | |||
410 | new_patches = [] | ||
411 | |||
412 | for patch in patches: | ||
413 | if not os.path.isfile(patch): | ||
414 | print("Couldn't find patch (%s), exiting" % patch) | ||
415 | sys.exit(1) | ||
416 | basename = os.path.basename(patch) | ||
417 | filesdir_patch = os.path.join(filesdir, basename) | ||
418 | shutil.copyfile(patch, filesdir_patch) | ||
419 | new_patches.append(basename) | ||
420 | |||
421 | cur_items = read_patch_items(scripts_path, machine) | ||
422 | cur_items.extend(new_patches) | ||
423 | write_patch_items(scripts_path, machine, cur_items) | ||
424 | |||
425 | print("Added patches:") | ||
426 | for n in new_patches: | ||
427 | print("\t%s" % n) | ||
428 | |||
429 | |||
430 | def inc_pr(line): | ||
431 | """ | ||
432 | Add 1 to the PR value in the given bbappend PR line. For the PR | ||
433 | lines in kernel .bbappends after modifications. Handles PRs of | ||
434 | the form PR := "${PR}.1" as well as PR = "r0". | ||
435 | """ | ||
436 | idx = line.find("\"") | ||
437 | |||
438 | pr_str = line[idx:] | ||
439 | pr_str = pr_str.replace('\"','') | ||
440 | fields = pr_str.split('.') | ||
441 | if len(fields) > 1: | ||
442 | fields[1] = str(int(fields[1]) + 1) | ||
443 | pr_str = "\"" + '.'.join(fields) + "\"\n" | ||
444 | else: | ||
445 | pr_val = pr_str[1:] | ||
446 | pr_str = "\"" + "r" + str(int(pr_val) + 1) + "\"\n" | ||
447 | idx2 = line.find("\"", idx + 1) | ||
448 | line = line[:idx] + pr_str | ||
449 | |||
450 | return line | ||
451 | |||
452 | |||
453 | def kernel_contents_changed(scripts_path, machine): | ||
454 | """ | ||
455 | Do what we need to do to notify the system that the kernel | ||
456 | recipe's contents have changed. | ||
457 | """ | ||
458 | layer = find_bsp_layer(machine) | ||
459 | |||
460 | kernel = find_current_kernel(layer, machine) | ||
461 | if not kernel: | ||
462 | print("Couldn't determine the kernel for this BSP, exiting.") | ||
463 | sys.exit(1) | ||
464 | |||
465 | kernel_bbfile = os.path.join(layer, "recipes-kernel/linux/" + kernel + ".bbappend") | ||
466 | if not os.path.isfile(kernel_bbfile): | ||
467 | kernel_bbfile = os.path.join(layer, "recipes-kernel/linux/" + kernel + ".bb") | ||
468 | if not os.path.isfile(kernel_bbfile): | ||
469 | return | ||
470 | kernel_bbfile_prev = kernel_bbfile + ".prev" | ||
471 | shutil.copyfile(kernel_bbfile, kernel_bbfile_prev) | ||
472 | |||
473 | ifile = open(kernel_bbfile_prev, "r") | ||
474 | ofile = open(kernel_bbfile, "w") | ||
475 | ifile_lines = ifile.readlines() | ||
476 | for ifile_line in ifile_lines: | ||
477 | if ifile_line.strip().startswith("PR"): | ||
478 | ifile_line = inc_pr(ifile_line) | ||
479 | ofile.write(ifile_line) | ||
480 | ofile.close() | ||
481 | ifile.close() | ||
482 | |||
483 | |||
484 | def kernels(context): | ||
485 | """ | ||
486 | Return the list of available kernels in the BSP i.e. corresponding | ||
487 | to the kernel .bbappends found in the layer. | ||
488 | """ | ||
489 | archdir = os.path.join(context["scripts_path"], "lib/bsp/substrate/target/arch/" + context["arch"]) | ||
490 | kerndir = os.path.join(archdir, "recipes-kernel/linux") | ||
491 | bbglob = os.path.join(kerndir, "*.bbappend") | ||
492 | |||
493 | bbappends = glob.glob(bbglob) | ||
494 | |||
495 | kernels = [] | ||
496 | |||
497 | for kernel in bbappends: | ||
498 | filename = os.path.splitext(os.path.basename(kernel))[0] | ||
499 | idx = filename.find(CLOSE_TAG) | ||
500 | if idx != -1: | ||
501 | filename = filename[idx + len(CLOSE_TAG):].strip() | ||
502 | kernels.append(filename) | ||
503 | |||
504 | kernels.append("custom") | ||
505 | |||
506 | return kernels | ||
507 | |||
508 | |||
509 | def extract_giturl(file): | ||
510 | """ | ||
511 | Extract the git url of the kernel repo from the kernel recipe's | ||
512 | SRC_URI. | ||
513 | """ | ||
514 | url = None | ||
515 | f = open(file, "r") | ||
516 | lines = f.readlines() | ||
517 | for line in lines: | ||
518 | line = line.strip() | ||
519 | if line.startswith("SRC_URI"): | ||
520 | line = line[len("SRC_URI"):].strip() | ||
521 | if line.startswith("="): | ||
522 | line = line[1:].strip() | ||
523 | if line.startswith("\""): | ||
524 | line = line[1:].strip() | ||
525 | prot = "git" | ||
526 | for s in line.split(";"): | ||
527 | if s.startswith("git://"): | ||
528 | url = s | ||
529 | if s.startswith("protocol="): | ||
530 | prot = s.split("=")[1] | ||
531 | if url: | ||
532 | url = prot + url[3:] | ||
533 | return url | ||
534 | |||
535 | |||
536 | def find_giturl(context): | ||
537 | """ | ||
538 | Find the git url of the kernel repo from the kernel recipe's | ||
539 | SRC_URI. | ||
540 | """ | ||
541 | name = context["name"] | ||
542 | filebase = context["filename"] | ||
543 | scripts_path = context["scripts_path"] | ||
544 | |||
545 | meta_layer = find_meta_layer() | ||
546 | |||
547 | kerndir = os.path.join(meta_layer, "recipes-kernel/linux") | ||
548 | bbglob = os.path.join(kerndir, "*.bb") | ||
549 | bbs = glob.glob(bbglob) | ||
550 | for kernel in bbs: | ||
551 | filename = os.path.splitext(os.path.basename(kernel))[0] | ||
552 | if filename in filebase: | ||
553 | giturl = extract_giturl(kernel) | ||
554 | return giturl | ||
555 | |||
556 | return None | ||
557 | |||
558 | |||
559 | def read_features(scripts_path, machine): | ||
560 | """ | ||
561 | Find and return a list of features in a machine's user-defined | ||
562 | features fragment [${machine}-user-features.scc]. | ||
563 | """ | ||
564 | features = [] | ||
565 | |||
566 | f = open_user_file(scripts_path, machine, machine+"-user-features.scc", "r") | ||
567 | lines = f.readlines() | ||
568 | for line in lines: | ||
569 | s = line.strip() | ||
570 | if s and not s.startswith("#"): | ||
571 | feature_include = s.split() | ||
572 | features.append(feature_include[1].strip()) | ||
573 | f.close() | ||
574 | |||
575 | return features | ||
576 | |||
577 | |||
578 | def write_features(scripts_path, machine, features): | ||
579 | """ | ||
580 | Write (replace) the list of feature items in a | ||
581 | machine's user-defined features fragment [${machine}=user-features.cfg]. | ||
582 | """ | ||
583 | f = open_user_file(scripts_path, machine, machine+"-user-features.scc", "w") | ||
584 | for item in features: | ||
585 | f.write("include " + item + "\n") | ||
586 | f.close() | ||
587 | |||
588 | kernel_contents_changed(scripts_path, machine) | ||
589 | |||
590 | |||
591 | def yocto_kernel_feature_list(scripts_path, machine): | ||
592 | """ | ||
593 | Display the list of features used in a machine's user-defined | ||
594 | features fragment [${machine}-user-features.scc]. | ||
595 | """ | ||
596 | features = read_features(scripts_path, machine) | ||
597 | |||
598 | print("The current set of machine-specific features for %s is:" % machine) | ||
599 | print(gen_choices_str(features)) | ||
600 | |||
601 | |||
602 | def yocto_kernel_feature_rm(scripts_path, machine): | ||
603 | """ | ||
604 | Display the list of features used in a machine's user-defined | ||
605 | features fragment [${machine}-user-features.scc], prompt the user | ||
606 | for one or more to remove, and remove them. | ||
607 | """ | ||
608 | features = read_features(scripts_path, machine) | ||
609 | |||
610 | print("Specify the features to remove:") | ||
611 | inp = input(gen_choices_str(features)) | ||
612 | rm_choices = inp.split() | ||
613 | rm_choices.sort() | ||
614 | |||
615 | removed = [] | ||
616 | |||
617 | for choice in reversed(rm_choices): | ||
618 | try: | ||
619 | idx = int(choice) - 1 | ||
620 | except ValueError: | ||
621 | print("Invalid choice (%s), exiting" % choice) | ||
622 | sys.exit(1) | ||
623 | if idx < 0 or idx >= len(features): | ||
624 | print("Invalid choice (%d), exiting" % (idx + 1)) | ||
625 | sys.exit(1) | ||
626 | removed.append(features.pop(idx)) | ||
627 | |||
628 | write_features(scripts_path, machine, features) | ||
629 | |||
630 | print("Removed features:") | ||
631 | for r in removed: | ||
632 | print("\t%s" % r) | ||
633 | |||
634 | |||
635 | def yocto_kernel_feature_add(scripts_path, machine, features): | ||
636 | """ | ||
637 | Add one or more features a machine's user-defined features | ||
638 | fragment [${machine}-user-features.scc]. | ||
639 | """ | ||
640 | new_items = [] | ||
641 | |||
642 | for item in features: | ||
643 | if not item.endswith(".scc"): | ||
644 | print("Invalid feature (%s), exiting" % item) | ||
645 | sys.exit(1) | ||
646 | new_items.append(item) | ||
647 | |||
648 | cur_items = read_features(scripts_path, machine) | ||
649 | cur_items.extend(new_items) | ||
650 | |||
651 | write_features(scripts_path, machine, cur_items) | ||
652 | |||
653 | print("Added features:") | ||
654 | for n in new_items: | ||
655 | print("\t%s" % n) | ||
656 | |||
657 | |||
658 | def find_feature_url(git_url): | ||
659 | """ | ||
660 | Find the url of the kern-features.rc kernel for the kernel repo | ||
661 | specified from the BSP's kernel recipe SRC_URI. | ||
662 | """ | ||
663 | feature_url = "" | ||
664 | if git_url.startswith("git://"): | ||
665 | git_url = git_url[len("git://"):].strip() | ||
666 | s = git_url.split("/") | ||
667 | if s[1].endswith(".git"): | ||
668 | s[1] = s[1][:len(s[1]) - len(".git")] | ||
669 | feature_url = "http://" + s[0] + "/cgit/cgit.cgi/" + s[1] + \ | ||
670 | "/plain/meta/cfg/kern-features.rc?h=meta" | ||
671 | |||
672 | return feature_url | ||
673 | |||
674 | |||
675 | def find_feature_desc(lines): | ||
676 | """ | ||
677 | Find the feature description and compatibility in the passed-in | ||
678 | set of lines. Returns a string string of the form 'desc | ||
679 | [compat]'. | ||
680 | """ | ||
681 | desc = "no description available" | ||
682 | compat = "unknown" | ||
683 | |||
684 | for line in lines: | ||
685 | idx = line.find("KFEATURE_DESCRIPTION") | ||
686 | if idx != -1: | ||
687 | desc = line[idx + len("KFEATURE_DESCRIPTION"):].strip() | ||
688 | if desc.startswith("\""): | ||
689 | desc = desc[1:] | ||
690 | if desc.endswith("\""): | ||
691 | desc = desc[:-1] | ||
692 | else: | ||
693 | idx = line.find("KFEATURE_COMPATIBILITY") | ||
694 | if idx != -1: | ||
695 | compat = line[idx + len("KFEATURE_COMPATIBILITY"):].strip() | ||
696 | |||
697 | return desc + " [" + compat + "]" | ||
698 | |||
699 | |||
700 | def print_feature_descs(layer, feature_dir): | ||
701 | """ | ||
702 | Print the feature descriptions for the features in feature_dir. | ||
703 | """ | ||
704 | kernel_files_features = os.path.join(layer, "recipes-kernel/linux/files/" + | ||
705 | feature_dir) | ||
706 | for root, dirs, files in os.walk(kernel_files_features): | ||
707 | for file in files: | ||
708 | if file.endswith("~") or file.endswith("#"): | ||
709 | continue | ||
710 | if file.endswith(".scc"): | ||
711 | fullpath = os.path.join(layer, "recipes-kernel/linux/files/" + | ||
712 | feature_dir + "/" + file) | ||
713 | f = open(fullpath) | ||
714 | feature_desc = find_feature_desc(f.readlines()) | ||
715 | print(feature_dir + "/" + file + ": " + feature_desc) | ||
716 | |||
717 | |||
718 | def yocto_kernel_available_features_list(scripts_path, machine): | ||
719 | """ | ||
720 | Display the list of all the kernel features available for use in | ||
721 | BSPs, as gathered from the set of feature sources. | ||
722 | """ | ||
723 | layer = find_bsp_layer(machine) | ||
724 | kernel = find_current_kernel(layer, machine) | ||
725 | if not kernel: | ||
726 | print("Couldn't determine the kernel for this BSP, exiting.") | ||
727 | sys.exit(1) | ||
728 | |||
729 | context = create_context(machine, "arch", scripts_path) | ||
730 | context["name"] = "name" | ||
731 | context["filename"] = kernel | ||
732 | giturl = find_giturl(context) | ||
733 | feature_url = find_feature_url(giturl) | ||
734 | |||
735 | feature_cmd = "wget -q -O - " + feature_url | ||
736 | tmp = subprocess.Popen(feature_cmd, shell=True, stdout=subprocess.PIPE).stdout.read().decode('utf-8') | ||
737 | |||
738 | print("The current set of kernel features available to %s is:\n" % machine) | ||
739 | |||
740 | if tmp: | ||
741 | tmpline = tmp.split("\n") | ||
742 | in_kernel_options = False | ||
743 | for line in tmpline: | ||
744 | if not "=" in line: | ||
745 | if in_kernel_options: | ||
746 | break | ||
747 | if "kernel-options" in line: | ||
748 | in_kernel_options = True | ||
749 | continue | ||
750 | if in_kernel_options: | ||
751 | feature_def = line.split("=") | ||
752 | feature_type = feature_def[0].strip() | ||
753 | feature = feature_def[1].strip() | ||
754 | desc = get_feature_desc(giturl, feature) | ||
755 | print("%s: %s" % (feature, desc)) | ||
756 | |||
757 | print("[local]") | ||
758 | |||
759 | print_feature_descs(layer, "cfg") | ||
760 | print_feature_descs(layer, "features") | ||
761 | |||
762 | |||
763 | def find_feature_desc_url(git_url, feature): | ||
764 | """ | ||
765 | Find the url of the kernel feature in the kernel repo specified | ||
766 | from the BSP's kernel recipe SRC_URI. | ||
767 | """ | ||
768 | feature_desc_url = "" | ||
769 | if git_url.startswith("git://"): | ||
770 | git_url = git_url[len("git://"):].strip() | ||
771 | s = git_url.split("/") | ||
772 | if s[1].endswith(".git"): | ||
773 | s[1] = s[1][:len(s[1]) - len(".git")] | ||
774 | feature_desc_url = "http://" + s[0] + "/cgit/cgit.cgi/" + s[1] + \ | ||
775 | "/plain/meta/cfg/kernel-cache/" + feature + "?h=meta" | ||
776 | |||
777 | return feature_desc_url | ||
778 | |||
779 | |||
780 | def get_feature_desc(git_url, feature): | ||
781 | """ | ||
782 | Return a feature description of the form 'description [compatibility] | ||
783 | BSPs, as gathered from the set of feature sources. | ||
784 | """ | ||
785 | feature_desc_url = find_feature_desc_url(git_url, feature) | ||
786 | feature_desc_cmd = "wget -q -O - " + feature_desc_url | ||
787 | tmp = subprocess.Popen(feature_desc_cmd, shell=True, stdout=subprocess.PIPE).stdout.read().decode('utf-8') | ||
788 | |||
789 | return find_feature_desc(tmp.split("\n")) | ||
790 | |||
791 | |||
792 | def yocto_kernel_feature_describe(scripts_path, machine, feature): | ||
793 | """ | ||
794 | Display the description of a specific kernel feature available for | ||
795 | use in a BSP. | ||
796 | """ | ||
797 | layer = find_bsp_layer(machine) | ||
798 | |||
799 | kernel = find_current_kernel(layer, machine) | ||
800 | if not kernel: | ||
801 | print("Couldn't determine the kernel for this BSP, exiting.") | ||
802 | sys.exit(1) | ||
803 | |||
804 | context = create_context(machine, "arch", scripts_path) | ||
805 | context["name"] = "name" | ||
806 | context["filename"] = kernel | ||
807 | giturl = find_giturl(context) | ||
808 | |||
809 | desc = get_feature_desc(giturl, feature) | ||
810 | |||
811 | print(desc) | ||
812 | |||
813 | |||
814 | def check_feature_name(feature_name): | ||
815 | """ | ||
816 | Sanity-check the feature name for create/destroy. Return False if not OK. | ||
817 | """ | ||
818 | if not feature_name.endswith(".scc"): | ||
819 | print("Invalid feature name (must end with .scc) [%s], exiting" % feature_name) | ||
820 | return False | ||
821 | |||
822 | if "/" in feature_name: | ||
823 | print("Invalid feature name (don't specify directory) [%s], exiting" % feature_name) | ||
824 | return False | ||
825 | |||
826 | return True | ||
827 | |||
828 | |||
829 | def check_create_input(feature_items): | ||
830 | """ | ||
831 | Sanity-check the create input. Return False if not OK. | ||
832 | """ | ||
833 | if not check_feature_name(feature_items[0]): | ||
834 | return False | ||
835 | |||
836 | if feature_items[1].endswith(".patch") or feature_items[1].startswith("CONFIG_"): | ||
837 | print("Missing description and/or compatibilty [%s], exiting" % feature_items[1]) | ||
838 | return False | ||
839 | |||
840 | if feature_items[2].endswith(".patch") or feature_items[2].startswith("CONFIG_"): | ||
841 | print("Missing description and/or compatibility [%s], exiting" % feature_items[1]) | ||
842 | return False | ||
843 | |||
844 | return True | ||
845 | |||
846 | |||
847 | def yocto_kernel_feature_create(scripts_path, machine, feature_items): | ||
848 | """ | ||
849 | Create a recipe-space kernel feature in a BSP. | ||
850 | """ | ||
851 | if not check_create_input(feature_items): | ||
852 | sys.exit(1) | ||
853 | |||
854 | feature = feature_items[0] | ||
855 | feature_basename = feature.split(".")[0] | ||
856 | feature_description = feature_items[1] | ||
857 | feature_compat = feature_items[2] | ||
858 | |||
859 | patches = [] | ||
860 | cfg_items = [] | ||
861 | |||
862 | for item in feature_items[3:]: | ||
863 | if item.endswith(".patch"): | ||
864 | patches.append(item) | ||
865 | elif item.startswith("CONFIG"): | ||
866 | if ("=y" in item or "=m" in item): | ||
867 | cfg_items.append(item) | ||
868 | else: | ||
869 | print("Invalid feature item (must be .patch or CONFIG_*) [%s], exiting" % item) | ||
870 | sys.exit(1) | ||
871 | |||
872 | feature_dirname = "cfg" | ||
873 | if patches: | ||
874 | feature_dirname = "features" | ||
875 | |||
876 | filesdir = find_filesdir(scripts_path, machine) | ||
877 | if not filesdir: | ||
878 | print("Couldn't add feature (%s), no 'files' dir found" % feature) | ||
879 | sys.exit(1) | ||
880 | |||
881 | featdir = os.path.join(filesdir, feature_dirname) | ||
882 | if not os.path.exists(featdir): | ||
883 | os.mkdir(featdir) | ||
884 | |||
885 | for patch in patches: | ||
886 | if not os.path.isfile(patch): | ||
887 | print("Couldn't find patch (%s), exiting" % patch) | ||
888 | sys.exit(1) | ||
889 | basename = os.path.basename(patch) | ||
890 | featdir_patch = os.path.join(featdir, basename) | ||
891 | shutil.copyfile(patch, featdir_patch) | ||
892 | |||
893 | new_cfg_filename = os.path.join(featdir, feature_basename + ".cfg") | ||
894 | new_cfg_file = open(new_cfg_filename, "w") | ||
895 | for cfg_item in cfg_items: | ||
896 | new_cfg_file.write(cfg_item + "\n") | ||
897 | new_cfg_file.close() | ||
898 | |||
899 | new_feature_filename = os.path.join(featdir, feature_basename + ".scc") | ||
900 | new_feature_file = open(new_feature_filename, "w") | ||
901 | new_feature_file.write("define KFEATURE_DESCRIPTION \"" + feature_description + "\"\n") | ||
902 | new_feature_file.write("define KFEATURE_COMPATIBILITY " + feature_compat + "\n\n") | ||
903 | |||
904 | for patch in patches: | ||
905 | patch_dir, patch_file = os.path.split(patch) | ||
906 | new_feature_file.write("patch " + patch_file + "\n") | ||
907 | |||
908 | new_feature_file.write("kconf non-hardware " + feature_basename + ".cfg\n") | ||
909 | new_feature_file.close() | ||
910 | |||
911 | print("Added feature:") | ||
912 | print("\t%s" % feature_dirname + "/" + feature) | ||
913 | |||
914 | |||
915 | def feature_in_use(scripts_path, machine, feature): | ||
916 | """ | ||
917 | Determine whether the specified feature is in use by the BSP. | ||
918 | Return True if so, False otherwise. | ||
919 | """ | ||
920 | features = read_features(scripts_path, machine) | ||
921 | for f in features: | ||
922 | if f == feature: | ||
923 | return True | ||
924 | return False | ||
925 | |||
926 | |||
927 | def feature_remove(scripts_path, machine, feature): | ||
928 | """ | ||
929 | Remove the specified feature from the available recipe-space | ||
930 | features defined for the BSP. | ||
931 | """ | ||
932 | features = read_features(scripts_path, machine) | ||
933 | new_features = [] | ||
934 | for f in features: | ||
935 | if f == feature: | ||
936 | continue | ||
937 | new_features.append(f) | ||
938 | write_features(scripts_path, machine, new_features) | ||
939 | |||
940 | |||
941 | def yocto_kernel_feature_destroy(scripts_path, machine, feature): | ||
942 | """ | ||
943 | Remove a recipe-space kernel feature from a BSP. | ||
944 | """ | ||
945 | if not check_feature_name(feature): | ||
946 | sys.exit(1) | ||
947 | |||
948 | if feature_in_use(scripts_path, machine, "features/" + feature) or \ | ||
949 | feature_in_use(scripts_path, machine, "cfg/" + feature): | ||
950 | print("Feature %s is in use (use 'feature rm' to un-use it first), exiting" % feature) | ||
951 | sys.exit(1) | ||
952 | |||
953 | filesdir = find_filesdir(scripts_path, machine) | ||
954 | if not filesdir: | ||
955 | print("Couldn't destroy feature (%s), no 'files' dir found" % feature) | ||
956 | sys.exit(1) | ||
957 | |||
958 | feature_dirname = "features" | ||
959 | featdir = os.path.join(filesdir, feature_dirname) | ||
960 | if not os.path.exists(featdir): | ||
961 | print("Couldn't find feature directory (%s)" % feature_dirname) | ||
962 | sys.exit(1) | ||
963 | |||
964 | feature_fqn = os.path.join(featdir, feature) | ||
965 | if not os.path.exists(feature_fqn): | ||
966 | feature_dirname = "cfg" | ||
967 | featdir = os.path.join(filesdir, feature_dirname) | ||
968 | if not os.path.exists(featdir): | ||
969 | print("Couldn't find feature directory (%s)" % feature_dirname) | ||
970 | sys.exit(1) | ||
971 | feature_fqn = os.path.join(featdir, feature_filename) | ||
972 | if not os.path.exists(feature_fqn): | ||
973 | print("Couldn't find feature (%s)" % feature) | ||
974 | sys.exit(1) | ||
975 | |||
976 | f = open(feature_fqn, "r") | ||
977 | lines = f.readlines() | ||
978 | for line in lines: | ||
979 | s = line.strip() | ||
980 | if s.startswith("patch ") or s.startswith("kconf "): | ||
981 | split_line = s.split() | ||
982 | filename = os.path.join(featdir, split_line[-1]) | ||
983 | if os.path.exists(filename): | ||
984 | os.remove(filename) | ||
985 | f.close() | ||
986 | os.remove(feature_fqn) | ||
987 | |||
988 | feature_remove(scripts_path, machine, feature) | ||
989 | |||
990 | print("Removed feature:") | ||
991 | print("\t%s" % feature_dirname + "/" + feature) | ||
992 | |||
993 | |||
994 | def base_branches(context): | ||
995 | """ | ||
996 | Return a list of the base branches found in the kernel git repo. | ||
997 | """ | ||
998 | giturl = find_giturl(context) | ||
999 | |||
1000 | print("Getting branches from remote repo %s..." % giturl) | ||
1001 | |||
1002 | gitcmd = "git ls-remote %s *heads* 2>&1" % (giturl) | ||
1003 | tmp = subprocess.Popen(gitcmd, shell=True, stdout=subprocess.PIPE).stdout.read().decode('utf-8') | ||
1004 | |||
1005 | branches = [] | ||
1006 | |||
1007 | if tmp: | ||
1008 | tmpline = tmp.split("\n") | ||
1009 | for line in tmpline: | ||
1010 | if len(line)==0: | ||
1011 | break; | ||
1012 | if not line.endswith("base"): | ||
1013 | continue; | ||
1014 | idx = line.find("refs/heads/") | ||
1015 | kbranch = line[idx + len("refs/heads/"):] | ||
1016 | if kbranch.find("/") == -1 and kbranch.find("base") == -1: | ||
1017 | continue | ||
1018 | idx = kbranch.find("base") | ||
1019 | branches.append(kbranch[:idx - 1]) | ||
1020 | |||
1021 | return branches | ||
1022 | |||
1023 | |||
1024 | def all_branches(context): | ||
1025 | """ | ||
1026 | Return a list of all the branches found in the kernel git repo. | ||
1027 | """ | ||
1028 | giturl = find_giturl(context) | ||
1029 | |||
1030 | print("Getting branches from remote repo %s..." % giturl) | ||
1031 | |||
1032 | gitcmd = "git ls-remote %s *heads* 2>&1" % (giturl) | ||
1033 | tmp = subprocess.Popen(gitcmd, shell=True, stdout=subprocess.PIPE).stdout.read().decode('utf-8') | ||
1034 | |||
1035 | branches = [] | ||
1036 | |||
1037 | base_prefixes = None | ||
1038 | |||
1039 | try: | ||
1040 | branches_base = context["branches_base"] | ||
1041 | if branches_base: | ||
1042 | base_prefixes = branches_base.split(":") | ||
1043 | except KeyError: | ||
1044 | pass | ||
1045 | |||
1046 | arch = context["arch"] | ||
1047 | |||
1048 | if tmp: | ||
1049 | tmpline = tmp.split("\n") | ||
1050 | for line in tmpline: | ||
1051 | if len(line)==0: | ||
1052 | break; | ||
1053 | idx = line.find("refs/heads/") | ||
1054 | kbranch = line[idx + len("refs/heads/"):] | ||
1055 | kbranch_prefix = kbranch.rsplit("/", 1)[0] | ||
1056 | |||
1057 | if base_prefixes: | ||
1058 | for base_prefix in base_prefixes: | ||
1059 | if kbranch_prefix == base_prefix: | ||
1060 | branches.append(kbranch) | ||
1061 | continue | ||
1062 | |||
1063 | if (kbranch.find("/") != -1 and | ||
1064 | (kbranch.find("standard") != -1 or kbranch.find("base") != -1) or | ||
1065 | kbranch == "base"): | ||
1066 | branches.append(kbranch) | ||
1067 | continue | ||
1068 | |||
1069 | return branches | ||