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