[PATCH 1/4] Makefile: Add a Python-based CONFIG checker

The existing shell script is a bit ugly. It was picked up by Chromium OS and then rewritten in Python, adding unit tests. Bring this new version into U-Boot.
Signed-off-by: Simon Glass sjg@chromium.org ---
scripts/kconfig_check.py | 338 ++++++++++++++++++++++++++++++++++ scripts/test_kconfig_check.py | 185 +++++++++++++++++++ 2 files changed, 523 insertions(+) create mode 100755 scripts/kconfig_check.py create mode 100755 scripts/test_kconfig_check.py
diff --git a/scripts/kconfig_check.py b/scripts/kconfig_check.py new file mode 100755 index 00000000000..8f0e142bdc6 --- /dev/null +++ b/scripts/kconfig_check.py @@ -0,0 +1,338 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0+ +"""Kconfig checker + +Checks that the .config file provided does not introduce any new ad-hoc CONFIG +options + +This tool is also present in the Chromium OS EC, so we should keep the two in +sync. + +The tool supports two formats for the 'configs' file: + + CONFIG_SOMETHING=xx + +and + + #define CONFIG_SOMETHING xx + +Use the -d flag to select the second format. +""" + +import argparse +import os +import re +import sys + +# Bring in the kconfig library +our_path = os.path.dirname(os.path.realpath(__file__)) +sys.path.insert(1, os.path.join(our_path, '..')) + +# Try to use kconfiglib if available, but fall back to a simple recursive grep +USE_KCONFIGLIB = False +try: + from tools.buildman import kconfiglib + USE_KCONFIGLIB = True +except ImportError: + pass + +# Where we put the new config_allowed file +NEW_ALLOWED_FNAME = '/tmp/new_config_allowed.txt' + + +def parse_args(argv): + """Parse the program arguments + + Args: + argv: List of arguments to parse, excluding the program name + + Returns: + argparse.Namespace object containing the results + """ + epilog = """Checks that new ad-hoc CONFIG options are not introduced without +a corresponding Kconfig option for Zephyr""" + + parser = argparse.ArgumentParser(epilog=epilog) + parser.add_argument('-a', '--allowed', type=str, + default='util/config_allowed.txt', + help='File containing list of allowed ad-hoc CONFIGs') + parser.add_argument('-c', '--configs', type=str, default='.config', + help='File containing CONFIG options to check') + parser.add_argument('-d', '--use-defines', action='store_true', + help='Lines in the configs file use #define') + parser.add_argument( + '-D', '--debug', action='store_true', + help='Enabling debugging (provides a full traceback on error)') + parser.add_argument('-p', '--prefix', type=str, default='', + help='Prefix to string from Kconfig options') + parser.add_argument('-s', '--srctree', type=str, default='zephyr/', + help='Path to source tree to look for Kconfigs') + + # The chroot uses a very old Python + if sys.version_info >= (3, 7): + subparsers = parser.add_subparsers(dest='cmd', required=True) + else: + subparsers = parser.add_subparsers(dest='cmd') + subparsers.required = True + subparsers.add_parser('check', help='Check for new ad-hoc CONFIGs') + + return parser.parse_args(argv) + + +class KconfigCheck: + """Class for handling checking of CONFIG options against Kconfig options + + The goal is to make sure that CONFIG_xxx options used by a build have an + equivalent Kconfig option defined as well. + + For example if a Kconfig file has: + + config PREFIX_MY_CONFIG + ... + + and the CONFIG files has + + CONFIG_MY_CONFIG + + then we consider these equivalent (with the prefix 'PREFIX_') and thus + CONFIG_MY_CONFIG is allowed to be used. + + If any CONFIG option is found that does not have an equivalent in the Kconfig, + the user is exhorted to add a new Kconfig. This helps avoid adding new ad-hoc + CONFIG options, eventually returning the number to zero. + """ + @classmethod + def find_new_adhoc(cls, configs, kconfigs, allowed): + """Get a list of new ad-hoc CONFIG options + + Arguments and return value should omit the 'CONFIG_' prefix, so + CONFIG_LTO should be provided as 'LTO'. + + Args: + configs: List of existing CONFIG options + kconfigs: List of existing Kconfig options + allowed: List of allowed CONFIG options + + Returns: + List of new CONFIG options, with the CONFIG_ prefix removed + """ + return sorted(list(set(configs) - set(kconfigs) - set(allowed))) + + @classmethod + def find_unneeded_adhoc(cls, kconfigs, allowed): + """Get a list of ad-hoc CONFIG options that now have Kconfig options + + Arguments and return value should omit the 'CONFIG_' prefix, so + CONFIG_LTO should be provided as 'LTO'. + + Args: + kconfigs: List of existing Kconfig options + allowed: List of allowed CONFIG options + + Returns: + List of new CONFIG options, with the CONFIG_ prefix removed + """ + return sorted(list(set(allowed) & set(kconfigs))) + + @classmethod + def get_updated_adhoc(cls, unneeded_adhoc, allowed): + """Get a list of ad-hoc CONFIG options that are still needed + + Arguments and return value should omit the 'CONFIG_' prefix, so + CONFIG_LTO should be provided as 'LTO'. + + Args: + unneeded_adhoc: List of ad-hoc CONFIG options to remove + allowed: Current list of allowed CONFIG options + + Returns: + New version of allowed CONFIG options, with the CONFIG_ prefix + removed + """ + return sorted(list(set(allowed) - set(unneeded_adhoc))) + + @classmethod + def read_configs(cls, configs_file, use_defines=False): + """Read CONFIG options from a file + + The file consists of a number of lines, each containing a CONFIG + option + + Args: + configs_file: Filename to read from (e.g. u-boot.cfg) + use_defines: True if each line of the file starts with #define + + Returns: + List of CONFIG_xxx options found in the file, with the 'CONFIG_' + prefix removed + """ + with open(configs_file, encoding='utf-8') as inf: + configs = re.findall('%sCONFIG_([A-Za-z0-9_]*)%s' % + ((use_defines and '#define ' or ''), + (use_defines and ' ' or '')), + inf.read()) + return configs + + @classmethod + def read_allowed(cls, allowed_file): + """Read allowed CONFIG options from a file + + Args: + allowed_file: Filename to read from + + Returns: + List of CONFIG_xxx options found in the file, with the 'CONFIG_' + prefix removed + """ + with open(allowed_file) as inf: + configs = re.findall('CONFIG_([A-Za-z0-9_]*)', inf.read()) + return configs + + @classmethod + def find_kconfigs(cls, srcdir): + """Find all the Kconfig files in a source directory, recursively + + Any subdirectory called 'Kconfig' is ignored, since Zephyr generates + this in its build directory. + + Args: + srcdir: Directory to scan + + Returns: + List of pathnames found + """ + kconfig_files = [] + for root, dirs, files in os.walk(srcdir): + kconfig_files += [os.path.join(root, fname) + for fname in files if fname.startswith('Kconfig')] + if 'Kconfig' in dirs: + dirs.remove('Kconfig') + return kconfig_files + + @classmethod + def scan_kconfigs(cls, srcdir, prefix=''): + """Scan a source tree for Kconfig options + + Args: + srcdir: Directory to scan (containing top-level Kconfig file) + prefix: Prefix to strip from the name (e.g. 'PLATFORM_EC_') + + Returns: + List of config and menuconfig options found + """ + if USE_KCONFIGLIB: + kconf = kconfiglib.Kconfig(os.path.join(srcdir, 'Kconfig'), + warn=False) + + # There is always a MODULES config, since kconfiglib is designed for + # linux, but we don't want it + kconfigs = filter(lambda name: name != 'MODULES', kconf.syms.keys()) + + if prefix: + re_drop_prefix = re.compile(r'^%s' % prefix) + kconfigs = [re_drop_prefix.sub('', name) for name in kconfigs] + else: + kconfigs = [] + # Remove the prefix if present + expr = re.compile(r'(config|menuconfig) (%s)?([A-Za-z0-9_]*)\n' % + prefix) + for fname in cls.find_kconfigs(srcdir): + with open(fname, encoding='utf-8') as inf: + found = re.findall(expr, inf.read()) + kconfigs += [name for kctype, _, name in found] + return kconfigs + + def check_adhoc_configs(self, configs_file, srcdir, allowed_file, + prefix='', use_defines=False): + """Find new and unneeded ad-hoc configs in the configs_file + + Args: + configs_file: Filename containing CONFIG options to check + srcdir: Source directory to scan for Kconfig files + allowed_file: File containing allowed CONFIG options + prefix: Prefix to strip from the start of each Kconfig + (e.g. 'PLATFORM_EC_') + use_defines: True if each line of the file starts with #define + + Returns: + Tuple: + List of new ad-hoc CONFIG options (without 'CONFIG_' prefix) + List of ad-hoc CONFIG options (without 'CONFIG_' prefix) that + are no-longer needed, since they now have an associated + Kconfig + List of ad-hoc CONFIG options that are still needed, given the + current state of the Kconfig options + """ + configs = self.read_configs(configs_file, use_defines) + kconfigs = self.scan_kconfigs(srcdir, prefix) + allowed = self.read_allowed(allowed_file) + new_adhoc = self.find_new_adhoc(configs, kconfigs, allowed) + unneeded_adhoc = self.find_unneeded_adhoc(kconfigs, allowed) + updated_adhoc = self.get_updated_adhoc(unneeded_adhoc, allowed) + return new_adhoc, unneeded_adhoc, updated_adhoc + + def do_check(self, configs_file, srcdir, allowed_file, prefix, use_defines): + """Find new ad-hoc configs in the configs_file + + Args: + configs_file: Filename containing CONFIG options to check + srcdir: Source directory to scan for Kconfig files + allowed_file: File containing allowed CONFIG options + prefix: Prefix to strip from the start of each Kconfig + (e.g. 'PLATFORM_EC_') + use_defines: True if each line of the file starts with #define + + Returns: + Exit code: 0 if OK, 1 if a problem was found + """ + new_adhoc, unneeded_adhoc, updated_adhoc = self.check_adhoc_configs( + configs_file, srcdir, allowed_file, prefix, use_defines) + if new_adhoc: + print("""Error: You must add new CONFIG options using Kconfig + +\tU-Boot uses Kconfig for configuration rather than ad-hoc #defines. +\tAny new CONFIG options must be added to Kconfig files. The following new +\tad-hoc CONFIG options were detected: + +%s + +Please add these via Kconfig instead. Find a suitable Kconfig +file in zephyr/ and add a 'config' or 'menuconfig' option. +Also see details in http://issuetracker.google.com/181253613 + +To temporarily disable this, use: ALLOW_CONFIG=1 make ... +""" % '\n'.join(['CONFIG_%s' % name for name in new_adhoc]), file=sys.stderr) + return 1 + + if unneeded_adhoc: + with open(NEW_ALLOWED_FNAME, 'w') as out: + for config in updated_adhoc: + print('CONFIG_%s' % config, file=out) + print("""Congratulations! The following options are now in Kconfig: + +%s + +Please run this to update the list of allowed ad-hoc CONFIGs and include this +update in your CL: + + cp %s util/config_allowed.txt +""" % ('\n'.join(['CONFIG_%s' % name for name in unneeded_adhoc]), + NEW_ALLOWED_FNAME)) + + return 0 + + +def main(argv): + """Main function""" + args = parse_args(argv) + if not args.debug: + sys.tracebacklimit = 0 + checker = KconfigCheck() + if args.cmd == 'check': + return checker.do_check(args.configs, args.srctree, args.allowed, + args.prefix, args.use_defines) + return 2 + + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) diff --git a/scripts/test_kconfig_check.py b/scripts/test_kconfig_check.py new file mode 100755 index 00000000000..bc997bedfe1 --- /dev/null +++ b/scripts/test_kconfig_check.py @@ -0,0 +1,185 @@ +#!/usr/bin/python +# SPDX-License-Identifier: GPL-2.0+ +"""Test for Kconfig checker""" + +import contextlib +import io +import os +import re +import sys +import tempfile +import unittest + +import kconfig_check + +# Prefix that we strip from each Kconfig option, when considering whether it is +# equivalent to a CONFIG option with the same name +PREFIX = 'PLATFORM_EC_' + +@contextlib.contextmanager +def capture_sys_output(): + """Capture output for testing purposes + + Use this to suppress stdout/stderr output: + with capture_sys_output() as (stdout, stderr) + ...do something... + """ + capture_out, capture_err = io.StringIO(), io.StringIO() + old_out, old_err = sys.stdout, sys.stderr + try: + sys.stdout, sys.stderr = capture_out, capture_err + yield capture_out, capture_err + finally: + sys.stdout, sys.stderr = old_out, old_err + + +# Use unittest since it produced less verbose output than pytest and can be run +# directly from Python. You can still run this test with 'pytest' if you like. +class KconfigCheck(unittest.TestCase): + """Tests for the KconfigCheck class""" + def test_simple_check(self): + """Check it detected a new ad-hoc CONFIG""" + checker = kconfig_check.KconfigCheck() + self.assertEqual(['NEW_ONE'], checker.find_new_adhoc( + configs=['NEW_ONE', 'OLD_ONE', 'IN_KCONFIG'], + kconfigs=['IN_KCONFIG'], + allowed=['OLD_ONE'])) + + def test_sorted_check(self): + """Check it sorts the results in order""" + checker = kconfig_check.KconfigCheck() + self.assertSequenceEqual( + ['ANOTHER_NEW_ONE', 'NEW_ONE'], + checker.find_new_adhoc( + configs=['NEW_ONE', 'ANOTHER_NEW_ONE', 'OLD_ONE', 'IN_KCONFIG'], + kconfigs=['IN_KCONFIG'], + allowed=['OLD_ONE'])) + + def check_read_configs(self, use_defines): + checker = kconfig_check.KconfigCheck() + with tempfile.NamedTemporaryFile() as configs: + with open(configs.name, 'w') as out: + out.write("""{prefix}CONFIG_OLD_ONE{suffix}y +{prefix}NOT_A_CONFIG{suffix} +{prefix}CONFIG_STRING{suffix}"something" +{prefix}CONFIG_INT{suffix}123 +{prefix}CONFIG_HEX{suffix}45ab +""".format(prefix='#define ' if use_defines else '', + suffix=' ' if use_defines else '=')) + self.assertEqual(['OLD_ONE', 'STRING', 'INT', 'HEX'], + checker.read_configs(configs.name, use_defines)) + + def test_read_configs(self): + """Test KconfigCheck.read_configs()""" + self.check_read_configs(False) + + def test_read_configs_defines(self): + """Test KconfigCheck.read_configs() containing #defines""" + self.check_read_configs(True) + + @classmethod + def setup_srctree(cls, srctree): + """Set up some Kconfig files in a directory and subdirs + + Args: + srctree: Directory to write to + """ + with open(os.path.join(srctree, 'Kconfig'), 'w') as out: + out.write('''config %sMY_KCONFIG +\tbool "my kconfig" + +rsource "subdir/Kconfig.wibble" +''' % PREFIX) + subdir = os.path.join(srctree, 'subdir') + os.mkdir(subdir) + with open(os.path.join(subdir, 'Kconfig.wibble'), 'w') as out: + out.write('menuconfig %sMENU_KCONFIG\n' % PREFIX) + + # Add a directory which should be ignored + bad_subdir = os.path.join(subdir, 'Kconfig') + os.mkdir(bad_subdir) + with open(os.path.join(bad_subdir, 'Kconfig.bad'), 'w') as out: + out.write('menuconfig %sBAD_KCONFIG' % PREFIX) + + def test_scan_kconfigs(self): + """Test KconfigCheck.scan_configs()""" + checker = kconfig_check.KconfigCheck() + with tempfile.TemporaryDirectory() as srctree: + self.setup_srctree(srctree) + self.assertEqual(['MY_KCONFIG', 'MENU_KCONFIG'], + checker.scan_kconfigs(srctree, PREFIX)) + + @classmethod + def setup_allowed_and_configs(cls, allowed_fname, configs_fname, + add_new_one=True): + """Set up the 'allowed' and 'configs' files for tests + + Args: + allowed_fname: Filename to write allowed CONFIGs to + configs_fname: Filename to which CONFIGs to check should be written + add_new_one: True to add CONFIG_NEW_ONE to the configs_fname file + """ + with open(allowed_fname, 'w') as out: + out.write('CONFIG_OLD_ONE\n') + out.write('CONFIG_MENU_KCONFIG\n') + with open(configs_fname, 'w') as out: + to_add = ['CONFIG_OLD_ONE', 'CONFIG_MY_KCONFIG'] + if add_new_one: + to_add.append('CONFIG_NEW_ONE') + out.write('\n'.join(to_add)) + + def test_check_adhoc_configs(self): + """Test KconfigCheck.check_adhoc_configs()""" + checker = kconfig_check.KconfigCheck() + with tempfile.TemporaryDirectory() as srctree: + self.setup_srctree(srctree) + with tempfile.NamedTemporaryFile() as allowed: + with tempfile.NamedTemporaryFile() as configs: + self.setup_allowed_and_configs(allowed.name, configs.name) + new_adhoc, unneeded_adhoc, updated_adhoc = ( + checker.check_adhoc_configs( + configs.name, srctree, allowed.name, PREFIX)) + self.assertEqual(['NEW_ONE'], new_adhoc) + self.assertEqual(['MENU_KCONFIG'], unneeded_adhoc) + self.assertEqual(['OLD_ONE'], updated_adhoc) + + def test_check(self): + """Test running the 'check' subcommand""" + with capture_sys_output() as (stdout, stderr): + with tempfile.TemporaryDirectory() as srctree: + self.setup_srctree(srctree) + with tempfile.NamedTemporaryFile() as allowed: + with tempfile.NamedTemporaryFile() as configs: + self.setup_allowed_and_configs(allowed.name, + configs.name) + ret_code = kconfig_check.main( + ['-c', configs.name, '-s', srctree, + '-a', allowed.name, '-p', PREFIX, 'check']) + self.assertEqual(1, ret_code) + self.assertEqual('', stdout.getvalue()) + found = re.findall('(CONFIG_.*)', stderr.getvalue()) + self.assertEqual(['CONFIG_NEW_ONE'], found) + + def test_check_unneeded(self): + """Test running the 'check' subcommand with unneeded ad-hoc configs""" + with capture_sys_output() as (stdout, stderr): + with tempfile.TemporaryDirectory() as srctree: + self.setup_srctree(srctree) + with tempfile.NamedTemporaryFile() as allowed: + with tempfile.NamedTemporaryFile() as configs: + self.setup_allowed_and_configs(allowed.name, + configs.name, False) + ret_code = kconfig_check.main( + ['-c', configs.name, '-s', srctree, + '-a', allowed.name, '-p', PREFIX, 'check']) + self.assertEqual(0, ret_code) + self.assertEqual('', stderr.getvalue()) + found = re.findall('(CONFIG_.*)', stdout.getvalue()) + self.assertEqual(['CONFIG_MENU_KCONFIG'], found) + with open(kconfig_check.NEW_ALLOWED_FNAME) as inf: + allowed = inf.read().splitlines() + self.assertEqual(['CONFIG_OLD_ONE'], allowed) + + +if __name__ == '__main__': + unittest.main()

