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