summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTom Zanussi <tom.zanussi@intel.com>2012-01-24 00:25:45 -0600
committerRichard Purdie <richard.purdie@linuxfoundation.org>2012-03-22 19:21:15 +0000
commit1e40e8a2306cac37e0dccfdc042a3d39c5cd9159 (patch)
tree592c5716f9cf482ab585877a482d52043d192e8d
parentcd8182e6892986d73a1f0252d38682b9d5c07b22 (diff)
downloadpoky-1e40e8a2306cac37e0dccfdc042a3d39c5cd9159.tar.gz
yocto-bsp: add kernel interface
Yocto BSP kernel-related functions, for interacting with the kernel tools and implementing the machinery behind the 'yocto-kernel' command. Signed-off-by: Tom Zanussi <tom.zanussi@intel.com>
-rw-r--r--scripts/lib/bsp/kernel.py723
1 files changed, 723 insertions, 0 deletions
diff --git a/scripts/lib/bsp/kernel.py b/scripts/lib/bsp/kernel.py
new file mode 100644
index 0000000000..b4e7fbf062
--- /dev/null
+++ b/scripts/lib/bsp/kernel.py
@@ -0,0 +1,723 @@
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
29import sys
30import os
31import shutil
32from tags import *
33import glob
34
35
36def find_bblayers(scripts_path):
37 """
38 Find and return a sanitized list of the layers found in BBLAYERS.
39 """
40 bblayers_conf = os.path.join(scripts_path, "../build/conf/bblayers.conf")
41
42 layers = []
43
44 f = open(bblayers_conf, "r")
45 lines = f.readlines()
46 bblayers_lines = []
47 in_bblayers = False
48 for line in lines:
49 line = line.strip()
50 if line.strip().startswith("BBLAYERS"):
51 bblayers_lines.append(line)
52 in_bblayers = True
53 quotes = line.strip().count('"')
54 if quotes > 1:
55 break
56 continue
57 if in_bblayers:
58 bblayers_lines.append(line)
59 if line.strip().endswith("\""):
60 break
61 else:
62 continue
63
64 for i, line in enumerate(bblayers_lines):
65 if line.strip().endswith("\\"):
66 bblayers_lines[i] = line.strip().replace('\\', '')
67
68 bblayers_line = " ".join(bblayers_lines)
69
70 start_quote = bblayers_line.find("\"")
71 if start_quote == -1:
72 print "Invalid BBLAYERS found in %s, exiting" % bblayers_conf
73 sys.exit(1)
74
75 start_quote += 1
76 end_quote = bblayers_line.find("\"", start_quote)
77 if end_quote == -1:
78 print "Invalid BBLAYERS found in %s, exiting" % bblayers_conf
79 sys.exit(1)
80
81 bblayers_line = bblayers_line[start_quote:end_quote]
82 layers = bblayers_line.split()
83
84 f.close()
85
86 return layers
87
88
89def find_meta_layer(scripts_path):
90 """
91 Find and return the meta layer in BBLAYERS.
92 """
93 layers = find_bblayers(scripts_path)
94
95 for layer in layers:
96 if layer.endswith("meta"):
97 return layer
98
99 return None
100
101
102def find_bsp_layer(scripts_path, machine):
103 """
104 Find and return a machine's BSP layer in BBLAYERS.
105 """
106 layers = find_bblayers(scripts_path)
107
108 for layer in layers:
109 if machine in layer:
110 return layer
111
112 print "Unable to find the BSP layer for machine %s." % machine
113 print "Please make sure it is listed in bblayers.conf"
114 sys.exit(1)
115
116
117def gen_choices_str(choices):
118 """
119 Generate a numbered list of choices from a list of choices for
120 display to the user.
121 """
122 choices_str = ""
123
124 for i, choice in enumerate(choices):
125 choices_str += "\t" + str(i + 1) + ") " + choice + "\n"
126
127 return choices_str
128
129
130def read_config_items(scripts_path, machine):
131 """
132 Find and return a list of config items (CONFIG_XXX) in a machine's
133 user-defined config fragment [user-config.cfg].
134 """
135 config_items = []
136
137 layer = find_bsp_layer(scripts_path, machine)
138 cfg = os.path.join(layer, "recipes-kernel/linux/files/user-config.cfg")
139
140 f = open(cfg, "r")
141 lines = f.readlines()
142 for line in lines:
143 s = line.strip()
144 if s:
145 config_items.append(s)
146 f.close()
147
148 return config_items
149
150
151def write_config_items(scripts_path, machine, config_items):
152 """
153 Write (replace) the list of config items (CONFIG_XXX) in a
154 machine's user-defined config fragment [user-config.cfg].
155 """
156 layer = find_bsp_layer(scripts_path, machine)
157 cfg = os.path.join(layer, "recipes-kernel/linux/files/user-config.cfg")
158
159 f = open(cfg, "w")
160 for item in config_items:
161 f.write(item + "\n")
162 f.close()
163
164 kernel_contents_changed(scripts_path, machine)
165
166
167def yocto_kernel_config_list(scripts_path, machine):
168 """
169 Display the list of config items (CONFIG_XXX) in a machine's
170 user-defined config fragment [user-config.cfg].
171 """
172 config_items = read_config_items(scripts_path, machine)
173
174 print "The current set of machine-specific kernel config items for %s is:" % machine
175 print gen_choices_str(config_items)
176
177
178def map_choice(choice_str, array):
179 """
180 Match the text of a choice with a list of choices, returning the
181 index of the match, or -1 if not found.
182 """
183 for i, item in enumerate(array):
184 if choice_str == array[i]:
185 return i
186
187 return -1
188
189
190def yocto_kernel_config_rm(scripts_path, machine):
191 """
192 Display the list of config items (CONFIG_XXX) in a machine's
193 user-defined config fragment [user-config.cfg], prompt the user
194 for one or more to remove, and remove them.
195 """
196 config_items = read_config_items(scripts_path, machine)
197
198 print "Specify the kernel config items to remove:"
199 input = raw_input(gen_choices_str(config_items))
200 rm_choices = input.split()
201 rm_choices.sort()
202
203 removed = []
204
205 for choice in reversed(rm_choices):
206 try:
207 idx = int(choice) - 1
208 except ValueError:
209 print "Invalid choice (%s), exiting" % choice
210 sys.exit(1)
211 if idx < 0 or idx >= len(config_items):
212 print "Invalid choice (%d), exiting" % (idx + 1)
213 sys.exit(1)
214 removed.append(config_items.pop(idx))
215
216 write_config_items(scripts_path, machine, config_items)
217
218 print "Removed items:"
219 for r in removed:
220 print "\t%s" % r
221
222
223def yocto_kernel_config_add(scripts_path, machine, config_items):
224 """
225 Add one or more config items (CONFIG_XXX) to a machine's
226 user-defined config fragment [user-config.cfg].
227 """
228 new_items = []
229
230 for item in config_items:
231 if not item.startswith("CONFIG") or (not "=y" in item and not "=m" in item):
232 print "Invalid config item (%s), exiting" % item
233 sys.exit(1)
234 new_items.append(item)
235
236 cur_items = read_config_items(scripts_path, machine)
237 cur_items.extend(new_items)
238
239 write_config_items(scripts_path, machine, cur_items)
240
241 print "Added items:"
242 for n in new_items:
243 print "\t%s" % n
244
245
246def find_current_kernel(bsp_layer, machine):
247 """
248 Determine the kernel and version currently being used in the BSP.
249 """
250 machine_conf = os.path.join(bsp_layer, "conf/machine/" + machine + ".conf")
251
252 preferred_kernel = preferred_kernel_version = preferred_version_varname = None
253
254 f = open(machine_conf, "r")
255 lines = f.readlines()
256 for line in lines:
257 if line.strip().startswith("PREFERRED_PROVIDER_virtual/kernel"):
258 preferred_kernel = line.split()[-1]
259 preferred_kernel = preferred_kernel.replace('\"','')
260 preferred_version_varname = "PREFERRED_VERSION_" + preferred_kernel
261 if preferred_version_varname and line.strip().startswith(preferred_version_varname):
262 preferred_kernel_version = line.split()[-1]
263 preferred_kernel_version = preferred_kernel_version.replace('\"','')
264 preferred_kernel_version = preferred_kernel_version.replace('%','')
265
266 if preferred_kernel and preferred_kernel_version:
267 return preferred_kernel + "_" + preferred_kernel_version
268
269
270def find_bsp_kernel_src_uri(scripts_path, machine, start_end_only = False):
271 """
272 Parse the SRC_URI append in the kernel .bbappend, returing a list
273 of individual components, and the start/end positions of the
274 SRC_URI statement, so it can be regenerated in the same position.
275 If start_end_only is True, don't return the list of elements, only
276 the start and end positions.
277
278 Returns (SRC_URI start line, SRC_URI end_line, list of split
279 SRC_URI items).
280
281 If no SRC_URI, start line = -1.
282
283 NOTE: this and all the src_uri functions are temporary and
284 deprecated and will be removed, but are needed until the
285 equivalent .scc mechanism works. i.e. for now we unfortunately
286 can't get around putting patches in the SRC_URI.
287 """
288 layer = find_bsp_layer(scripts_path, machine)
289
290 kernel = find_current_kernel(layer, machine)
291 if not kernel:
292 print "Couldn't determine the kernel for this BSP, exiting."
293 sys.exit(1)
294
295 kernel_bbappend = os.path.join(layer, "recipes-kernel/linux/" + kernel + ".bbappend")
296
297 f = open(kernel_bbappend, "r")
298 src_uri_line = ""
299 in_src_uri = False
300 lines = f.readlines()
301 first_line = last_line = -1
302 quote_start = quote_end = -1
303 for n, line in enumerate(lines):
304 line = line.strip()
305 if line.startswith("SRC_URI"):
306 first_line = n
307 in_src_uri = True
308 if in_src_uri:
309 src_uri_line += line
310 if quote_start == -1:
311 idx = line.find("\"")
312 if idx != -1:
313 quote_start = idx + 1
314 idx = line.find("\"", quote_start)
315 quote_start = 0 # set to 0 for all but first line
316 if idx != -1:
317 quote_end = idx
318 last_line = n
319 break
320
321 if first_line == -1: # no SRC_URI, which is fine too
322 return (-1, -1, None)
323 if quote_start == -1:
324 print "Bad kernel SRC_URI (missing opening quote), exiting."
325 sys.exit(1)
326 if quote_end == -1:
327 print "Bad SRC_URI (missing closing quote), exiting."
328 sys.exit(1)
329 if start_end_only:
330 return (first_line, last_line, None)
331
332 idx = src_uri_line.find("\"")
333 src_uri_line = src_uri_line[idx + 1:]
334 idx = src_uri_line.find("\"")
335 src_uri_line = src_uri_line[:idx]
336
337 src_uri = src_uri_line.split()
338 for i, item in enumerate(src_uri):
339 idx = item.find("\\")
340 if idx != -1:
341 src_uri[i] = item[idx + 1:]
342
343 if not src_uri[len(src_uri) - 1]:
344 src_uri.pop()
345
346 for i, item in enumerate(src_uri):
347 idx = item.find(SRC_URI_FILE)
348 if idx == -1:
349 print "Bad SRC_URI (invalid item, %s), exiting." % item
350 sys.exit(1)
351 src_uri[i] = item[idx + len(SRC_URI_FILE):]
352
353 return (first_line, last_line, src_uri)
354
355
356def find_patches(src_uri):
357 """
358 Filter out the top-level patches from the SRC_URI.
359 """
360 patches = []
361 for item in src_uri:
362 if item.endswith(".patch") and "/" not in item:
363 patches.append(item)
364 return patches
365
366
367def read_patch_items(scripts_path, machine):
368 """
369 Find and return a list of patch items in a machine's user-defined
370 patch list [user-patches.scc].
371 """
372 patch_items = []
373
374 layer = find_bsp_layer(scripts_path, machine)
375 patches = os.path.join(layer, "recipes-kernel/linux/files/user-patches.scc")
376
377 f = open(patches, "r")
378 lines = f.readlines()
379 for line in lines:
380 s = line.strip()
381 if s:
382 fields = s.split()
383 if not fields[0] == "patch":
384 continue
385 patch_items.append(fields[1])
386 f.close()
387
388 return patch_items
389
390
391def write_patch_items(scripts_path, machine, patch_items):
392 """
393 Write (replace) the list of patches in a machine's user-defined
394 patch list [user-patches.scc].
395 """
396 layer = find_bsp_layer(scripts_path, machine)
397
398 patches = os.path.join(layer, "recipes-kernel/linux/files/user-patches.scc")
399
400 f = open(patches, "w")
401 for item in patch_items:
402 pass
403 # this currently breaks do_patch, but is really what we want
404 # once this works, we can remove all the src_uri stuff
405 # f.write("patch " + item + "\n")
406 f.close()
407
408 kernel_contents_changed(scripts_path, machine)
409
410
411def yocto_kernel_patch_list(scripts_path, machine):
412 """
413 Display the list of patches in a machine's user-defined patch list
414 [user-patches.scc].
415 """
416 (start_line, end_line, src_uri) = find_bsp_kernel_src_uri(scripts_path, machine)
417 patches = find_patches(src_uri)
418
419 print "The current set of machine-specific patches for %s is:" % machine
420 print gen_choices_str(patches)
421
422
423def yocto_kernel_patch_rm(scripts_path, machine):
424 """
425 Remove one or more patches from a machine's user-defined patch
426 list [user-patches.scc].
427 """
428 (start_line, end_line, src_uri) = find_bsp_kernel_src_uri(scripts_path, machine)
429 patches = find_patches(src_uri)
430
431 print "Specify the patches to remove:"
432 input = raw_input(gen_choices_str(patches))
433 rm_choices = input.split()
434 rm_choices.sort()
435
436 removed = []
437
438 layer = find_bsp_layer(scripts_path, machine)
439 src_uri_dir = os.path.join(layer, "recipes-kernel/linux/files")
440
441 for choice in reversed(rm_choices):
442 try:
443 idx = int(choice) - 1
444 except ValueError:
445 print "Invalid choice (%s), exiting" % choice
446 sys.exit(1)
447 if idx < 0 or idx >= len(patches):
448 print "Invalid choice (%d), exiting" % (idx + 1)
449 sys.exit(1)
450 src_uri_patch = os.path.join(src_uri_dir, patches[idx])
451 if os.path.isfile(src_uri_patch):
452 os.remove(src_uri_patch)
453 idx = map_choice(patches[idx], src_uri)
454 removed.append(src_uri.pop(idx))
455
456 write_patch_items(scripts_path, machine, patches)
457 write_kernel_src_uri(scripts_path, machine, src_uri)
458
459 print "Removed patches:"
460 for r in removed:
461 print "\t%s" % r
462
463
464def yocto_kernel_patch_add(scripts_path, machine, patches):
465 """
466 Add one or more patches to a machine's user-defined patch list
467 [user-patches.scc].
468 """
469 (start_line, end_line, src_uri) = find_bsp_kernel_src_uri(scripts_path, machine)
470 src_uri_patches = find_patches(src_uri)
471
472 for patch in patches:
473 if os.path.basename(patch) in src_uri_patches:
474 print "Couldn't add patch (%s) since it's already been added" % os.path.basename(patch)
475 sys.exit(1)
476
477 layer = find_bsp_layer(scripts_path, machine)
478 src_uri_dir = os.path.join(layer, "recipes-kernel/linux/files")
479
480 new_patches = []
481
482 for patch in patches:
483 if not os.path.isfile(patch):
484 print "Couldn't find patch (%s), exiting" % patch
485 sys.exit(1)
486 basename = os.path.basename(patch)
487 src_uri_patch = os.path.join(src_uri_dir, basename)
488 shutil.copyfile(patch, src_uri_patch)
489 new_patches.append(basename)
490
491 cur_items = read_patch_items(scripts_path, machine)
492 cur_items.extend(new_patches)
493 write_patch_items(scripts_path, machine, cur_items)
494
495 (unused, unused, src_uri) = find_bsp_kernel_src_uri(scripts_path, machine)
496 src_uri.extend(new_patches)
497 write_kernel_src_uri(scripts_path, machine, src_uri)
498
499 print "Added patches:"
500 for n in new_patches:
501 print "\t%s" % n
502
503
504def write_uri_lines(ofile, src_uri):
505 """
506 Write URI elements to output file ofile.
507 """
508 ofile.write("SRC_URI += \" \\\n")
509 for item in src_uri:
510 ofile.write("\t%s%s \\\n" % (SRC_URI_FILE, item))
511 ofile.write("\t\"\n")
512
513
514def inc_pr(line):
515 """
516 Add 1 to the PR value in the given bbappend PR line. For the PR
517 lines in kernel .bbappends after modifications.
518 """
519 idx = line.find("\"")
520
521 pr_str = line[idx:]
522 pr_str = pr_str.replace('\"','')
523 fields = pr_str.split('.')
524 fields[1] = str(int(fields[1]) + 1)
525 pr_str = "\"" + '.'.join(fields) + "\"\n"
526
527 idx2 = line.find("\"", idx + 1)
528 line = line[:idx] + pr_str
529
530 return line
531
532
533def kernel_contents_changed(scripts_path, machine):
534 """
535 Do what we need to do to notify the system that the kernel
536 recipe's contents have changed.
537 """
538 layer = find_bsp_layer(scripts_path, machine)
539
540 kernel = find_current_kernel(layer, machine)
541 if not kernel:
542 print "Couldn't determine the kernel for this BSP, exiting."
543 sys.exit(1)
544
545 kernel_bbappend = os.path.join(layer, "recipes-kernel/linux/" + kernel + ".bbappend")
546 kernel_bbappend_prev = kernel_bbappend + ".prev"
547 shutil.copyfile(kernel_bbappend, kernel_bbappend_prev)
548
549 ifile = open(kernel_bbappend_prev, "r")
550 ofile = open(kernel_bbappend, "w")
551 ifile_lines = ifile.readlines()
552 for ifile_line in ifile_lines:
553 if ifile_line.strip().startswith("PR"):
554 ifile_line = inc_pr(ifile_line)
555 ofile.write(ifile_line)
556 ofile.close()
557 ifile.close()
558
559
560def write_kernel_src_uri(scripts_path, machine, src_uri):
561 """
562 Write (replace) the SRC_URI append for a machine from a list
563 SRC_URI elements.
564 """
565 layer = find_bsp_layer(scripts_path, machine)
566
567 kernel = find_current_kernel(layer, machine)
568 if not kernel:
569 print "Couldn't determine the kernel for this BSP, exiting."
570 sys.exit(1)
571
572 kernel_bbappend = os.path.join(layer, "recipes-kernel/linux/" + kernel + ".bbappend")
573
574 (uri_start_line, uri_end_line, unused) = find_bsp_kernel_src_uri(scripts_path, machine, True)
575
576 kernel_bbappend_prev = kernel_bbappend + ".prev"
577 shutil.copyfile(kernel_bbappend, kernel_bbappend_prev)
578 ifile = open(kernel_bbappend_prev, "r")
579 ofile = open(kernel_bbappend, "w")
580
581 ifile_lines = ifile.readlines()
582 if uri_start_line == -1:
583 uri_end_line = len(ifile_lines) # make sure we add at end
584 wrote_src_uri = False
585 for i, ifile_line in enumerate(ifile_lines):
586 if ifile_line.strip().startswith("PR"):
587 ifile_line = inc_pr(ifile_line)
588 if i < uri_start_line:
589 ofile.write(ifile_line)
590 elif i > uri_end_line:
591 ofile.write(ifile_line)
592 else:
593 if not wrote_src_uri:
594 write_uri_lines(ofile, src_uri)
595 wrote_src_uri = True
596 if uri_start_line == -1:
597 write_uri_lines(ofile, src_uri)
598
599
600def kernels(context):
601 """
602 Return the list of available kernels in the BSP i.e. corresponding
603 to the kernel .bbappends found in the layer.
604 """
605 archdir = os.path.join(context["scripts_path"], "lib/bsp/substrate/target/arch/" + context["arch"])
606 kerndir = os.path.join(archdir, "recipes-kernel/linux")
607 bbglob = os.path.join(kerndir, "*.bbappend")
608
609 bbappends = glob.glob(bbglob)
610
611 kernels = []
612
613 for kernel in bbappends:
614 filename = os.path.splitext(os.path.basename(kernel))[0]
615 idx = filename.find(CLOSE_TAG)
616 if idx != -1:
617 filename = filename[idx + len(CLOSE_TAG):].strip()
618 kernels.append(filename)
619
620 return kernels
621
622
623def extract_giturl(file):
624 """
625 Extract the git url of the kernel repo from the kernel recipe's
626 SRC_URI.
627 """
628 f = open(file, "r")
629 lines = f.readlines()
630 for line in lines:
631 line = line.strip()
632 if line.startswith("SRC_URI"):
633 line = line[len("SRC_URI"):].strip()
634 if line.startswith("="):
635 line = line[1:].strip()
636 if line.startswith("\""):
637 line = line[1:].strip()
638 fields = line.split(";")
639 if fields:
640 return fields[0]
641 return None
642
643
644def find_giturl(context):
645 """
646 Find the git url of the kernel repo from the kernel recipe's
647 SRC_URI.
648 """
649 name = context["name"]
650 filebase = context["filename"]
651 scripts_path = context["scripts_path"]
652
653 meta_layer = find_meta_layer(scripts_path)
654
655 kerndir = os.path.join(meta_layer, "recipes-kernel/linux")
656 bbglob = os.path.join(kerndir, "*.bb")
657 bbs = glob.glob(bbglob)
658 for kernel in bbs:
659 filename = os.path.splitext(os.path.basename(kernel))[0]
660 if filename == filebase:
661 giturl = extract_giturl(kernel)
662 return giturl
663
664 return None
665
666
667def base_branches(context):
668 """
669 Return a list of the base branches found in the kernel git repo.
670 """
671 giturl = find_giturl(context)
672
673 print "Getting branches from remote repo %s..." % giturl
674
675 gitcmd = "git ls-remote %s *heads* 2>&1" % (giturl)
676 tmp = os.popen(gitcmd).read()
677
678 branches = []
679
680 if tmp:
681 tmpline = tmp.split("\n")
682 for line in tmpline:
683 if len(line)==0:
684 break;
685 if not line.endswith("base"):
686 continue;
687 idx = line.find("refs/heads/")
688 kbranch = line[idx + len("refs/heads/"):]
689 if kbranch.find("/") == -1 and kbranch.find("base") == -1:
690 continue
691 idx = kbranch.find("base")
692 branches.append(kbranch[:idx - 1])
693
694 return branches
695
696
697def all_branches(context):
698 """
699 Return a list of all the branches found in the kernel git repo.
700 """
701 giturl = find_giturl(context)
702
703 print "Getting branches from remote repo %s..." % giturl
704
705 gitcmd = "git ls-remote %s *heads* 2>&1" % (giturl)
706 tmp = os.popen(gitcmd).read()
707
708 branches = []
709
710 if tmp:
711 tmpline = tmp.split("\n")
712 for line in tmpline:
713 if len(line)==0:
714 break;
715 idx = line.find("refs/heads/")
716 kbranch = line[idx + len("refs/heads/"):]
717 if (kbranch.find("/") != -1 and
718 (kbranch.find("standard") != -1 or kbranch.find("base") != -1) or
719 kbranch == "base"):
720 branches.append(kbranch)
721 continue
722
723 return branches