Drop the old shell scripts and use the Python script instead.
Signed-off-by: Simon Glass sjg@chromium.org ---
Makefile | 4 +-- scripts/build-whitelist.sh | 45 --------------------------- scripts/check-config.sh | 63 -------------------------------------- 3 files changed, 2 insertions(+), 110 deletions(-) delete mode 100755 scripts/build-whitelist.sh delete mode 100755 scripts/check-config.sh
diff --git a/Makefile b/Makefile index 541e942ed51..7dc78d5bec1 100644 --- a/Makefile +++ b/Makefile @@ -1077,8 +1077,8 @@ cmd_lzma = lzma -c -z -k -9 $< > $@ cfg: u-boot.cfg
quiet_cmd_cfgcheck = CFGCHK $2 -cmd_cfgcheck = $(srctree)/scripts/check-config.sh $2 \ - $(srctree)/scripts/config_whitelist.txt $(srctree) +cmd_cfgcheck = $(srctree)/scripts/kconfig_check.py -c $2 \ + -a $(srctree)/scripts/config_whitelist.txt -s $(srctree) -d check
quiet_cmd_ofcheck = OFCHK $2 cmd_ofcheck = $(srctree)/scripts/check-of.sh $2 \ diff --git a/scripts/build-whitelist.sh b/scripts/build-whitelist.sh deleted file mode 100755 index 37630c0271c..00000000000 --- a/scripts/build-whitelist.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/bin/sh -# Copyright (c) 2016 Google, Inc -# Written by Simon Glass sjg@chromium.org -# - -# This script creates the configuration whitelist file. This file contains -# all the config options which are allowed to be used outside Kconfig. -# Please do not add things to the whitelist. Instead, add your new option -# to Kconfig. -# -export LC_ALL=C LC_COLLATE=C - -# Looks for the rest of the CONFIG options, but exclude those in Kconfig and -# defconfig files. -# -git grep CONFIG_ | \ - egrep -vi "(Kconfig:|defconfig:|README|.py|.pl:)" \ - | tr ' \t' '\n\n' \ - | sed -n 's/^(CONFIG_[A-Za-z0-9_]*).*/\1/p' \ - |sort |uniq >scripts/config_whitelist.txt.tmp1; - -# Finally, we need a list of the valid Kconfig options to exclude these from -# the whitelist. -cat `find . -name "Kconfig*"` |sed -n \ - -e 's/^\s*config *([A-Za-z0-9_]*).*$/CONFIG_\1/p' \ - -e 's/^\s*menuconfig *([A-Za-z0-9_]*).*$/CONFIG_\1/p' \ - |sort |uniq >scripts/config_whitelist.txt.tmp2 - -# Use only the options that are present in the first file but not the second. -comm -23 scripts/config_whitelist.txt.tmp1 scripts/config_whitelist.txt.tmp2 \ - |sort |uniq >scripts/config_whitelist.txt.tmp3 - -# If scripts/config_whitelist.txt already exists, take the intersection of the -# current list and the new one. We do not want to increase whitelist options. -if [ -r scripts/config_whitelist.txt ]; then - comm -12 scripts/config_whitelist.txt.tmp3 scripts/config_whitelist.txt \ - > scripts/config_whitelist.txt.tmp4 - mv scripts/config_whitelist.txt.tmp4 scripts/config_whitelist.txt -else - mv scripts/config_whitelist.txt.tmp3 scripts/config_whitelist.txt -fi - -rm scripts/config_whitelist.txt.tmp* - -unset LC_ALL LC_COLLATE diff --git a/scripts/check-config.sh b/scripts/check-config.sh deleted file mode 100755 index cc1c9a54d95..00000000000 --- a/scripts/check-config.sh +++ /dev/null @@ -1,63 +0,0 @@ -#!/bin/sh -# Copyright (c) 2016 Google, Inc -# Written by Simon Glass sjg@chromium.org -# -# Check that the u-boot.cfg file provided does not introduce any new -# ad-hoc CONFIG options -# -# Use scripts/build-whitelist.sh to generate the list of current ad-hoc -# CONFIG options (those which are not in Kconfig). - -# Usage -# check-config.sh <path to u-boot.cfg> <path to whitelist file> <source dir> -# -# For example: -# scripts/check-config.sh b/chromebook_link/u-boot.cfg kconfig_whitelist.txt . - -set -e -set -u - -PROG_NAME="${0##*/}" - -usage() { - echo "$PROG_NAME <path to u-boot.cfg> <path to whitelist file> <source dir>" - exit 1 -} - -[ $# -ge 3 ] || usage - -path="$1" -whitelist="$2" -srctree="$3" - -# Temporary files -configs="${path}.configs" -suspects="${path}.suspects" -ok="${path}.ok" -new_adhoc="${path}.adhoc" - -export LC_ALL=C -export LC_COLLATE=C - -cat ${path} |sed -nr 's/^#define (CONFIG_[A-Za-z0-9_]*).*/\1/p' |sort |uniq \ - >${configs} - -comm -23 ${configs} ${whitelist} > ${suspects} - -cat `find ${srctree} -name "Kconfig*"` |sed -nr \ - -e 's/^[[:blank:]]*config *([A-Za-z0-9_]*).*$/CONFIG_\1/p' \ - -e 's/^[[:blank:]]*menuconfig ([A-Za-z0-9_]*).*$/CONFIG_\1/p' \ - |sort |uniq > ${ok} -comm -23 ${suspects} ${ok} >${new_adhoc} -if [ -s ${new_adhoc} ]; then - echo >&2 "Error: You must add new CONFIG options using Kconfig" - echo >&2 "The following new ad-hoc CONFIG options were detected:" - cat >&2 ${new_adhoc} - echo >&2 - echo >&2 "Please add these via Kconfig instead. Find a suitable Kconfig" - echo >&2 "file and add a 'config' or 'menuconfig' option." - # Don't delete the temporary files in case they are useful - exit 1 -else - rm ${suspects} ${ok} ${new_adhoc} -fi

