blob: a3ee325a8fc5ffec02f9b2ce4bdbaa04ea362805 [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")
334 for item in patch_items:
335 f.write("patch " + item + "\n")
336 f.close()
337
338 kernel_contents_changed(scripts_path, machine)
339
340
341def yocto_kernel_patch_list(scripts_path, machine):
342 """
343 Display the list of patches in a machine's user-defined patch list
344 [${machine}-user-patches.scc].
345 """
346 patches = read_patch_items(scripts_path, machine)
347
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600348 print("The current set of machine-specific patches for %s is:" % machine)
349 print(gen_choices_str(patches))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500350
351
352def yocto_kernel_patch_rm(scripts_path, machine):
353 """
354 Remove one or more patches from a machine's user-defined patch
355 list [${machine}-user-patches.scc].
356 """
357 patches = read_patch_items(scripts_path, machine)
358
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600359 print("Specify the patches to remove:")
360 inp = input(gen_choices_str(patches))
361 rm_choices = inp.split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500362 rm_choices.sort()
363
364 removed = []
365
366 filesdir = find_filesdir(scripts_path, machine)
367 if not filesdir:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600368 print("Couldn't rm patch(es) since we couldn't find a 'files' dir")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500369 sys.exit(1)
370
371 for choice in reversed(rm_choices):
372 try:
373 idx = int(choice) - 1
374 except ValueError:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600375 print("Invalid choice (%s), exiting" % choice)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500376 sys.exit(1)
377 if idx < 0 or idx >= len(patches):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600378 print("Invalid choice (%d), exiting" % (idx + 1))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500379 sys.exit(1)
380 filesdir_patch = os.path.join(filesdir, patches[idx])
381 if os.path.isfile(filesdir_patch):
382 os.remove(filesdir_patch)
383 removed.append(patches[idx])
384 patches.pop(idx)
385
386 write_patch_items(scripts_path, machine, patches)
387
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600388 print("Removed patches:")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500389 for r in removed:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600390 print("\t%s" % r)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500391
392
393def yocto_kernel_patch_add(scripts_path, machine, patches):
394 """
395 Add one or more patches to a machine's user-defined patch list
396 [${machine}-user-patches.scc].
397 """
398 existing_patches = read_patch_items(scripts_path, machine)
399
400 for patch in patches:
401 if os.path.basename(patch) in existing_patches:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600402 print("Couldn't add patch (%s) since it's already been added" % os.path.basename(patch))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500403 sys.exit(1)
404
405 filesdir = find_filesdir(scripts_path, machine)
406 if not filesdir:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600407 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 -0500408 sys.exit(1)
409
410 new_patches = []
411
412 for patch in patches:
413 if not os.path.isfile(patch):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600414 print("Couldn't find patch (%s), exiting" % patch)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500415 sys.exit(1)
416 basename = os.path.basename(patch)
417 filesdir_patch = os.path.join(filesdir, basename)
418 shutil.copyfile(patch, filesdir_patch)
419 new_patches.append(basename)
420
421 cur_items = read_patch_items(scripts_path, machine)
422 cur_items.extend(new_patches)
423 write_patch_items(scripts_path, machine, cur_items)
424
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600425 print("Added patches:")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500426 for n in new_patches:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600427 print("\t%s" % n)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500428
429
430def inc_pr(line):
431 """
432 Add 1 to the PR value in the given bbappend PR line. For the PR
433 lines in kernel .bbappends after modifications. Handles PRs of
434 the form PR := "${PR}.1" as well as PR = "r0".
435 """
436 idx = line.find("\"")
437
438 pr_str = line[idx:]
439 pr_str = pr_str.replace('\"','')
440 fields = pr_str.split('.')
441 if len(fields) > 1:
442 fields[1] = str(int(fields[1]) + 1)
443 pr_str = "\"" + '.'.join(fields) + "\"\n"
444 else:
445 pr_val = pr_str[1:]
446 pr_str = "\"" + "r" + str(int(pr_val) + 1) + "\"\n"
447 idx2 = line.find("\"", idx + 1)
448 line = line[:idx] + pr_str
449
450 return line
451
452
453def kernel_contents_changed(scripts_path, machine):
454 """
455 Do what we need to do to notify the system that the kernel
456 recipe's contents have changed.
457 """
458 layer = find_bsp_layer(machine)
459
460 kernel = find_current_kernel(layer, machine)
461 if not kernel:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600462 print("Couldn't determine the kernel for this BSP, exiting.")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500463 sys.exit(1)
464
465 kernel_bbfile = os.path.join(layer, "recipes-kernel/linux/" + kernel + ".bbappend")
466 if not os.path.isfile(kernel_bbfile):
467 kernel_bbfile = os.path.join(layer, "recipes-kernel/linux/" + kernel + ".bb")
468 if not os.path.isfile(kernel_bbfile):
469 return
470 kernel_bbfile_prev = kernel_bbfile + ".prev"
471 shutil.copyfile(kernel_bbfile, kernel_bbfile_prev)
472
473 ifile = open(kernel_bbfile_prev, "r")
474 ofile = open(kernel_bbfile, "w")
475 ifile_lines = ifile.readlines()
476 for ifile_line in ifile_lines:
477 if ifile_line.strip().startswith("PR"):
478 ifile_line = inc_pr(ifile_line)
479 ofile.write(ifile_line)
480 ofile.close()
481 ifile.close()
482
483
484def kernels(context):
485 """
486 Return the list of available kernels in the BSP i.e. corresponding
487 to the kernel .bbappends found in the layer.
488 """
489 archdir = os.path.join(context["scripts_path"], "lib/bsp/substrate/target/arch/" + context["arch"])
490 kerndir = os.path.join(archdir, "recipes-kernel/linux")
491 bbglob = os.path.join(kerndir, "*.bbappend")
492
493 bbappends = glob.glob(bbglob)
494
495 kernels = []
496
497 for kernel in bbappends:
498 filename = os.path.splitext(os.path.basename(kernel))[0]
499 idx = filename.find(CLOSE_TAG)
500 if idx != -1:
501 filename = filename[idx + len(CLOSE_TAG):].strip()
502 kernels.append(filename)
503
504 kernels.append("custom")
505
506 return kernels
507
508
509def extract_giturl(file):
510 """
511 Extract the git url of the kernel repo from the kernel recipe's
512 SRC_URI.
513 """
514 url = None
515 f = open(file, "r")
516 lines = f.readlines()
517 for line in lines:
518 line = line.strip()
519 if line.startswith("SRC_URI"):
520 line = line[len("SRC_URI"):].strip()
521 if line.startswith("="):
522 line = line[1:].strip()
523 if line.startswith("\""):
524 line = line[1:].strip()
525 prot = "git"
526 for s in line.split(";"):
527 if s.startswith("git://"):
528 url = s
529 if s.startswith("protocol="):
530 prot = s.split("=")[1]
531 if url:
532 url = prot + url[3:]
533 return url
534
535
536def find_giturl(context):
537 """
538 Find the git url of the kernel repo from the kernel recipe's
539 SRC_URI.
540 """
541 name = context["name"]
542 filebase = context["filename"]
543 scripts_path = context["scripts_path"]
544
545 meta_layer = find_meta_layer()
546
547 kerndir = os.path.join(meta_layer, "recipes-kernel/linux")
548 bbglob = os.path.join(kerndir, "*.bb")
549 bbs = glob.glob(bbglob)
550 for kernel in bbs:
551 filename = os.path.splitext(os.path.basename(kernel))[0]
552 if filename in filebase:
553 giturl = extract_giturl(kernel)
554 return giturl
555
556 return None
557
558
559def read_features(scripts_path, machine):
560 """
561 Find and return a list of features in a machine's user-defined
562 features fragment [${machine}-user-features.scc].
563 """
564 features = []
565
566 f = open_user_file(scripts_path, machine, machine+"-user-features.scc", "r")
567 lines = f.readlines()
568 for line in lines:
569 s = line.strip()
570 if s and not s.startswith("#"):
571 feature_include = s.split()
572 features.append(feature_include[1].strip())
573 f.close()
574
575 return features
576
577
578def write_features(scripts_path, machine, features):
579 """
580 Write (replace) the list of feature items in a
581 machine's user-defined features fragment [${machine}=user-features.cfg].
582 """
583 f = open_user_file(scripts_path, machine, machine+"-user-features.scc", "w")
584 for item in features:
585 f.write("include " + item + "\n")
586 f.close()
587
588 kernel_contents_changed(scripts_path, machine)
589
590
591def yocto_kernel_feature_list(scripts_path, machine):
592 """
593 Display the list of features used in a machine's user-defined
594 features fragment [${machine}-user-features.scc].
595 """
596 features = read_features(scripts_path, machine)
597
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600598 print("The current set of machine-specific features for %s is:" % machine)
599 print(gen_choices_str(features))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500600
601
602def yocto_kernel_feature_rm(scripts_path, machine):
603 """
604 Display the list of features used in a machine's user-defined
605 features fragment [${machine}-user-features.scc], prompt the user
606 for one or more to remove, and remove them.
607 """
608 features = read_features(scripts_path, machine)
609
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600610 print("Specify the features to remove:")
611 inp = input(gen_choices_str(features))
612 rm_choices = inp.split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500613 rm_choices.sort()
614
615 removed = []
616
617 for choice in reversed(rm_choices):
618 try:
619 idx = int(choice) - 1
620 except ValueError:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600621 print("Invalid choice (%s), exiting" % choice)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500622 sys.exit(1)
623 if idx < 0 or idx >= len(features):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600624 print("Invalid choice (%d), exiting" % (idx + 1))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500625 sys.exit(1)
626 removed.append(features.pop(idx))
627
628 write_features(scripts_path, machine, features)
629
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600630 print("Removed features:")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500631 for r in removed:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600632 print("\t%s" % r)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500633
634
635def yocto_kernel_feature_add(scripts_path, machine, features):
636 """
637 Add one or more features a machine's user-defined features
638 fragment [${machine}-user-features.scc].
639 """
640 new_items = []
641
642 for item in features:
643 if not item.endswith(".scc"):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600644 print("Invalid feature (%s), exiting" % item)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500645 sys.exit(1)
646 new_items.append(item)
647
648 cur_items = read_features(scripts_path, machine)
649 cur_items.extend(new_items)
650
651 write_features(scripts_path, machine, cur_items)
652
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600653 print("Added features:")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500654 for n in new_items:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600655 print("\t%s" % n)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500656
657
658def find_feature_url(git_url):
659 """
660 Find the url of the kern-features.rc kernel for the kernel repo
661 specified from the BSP's kernel recipe SRC_URI.
662 """
663 feature_url = ""
664 if git_url.startswith("git://"):
665 git_url = git_url[len("git://"):].strip()
666 s = git_url.split("/")
667 if s[1].endswith(".git"):
668 s[1] = s[1][:len(s[1]) - len(".git")]
669 feature_url = "http://" + s[0] + "/cgit/cgit.cgi/" + s[1] + \
670 "/plain/meta/cfg/kern-features.rc?h=meta"
671
672 return feature_url
673
674
675def find_feature_desc(lines):
676 """
677 Find the feature description and compatibility in the passed-in
678 set of lines. Returns a string string of the form 'desc
679 [compat]'.
680 """
681 desc = "no description available"
682 compat = "unknown"
683
684 for line in lines:
685 idx = line.find("KFEATURE_DESCRIPTION")
686 if idx != -1:
687 desc = line[idx + len("KFEATURE_DESCRIPTION"):].strip()
688 if desc.startswith("\""):
689 desc = desc[1:]
690 if desc.endswith("\""):
691 desc = desc[:-1]
692 else:
693 idx = line.find("KFEATURE_COMPATIBILITY")
694 if idx != -1:
695 compat = line[idx + len("KFEATURE_COMPATIBILITY"):].strip()
696
697 return desc + " [" + compat + "]"
698
699
700def print_feature_descs(layer, feature_dir):
701 """
702 Print the feature descriptions for the features in feature_dir.
703 """
704 kernel_files_features = os.path.join(layer, "recipes-kernel/linux/files/" +
705 feature_dir)
706 for root, dirs, files in os.walk(kernel_files_features):
707 for file in files:
708 if file.endswith("~") or file.endswith("#"):
709 continue
710 if file.endswith(".scc"):
711 fullpath = os.path.join(layer, "recipes-kernel/linux/files/" +
712 feature_dir + "/" + file)
713 f = open(fullpath)
714 feature_desc = find_feature_desc(f.readlines())
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600715 print(feature_dir + "/" + file + ": " + feature_desc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500716
717
718def yocto_kernel_available_features_list(scripts_path, machine):
719 """
720 Display the list of all the kernel features available for use in
721 BSPs, as gathered from the set of feature sources.
722 """
723 layer = find_bsp_layer(machine)
724 kernel = find_current_kernel(layer, machine)
725 if not kernel:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600726 print("Couldn't determine the kernel for this BSP, exiting.")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500727 sys.exit(1)
728
729 context = create_context(machine, "arch", scripts_path)
730 context["name"] = "name"
731 context["filename"] = kernel
732 giturl = find_giturl(context)
733 feature_url = find_feature_url(giturl)
734
735 feature_cmd = "wget -q -O - " + feature_url
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600736 tmp = subprocess.Popen(feature_cmd, shell=True, stdout=subprocess.PIPE).stdout.read().decode('utf-8')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500737
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600738 print("The current set of kernel features available to %s is:\n" % machine)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500739
740 if tmp:
741 tmpline = tmp.split("\n")
742 in_kernel_options = False
743 for line in tmpline:
744 if not "=" in line:
745 if in_kernel_options:
746 break
747 if "kernel-options" in line:
748 in_kernel_options = True
749 continue
750 if in_kernel_options:
751 feature_def = line.split("=")
752 feature_type = feature_def[0].strip()
753 feature = feature_def[1].strip()
754 desc = get_feature_desc(giturl, feature)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600755 print("%s: %s" % (feature, desc))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500756
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600757 print("[local]")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500758
759 print_feature_descs(layer, "cfg")
760 print_feature_descs(layer, "features")
761
762
763def find_feature_desc_url(git_url, feature):
764 """
765 Find the url of the kernel feature in the kernel repo specified
766 from the BSP's kernel recipe SRC_URI.
767 """
768 feature_desc_url = ""
769 if git_url.startswith("git://"):
770 git_url = git_url[len("git://"):].strip()
771 s = git_url.split("/")
772 if s[1].endswith(".git"):
773 s[1] = s[1][:len(s[1]) - len(".git")]
774 feature_desc_url = "http://" + s[0] + "/cgit/cgit.cgi/" + s[1] + \
775 "/plain/meta/cfg/kernel-cache/" + feature + "?h=meta"
776
777 return feature_desc_url
778
779
780def get_feature_desc(git_url, feature):
781 """
782 Return a feature description of the form 'description [compatibility]
783 BSPs, as gathered from the set of feature sources.
784 """
785 feature_desc_url = find_feature_desc_url(git_url, feature)
786 feature_desc_cmd = "wget -q -O - " + feature_desc_url
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600787 tmp = subprocess.Popen(feature_desc_cmd, shell=True, stdout=subprocess.PIPE).stdout.read().decode('utf-8')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500788
789 return find_feature_desc(tmp.split("\n"))
790
791
792def yocto_kernel_feature_describe(scripts_path, machine, feature):
793 """
794 Display the description of a specific kernel feature available for
795 use in a BSP.
796 """
797 layer = find_bsp_layer(machine)
798
799 kernel = find_current_kernel(layer, machine)
800 if not kernel:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600801 print("Couldn't determine the kernel for this BSP, exiting.")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500802 sys.exit(1)
803
804 context = create_context(machine, "arch", scripts_path)
805 context["name"] = "name"
806 context["filename"] = kernel
807 giturl = find_giturl(context)
808
809 desc = get_feature_desc(giturl, feature)
810
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600811 print(desc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500812
813
814def check_feature_name(feature_name):
815 """
816 Sanity-check the feature name for create/destroy. Return False if not OK.
817 """
818 if not feature_name.endswith(".scc"):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600819 print("Invalid feature name (must end with .scc) [%s], exiting" % feature_name)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500820 return False
821
822 if "/" in feature_name:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600823 print("Invalid feature name (don't specify directory) [%s], exiting" % feature_name)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500824 return False
825
826 return True
827
828
829def check_create_input(feature_items):
830 """
831 Sanity-check the create input. Return False if not OK.
832 """
833 if not check_feature_name(feature_items[0]):
834 return False
835
836 if feature_items[1].endswith(".patch") or feature_items[1].startswith("CONFIG_"):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600837 print("Missing description and/or compatibilty [%s], exiting" % feature_items[1])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500838 return False
839
840 if feature_items[2].endswith(".patch") or feature_items[2].startswith("CONFIG_"):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600841 print("Missing description and/or compatibility [%s], exiting" % feature_items[1])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500842 return False
843
844 return True
845
846
847def yocto_kernel_feature_create(scripts_path, machine, feature_items):
848 """
849 Create a recipe-space kernel feature in a BSP.
850 """
851 if not check_create_input(feature_items):
852 sys.exit(1)
853
854 feature = feature_items[0]
855 feature_basename = feature.split(".")[0]
856 feature_description = feature_items[1]
857 feature_compat = feature_items[2]
858
859 patches = []
860 cfg_items = []
861
862 for item in feature_items[3:]:
863 if item.endswith(".patch"):
864 patches.append(item)
865 elif item.startswith("CONFIG"):
866 if ("=y" in item or "=m" in item):
867 cfg_items.append(item)
868 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600869 print("Invalid feature item (must be .patch or CONFIG_*) [%s], exiting" % item)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500870 sys.exit(1)
871
872 feature_dirname = "cfg"
873 if patches:
874 feature_dirname = "features"
875
876 filesdir = find_filesdir(scripts_path, machine)
877 if not filesdir:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600878 print("Couldn't add feature (%s), no 'files' dir found" % feature)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500879 sys.exit(1)
880
881 featdir = os.path.join(filesdir, feature_dirname)
882 if not os.path.exists(featdir):
883 os.mkdir(featdir)
884
885 for patch in patches:
886 if not os.path.isfile(patch):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600887 print("Couldn't find patch (%s), exiting" % patch)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500888 sys.exit(1)
889 basename = os.path.basename(patch)
890 featdir_patch = os.path.join(featdir, basename)
891 shutil.copyfile(patch, featdir_patch)
892
893 new_cfg_filename = os.path.join(featdir, feature_basename + ".cfg")
894 new_cfg_file = open(new_cfg_filename, "w")
895 for cfg_item in cfg_items:
896 new_cfg_file.write(cfg_item + "\n")
897 new_cfg_file.close()
898
899 new_feature_filename = os.path.join(featdir, feature_basename + ".scc")
900 new_feature_file = open(new_feature_filename, "w")
901 new_feature_file.write("define KFEATURE_DESCRIPTION \"" + feature_description + "\"\n")
902 new_feature_file.write("define KFEATURE_COMPATIBILITY " + feature_compat + "\n\n")
903
904 for patch in patches:
905 patch_dir, patch_file = os.path.split(patch)
906 new_feature_file.write("patch " + patch_file + "\n")
907
908 new_feature_file.write("kconf non-hardware " + feature_basename + ".cfg\n")
909 new_feature_file.close()
910
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600911 print("Added feature:")
912 print("\t%s" % feature_dirname + "/" + feature)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500913
914
915def feature_in_use(scripts_path, machine, feature):
916 """
917 Determine whether the specified feature is in use by the BSP.
918 Return True if so, False otherwise.
919 """
920 features = read_features(scripts_path, machine)
921 for f in features:
922 if f == feature:
923 return True
924 return False
925
926
927def feature_remove(scripts_path, machine, feature):
928 """
929 Remove the specified feature from the available recipe-space
930 features defined for the BSP.
931 """
932 features = read_features(scripts_path, machine)
933 new_features = []
934 for f in features:
935 if f == feature:
936 continue
937 new_features.append(f)
938 write_features(scripts_path, machine, new_features)
939
940
941def yocto_kernel_feature_destroy(scripts_path, machine, feature):
942 """
943 Remove a recipe-space kernel feature from a BSP.
944 """
945 if not check_feature_name(feature):
946 sys.exit(1)
947
948 if feature_in_use(scripts_path, machine, "features/" + feature) or \
949 feature_in_use(scripts_path, machine, "cfg/" + feature):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600950 print("Feature %s is in use (use 'feature rm' to un-use it first), exiting" % feature)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500951 sys.exit(1)
952
953 filesdir = find_filesdir(scripts_path, machine)
954 if not filesdir:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600955 print("Couldn't destroy feature (%s), no 'files' dir found" % feature)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500956 sys.exit(1)
957
958 feature_dirname = "features"
959 featdir = os.path.join(filesdir, feature_dirname)
960 if not os.path.exists(featdir):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600961 print("Couldn't find feature directory (%s)" % feature_dirname)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500962 sys.exit(1)
963
964 feature_fqn = os.path.join(featdir, feature)
965 if not os.path.exists(feature_fqn):
966 feature_dirname = "cfg"
967 featdir = os.path.join(filesdir, feature_dirname)
968 if not os.path.exists(featdir):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600969 print("Couldn't find feature directory (%s)" % feature_dirname)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500970 sys.exit(1)
971 feature_fqn = os.path.join(featdir, feature_filename)
972 if not os.path.exists(feature_fqn):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600973 print("Couldn't find feature (%s)" % feature)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500974 sys.exit(1)
975
976 f = open(feature_fqn, "r")
977 lines = f.readlines()
978 for line in lines:
979 s = line.strip()
980 if s.startswith("patch ") or s.startswith("kconf "):
981 split_line = s.split()
982 filename = os.path.join(featdir, split_line[-1])
983 if os.path.exists(filename):
984 os.remove(filename)
985 f.close()
986 os.remove(feature_fqn)
987
988 feature_remove(scripts_path, machine, feature)
989
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600990 print("Removed feature:")
991 print("\t%s" % feature_dirname + "/" + feature)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500992
993
994def base_branches(context):
995 """
996 Return a list of the base branches found in the kernel git repo.
997 """
998 giturl = find_giturl(context)
999
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001000 print("Getting branches from remote repo %s..." % giturl)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001001
1002 gitcmd = "git ls-remote %s *heads* 2>&1" % (giturl)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001003 tmp = subprocess.Popen(gitcmd, shell=True, stdout=subprocess.PIPE).stdout.read().decode('utf-8')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001004
1005 branches = []
1006
1007 if tmp:
1008 tmpline = tmp.split("\n")
1009 for line in tmpline:
1010 if len(line)==0:
1011 break;
1012 if not line.endswith("base"):
1013 continue;
1014 idx = line.find("refs/heads/")
1015 kbranch = line[idx + len("refs/heads/"):]
1016 if kbranch.find("/") == -1 and kbranch.find("base") == -1:
1017 continue
1018 idx = kbranch.find("base")
1019 branches.append(kbranch[:idx - 1])
1020
1021 return branches
1022
1023
1024def all_branches(context):
1025 """
1026 Return a list of all the branches found in the kernel git repo.
1027 """
1028 giturl = find_giturl(context)
1029
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001030 print("Getting branches from remote repo %s..." % giturl)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001031
1032 gitcmd = "git ls-remote %s *heads* 2>&1" % (giturl)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001033 tmp = subprocess.Popen(gitcmd, shell=True, stdout=subprocess.PIPE).stdout.read().decode('utf-8')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001034
1035 branches = []
1036
1037 base_prefixes = None
1038
1039 try:
1040 branches_base = context["branches_base"]
1041 if branches_base:
1042 base_prefixes = branches_base.split(":")
1043 except KeyError:
1044 pass
1045
1046 arch = context["arch"]
1047
1048 if tmp:
1049 tmpline = tmp.split("\n")
1050 for line in tmpline:
1051 if len(line)==0:
1052 break;
1053 idx = line.find("refs/heads/")
1054 kbranch = line[idx + len("refs/heads/"):]
1055 kbranch_prefix = kbranch.rsplit("/", 1)[0]
1056
1057 if base_prefixes:
1058 for base_prefix in base_prefixes:
1059 if kbranch_prefix == base_prefix:
1060 branches.append(kbranch)
1061 continue
1062
1063 if (kbranch.find("/") != -1 and
1064 (kbranch.find("standard") != -1 or kbranch.find("base") != -1) or
1065 kbranch == "base"):
1066 branches.append(kbranch)
1067 continue
1068
1069 return branches