3be73dcd7a
With some recent changes in the kern tools, we can drop some changes in the yocto-bsp and yocto-kernel tools that ensured proper patching and BSP inheritance. In particular, we no longer need to signify the start of patching, and we must instruct the tools that we only want configuration fragments via inheritance .. no patches (since they are already applied). (From meta-yocto rev: 34ed5eebd0b5baab98b6b2d7b3f06ca40932b37d) Signed-off-by: Bruce Ashfield <bruce.ashfield@windriver.com> Signed-off-by: Ross Burton <ross.burton@intel.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
1070 lines
33 KiB
Python
1070 lines
33 KiB
Python
# ex:ts=4:sw=4:sts=4:et
|
|
# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
|
|
#
|
|
# Copyright (c) 2012, Intel Corporation.
|
|
# All rights reserved.
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License version 2 as
|
|
# published by the Free Software Foundation.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License along
|
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
#
|
|
# DESCRIPTION
|
|
# This module implements the kernel-related functions used by
|
|
# 'yocto-kernel' to manage kernel config items and patches for Yocto
|
|
# BSPs.
|
|
#
|
|
# AUTHORS
|
|
# Tom Zanussi <tom.zanussi (at] intel.com>
|
|
#
|
|
|
|
import sys
|
|
import os
|
|
import shutil
|
|
from .tags import *
|
|
import glob
|
|
import subprocess
|
|
from .engine import create_context
|
|
|
|
def find_bblayers():
|
|
"""
|
|
Find and return a sanitized list of the layers found in BBLAYERS.
|
|
"""
|
|
try:
|
|
builddir = os.environ["BUILDDIR"]
|
|
except KeyError:
|
|
print("BUILDDIR not found, exiting. (Did you forget to source oe-init-build-env?)")
|
|
sys.exit(1)
|
|
bblayers_conf = os.path.join(builddir, "conf/bblayers.conf")
|
|
|
|
layers = []
|
|
|
|
bitbake_env_cmd = "bitbake -e"
|
|
bitbake_env_lines = subprocess.Popen(bitbake_env_cmd, shell=True,
|
|
stdout=subprocess.PIPE).stdout.read().decode('utf-8')
|
|
|
|
if not bitbake_env_lines:
|
|
print("Couldn't get '%s' output, exiting." % bitbake_env_cmd)
|
|
sys.exit(1)
|
|
|
|
for line in bitbake_env_lines.split('\n'):
|
|
bblayers = get_line_val(line, "BBLAYERS")
|
|
if (bblayers):
|
|
break
|
|
|
|
if not bblayers:
|
|
print("Couldn't find BBLAYERS in %s output, exiting." % bitbake_env_cmd)
|
|
sys.exit(1)
|
|
|
|
raw_layers = bblayers.split()
|
|
|
|
for layer in raw_layers:
|
|
if layer == 'BBLAYERS' or '=' in layer:
|
|
continue
|
|
layers.append(layer)
|
|
|
|
return layers
|
|
|
|
|
|
def get_line_val(line, key):
|
|
"""
|
|
Extract the value from the VAR="val" string
|
|
"""
|
|
if line.startswith(key + "="):
|
|
stripped_line = line.split('=')[1]
|
|
stripped_line = stripped_line.replace('\"', '')
|
|
return stripped_line
|
|
return None
|
|
|
|
|
|
def find_meta_layer():
|
|
"""
|
|
Find and return the meta layer in BBLAYERS.
|
|
"""
|
|
layers = find_bblayers()
|
|
|
|
for layer in layers:
|
|
if layer.endswith("meta"):
|
|
return layer
|
|
|
|
return None
|
|
|
|
|
|
def find_bsp_layer(machine):
|
|
"""
|
|
Find and return a machine's BSP layer in BBLAYERS.
|
|
"""
|
|
layers = find_bblayers()
|
|
|
|
for layer in layers:
|
|
if layer.endswith(machine):
|
|
return layer
|
|
|
|
print("Unable to find the BSP layer for machine %s." % machine)
|
|
print("Please make sure it is listed in bblayers.conf")
|
|
sys.exit(1)
|
|
|
|
|
|
def gen_choices_str(choices):
|
|
"""
|
|
Generate a numbered list of choices from a list of choices for
|
|
display to the user.
|
|
"""
|
|
choices_str = ""
|
|
|
|
for i, choice in enumerate(choices):
|
|
choices_str += "\t" + str(i + 1) + ") " + choice + "\n"
|
|
|
|
return choices_str
|
|
|
|
|
|
def open_user_file(scripts_path, machine, userfile, mode):
|
|
"""
|
|
Find one of the user files (user-config.cfg, user-patches.scc)
|
|
associated with the machine (could be in files/,
|
|
linux-yocto-custom/, etc). Returns the open file if found, None
|
|
otherwise.
|
|
|
|
The caller is responsible for closing the file returned.
|
|
"""
|
|
layer = find_bsp_layer(machine)
|
|
linuxdir = os.path.join(layer, "recipes-kernel/linux")
|
|
linuxdir_list = os.listdir(linuxdir)
|
|
for fileobj in linuxdir_list:
|
|
fileobj_path = os.path.join(linuxdir, fileobj)
|
|
if os.path.isdir(fileobj_path):
|
|
userfile_name = os.path.join(fileobj_path, userfile)
|
|
try:
|
|
f = open(userfile_name, mode)
|
|
return f
|
|
except IOError:
|
|
continue
|
|
return None
|
|
|
|
|
|
def read_config_items(scripts_path, machine):
|
|
"""
|
|
Find and return a list of config items (CONFIG_XXX) in a machine's
|
|
user-defined config fragment [${machine}-user-config.cfg].
|
|
"""
|
|
config_items = []
|
|
|
|
f = open_user_file(scripts_path, machine, machine+"-user-config.cfg", "r")
|
|
lines = f.readlines()
|
|
for line in lines:
|
|
s = line.strip()
|
|
if s and not s.startswith("#"):
|
|
config_items.append(s)
|
|
f.close()
|
|
|
|
return config_items
|
|
|
|
|
|
def write_config_items(scripts_path, machine, config_items):
|
|
"""
|
|
Write (replace) the list of config items (CONFIG_XXX) in a
|
|
machine's user-defined config fragment [${machine}=user-config.cfg].
|
|
"""
|
|
f = open_user_file(scripts_path, machine, machine+"-user-config.cfg", "w")
|
|
for item in config_items:
|
|
f.write(item + "\n")
|
|
f.close()
|
|
|
|
kernel_contents_changed(scripts_path, machine)
|
|
|
|
|
|
def yocto_kernel_config_list(scripts_path, machine):
|
|
"""
|
|
Display the list of config items (CONFIG_XXX) in a machine's
|
|
user-defined config fragment [${machine}-user-config.cfg].
|
|
"""
|
|
config_items = read_config_items(scripts_path, machine)
|
|
|
|
print("The current set of machine-specific kernel config items for %s is:" % machine)
|
|
print(gen_choices_str(config_items))
|
|
|
|
|
|
def yocto_kernel_config_rm(scripts_path, machine):
|
|
"""
|
|
Display the list of config items (CONFIG_XXX) in a machine's
|
|
user-defined config fragment [${machine}-user-config.cfg], prompt the user
|
|
for one or more to remove, and remove them.
|
|
"""
|
|
config_items = read_config_items(scripts_path, machine)
|
|
|
|
print("Specify the kernel config items to remove:")
|
|
inp = input(gen_choices_str(config_items))
|
|
rm_choices = inp.split()
|
|
rm_choices.sort()
|
|
|
|
removed = []
|
|
|
|
for choice in reversed(rm_choices):
|
|
try:
|
|
idx = int(choice) - 1
|
|
except ValueError:
|
|
print("Invalid choice (%s), exiting" % choice)
|
|
sys.exit(1)
|
|
if idx < 0 or idx >= len(config_items):
|
|
print("Invalid choice (%d), exiting" % (idx + 1))
|
|
sys.exit(1)
|
|
removed.append(config_items.pop(idx))
|
|
|
|
write_config_items(scripts_path, machine, config_items)
|
|
|
|
print("Removed items:")
|
|
for r in removed:
|
|
print("\t%s" % r)
|
|
|
|
|
|
def yocto_kernel_config_add(scripts_path, machine, config_items):
|
|
"""
|
|
Add one or more config items (CONFIG_XXX) to a machine's
|
|
user-defined config fragment [${machine}-user-config.cfg].
|
|
"""
|
|
new_items = []
|
|
dup_items = []
|
|
|
|
cur_items = read_config_items(scripts_path, machine)
|
|
|
|
for item in config_items:
|
|
if not item.startswith("CONFIG") or (not "=y" in item and not "=m" in item):
|
|
print("Invalid config item (%s), exiting" % item)
|
|
sys.exit(1)
|
|
if item not in cur_items and item not in new_items:
|
|
new_items.append(item)
|
|
else:
|
|
dup_items.append(item)
|
|
|
|
if len(new_items) > 0:
|
|
cur_items.extend(new_items)
|
|
write_config_items(scripts_path, machine, cur_items)
|
|
print("Added item%s:" % ("" if len(new_items)==1 else "s"))
|
|
for n in new_items:
|
|
print("\t%s" % n)
|
|
|
|
if len(dup_items) > 0:
|
|
output="The following item%s already exist%s in the current configuration, ignoring %s:" % \
|
|
(("","s", "it") if len(dup_items)==1 else ("s", "", "them" ))
|
|
print(output)
|
|
for n in dup_items:
|
|
print("\t%s" % n)
|
|
|
|
def find_current_kernel(bsp_layer, machine):
|
|
"""
|
|
Determine the kernel and version currently being used in the BSP.
|
|
"""
|
|
machine_conf = os.path.join(bsp_layer, "conf/machine/" + machine + ".conf")
|
|
|
|
preferred_kernel = preferred_kernel_version = preferred_version_varname = None
|
|
|
|
f = open(machine_conf, "r")
|
|
lines = f.readlines()
|
|
for line in lines:
|
|
if line.strip().startswith("PREFERRED_PROVIDER_virtual/kernel"):
|
|
preferred_kernel = line.split()[-1]
|
|
preferred_kernel = preferred_kernel.replace('\"','')
|
|
preferred_version_varname = "PREFERRED_VERSION_" + preferred_kernel
|
|
if preferred_version_varname and line.strip().startswith(preferred_version_varname):
|
|
preferred_kernel_version = line.split()[-1]
|
|
preferred_kernel_version = preferred_kernel_version.replace('\"','')
|
|
preferred_kernel_version = preferred_kernel_version.replace('%','')
|
|
|
|
if preferred_kernel and preferred_kernel_version:
|
|
return preferred_kernel + "_" + preferred_kernel_version
|
|
elif preferred_kernel:
|
|
return preferred_kernel
|
|
|
|
|
|
def find_filesdir(scripts_path, machine):
|
|
"""
|
|
Find the name of the 'files' dir associated with the machine
|
|
(could be in files/, linux-yocto-custom/, etc). Returns the name
|
|
of the files dir if found, None otherwise.
|
|
"""
|
|
layer = find_bsp_layer(machine)
|
|
filesdir = None
|
|
linuxdir = os.path.join(layer, "recipes-kernel/linux")
|
|
linuxdir_list = os.listdir(linuxdir)
|
|
for fileobj in linuxdir_list:
|
|
fileobj_path = os.path.join(linuxdir, fileobj)
|
|
if os.path.isdir(fileobj_path):
|
|
# this could be files/ or linux-yocto-custom/, we have no way of distinguishing
|
|
# so we take the first (and normally only) dir we find as the 'filesdir'
|
|
filesdir = fileobj_path
|
|
|
|
return filesdir
|
|
|
|
|
|
def read_patch_items(scripts_path, machine):
|
|
"""
|
|
Find and return a list of patch items in a machine's user-defined
|
|
patch list [${machine}-user-patches.scc].
|
|
"""
|
|
patch_items = []
|
|
|
|
f = open_user_file(scripts_path, machine, machine+"-user-patches.scc", "r")
|
|
lines = f.readlines()
|
|
for line in lines:
|
|
s = line.strip()
|
|
if s and not s.startswith("#"):
|
|
fields = s.split()
|
|
if not fields[0] == "patch":
|
|
continue
|
|
patch_items.append(fields[1])
|
|
f.close()
|
|
|
|
return patch_items
|
|
|
|
|
|
def write_patch_items(scripts_path, machine, patch_items):
|
|
"""
|
|
Write (replace) the list of patches in a machine's user-defined
|
|
patch list [${machine}-user-patches.scc].
|
|
"""
|
|
f = open_user_file(scripts_path, machine, machine+"-user-patches.scc", "w")
|
|
for item in patch_items:
|
|
f.write("patch " + item + "\n")
|
|
f.close()
|
|
|
|
kernel_contents_changed(scripts_path, machine)
|
|
|
|
|
|
def yocto_kernel_patch_list(scripts_path, machine):
|
|
"""
|
|
Display the list of patches in a machine's user-defined patch list
|
|
[${machine}-user-patches.scc].
|
|
"""
|
|
patches = read_patch_items(scripts_path, machine)
|
|
|
|
print("The current set of machine-specific patches for %s is:" % machine)
|
|
print(gen_choices_str(patches))
|
|
|
|
|
|
def yocto_kernel_patch_rm(scripts_path, machine):
|
|
"""
|
|
Remove one or more patches from a machine's user-defined patch
|
|
list [${machine}-user-patches.scc].
|
|
"""
|
|
patches = read_patch_items(scripts_path, machine)
|
|
|
|
print("Specify the patches to remove:")
|
|
inp = input(gen_choices_str(patches))
|
|
rm_choices = inp.split()
|
|
rm_choices.sort()
|
|
|
|
removed = []
|
|
|
|
filesdir = find_filesdir(scripts_path, machine)
|
|
if not filesdir:
|
|
print("Couldn't rm patch(es) since we couldn't find a 'files' dir")
|
|
sys.exit(1)
|
|
|
|
for choice in reversed(rm_choices):
|
|
try:
|
|
idx = int(choice) - 1
|
|
except ValueError:
|
|
print("Invalid choice (%s), exiting" % choice)
|
|
sys.exit(1)
|
|
if idx < 0 or idx >= len(patches):
|
|
print("Invalid choice (%d), exiting" % (idx + 1))
|
|
sys.exit(1)
|
|
filesdir_patch = os.path.join(filesdir, patches[idx])
|
|
if os.path.isfile(filesdir_patch):
|
|
os.remove(filesdir_patch)
|
|
removed.append(patches[idx])
|
|
patches.pop(idx)
|
|
|
|
write_patch_items(scripts_path, machine, patches)
|
|
|
|
print("Removed patches:")
|
|
for r in removed:
|
|
print("\t%s" % r)
|
|
|
|
|
|
def yocto_kernel_patch_add(scripts_path, machine, patches):
|
|
"""
|
|
Add one or more patches to a machine's user-defined patch list
|
|
[${machine}-user-patches.scc].
|
|
"""
|
|
existing_patches = read_patch_items(scripts_path, machine)
|
|
|
|
for patch in patches:
|
|
if os.path.basename(patch) in existing_patches:
|
|
print("Couldn't add patch (%s) since it's already been added" % os.path.basename(patch))
|
|
sys.exit(1)
|
|
|
|
filesdir = find_filesdir(scripts_path, machine)
|
|
if not filesdir:
|
|
print("Couldn't add patch (%s) since we couldn't find a 'files' dir to add it to" % os.path.basename(patch))
|
|
sys.exit(1)
|
|
|
|
new_patches = []
|
|
|
|
for patch in patches:
|
|
if not os.path.isfile(patch):
|
|
print("Couldn't find patch (%s), exiting" % patch)
|
|
sys.exit(1)
|
|
basename = os.path.basename(patch)
|
|
filesdir_patch = os.path.join(filesdir, basename)
|
|
shutil.copyfile(patch, filesdir_patch)
|
|
new_patches.append(basename)
|
|
|
|
cur_items = read_patch_items(scripts_path, machine)
|
|
cur_items.extend(new_patches)
|
|
write_patch_items(scripts_path, machine, cur_items)
|
|
|
|
print("Added patches:")
|
|
for n in new_patches:
|
|
print("\t%s" % n)
|
|
|
|
|
|
def inc_pr(line):
|
|
"""
|
|
Add 1 to the PR value in the given bbappend PR line. For the PR
|
|
lines in kernel .bbappends after modifications. Handles PRs of
|
|
the form PR := "${PR}.1" as well as PR = "r0".
|
|
"""
|
|
idx = line.find("\"")
|
|
|
|
pr_str = line[idx:]
|
|
pr_str = pr_str.replace('\"','')
|
|
fields = pr_str.split('.')
|
|
if len(fields) > 1:
|
|
fields[1] = str(int(fields[1]) + 1)
|
|
pr_str = "\"" + '.'.join(fields) + "\"\n"
|
|
else:
|
|
pr_val = pr_str[1:]
|
|
pr_str = "\"" + "r" + str(int(pr_val) + 1) + "\"\n"
|
|
idx2 = line.find("\"", idx + 1)
|
|
line = line[:idx] + pr_str
|
|
|
|
return line
|
|
|
|
|
|
def kernel_contents_changed(scripts_path, machine):
|
|
"""
|
|
Do what we need to do to notify the system that the kernel
|
|
recipe's contents have changed.
|
|
"""
|
|
layer = find_bsp_layer(machine)
|
|
|
|
kernel = find_current_kernel(layer, machine)
|
|
if not kernel:
|
|
print("Couldn't determine the kernel for this BSP, exiting.")
|
|
sys.exit(1)
|
|
|
|
kernel_bbfile = os.path.join(layer, "recipes-kernel/linux/" + kernel + ".bbappend")
|
|
if not os.path.isfile(kernel_bbfile):
|
|
kernel_bbfile = os.path.join(layer, "recipes-kernel/linux/" + kernel + ".bb")
|
|
if not os.path.isfile(kernel_bbfile):
|
|
return
|
|
kernel_bbfile_prev = kernel_bbfile + ".prev"
|
|
shutil.copyfile(kernel_bbfile, kernel_bbfile_prev)
|
|
|
|
ifile = open(kernel_bbfile_prev, "r")
|
|
ofile = open(kernel_bbfile, "w")
|
|
ifile_lines = ifile.readlines()
|
|
for ifile_line in ifile_lines:
|
|
if ifile_line.strip().startswith("PR"):
|
|
ifile_line = inc_pr(ifile_line)
|
|
ofile.write(ifile_line)
|
|
ofile.close()
|
|
ifile.close()
|
|
|
|
|
|
def kernels(context):
|
|
"""
|
|
Return the list of available kernels in the BSP i.e. corresponding
|
|
to the kernel .bbappends found in the layer.
|
|
"""
|
|
archdir = os.path.join(context["scripts_path"], "lib/bsp/substrate/target/arch/" + context["arch"])
|
|
kerndir = os.path.join(archdir, "recipes-kernel/linux")
|
|
bbglob = os.path.join(kerndir, "*.bbappend")
|
|
|
|
bbappends = glob.glob(bbglob)
|
|
|
|
kernels = []
|
|
|
|
for kernel in bbappends:
|
|
filename = os.path.splitext(os.path.basename(kernel))[0]
|
|
idx = filename.find(CLOSE_TAG)
|
|
if idx != -1:
|
|
filename = filename[idx + len(CLOSE_TAG):].strip()
|
|
kernels.append(filename)
|
|
|
|
kernels.append("custom")
|
|
|
|
return kernels
|
|
|
|
|
|
def extract_giturl(file):
|
|
"""
|
|
Extract the git url of the kernel repo from the kernel recipe's
|
|
SRC_URI.
|
|
"""
|
|
url = None
|
|
f = open(file, "r")
|
|
lines = f.readlines()
|
|
for line in lines:
|
|
line = line.strip()
|
|
if line.startswith("SRC_URI"):
|
|
line = line[len("SRC_URI"):].strip()
|
|
if line.startswith("="):
|
|
line = line[1:].strip()
|
|
if line.startswith("\""):
|
|
line = line[1:].strip()
|
|
prot = "git"
|
|
for s in line.split(";"):
|
|
if s.startswith("git://"):
|
|
url = s
|
|
if s.startswith("protocol="):
|
|
prot = s.split("=")[1]
|
|
if url:
|
|
url = prot + url[3:]
|
|
return url
|
|
|
|
|
|
def find_giturl(context):
|
|
"""
|
|
Find the git url of the kernel repo from the kernel recipe's
|
|
SRC_URI.
|
|
"""
|
|
name = context["name"]
|
|
filebase = context["filename"]
|
|
scripts_path = context["scripts_path"]
|
|
|
|
meta_layer = find_meta_layer()
|
|
|
|
kerndir = os.path.join(meta_layer, "recipes-kernel/linux")
|
|
bbglob = os.path.join(kerndir, "*.bb")
|
|
bbs = glob.glob(bbglob)
|
|
for kernel in bbs:
|
|
filename = os.path.splitext(os.path.basename(kernel))[0]
|
|
if filename in filebase:
|
|
giturl = extract_giturl(kernel)
|
|
return giturl
|
|
|
|
return None
|
|
|
|
|
|
def read_features(scripts_path, machine):
|
|
"""
|
|
Find and return a list of features in a machine's user-defined
|
|
features fragment [${machine}-user-features.scc].
|
|
"""
|
|
features = []
|
|
|
|
f = open_user_file(scripts_path, machine, machine+"-user-features.scc", "r")
|
|
lines = f.readlines()
|
|
for line in lines:
|
|
s = line.strip()
|
|
if s and not s.startswith("#"):
|
|
feature_include = s.split()
|
|
features.append(feature_include[1].strip())
|
|
f.close()
|
|
|
|
return features
|
|
|
|
|
|
def write_features(scripts_path, machine, features):
|
|
"""
|
|
Write (replace) the list of feature items in a
|
|
machine's user-defined features fragment [${machine}=user-features.cfg].
|
|
"""
|
|
f = open_user_file(scripts_path, machine, machine+"-user-features.scc", "w")
|
|
for item in features:
|
|
f.write("include " + item + "\n")
|
|
f.close()
|
|
|
|
kernel_contents_changed(scripts_path, machine)
|
|
|
|
|
|
def yocto_kernel_feature_list(scripts_path, machine):
|
|
"""
|
|
Display the list of features used in a machine's user-defined
|
|
features fragment [${machine}-user-features.scc].
|
|
"""
|
|
features = read_features(scripts_path, machine)
|
|
|
|
print("The current set of machine-specific features for %s is:" % machine)
|
|
print(gen_choices_str(features))
|
|
|
|
|
|
def yocto_kernel_feature_rm(scripts_path, machine):
|
|
"""
|
|
Display the list of features used in a machine's user-defined
|
|
features fragment [${machine}-user-features.scc], prompt the user
|
|
for one or more to remove, and remove them.
|
|
"""
|
|
features = read_features(scripts_path, machine)
|
|
|
|
print("Specify the features to remove:")
|
|
inp = input(gen_choices_str(features))
|
|
rm_choices = inp.split()
|
|
rm_choices.sort()
|
|
|
|
removed = []
|
|
|
|
for choice in reversed(rm_choices):
|
|
try:
|
|
idx = int(choice) - 1
|
|
except ValueError:
|
|
print("Invalid choice (%s), exiting" % choice)
|
|
sys.exit(1)
|
|
if idx < 0 or idx >= len(features):
|
|
print("Invalid choice (%d), exiting" % (idx + 1))
|
|
sys.exit(1)
|
|
removed.append(features.pop(idx))
|
|
|
|
write_features(scripts_path, machine, features)
|
|
|
|
print("Removed features:")
|
|
for r in removed:
|
|
print("\t%s" % r)
|
|
|
|
|
|
def yocto_kernel_feature_add(scripts_path, machine, features):
|
|
"""
|
|
Add one or more features a machine's user-defined features
|
|
fragment [${machine}-user-features.scc].
|
|
"""
|
|
new_items = []
|
|
|
|
for item in features:
|
|
if not item.endswith(".scc"):
|
|
print("Invalid feature (%s), exiting" % item)
|
|
sys.exit(1)
|
|
new_items.append(item)
|
|
|
|
cur_items = read_features(scripts_path, machine)
|
|
cur_items.extend(new_items)
|
|
|
|
write_features(scripts_path, machine, cur_items)
|
|
|
|
print("Added features:")
|
|
for n in new_items:
|
|
print("\t%s" % n)
|
|
|
|
|
|
def find_feature_url(git_url):
|
|
"""
|
|
Find the url of the kern-features.rc kernel for the kernel repo
|
|
specified from the BSP's kernel recipe SRC_URI.
|
|
"""
|
|
feature_url = ""
|
|
if git_url.startswith("git://"):
|
|
git_url = git_url[len("git://"):].strip()
|
|
s = git_url.split("/")
|
|
if s[1].endswith(".git"):
|
|
s[1] = s[1][:len(s[1]) - len(".git")]
|
|
feature_url = "http://" + s[0] + "/cgit/cgit.cgi/" + s[1] + \
|
|
"/plain/meta/cfg/kern-features.rc?h=meta"
|
|
|
|
return feature_url
|
|
|
|
|
|
def find_feature_desc(lines):
|
|
"""
|
|
Find the feature description and compatibility in the passed-in
|
|
set of lines. Returns a string string of the form 'desc
|
|
[compat]'.
|
|
"""
|
|
desc = "no description available"
|
|
compat = "unknown"
|
|
|
|
for line in lines:
|
|
idx = line.find("KFEATURE_DESCRIPTION")
|
|
if idx != -1:
|
|
desc = line[idx + len("KFEATURE_DESCRIPTION"):].strip()
|
|
if desc.startswith("\""):
|
|
desc = desc[1:]
|
|
if desc.endswith("\""):
|
|
desc = desc[:-1]
|
|
else:
|
|
idx = line.find("KFEATURE_COMPATIBILITY")
|
|
if idx != -1:
|
|
compat = line[idx + len("KFEATURE_COMPATIBILITY"):].strip()
|
|
|
|
return desc + " [" + compat + "]"
|
|
|
|
|
|
def print_feature_descs(layer, feature_dir):
|
|
"""
|
|
Print the feature descriptions for the features in feature_dir.
|
|
"""
|
|
kernel_files_features = os.path.join(layer, "recipes-kernel/linux/files/" +
|
|
feature_dir)
|
|
for root, dirs, files in os.walk(kernel_files_features):
|
|
for file in files:
|
|
if file.endswith("~") or file.endswith("#"):
|
|
continue
|
|
if file.endswith(".scc"):
|
|
fullpath = os.path.join(layer, "recipes-kernel/linux/files/" +
|
|
feature_dir + "/" + file)
|
|
f = open(fullpath)
|
|
feature_desc = find_feature_desc(f.readlines())
|
|
print(feature_dir + "/" + file + ": " + feature_desc)
|
|
|
|
|
|
def yocto_kernel_available_features_list(scripts_path, machine):
|
|
"""
|
|
Display the list of all the kernel features available for use in
|
|
BSPs, as gathered from the set of feature sources.
|
|
"""
|
|
layer = find_bsp_layer(machine)
|
|
kernel = find_current_kernel(layer, machine)
|
|
if not kernel:
|
|
print("Couldn't determine the kernel for this BSP, exiting.")
|
|
sys.exit(1)
|
|
|
|
context = create_context(machine, "arch", scripts_path)
|
|
context["name"] = "name"
|
|
context["filename"] = kernel
|
|
giturl = find_giturl(context)
|
|
feature_url = find_feature_url(giturl)
|
|
|
|
feature_cmd = "wget -q -O - " + feature_url
|
|
tmp = subprocess.Popen(feature_cmd, shell=True, stdout=subprocess.PIPE).stdout.read().decode('utf-8')
|
|
|
|
print("The current set of kernel features available to %s is:\n" % machine)
|
|
|
|
if tmp:
|
|
tmpline = tmp.split("\n")
|
|
in_kernel_options = False
|
|
for line in tmpline:
|
|
if not "=" in line:
|
|
if in_kernel_options:
|
|
break
|
|
if "kernel-options" in line:
|
|
in_kernel_options = True
|
|
continue
|
|
if in_kernel_options:
|
|
feature_def = line.split("=")
|
|
feature_type = feature_def[0].strip()
|
|
feature = feature_def[1].strip()
|
|
desc = get_feature_desc(giturl, feature)
|
|
print("%s: %s" % (feature, desc))
|
|
|
|
print("[local]")
|
|
|
|
print_feature_descs(layer, "cfg")
|
|
print_feature_descs(layer, "features")
|
|
|
|
|
|
def find_feature_desc_url(git_url, feature):
|
|
"""
|
|
Find the url of the kernel feature in the kernel repo specified
|
|
from the BSP's kernel recipe SRC_URI.
|
|
"""
|
|
feature_desc_url = ""
|
|
if git_url.startswith("git://"):
|
|
git_url = git_url[len("git://"):].strip()
|
|
s = git_url.split("/")
|
|
if s[1].endswith(".git"):
|
|
s[1] = s[1][:len(s[1]) - len(".git")]
|
|
feature_desc_url = "http://" + s[0] + "/cgit/cgit.cgi/" + s[1] + \
|
|
"/plain/meta/cfg/kernel-cache/" + feature + "?h=meta"
|
|
|
|
return feature_desc_url
|
|
|
|
|
|
def get_feature_desc(git_url, feature):
|
|
"""
|
|
Return a feature description of the form 'description [compatibility]
|
|
BSPs, as gathered from the set of feature sources.
|
|
"""
|
|
feature_desc_url = find_feature_desc_url(git_url, feature)
|
|
feature_desc_cmd = "wget -q -O - " + feature_desc_url
|
|
tmp = subprocess.Popen(feature_desc_cmd, shell=True, stdout=subprocess.PIPE).stdout.read().decode('utf-8')
|
|
|
|
return find_feature_desc(tmp.split("\n"))
|
|
|
|
|
|
def yocto_kernel_feature_describe(scripts_path, machine, feature):
|
|
"""
|
|
Display the description of a specific kernel feature available for
|
|
use in a BSP.
|
|
"""
|
|
layer = find_bsp_layer(machine)
|
|
|
|
kernel = find_current_kernel(layer, machine)
|
|
if not kernel:
|
|
print("Couldn't determine the kernel for this BSP, exiting.")
|
|
sys.exit(1)
|
|
|
|
context = create_context(machine, "arch", scripts_path)
|
|
context["name"] = "name"
|
|
context["filename"] = kernel
|
|
giturl = find_giturl(context)
|
|
|
|
desc = get_feature_desc(giturl, feature)
|
|
|
|
print(desc)
|
|
|
|
|
|
def check_feature_name(feature_name):
|
|
"""
|
|
Sanity-check the feature name for create/destroy. Return False if not OK.
|
|
"""
|
|
if not feature_name.endswith(".scc"):
|
|
print("Invalid feature name (must end with .scc) [%s], exiting" % feature_name)
|
|
return False
|
|
|
|
if "/" in feature_name:
|
|
print("Invalid feature name (don't specify directory) [%s], exiting" % feature_name)
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
def check_create_input(feature_items):
|
|
"""
|
|
Sanity-check the create input. Return False if not OK.
|
|
"""
|
|
if not check_feature_name(feature_items[0]):
|
|
return False
|
|
|
|
if feature_items[1].endswith(".patch") or feature_items[1].startswith("CONFIG_"):
|
|
print("Missing description and/or compatibilty [%s], exiting" % feature_items[1])
|
|
return False
|
|
|
|
if feature_items[2].endswith(".patch") or feature_items[2].startswith("CONFIG_"):
|
|
print("Missing description and/or compatibility [%s], exiting" % feature_items[1])
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
def yocto_kernel_feature_create(scripts_path, machine, feature_items):
|
|
"""
|
|
Create a recipe-space kernel feature in a BSP.
|
|
"""
|
|
if not check_create_input(feature_items):
|
|
sys.exit(1)
|
|
|
|
feature = feature_items[0]
|
|
feature_basename = feature.split(".")[0]
|
|
feature_description = feature_items[1]
|
|
feature_compat = feature_items[2]
|
|
|
|
patches = []
|
|
cfg_items = []
|
|
|
|
for item in feature_items[3:]:
|
|
if item.endswith(".patch"):
|
|
patches.append(item)
|
|
elif item.startswith("CONFIG"):
|
|
if ("=y" in item or "=m" in item):
|
|
cfg_items.append(item)
|
|
else:
|
|
print("Invalid feature item (must be .patch or CONFIG_*) [%s], exiting" % item)
|
|
sys.exit(1)
|
|
|
|
feature_dirname = "cfg"
|
|
if patches:
|
|
feature_dirname = "features"
|
|
|
|
filesdir = find_filesdir(scripts_path, machine)
|
|
if not filesdir:
|
|
print("Couldn't add feature (%s), no 'files' dir found" % feature)
|
|
sys.exit(1)
|
|
|
|
featdir = os.path.join(filesdir, feature_dirname)
|
|
if not os.path.exists(featdir):
|
|
os.mkdir(featdir)
|
|
|
|
for patch in patches:
|
|
if not os.path.isfile(patch):
|
|
print("Couldn't find patch (%s), exiting" % patch)
|
|
sys.exit(1)
|
|
basename = os.path.basename(patch)
|
|
featdir_patch = os.path.join(featdir, basename)
|
|
shutil.copyfile(patch, featdir_patch)
|
|
|
|
new_cfg_filename = os.path.join(featdir, feature_basename + ".cfg")
|
|
new_cfg_file = open(new_cfg_filename, "w")
|
|
for cfg_item in cfg_items:
|
|
new_cfg_file.write(cfg_item + "\n")
|
|
new_cfg_file.close()
|
|
|
|
new_feature_filename = os.path.join(featdir, feature_basename + ".scc")
|
|
new_feature_file = open(new_feature_filename, "w")
|
|
new_feature_file.write("define KFEATURE_DESCRIPTION \"" + feature_description + "\"\n")
|
|
new_feature_file.write("define KFEATURE_COMPATIBILITY " + feature_compat + "\n\n")
|
|
|
|
for patch in patches:
|
|
patch_dir, patch_file = os.path.split(patch)
|
|
new_feature_file.write("patch " + patch_file + "\n")
|
|
|
|
new_feature_file.write("kconf non-hardware " + feature_basename + ".cfg\n")
|
|
new_feature_file.close()
|
|
|
|
print("Added feature:")
|
|
print("\t%s" % feature_dirname + "/" + feature)
|
|
|
|
|
|
def feature_in_use(scripts_path, machine, feature):
|
|
"""
|
|
Determine whether the specified feature is in use by the BSP.
|
|
Return True if so, False otherwise.
|
|
"""
|
|
features = read_features(scripts_path, machine)
|
|
for f in features:
|
|
if f == feature:
|
|
return True
|
|
return False
|
|
|
|
|
|
def feature_remove(scripts_path, machine, feature):
|
|
"""
|
|
Remove the specified feature from the available recipe-space
|
|
features defined for the BSP.
|
|
"""
|
|
features = read_features(scripts_path, machine)
|
|
new_features = []
|
|
for f in features:
|
|
if f == feature:
|
|
continue
|
|
new_features.append(f)
|
|
write_features(scripts_path, machine, new_features)
|
|
|
|
|
|
def yocto_kernel_feature_destroy(scripts_path, machine, feature):
|
|
"""
|
|
Remove a recipe-space kernel feature from a BSP.
|
|
"""
|
|
if not check_feature_name(feature):
|
|
sys.exit(1)
|
|
|
|
if feature_in_use(scripts_path, machine, "features/" + feature) or \
|
|
feature_in_use(scripts_path, machine, "cfg/" + feature):
|
|
print("Feature %s is in use (use 'feature rm' to un-use it first), exiting" % feature)
|
|
sys.exit(1)
|
|
|
|
filesdir = find_filesdir(scripts_path, machine)
|
|
if not filesdir:
|
|
print("Couldn't destroy feature (%s), no 'files' dir found" % feature)
|
|
sys.exit(1)
|
|
|
|
feature_dirname = "features"
|
|
featdir = os.path.join(filesdir, feature_dirname)
|
|
if not os.path.exists(featdir):
|
|
print("Couldn't find feature directory (%s)" % feature_dirname)
|
|
sys.exit(1)
|
|
|
|
feature_fqn = os.path.join(featdir, feature)
|
|
if not os.path.exists(feature_fqn):
|
|
feature_dirname = "cfg"
|
|
featdir = os.path.join(filesdir, feature_dirname)
|
|
if not os.path.exists(featdir):
|
|
print("Couldn't find feature directory (%s)" % feature_dirname)
|
|
sys.exit(1)
|
|
feature_fqn = os.path.join(featdir, feature_filename)
|
|
if not os.path.exists(feature_fqn):
|
|
print("Couldn't find feature (%s)" % feature)
|
|
sys.exit(1)
|
|
|
|
f = open(feature_fqn, "r")
|
|
lines = f.readlines()
|
|
for line in lines:
|
|
s = line.strip()
|
|
if s.startswith("patch ") or s.startswith("kconf "):
|
|
split_line = s.split()
|
|
filename = os.path.join(featdir, split_line[-1])
|
|
if os.path.exists(filename):
|
|
os.remove(filename)
|
|
f.close()
|
|
os.remove(feature_fqn)
|
|
|
|
feature_remove(scripts_path, machine, feature)
|
|
|
|
print("Removed feature:")
|
|
print("\t%s" % feature_dirname + "/" + feature)
|
|
|
|
|
|
def base_branches(context):
|
|
"""
|
|
Return a list of the base branches found in the kernel git repo.
|
|
"""
|
|
giturl = find_giturl(context)
|
|
|
|
print("Getting branches from remote repo %s..." % giturl)
|
|
|
|
gitcmd = "git ls-remote %s *heads* 2>&1" % (giturl)
|
|
tmp = subprocess.Popen(gitcmd, shell=True, stdout=subprocess.PIPE).stdout.read().decode('utf-8')
|
|
|
|
branches = []
|
|
|
|
if tmp:
|
|
tmpline = tmp.split("\n")
|
|
for line in tmpline:
|
|
if len(line)==0:
|
|
break;
|
|
if not line.endswith("base"):
|
|
continue;
|
|
idx = line.find("refs/heads/")
|
|
kbranch = line[idx + len("refs/heads/"):]
|
|
if kbranch.find("/") == -1 and kbranch.find("base") == -1:
|
|
continue
|
|
idx = kbranch.find("base")
|
|
branches.append(kbranch[:idx - 1])
|
|
|
|
return branches
|
|
|
|
|
|
def all_branches(context):
|
|
"""
|
|
Return a list of all the branches found in the kernel git repo.
|
|
"""
|
|
giturl = find_giturl(context)
|
|
|
|
print("Getting branches from remote repo %s..." % giturl)
|
|
|
|
gitcmd = "git ls-remote %s *heads* 2>&1" % (giturl)
|
|
tmp = subprocess.Popen(gitcmd, shell=True, stdout=subprocess.PIPE).stdout.read().decode('utf-8')
|
|
|
|
branches = []
|
|
|
|
base_prefixes = None
|
|
|
|
try:
|
|
branches_base = context["branches_base"]
|
|
if branches_base:
|
|
base_prefixes = branches_base.split(":")
|
|
except KeyError:
|
|
pass
|
|
|
|
arch = context["arch"]
|
|
|
|
if tmp:
|
|
tmpline = tmp.split("\n")
|
|
for line in tmpline:
|
|
if len(line)==0:
|
|
break;
|
|
idx = line.find("refs/heads/")
|
|
kbranch = line[idx + len("refs/heads/"):]
|
|
kbranch_prefix = kbranch.rsplit("/", 1)[0]
|
|
|
|
if base_prefixes:
|
|
for base_prefix in base_prefixes:
|
|
if kbranch_prefix == base_prefix:
|
|
branches.append(kbranch)
|
|
continue
|
|
|
|
if (kbranch.find("/") != -1 and
|
|
(kbranch.find("standard") != -1 or kbranch.find("base") != -1) or
|
|
kbranch == "base"):
|
|
branches.append(kbranch)
|
|
continue
|
|
|
|
return branches
|