diff options
author | Simon Glass <sjg@chromium.org> | 2021-04-30 15:39:47 -0600 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2022-01-28 22:02:44 +0000 |
commit | da38ba6b8abe06d2c67505b05fdce6b093cadd07 (patch) | |
tree | 039b4e6cb19e73dc4b82536c80774bcc3ad92075 /util/kconfig_check.py | |
parent | da3488be27eb0ce7fedbae2414ef8755f46c6c4e (diff) | |
download | chrome-ec-da38ba6b8abe06d2c67505b05fdce6b093cadd07.tar.gz |
util: Support using kconfiglib in the CONFIG checker
Update this to use kconfiglib so that it might be easier to maintain.
This needs a more correct Kconfig setup in the tests. Also we need to
provide the root Kconfig instead of just the directory.
Overall this takes about 50% more time, but the total is only about
50ms, so this is not a significant factor in build performance.
We must leave it optional, since U-Boot is unlikely to want this extra
dependency.
BUG=b:181323955
BRANCH=none
TEST=python3 util/test_kconfig_check.py
Signed-off-by: Simon Glass <sjg@chromium.org>
Change-Id: I5cce2ca1fa16a7c334cef15b1f66dd7ede2644bd
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2923227
Reviewed-by: Jack Rosenthal <jrosenth@chromium.org>
Diffstat (limited to 'util/kconfig_check.py')
-rwxr-xr-x | util/kconfig_check.py | 141 |
1 files changed, 109 insertions, 32 deletions
diff --git a/util/kconfig_check.py b/util/kconfig_check.py index b6b9fda8de..e61e604c5a 100755 --- a/util/kconfig_check.py +++ b/util/kconfig_check.py @@ -6,6 +6,18 @@ Checks that the .config file provided does not introduce any new ad-hoc CONFIG options + +This tool is also present in U-Boot, 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 @@ -13,6 +25,16 @@ import os import re import sys +# Try to use kconfiglib if available, but fall back to a simple recursive grep. +# This is used by U-Boot in some situations so we keep it to avoid forking this +# script. +USE_KCONFIGLIB = False +try: + import kconfiglib + USE_KCONFIGLIB = True +except ImportError: + pass + def parse_args(argv): """Parse the program arguments @@ -23,8 +45,8 @@ def parse_args(argv): 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""" + 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, @@ -32,12 +54,16 @@ a corresponding Kconfig option for Zephyr""" 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('-I', '--search-path', type=str, action='append', + help='Search paths to look for Kconfigs') parser.add_argument('-p', '--prefix', type=str, default='PLATFORM_EC_', help='Prefix to string from Kconfig options') - parser.add_argument('-s', '--srctree', type=str, default='.', + parser.add_argument('-s', '--srctree', type=str, default='zephyr/', help='Path to source tree to look for Kconfigs') subparsers = parser.add_subparsers(dest='cmd', required=True) @@ -83,20 +109,42 @@ class KconfigCheck: Returns: List of new CONFIG options, with the CONFIG_ prefix removed """ - return sorted(list(set(configs) - set(kconfigs) - set(allowed))) + return sorted(set(configs) - set(kconfigs) - set(allowed)) @classmethod - def read_configs(cls, configs_file): + 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 + 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_' - prefixremoved + prefix removed """ with open(configs_file, 'r') 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, 'r') as inf: configs = re.findall('CONFIG_([A-Za-z0-9_]*)', inf.read()) return configs @@ -121,29 +169,47 @@ class KconfigCheck: dirs.remove('Kconfig') return kconfig_files - def scan_kconfigs(self, srcdir, prefix=''): + @classmethod + def scan_kconfigs(cls, srcdir, prefix='', search_paths=None, + try_kconfiglib=True): """Scan a source tree for Kconfig options Args: - srcdir: Directory to scan + srcdir: Directory to scan (containing top-level Kconfig file) prefix: Prefix to strip from the name (e.g. 'PLATFORM_EC_') + search_paths: List of project paths to search for Kconfig files, in + addition to the current directory + try_kconfiglib: Use kconfiglib if available Returns: - List of config and menuconfig options found, + List of config and menuconfig options found """ - kconfigs = [] - - # Remove the prefix if present - expr = re.compile(r'(config|menuconfig) (%s)?([A-Za-z0-9_]*)\n' % - prefix) - for fname in self.find_kconfigs(srcdir): - with open(fname) as inf: - found = re.findall(expr, inf.read()) - kconfigs += [name for kctype, _, name in found] - return kconfigs + if USE_KCONFIGLIB and try_kconfiglib: + os.environ['srctree'] = srcdir + kconf = kconfiglib.Kconfig('Kconfig', warn=False, + search_paths=search_paths, + allow_empty_macros=True) + + # There is always a MODULES config, since kconfiglib is designed for + # linux, but we don't want it + kconfigs = [name for name in kconf.syms if name != 'MODULES'] + + 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'\n(config|menuconfig) (%s)?([A-Za-z0-9_]*)\n' % + prefix) + for fname in cls.find_kconfigs(srcdir): + with open(fname) as inf: + found = re.findall(expr, inf.read()) + kconfigs += [name for kctype, _, name in found] + return sorted(kconfigs) def find_new_adhoc_configs(self, configs_file, srcdir, allowed_file, - prefix=''): + prefix='', use_defines=False, search_paths=None): """Find new ad-hoc configs in the configs_file Args: @@ -152,14 +218,18 @@ class KconfigCheck: 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 + search_paths: List of project paths to search for Kconfig files, in + addition to the current directory """ - configs = self.read_configs(configs_file) - kconfigs = self.scan_kconfigs(srcdir, prefix) - allowed = self.read_configs(allowed_file) + configs = self.read_configs(configs_file, use_defines) + kconfigs = self.scan_kconfigs(srcdir, prefix, search_paths) + allowed = self.read_allowed(allowed_file) new_adhoc = self.find_new_adhoc(configs, kconfigs, allowed) return new_adhoc - def do_check(self, configs_file, srcdir, allowed_file, prefix): + def do_check(self, configs_file, srcdir, allowed_file, prefix, use_defines, + search_paths): """Find new ad-hoc configs in the configs_file Args: @@ -168,27 +238,32 @@ class KconfigCheck: 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 + search_paths: List of project paths to search for Kconfig files, in + addition to the current directory Returns: Exit code: 0 if OK, 1 if a problem was found """ - new_adhoc = self.find_new_adhoc_configs(configs_file, srcdir, - allowed_file, prefix) + new_adhoc = self.find_new_adhoc_configs( + configs_file, srcdir, allowed_file, prefix, use_defines, + search_paths) if new_adhoc: - print("""Error:\tThe EC is in the process of migrating to Zephyr. + file_list = '\n'.join(['CONFIG_%s' % name for name in new_adhoc]) + print(f'''Error:\tThe EC is in the process of migrating to Zephyr. \tZephyr uses Kconfig for configuration rather than ad-hoc #defines. \tAny new EC CONFIG options must ALSO be added to Zephyr so that new \tfunctionality is available in Zephyr also. The following new ad-hoc \tCONFIG options were detected: -%s +{file_list} 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) +''', file=sys.stderr) return 1 return 0 @@ -200,8 +275,10 @@ def main(argv): sys.tracebacklimit = 0 checker = KconfigCheck() if args.cmd == 'check': - return checker.do_check(args.configs, args.srctree, args.allowed, - args.prefix) + return checker.do_check( + configs_file=args.configs, srcdir=args.srctree, + allowed_file=args.allowed, prefix=args.prefix, + use_defines=args.use_defines, search_paths=args.search_path) return 2 |