632 lines
19 KiB
Python
632 lines
19 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
|
|
|
|
|
|
def find_bblayers(scripts_path):
|
|
"""
|
|
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 = []
|
|
|
|
f = open(bblayers_conf, "r")
|
|
lines = f.readlines()
|
|
bblayers_lines = []
|
|
in_bblayers = False
|
|
for line in lines:
|
|
line = line.strip()
|
|
if line.strip().startswith("BBLAYERS"):
|
|
bblayers_lines.append(line)
|
|
in_bblayers = True
|
|
quotes = line.strip().count('"')
|
|
if quotes > 1:
|
|
break
|
|
continue
|
|
if in_bblayers:
|
|
bblayers_lines.append(line)
|
|
if line.strip().endswith("\""):
|
|
break
|
|
else:
|
|
continue
|
|
|
|
for i, line in enumerate(bblayers_lines):
|
|
if line.strip().endswith("\\"):
|
|
bblayers_lines[i] = line.strip().replace('\\', '')
|
|
|
|
bblayers_line = " ".join(bblayers_lines)
|
|
|
|
start_quote = bblayers_line.find("\"")
|
|
if start_quote == -1:
|
|
print "Invalid BBLAYERS found in %s, exiting" % bblayers_conf
|
|
sys.exit(1)
|
|
|
|
start_quote += 1
|
|
end_quote = bblayers_line.find("\"", start_quote)
|
|
if end_quote == -1:
|
|
print "Invalid BBLAYERS found in %s, exiting" % bblayers_conf
|
|
sys.exit(1)
|
|
|
|
bblayers_line = bblayers_line[start_quote:end_quote]
|
|
layers = bblayers_line.split()
|
|
|
|
f.close()
|
|
|
|
return layers
|
|
|
|
|
|
def find_meta_layer(scripts_path):
|
|
"""
|
|
Find and return the meta layer in BBLAYERS.
|
|
"""
|
|
layers = find_bblayers(scripts_path)
|
|
|
|
for layer in layers:
|
|
if layer.endswith("meta"):
|
|
return layer
|
|
|
|
return None
|
|
|
|
|
|
def find_bsp_layer(scripts_path, machine):
|
|
"""
|
|
Find and return a machine's BSP layer in BBLAYERS.
|
|
"""
|
|
layers = find_bblayers(scripts_path)
|
|
|
|
for layer in layers:
|
|
if machine in layer:
|
|
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(scripts_path, 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 [user-config.cfg].
|
|
"""
|
|
config_items = []
|
|
|
|
f = open_user_file(scripts_path, 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 [user-config.cfg].
|
|
"""
|
|
f = open_user_file(scripts_path, 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 [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 [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:"
|
|
input = raw_input(gen_choices_str(config_items))
|
|
rm_choices = input.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 [user-config.cfg].
|
|
"""
|
|
new_items = []
|
|
|
|
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)
|
|
new_items.append(item)
|
|
|
|
cur_items = read_config_items(scripts_path, machine)
|
|
cur_items.extend(new_items)
|
|
|
|
write_config_items(scripts_path, machine, cur_items)
|
|
|
|
print "Added items:"
|
|
for n in new_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(scripts_path, 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 [user-patches.scc].
|
|
"""
|
|
patch_items = []
|
|
|
|
f = open_user_file(scripts_path, 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 [user-patches.scc].
|
|
"""
|
|
f = open_user_file(scripts_path, 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
|
|
[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 [user-patches.scc].
|
|
"""
|
|
patches = read_patch_items(scripts_path, machine)
|
|
|
|
print "Specify the patches to remove:"
|
|
input = raw_input(gen_choices_str(patches))
|
|
rm_choices = input.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
|
|
[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(scripts_path, 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(scripts_path)
|
|
|
|
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 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()
|
|
|
|
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()
|
|
|
|
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
|