blob: ba68b60fcb1622ee3381c1c36317a4db113955a5 [file] [log] [blame]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001# 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
29import sys
30import os
31import shutil
32from tags import *
33import glob
34import subprocess
35from engine import create_context
36
37
38def 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
79def 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
90def 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
103def 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
118def 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
131def 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
155def 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
173def 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
186def 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
197def 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
230def 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
263def 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
289def 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
309def 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
330def 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
343def 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
354def 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
395def 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
432def 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
455def 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
486def 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
511def 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
538def 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
561def 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
580def 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
593def 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
604def 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
637def 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
660def 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
677def 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
702def 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
720def 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
765def 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
782def 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
794def 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
816def 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
831def 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
849def 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
917def 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
929def 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
943def 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
996def 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
1026def 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