Run this script along with the others.
Signed-off-by: Simon Glass sjg@chromium.org ---
.azure-pipelines.yml | 1 + .gitlab-ci.yml | 1 + 2 files changed, 2 insertions(+)
diff --git a/.azure-pipelines.yml b/.azure-pipelines.yml index 0fa92479b4c..7bb70ea9eba 100644 --- a/.azure-pipelines.yml +++ b/.azure-pipelines.yml @@ -188,6 +188,7 @@ stages: ./tools/buildman/buildman -t ./tools/dtoc/dtoc -t ./tools/patman/patman test + ./scripts/test_kconfig_check.py; make O=${UBOOT_TRAVIS_BUILD_DIR} testconfig EOF cat build.sh diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 5592862f74b..db690363f07 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -215,6 +215,7 @@ Run binman, buildman, dtoc, Kconfig and patman testsuites: ./tools/buildman/buildman -t; ./tools/dtoc/dtoc -t; ./tools/patman/patman test; + ./scripts/test_kconfig_check.py; make testconfig
Run tests for Nokia RX-51 (aka N900):

From: Ross Burton ross.burton@arm.com
Since Python 2.5 the argument parsing functions when parsing expressions such as s# (string plus length) expect the length to be an int or a ssize_t, depending on whether PY_SSIZE_T_CLEAN is defined or not.
Python 3.8 deprecated the use of int, and with Python 3.10 this symbol must be defined and ssize_t used[1].
Define the magic symbol when building the extension, and cast the ints from the libfdt API to ssize_t as appropriate.
[1] https://docs.python.org/3.10/whatsnew/3.10.html#id2
Signed-off-by: Ross Burton ross.burton@arm.com [dwg: Adjust for new location of setup.py] Signed-off-by: David Gibson david@gibson.dropbear.id.au Modified for U-Boot: Signed-off-by: Simon Glass sjg@chromium.org ---
scripts/dtc/pylibfdt/libfdt.i_shipped | 4 ++-- scripts/dtc/pylibfdt/setup.py | 1 + 2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/scripts/dtc/pylibfdt/libfdt.i_shipped b/scripts/dtc/pylibfdt/libfdt.i_shipped index 27c29ea2603..7ee43393e11 100644 --- a/scripts/dtc/pylibfdt/libfdt.i_shipped +++ b/scripts/dtc/pylibfdt/libfdt.i_shipped @@ -1045,9 +1045,9 @@ typedef uint32_t fdt32_t; $result = Py_None; else %#if PY_VERSION_HEX >= 0x03000000 - $result = Py_BuildValue("y#", $1, *arg4); + $result = Py_BuildValue("y#", $1, (Py_ssize_t)*arg4); %#else - $result = Py_BuildValue("s#", $1, *arg4); + $result = Py_BuildValue("s#", $1, (Py_ssize_t)*arg4); %#endif }
diff --git a/scripts/dtc/pylibfdt/setup.py b/scripts/dtc/pylibfdt/setup.py index 992cdec30f5..d0aa9676cc9 100755 --- a/scripts/dtc/pylibfdt/setup.py +++ b/scripts/dtc/pylibfdt/setup.py @@ -110,6 +110,7 @@ libfdt_module = Extension( sources = files, extra_compile_args = cflags, swig_opts = swig_opts, + define_macros=[('PY_SSIZE_T_CLEAN', None)], )
setup(

On Mon, Aug 29, 2022 at 07:57:04AM -0600, Simon Glass wrote:
The existing shell script is a bit ugly. It was picked up by Chromium OS and then rewritten in Python, adding unit tests. Bring this new version into U-Boot.
Signed-off-by: Simon Glass sjg@chromium.org
scripts/kconfig_check.py | 338 ++++++++++++++++++++++++++++++++++ scripts/test_kconfig_check.py | 185 +++++++++++++++++++ 2 files changed, 523 insertions(+) create mode 100755 scripts/kconfig_check.py create mode 100755 scripts/test_kconfig_check.py
All told, this ends up being +400 or so lines to replace a shell script with Python. I'm not sure that's a win overall.

Hi Tom,
On Wed, 14 Sept 2022 at 12:47, Tom Rini trini@konsulko.com wrote:
On Mon, Aug 29, 2022 at 07:57:04AM -0600, Simon Glass wrote:
The existing shell script is a bit ugly. It was picked up by Chromium OS and then rewritten in Python, adding unit tests. Bring this new version into U-Boot.
Signed-off-by: Simon Glass sjg@chromium.org
scripts/kconfig_check.py | 338 ++++++++++++++++++++++++++++++++++ scripts/test_kconfig_check.py | 185 +++++++++++++++++++ 2 files changed, 523 insertions(+) create mode 100755 scripts/kconfig_check.py create mode 100755 scripts/test_kconfig_check.py
All told, this ends up being +400 or so lines to replace a shell script with Python. I'm not sure that's a win overall.
It is more maintainable, has tests (which should not detract from line count) and uses kconfiglib. We could remove the non-kconfig code perhaps, but half the code is comments.
Perhaps we are going to delete all this anyway soon, I'm not sure?
Regards, Simon
-- Tom

On Wed, Sep 14, 2022 at 04:39:18PM -0600, Simon Glass wrote:
Hi Tom,
On Wed, 14 Sept 2022 at 12:47, Tom Rini trini@konsulko.com wrote:
On Mon, Aug 29, 2022 at 07:57:04AM -0600, Simon Glass wrote:
The existing shell script is a bit ugly. It was picked up by Chromium OS and then rewritten in Python, adding unit tests. Bring this new version into U-Boot.
Signed-off-by: Simon Glass sjg@chromium.org
scripts/kconfig_check.py | 338 ++++++++++++++++++++++++++++++++++ scripts/test_kconfig_check.py | 185 +++++++++++++++++++ 2 files changed, 523 insertions(+) create mode 100755 scripts/kconfig_check.py create mode 100755 scripts/test_kconfig_check.py
All told, this ends up being +400 or so lines to replace a shell script with Python. I'm not sure that's a win overall.
It is more maintainable, has tests (which should not detract from line count) and uses kconfiglib. We could remove the non-kconfig code perhaps, but half the code is comments.
Perhaps we are going to delete all this anyway soon, I'm not sure?
I don't know just how "soon", but yes, I'd rather not invest further time in tooling that works and we're aiming to remove.
And as an aside, the current tool got me to learn comm which has been quite handy in other things.

Hi Tom,
On Thu, 15 Sept 2022 at 12:32, Tom Rini trini@konsulko.com wrote:
On Wed, Sep 14, 2022 at 04:39:18PM -0600, Simon Glass wrote:
Hi Tom,
On Wed, 14 Sept 2022 at 12:47, Tom Rini trini@konsulko.com wrote:
On Mon, Aug 29, 2022 at 07:57:04AM -0600, Simon Glass wrote:
The existing shell script is a bit ugly. It was picked up by Chromium OS and then rewritten in Python, adding unit tests. Bring this new version into U-Boot.
Signed-off-by: Simon Glass sjg@chromium.org
scripts/kconfig_check.py | 338 ++++++++++++++++++++++++++++++++++ scripts/test_kconfig_check.py | 185 +++++++++++++++++++ 2 files changed, 523 insertions(+) create mode 100755 scripts/kconfig_check.py create mode 100755 scripts/test_kconfig_check.py
All told, this ends up being +400 or so lines to replace a shell script with Python. I'm not sure that's a win overall.
It is more maintainable, has tests (which should not detract from line count) and uses kconfiglib. We could remove the non-kconfig code perhaps, but half the code is comments.
Perhaps we are going to delete all this anyway soon, I'm not sure?
I don't know just how "soon", but yes, I'd rather not invest further time in tooling that works and we're aiming to remove.
Fair enough.
And as an aside, the current tool got me to learn comm which has been quite handy in other things.
Yes, I remember learning about it at uni and then forgetting about it for a decade :-)
Regards, Simon
participants (2)
-
Simon Glass
-
Tom Rini