|
@@ -0,0 +1,410 @@
|
|
|
+#!/usr/bin/env python
|
|
|
+#
|
|
|
+# Copyright (C) 2014, Masahiro Yamada <yamada.m@jp.panasonic.com>
|
|
|
+#
|
|
|
+# SPDX-License-Identifier: GPL-2.0+
|
|
|
+#
|
|
|
+
|
|
|
+"""
|
|
|
+A wrapper script to adjust Kconfig for U-Boot
|
|
|
+
|
|
|
+The biggest difference between Linux Kernel and U-Boot in terms of the
|
|
|
+board configuration is that U-Boot has to configure multiple boot images
|
|
|
+per board: Normal, SPL, TPL.
|
|
|
+We need to expand the functions of Kconfig to handle multiple boot
|
|
|
+images.
|
|
|
+
|
|
|
+Instead of touching various parts under the scripts/kconfig/ directory,
|
|
|
+pushing necessary adjustments into this single script would be better
|
|
|
+for code maintainance. All the make targets related to the configuration
|
|
|
+(make %config) should be invoked via this script.
|
|
|
+
|
|
|
+Let's see what is different from the original Kconfig.
|
|
|
+
|
|
|
+- config, menuconfig, etc.
|
|
|
+
|
|
|
+The commands 'make config', 'make menuconfig', etc. are used to create
|
|
|
+or modify the .config file, which stores configs for Normal boot image.
|
|
|
+
|
|
|
+The location of the one for SPL, TPL image is spl/.config, tpl/.config,
|
|
|
+respectively. Use 'make spl/config', 'make spl/menuconfig', etc.
|
|
|
+to create or modify the spl/.config file, which contains configs
|
|
|
+for SPL image.
|
|
|
+Do likewise for the tpl/.config file.
|
|
|
+The generic syntax for SPL, TPL configuration is
|
|
|
+'make <target_image>/<config_command>'.
|
|
|
+
|
|
|
+- silentoldconfig
|
|
|
+
|
|
|
+The command 'make silentoldconfig' updates .config, if necessary, and
|
|
|
+additionally updates include/generated/autoconf.h and files under
|
|
|
+include/configs/ directory. In U-Boot, it should do the same things for
|
|
|
+SPL, TPL images for boards supporting them.
|
|
|
+Depending on whether CONFIG_SPL, CONFIG_TPL is defined or not,
|
|
|
+'make silentoldconfig' iterates three times at most changing the target
|
|
|
+directory.
|
|
|
+
|
|
|
+To sum up, 'make silentoldconfig' possibly updates
|
|
|
+ - .config, include/generated/autoconf.h, include/config/*
|
|
|
+ - spl/.config, spl/include/generated/autoconf.h, spl/include/config/*
|
|
|
+ (in case CONFIG_SPL=y)
|
|
|
+ - tpl/.config, tpl/include/generated/autoconf.h, tpl/include/config/*
|
|
|
+ (in case CONFIG_TPL=y)
|
|
|
+
|
|
|
+- defconfig, <board>_defconfig
|
|
|
+
|
|
|
+The command 'make <board>_defconfig' creates a new .config based on the
|
|
|
+file configs/<board>_defconfig. The command 'make defconfig' is the same
|
|
|
+but the difference is it uses the file specified with KBUILD_DEFCONFIG
|
|
|
+environment.
|
|
|
+
|
|
|
+We need to create .config, spl/.config, tpl/.config for boards where SPL
|
|
|
+and TPL images are supported. One possible solution for that is to have
|
|
|
+multiple defconfig files per board, but it would produce duplication
|
|
|
+among the defconfigs.
|
|
|
+The approach chosen here is to expand the feature and support
|
|
|
+conditional definition in defconfig, that is, each line in defconfig
|
|
|
+files has the form of:
|
|
|
+<condition>:<macro definition>
|
|
|
+
|
|
|
+The '<condition>:' prefix specifies which image the line is valid for.
|
|
|
+The '<condition>:' is one of:
|
|
|
+ None - the line is valid only for Normal image
|
|
|
+ S: - the line is valid only for SPL image
|
|
|
+ T: - the line is valid only for TPL image
|
|
|
+ ST: - the line is valid for SPL and TPL images
|
|
|
+ +S: - the line is valid for Normal and SPL images
|
|
|
+ +T: - the line is valid for Normal and TPL images
|
|
|
+ +ST: - the line is valid for Normal, SPL and SPL images
|
|
|
+
|
|
|
+So, if neither CONFIG_SPL nor CONFIG_TPL is defined, the defconfig file
|
|
|
+has no '<condition>:' part and therefore has the same form of that of
|
|
|
+Linux Kernel.
|
|
|
+
|
|
|
+In U-Boot, for example, a defconfig file can be written like this:
|
|
|
+
|
|
|
+ CONFIG_FOO=100
|
|
|
+ S:CONFIG_FOO=200
|
|
|
+ T:CONFIG_FOO=300
|
|
|
+ ST:CONFIG_BAR=y
|
|
|
+ +S:CONFIG_BAZ=y
|
|
|
+ +T:CONFIG_QUX=y
|
|
|
+ +ST:CONFIG_QUUX=y
|
|
|
+
|
|
|
+The defconfig above is parsed by this script and internally divided into
|
|
|
+three temporary defconfig files.
|
|
|
+
|
|
|
+ - Temporary defconfig for Normal image
|
|
|
+ CONFIG_FOO=100
|
|
|
+ CONFIG_BAZ=y
|
|
|
+ CONFIG_QUX=y
|
|
|
+ CONFIG_QUUX=y
|
|
|
+
|
|
|
+ - Temporary defconfig for SPL image
|
|
|
+ CONFIG_FOO=200
|
|
|
+ CONFIG_BAR=y
|
|
|
+ CONFIG_BAZ=y
|
|
|
+ CONFIG_QUUX=y
|
|
|
+
|
|
|
+ - Temporary defconfig for TPL image
|
|
|
+ CONFIG_FOO=300
|
|
|
+ CONFIG_BAR=y
|
|
|
+ CONFIG_QUX=y
|
|
|
+ CONFIG_QUUX=y
|
|
|
+
|
|
|
+They are passed to scripts/kconfig/conf, each is used for generating
|
|
|
+.config, spl/.config, tpl/.config, respectively.
|
|
|
+
|
|
|
+- savedefconfig
|
|
|
+
|
|
|
+This is the reverse operation of 'make defconfig'.
|
|
|
+If neither CONFIG_SPL nor CONFIG_TPL is defined in the .config file,
|
|
|
+it works as 'make savedefconfig' in Linux Kernel: create the minimal set
|
|
|
+of config based on the .config and save it into 'defconfig' file.
|
|
|
+
|
|
|
+If CONFIG_SPL or CONFIG_TPL is defined, the common lines among .config,
|
|
|
+spl/.config, tpl/.config are coalesced together and output to the file
|
|
|
+'defconfig' in the form like:
|
|
|
+
|
|
|
+ CONFIG_FOO=100
|
|
|
+ S:CONFIG_FOO=200
|
|
|
+ T:CONFIG_FOO=300
|
|
|
+ ST:CONFIG_BAR=y
|
|
|
+ +S:CONFIG_BAZ=y
|
|
|
+ +T:CONFIG_QUX=y
|
|
|
+ +ST:CONFIG_QUUX=y
|
|
|
+
|
|
|
+This can be used as an input of 'make <board>_defconfig' command.
|
|
|
+"""
|
|
|
+
|
|
|
+import errno
|
|
|
+import os
|
|
|
+import re
|
|
|
+import subprocess
|
|
|
+import sys
|
|
|
+
|
|
|
+# Constant variables
|
|
|
+SUB_IMAGES = ('spl', 'tpl')
|
|
|
+IMAGES = ('',) + SUB_IMAGES
|
|
|
+SYMBOL_MAP = {'': '+', 'spl': 'S', 'tpl': 'T'}
|
|
|
+PATTERN_SYMBOL = re.compile(r'(\+?)(S?)(T?):(.*)')
|
|
|
+
|
|
|
+# Environment variables (should be defined in the top Makefile)
|
|
|
+# .get('key', 'default_value') method is useful for standalone testing.
|
|
|
+MAKE = os.environ.get('MAKE', 'make')
|
|
|
+srctree = os.environ.get('srctree', '.')
|
|
|
+KCONFIG_CONFIG = os.environ.get('KCONFIG_CONFIG', '.config')
|
|
|
+
|
|
|
+# Useful shorthand
|
|
|
+build = '%s -f %s/scripts/Makefile.build obj=scripts/kconfig %%s' % (MAKE, srctree)
|
|
|
+autoconf = '%s -f %s/scripts/Makefile.autoconf obj=%%s %%s' % (MAKE, srctree)
|
|
|
+
|
|
|
+### helper functions ###
|
|
|
+def mkdirs(*dirs):
|
|
|
+ """Make directories ignoring 'File exists' error."""
|
|
|
+ for d in dirs:
|
|
|
+ try:
|
|
|
+ os.makedirs(d)
|
|
|
+ except OSError as exception:
|
|
|
+ # Ignore 'File exists' error
|
|
|
+ if exception.errno != errno.EEXIST:
|
|
|
+ raise
|
|
|
+
|
|
|
+def rmfiles(*files):
|
|
|
+ """Remove files ignoring 'No such file or directory' error."""
|
|
|
+ for f in files:
|
|
|
+ try:
|
|
|
+ os.remove(f)
|
|
|
+ except OSError as exception:
|
|
|
+ # Ignore 'No such file or directory' error
|
|
|
+ if exception.errno != errno.ENOENT:
|
|
|
+ raise
|
|
|
+
|
|
|
+def rmdirs(*dirs):
|
|
|
+ """Remove directories ignoring 'No such file or directory'
|
|
|
+ and 'Directory not empty' error.
|
|
|
+ """
|
|
|
+ for d in dirs:
|
|
|
+ try:
|
|
|
+ os.rmdir(d)
|
|
|
+ except OSError as exception:
|
|
|
+ # Ignore 'No such file or directory'
|
|
|
+ # and 'Directory not empty' error
|
|
|
+ if exception.errno != errno.ENOENT and \
|
|
|
+ exception.errno != errno.ENOTEMPTY:
|
|
|
+ raise
|
|
|
+
|
|
|
+def error(msg):
|
|
|
+ """Output the given argument to stderr and exit with return code 1."""
|
|
|
+ print >> sys.stderr, msg
|
|
|
+ sys.exit(1)
|
|
|
+
|
|
|
+def run_command(command, callback_on_error=None):
|
|
|
+ """Run the given command in a sub-shell (and exit if it fails).
|
|
|
+
|
|
|
+ Arguments:
|
|
|
+ command: A string of the command
|
|
|
+ callback_on_error: Callback handler invoked just before exit
|
|
|
+ when the command fails (Default=None)
|
|
|
+ """
|
|
|
+ retcode = subprocess.call(command, shell=True)
|
|
|
+ if retcode:
|
|
|
+ if callback_on_error:
|
|
|
+ callback_on_error()
|
|
|
+ error("'%s' Failed" % command)
|
|
|
+
|
|
|
+def run_make_config(cmd, objdir, callback_on_error=None):
|
|
|
+ """Run the make command in a sub-shell (and exit if it fails).
|
|
|
+
|
|
|
+ Arguments:
|
|
|
+ cmd: Make target such as 'config', 'menuconfig', 'defconfig', etc.
|
|
|
+ objdir: Target directory where the make command is run.
|
|
|
+ Typically '', 'spl', 'tpl' for Normal, SPL, TPL image,
|
|
|
+ respectively.
|
|
|
+ callback_on_error: Callback handler invoked just before exit
|
|
|
+ when the command fails (Default=None)
|
|
|
+ """
|
|
|
+ # Linux expects defconfig files in arch/$(SRCARCH)/configs/ directory,
|
|
|
+ # but U-Boot puts them in configs/ directory.
|
|
|
+ # Give SRCARCH=.. to fake scripts/kconfig/Makefile.
|
|
|
+ options = 'SRCARCH=.. KCONFIG_OBJDIR=%s' % objdir
|
|
|
+ if objdir:
|
|
|
+ options += ' KCONFIG_CONFIG=%s/%s' % (objdir, KCONFIG_CONFIG)
|
|
|
+ mkdirs(objdir)
|
|
|
+ run_command(build % cmd + ' ' + options, callback_on_error)
|
|
|
+
|
|
|
+def get_enabled_subimages(ignore_error=False):
|
|
|
+ """Parse .config file to detect if CONFIG_SPL, CONFIG_TPL is enabled
|
|
|
+ and return a tuple of enabled subimages.
|
|
|
+
|
|
|
+ Arguments:
|
|
|
+ ignore_error: Specify the behavior when '.config' is not found;
|
|
|
+ Raise an exception if this flag is False.
|
|
|
+ Return a null tuple if this flag is True.
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ A tuple of enabled subimages as follows:
|
|
|
+ () if neither CONFIG_SPL nor CONFIG_TPL is defined
|
|
|
+ ('spl',) if CONFIG_SPL is defined but CONFIG_TPL is not
|
|
|
+ ('spl', 'tpl') if both CONFIG_SPL and CONFIG_TPL are defined
|
|
|
+ """
|
|
|
+ enabled = ()
|
|
|
+ match_patterns = [ (img, 'CONFIG_' + img.upper() + '=y\n')
|
|
|
+ for img in SUB_IMAGES ]
|
|
|
+ try:
|
|
|
+ f = open(KCONFIG_CONFIG)
|
|
|
+ except IOError as exception:
|
|
|
+ if not ignore_error or exception.errno != errno.ENOENT:
|
|
|
+ raise
|
|
|
+ return enabled
|
|
|
+ with f:
|
|
|
+ for line in f:
|
|
|
+ for img, pattern in match_patterns:
|
|
|
+ if line == pattern:
|
|
|
+ enabled += (img,)
|
|
|
+ return enabled
|
|
|
+
|
|
|
+def do_silentoldconfig(cmd):
|
|
|
+ """Run 'make silentoldconfig' for all the enabled images.
|
|
|
+
|
|
|
+ Arguments:
|
|
|
+ cmd: should always be a string 'silentoldconfig'
|
|
|
+ """
|
|
|
+ run_make_config(cmd, '')
|
|
|
+ subimages = get_enabled_subimages()
|
|
|
+ for obj in subimages:
|
|
|
+ mkdirs(os.path.join(obj, 'include', 'config'),
|
|
|
+ os.path.join(obj, 'include', 'generated'))
|
|
|
+ run_make_config(cmd, obj)
|
|
|
+ remove_auto_conf = lambda : rmfiles('include/config/auto.conf')
|
|
|
+ # If the following part failed, include/config/auto.conf should be deleted
|
|
|
+ # so 'make silentoldconfig' will be re-run on the next build.
|
|
|
+ run_command(autoconf %
|
|
|
+ ('include', 'include/autoconf.mk include/autoconf.mk.dep'),
|
|
|
+ remove_auto_conf)
|
|
|
+ # include/config.h has been updated after 'make silentoldconfig'.
|
|
|
+ # We need to touch include/config/auto.conf so it gets newer
|
|
|
+ # than include/config.h.
|
|
|
+ # Otherwise, 'make silentoldconfig' would be invoked twice.
|
|
|
+ os.utime('include/config/auto.conf', None)
|
|
|
+ for obj in subimages:
|
|
|
+ run_command(autoconf % (obj + '/include',
|
|
|
+ obj + '/include/autoconf.mk'),
|
|
|
+ remove_auto_conf)
|
|
|
+
|
|
|
+def do_tmp_defconfig(output_lines, img):
|
|
|
+ """Helper function for do_board_defconfig().
|
|
|
+
|
|
|
+ Write the defconfig contents into a file '.tmp_defconfig' and
|
|
|
+ invoke 'make .tmp_defconfig'.
|
|
|
+
|
|
|
+ Arguments:
|
|
|
+ output_lines: A sequence of defconfig lines of each image
|
|
|
+ img: Target image. Typically '', 'spl', 'tpl' for
|
|
|
+ Normal, SPL, TPL images, respectively.
|
|
|
+ """
|
|
|
+ TMP_DEFCONFIG = '.tmp_defconfig'
|
|
|
+ TMP_DIRS = ('arch', 'configs')
|
|
|
+ defconfig_path = os.path.join('configs', TMP_DEFCONFIG)
|
|
|
+ mkdirs(*TMP_DIRS)
|
|
|
+ with open(defconfig_path, 'w') as f:
|
|
|
+ f.write(''.join(output_lines[img]))
|
|
|
+ cleanup = lambda: (rmfiles(defconfig_path), rmdirs(*TMP_DIRS))
|
|
|
+ run_make_config(TMP_DEFCONFIG, img, cleanup)
|
|
|
+ cleanup()
|
|
|
+
|
|
|
+def do_board_defconfig(cmd):
|
|
|
+ """Run 'make <board>_defconfig'.
|
|
|
+
|
|
|
+ Arguments:
|
|
|
+ cmd: should be a string '<board>_defconfig'
|
|
|
+ """
|
|
|
+ defconfig_path = os.path.join(srctree, 'configs', cmd)
|
|
|
+ output_lines = dict([ (img, []) for img in IMAGES ])
|
|
|
+ with open(defconfig_path) as f:
|
|
|
+ for line in f:
|
|
|
+ m = PATTERN_SYMBOL.match(line)
|
|
|
+ if m:
|
|
|
+ for idx, img in enumerate(IMAGES):
|
|
|
+ if m.group(idx + 1):
|
|
|
+ output_lines[img].append(m.group(4) + '\n')
|
|
|
+ continue
|
|
|
+ output_lines[''].append(line)
|
|
|
+ do_tmp_defconfig(output_lines, '')
|
|
|
+ for img in get_enabled_subimages():
|
|
|
+ do_tmp_defconfig(output_lines, img)
|
|
|
+
|
|
|
+def do_defconfig(cmd):
|
|
|
+ """Run 'make defconfig'.
|
|
|
+
|
|
|
+ Arguments:
|
|
|
+ cmd: should always be a string 'defconfig'
|
|
|
+ """
|
|
|
+ KBUILD_DEFCONFIG = os.environ['KBUILD_DEFCONFIG']
|
|
|
+ print "*** Default configuration is based on '%s'" % KBUILD_DEFCONFIG
|
|
|
+ do_board_defconfig(KBUILD_DEFCONFIG)
|
|
|
+
|
|
|
+def do_savedefconfig(cmd):
|
|
|
+ """Run 'make savedefconfig'.
|
|
|
+
|
|
|
+ Arguments:
|
|
|
+ cmd: should always be a string 'savedefconfig'
|
|
|
+ """
|
|
|
+ DEFCONFIG = 'defconfig'
|
|
|
+ # Continue even if '.config' does not exist
|
|
|
+ subimages = get_enabled_subimages(True)
|
|
|
+ run_make_config(cmd, '')
|
|
|
+ output_lines = []
|
|
|
+ prefix = {}
|
|
|
+ with open(DEFCONFIG) as f:
|
|
|
+ for line in f:
|
|
|
+ output_lines.append(line)
|
|
|
+ prefix[line] = '+'
|
|
|
+ for img in subimages:
|
|
|
+ run_make_config(cmd, img)
|
|
|
+ unmatched_lines = []
|
|
|
+ with open(DEFCONFIG) as f:
|
|
|
+ for line in f:
|
|
|
+ if line in output_lines:
|
|
|
+ index = output_lines.index(line)
|
|
|
+ output_lines[index:index] = unmatched_lines
|
|
|
+ unmatched_lines = []
|
|
|
+ prefix[line] += SYMBOL_MAP[img]
|
|
|
+ else:
|
|
|
+ ummatched_lines.append(line)
|
|
|
+ prefix[line] = SYMBOL_MAP[img]
|
|
|
+ with open(DEFCONFIG, 'w') as f:
|
|
|
+ for line in output_lines:
|
|
|
+ if prefix[line] == '+':
|
|
|
+ f.write(line)
|
|
|
+ else:
|
|
|
+ f.write(prefix[line] + ':' + line)
|
|
|
+
|
|
|
+def do_others(cmd):
|
|
|
+ """Run the make command other than 'silentoldconfig', 'defconfig',
|
|
|
+ '<board>_defconfig' and 'savedefconfig'.
|
|
|
+
|
|
|
+ Arguments:
|
|
|
+ cmd: Make target in the form of '<target_image>/<config_command>'
|
|
|
+ The field '<target_image>/' is typically empty, 'spl/', 'tpl/'
|
|
|
+ for Normal, SPL, TPL images, respectively.
|
|
|
+ The field '<config_command>' is make target such as 'config',
|
|
|
+ 'menuconfig', etc.
|
|
|
+ """
|
|
|
+ objdir, _, cmd = cmd.rpartition('/')
|
|
|
+ run_make_config(cmd, objdir)
|
|
|
+
|
|
|
+cmd_list = {'silentoldconfig': do_silentoldconfig,
|
|
|
+ 'defconfig': do_defconfig,
|
|
|
+ 'savedefconfig': do_savedefconfig}
|
|
|
+
|
|
|
+def main():
|
|
|
+ cmd = sys.argv[1]
|
|
|
+ if cmd.endswith('_defconfig'):
|
|
|
+ do_board_defconfig(cmd)
|
|
|
+ else:
|
|
|
+ func = cmd_list.get(cmd, do_others)
|
|
|
+ func(cmd)
|
|
|
+
|
|
|
+if __name__ == '__main__':
|
|
|
+ main()
|