From ac5f13daa5f69299343fdde85cfc6189e754a304 Mon Sep 17 00:00:00 2001 From: Gabor Mezei Date: Tue, 10 Sep 2024 15:40:46 +0200 Subject: [PATCH 01/11] Move commonly used part of config.py Move the Setting, Config, ConfigFile and ConfigTool classes from config.py. Signed-off-by: Gabor Mezei --- scripts/mbedtls_framework/config_common.py | 426 +++++++++++++++++++++ 1 file changed, 426 insertions(+) create mode 100644 scripts/mbedtls_framework/config_common.py diff --git a/scripts/mbedtls_framework/config_common.py b/scripts/mbedtls_framework/config_common.py new file mode 100644 index 000000000..23733b2d6 --- /dev/null +++ b/scripts/mbedtls_framework/config_common.py @@ -0,0 +1,426 @@ +"""Mbed TLS and PSA configuration file manipulation library +""" + +## Copyright The Mbed TLS Contributors +## SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later +## + +import argparse +import os +import re +import sys + +from abc import ABCMeta + + +class Setting: + """Representation of one Mbed TLS mbedtls_config.h pr PSA crypto_config.h setting. + + Fields: + * name: the symbol name ('MBEDTLS_xxx'). + * value: the value of the macro. The empty string for a plain #define + with no value. + * active: True if name is defined, False if a #define for name is + present in mbedtls_config.h but commented out. + * section: the name of the section that contains this symbol. + * configfile: the file the settings is defined + """ + # pylint: disable=too-few-public-methods, too-many-arguments + def __init__(self, configfile, active, name, value='', section=None): + self.active = active + self.name = name + self.value = value + self.section = section + self.configfile = configfile + + +class Config: + """Representation of the Mbed TLS and PSA configuration. + + In the documentation of this class, a symbol is said to be *active* + if there is a #define for it that is not commented out, and *known* + if there is a #define for it whether commented out or not. + + This class supports the following protocols: + * `name in config` is `True` if the symbol `name` is active, `False` + otherwise (whether `name` is inactive or not known). + * `config[name]` is the value of the macro `name`. If `name` is inactive, + raise `KeyError` (even if `name` is known). + * `config[name] = value` sets the value associated to `name`. `name` + must be known, but does not need to be set. This does not cause + name to become set. + """ + + def __init__(self): + self.settings = {} + self.configfiles = [] + + def __contains__(self, name): + """True if the given symbol is active (i.e. set). + + False if the given symbol is not set, even if a definition + is present but commented out. + """ + return name in self.settings and self.settings[name].active + + def all(self, *names): + """True if all the elements of names are active (i.e. set).""" + return all(name in self for name in names) + + def any(self, *names): + """True if at least one symbol in names are active (i.e. set).""" + return any(name in self for name in names) + + def known(self, name): + """True if a #define for name is present, whether it's commented out or not.""" + return name in self.settings + + def __getitem__(self, name): + """Get the value of name, i.e. what the preprocessor symbol expands to. + + If name is not known, raise KeyError. name does not need to be active. + """ + return self.settings[name].value + + def get(self, name, default=None): + """Get the value of name. If name is inactive (not set), return default. + + If a #define for name is present and not commented out, return + its expansion, even if this is the empty string. + + If a #define for name is present but commented out, return default. + """ + if name in self.settings: + return self.settings[name].value + else: + return default + + def __setitem__(self, name, value): + """If name is known, set its value. + + If name is not known, raise KeyError. + """ + setting = self.settings[name] + if setting != value: + setting.configfile.modified = True + + setting.value = value + + def set(self, name, value=None): + """Set name to the given value and make it active. + + If value is None and name is already known, don't change its value. + If value is None and name is not known, set its value. + """ + if name in self.settings: + setting = self.settings[name] + if setting.value != value or not setting.active: + setting.configfile.modified = True + if value is not None: + setting.value = value + setting.active = True + else: + configfile = self._get_configfile(name) + self.settings[name] = Setting(configfile, True, name, value=value) + configfile.modified = True + + def unset(self, name): + """Make name unset (inactive). + + name remains known if it was known before. + """ + if name not in self.settings: + return + + setting = self.settings[name] + # Check if modifying the config file + if setting.active: + setting.configfile.modified = True + + setting.active = False + + def adapt(self, adapter): + """Run adapter on each known symbol and (de)activate it accordingly. + + `adapter` must be a function that returns a boolean. It is called as + `adapter(name, active, section)` for each setting, where `active` is + `True` if `name` is set and `False` if `name` is known but unset, + and `section` is the name of the section containing `name`. If + `adapter` returns `True`, then set `name` (i.e. make it active), + otherwise unset `name` (i.e. make it known but inactive). + """ + for setting in self.settings.values(): + is_active = setting.active + setting.active = adapter(setting.name, setting.active, + setting.section) + # Check if modifying the config file + if setting.active != is_active: + setting.configfile.modified = True + + def change_matching(self, regexs, enable): + """Change all symbols matching one of the regexs to the desired state.""" + if not regexs: + return + regex = re.compile('|'.join(regexs)) + for setting in self.settings.values(): + if regex.search(setting.name): + # Check if modifying the config file + if setting.active != enable: + setting.configfile.modified = True + setting.active = enable + + def _get_configfile(self, name=None): + """Find a config for a setting name. + + If more then one configfile is used this function must be overridden. + """ + + if name and name in self.settings: + return self.get(name).configfile + return self.configfiles[0] + + def write(self, filename=None): + """Write the whole configuration to the file it was read from. + + If filename is specified, write to this file instead. + """ + + for configfile in self.configfiles: + configfile.write(self.settings, filename) + + def filename(self, name=None): + """Get the name of the config file.""" + + return self._get_configfile(name).filename + + +class ConfigFile(metaclass=ABCMeta): + """Representation of a configuration file.""" + + def __init__(self, default_path, name, filename=None): + """Check if the config file exists.""" + if filename is None: + for candidate in default_path: + if os.path.lexists(candidate): + filename = candidate + break + else: + raise FileNotFoundError(f'{name} configuration file not found: ' + f'{filename if filename else default_path}') + + self.filename = filename + self.templates = [] + self.current_section = None + self.inclusion_guard = None + self.modified = False + + _define_line_regexp = (r'(?P\s*)' + + r'(?P(//\s*)?)' + + r'(?P#\s*define\s+)' + + r'(?P\w+)' + + r'(?P(?:\((?:\w|\s|,)*\))?)' + + r'(?P\s*)' + + r'(?P.*)') + _ifndef_line_regexp = r'#ifndef (?P\w+)' + _section_line_regexp = (r'\s*/?\*+\s*[\\@]name\s+SECTION:\s*' + + r'(?P
.*)[ */]*') + _config_line_regexp = re.compile(r'|'.join([_define_line_regexp, + _ifndef_line_regexp, + _section_line_regexp])) + def _parse_line(self, line): + """Parse a line in the config file, save the templates representing the lines + and return the corresponding setting element. + """ + + line = line.rstrip('\r\n') + m = re.match(self._config_line_regexp, line) + if m is None: + self.templates.append(line) + return None + elif m.group('section'): + self.current_section = m.group('section') + self.templates.append(line) + return None + elif m.group('inclusion_guard') and self.inclusion_guard is None: + self.inclusion_guard = m.group('inclusion_guard') + self.templates.append(line) + return None + else: + active = not m.group('commented_out') + name = m.group('name') + value = m.group('value') + if name == self.inclusion_guard and value == '': + # The file double-inclusion guard is not an option. + self.templates.append(line) + return None + template = (name, + m.group('indentation'), + m.group('define') + name + + m.group('arguments') + m.group('separator')) + self.templates.append(template) + + return (active, name, value, self.current_section) + + def parse_file(self): + """Parse the whole file and return the settings.""" + + with open(self.filename, 'r', encoding='utf-8') as file: + for line in file: + setting = self._parse_line(line) + if setting is not None: + yield setting + self.current_section = None + + #pylint: disable=no-self-use + def _format_template(self, setting, indent, middle): + """Build a line for the config file for the given setting. + + The line has the form "#define " + where is "#define ". + """ + + value = setting.value + if value is None: + value = '' + # Normally the whitespace to separate the symbol name from the + # value is part of middle, and there's no whitespace for a symbol + # with no value. But if a symbol has been changed from having a + # value to not having one, the whitespace is wrong, so fix it. + if value: + if middle[-1] not in '\t ': + middle += ' ' + else: + middle = middle.rstrip() + return ''.join([indent, + '' if setting.active else '//', + middle, + value]).rstrip() + + def write_to_stream(self, settings, output): + """Write the whole configuration to output.""" + + for template in self.templates: + if isinstance(template, str): + line = template + else: + name, indent, middle = template + line = self._format_template(settings[name], indent, middle) + output.write(line + '\n') + + def write(self, settings, filename=None): + """Write the whole configuration to the file it was read from. + + If filename is specified, write to this file instead. + """ + + if filename is None: + filename = self.filename + + # Not modified so no need to write to the file + if not self.modified and filename == self.filename: + return + + with open(filename, 'w', encoding='utf-8') as output: + self.write_to_stream(settings, output) + + +class ConfigTool(metaclass=ABCMeta): + """Command line config manipulation tool. + + Custom parser option can be added by overriding 'custom_parser_options'. + """ + + def __init__(self, file_type): + """Create parser for config manipulation tool.""" + + self.parser = argparse.ArgumentParser(description=""" + Configuration file manipulation tool.""") + self.subparsers = self.parser.add_subparsers(dest='command', + title='Commands') + self._common_parser_options(file_type) + self.custom_parser_options() + self.parser_args = self.parser.parse_args() + self.config = Config() # Make the pylint happy + + def add_adapter(self, name, function, description): + """Creates a command in the tool for a configuration adapter.""" + + subparser = self.subparsers.add_parser(name, help=description) + subparser.set_defaults(adapter=function) + + def _common_parser_options(self, file_type): + """Common parser options for config manipulation tool.""" + + self.parser.add_argument( + '--file', '-f', + help="""File to read (and modify if requested). Default: {}. + """.format(file_type.default_path)) + self.parser.add_argument( + '--force', '-o', + action='store_true', + help="""For the set command, if SYMBOL is not present, add a definition for it.""") + self.parser.add_argument( + '--write', '-w', + metavar='FILE', + help="""File to write to instead of the input file.""") + + parser_get = self.subparsers.add_parser( + 'get', + help="""Find the value of SYMBOL and print it. Exit with + status 0 if a #define for SYMBOL is found, 1 otherwise.""") + parser_get.add_argument('symbol', metavar='SYMBOL') + parser_set = self.subparsers.add_parser( + 'set', + help="""Set SYMBOL to VALUE. If VALUE is omitted, just uncomment + the #define for SYMBOL. Error out of a line defining + SYMBOL (commented or not) is not found, unless --force is passed. """) + parser_set.add_argument('symbol', metavar='SYMBOL') + parser_set.add_argument('value', metavar='VALUE', nargs='?', default='') + parser_set_all = self.subparsers.add_parser( + 'set-all', + help="""Uncomment all #define whose name contains a match for REGEX.""") + parser_set_all.add_argument('regexs', metavar='REGEX', nargs='*') + parser_unset = self.subparsers.add_parser( + 'unset', + help="""Comment out the #define for SYMBOL. Do nothing if none is present.""") + parser_unset.add_argument('symbol', metavar='SYMBOL') + parser_unset_all = self.subparsers.add_parser( + 'unset-all', + help="""Comment out all #define whose name contains a match for REGEX.""") + parser_unset_all.add_argument('regexs', metavar='REGEX', nargs='*') + + def custom_parser_options(self): + """Adds custom options for the parser. Designed for overridden by descendant.""" + pass + + def main(self): + """Common main fuction for config manipulation tool.""" + + if self.parser_args.command is None: + self.parser.print_help() + return 1 + if self.parser_args.command == 'get': + if self.parser_args.symbol in self.config: + value = self.config[self.parser_args.symbol] + if value: + sys.stdout.write(value + '\n') + return 0 if self.parser_args.symbol in self.config else 1 + elif self.parser_args.command == 'set': + if not self.parser_args.force and self.parser_args.symbol not in self.config.settings: + sys.stderr.write( + "A #define for the symbol {} was not found in {}\n" + .format(self.parser_args.symbol, + self.config.filename(self.parser_args.symbol))) + return 1 + self.config.set(self.parser_args.symbol, value=self.parser_args.value) + elif self.parser_args.command == 'set-all': + self.config.change_matching(self.parser_args.regexs, True) + elif self.parser_args.command == 'unset': + self.config.unset(self.parser_args.symbol) + elif self.parser_args.command == 'unset-all': + self.config.change_matching(self.parser_args.regexs, False) + else: + self.config.adapt(self.parser_args.adapter) + self.config.write(self.parser_args.write) + + return 0 From d4f21c170b6f162c821ee7bbbdd39c1349a54671 Mon Sep 17 00:00:00 2001 From: Gabor Mezei Date: Wed, 7 Aug 2024 17:56:41 +0200 Subject: [PATCH 02/11] Update references to config objects Signed-off-by: Gabor Mezei --- scripts/generate_config_tests.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/scripts/generate_config_tests.py b/scripts/generate_config_tests.py index 3438f8460..90b393ce8 100755 --- a/scripts/generate_config_tests.py +++ b/scripts/generate_config_tests.py @@ -12,11 +12,12 @@ from typing import Iterable, Iterator, List, Optional, Tuple import project_scripts # pylint: disable=unused-import import config +from mbedtls_framework import config_common from mbedtls_framework import test_case from mbedtls_framework import test_data_generation -def single_setting_case(setting: config.Setting, when_on: bool, +def single_setting_case(setting: config_common.Setting, when_on: bool, dependencies: List[str], note: Optional[str]) -> test_case.TestCase: """Construct a test case for a boolean setting. @@ -64,8 +65,8 @@ SIMPLE_DEPENDENCIES = { 'MBEDTLS_PSA_ASSUME_EXCLUSIVE_BUFFERS': 'MBEDTLS_PSA_CRYPTO_C', } -def dependencies_of_setting(cfg: config.Config, - setting: config.Setting) -> Optional[str]: +def dependencies_of_setting(cfg: config_common.Config, + setting: config_common.Setting) -> Optional[str]: """Return dependencies without which a setting is not meaningful. The dependencies of a setting express when a setting can be enabled and @@ -112,8 +113,8 @@ def dependencies_of_setting(cfg: config.Config, return m.group('prefix') + 'BASIC' return None -def conditions_for_setting(cfg: config.Config, - setting: config.Setting +def conditions_for_setting(cfg: config_common.Config, + setting: config_common.Setting ) -> Iterator[Tuple[List[str], str]]: """Enumerate the conditions under which to test the given setting. @@ -142,7 +143,7 @@ def conditions_for_setting(cfg: config.Config, yield [], '' -def enumerate_boolean_setting_cases(cfg: config.Config +def enumerate_boolean_setting_cases(cfg: config_common.Config ) -> Iterable[test_case.TestCase]: """Emit test cases for all boolean settings.""" for name in sorted(cfg.settings.keys()): From 025c8e7419164d6d2819141e034628b8f6d3eb13 Mon Sep 17 00:00:00 2001 From: Gabor Mezei Date: Wed, 7 Aug 2024 18:03:08 +0200 Subject: [PATCH 03/11] Remove temporary solution for 3.6 Signed-off-by: Gabor Mezei --- scripts/generate_config_tests.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/scripts/generate_config_tests.py b/scripts/generate_config_tests.py index 90b393ce8..54c1eabbe 100755 --- a/scripts/generate_config_tests.py +++ b/scripts/generate_config_tests.py @@ -5,7 +5,6 @@ # Copyright The Mbed TLS Contributors # SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later -import abc import re import sys from typing import Iterable, Iterator, List, Optional, Tuple @@ -160,15 +159,10 @@ class ConfigTestGenerator(test_data_generation.TestGenerator): """Generate test cases for configuration reporting.""" def __init__(self, settings): - # Temporarily use different config classes for 3.6. With the config.py moving to - # the framework it will be unified. - is_3_6 = not isinstance(config.ConfigFile, abc.ABCMeta) - # pylint: disable=no-value-for-parameter, no-member - self.mbedtls_config = config.ConfigFile() if is_3_6 else config.MbedTLSConfig() + self.mbedtls_config = config.MbedTLSConfig() self.targets['test_suite_config.mbedtls_boolean'] = \ lambda: enumerate_boolean_setting_cases(self.mbedtls_config) - self.psa_config = config.ConfigFile('include/psa/crypto_config.h') if is_3_6 else \ - config.CryptoConfig() + self.psa_config = config.CryptoConfig() self.targets['test_suite_config.psa_boolean'] = \ lambda: enumerate_boolean_setting_cases(self.psa_config) super().__init__(settings) From c1cdecd4e626f2069e910b3b7acb451348569db8 Mon Sep 17 00:00:00 2001 From: Gabor Mezei Date: Tue, 17 Sep 2024 13:06:17 +0200 Subject: [PATCH 04/11] Use local variables Signed-off-by: Gabor Mezei --- scripts/mbedtls_framework/config_common.py | 39 ++++++++++++---------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/scripts/mbedtls_framework/config_common.py b/scripts/mbedtls_framework/config_common.py index 23733b2d6..eb7850b6f 100644 --- a/scripts/mbedtls_framework/config_common.py +++ b/scripts/mbedtls_framework/config_common.py @@ -396,31 +396,34 @@ class ConfigTool(metaclass=ABCMeta): def main(self): """Common main fuction for config manipulation tool.""" - if self.parser_args.command is None: + args = self.parser_args + config = self.config + + if args.command is None: self.parser.print_help() return 1 - if self.parser_args.command == 'get': - if self.parser_args.symbol in self.config: - value = self.config[self.parser_args.symbol] + if args.command == 'get': + if args.symbol in config: + value = config[args.symbol] if value: sys.stdout.write(value + '\n') - return 0 if self.parser_args.symbol in self.config else 1 - elif self.parser_args.command == 'set': - if not self.parser_args.force and self.parser_args.symbol not in self.config.settings: + return 0 if args.symbol in config else 1 + elif args.command == 'set': + if not args.force and args.symbol not in config.settings: sys.stderr.write( "A #define for the symbol {} was not found in {}\n" - .format(self.parser_args.symbol, - self.config.filename(self.parser_args.symbol))) + .format(args.symbol, + config.filename(args.symbol))) return 1 - self.config.set(self.parser_args.symbol, value=self.parser_args.value) - elif self.parser_args.command == 'set-all': - self.config.change_matching(self.parser_args.regexs, True) - elif self.parser_args.command == 'unset': - self.config.unset(self.parser_args.symbol) - elif self.parser_args.command == 'unset-all': - self.config.change_matching(self.parser_args.regexs, False) + config.set(args.symbol, value=args.value) + elif args.command == 'set-all': + config.change_matching(args.regexs, True) + elif args.command == 'unset': + config.unset(args.symbol) + elif args.command == 'unset-all': + config.change_matching(args.regexs, False) else: - self.config.adapt(self.parser_args.adapter) - self.config.write(self.parser_args.write) + config.adapt(args.adapter) + config.write(args.write) return 0 From 392bf7b380dc07452da082ce7906765e5bf00e6f Mon Sep 17 00:00:00 2001 From: Gabor Mezei Date: Tue, 17 Sep 2024 13:14:56 +0200 Subject: [PATCH 05/11] Rename member variable Signed-off-by: Gabor Mezei --- scripts/mbedtls_framework/config_common.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/mbedtls_framework/config_common.py b/scripts/mbedtls_framework/config_common.py index eb7850b6f..1ad93bcaf 100644 --- a/scripts/mbedtls_framework/config_common.py +++ b/scripts/mbedtls_framework/config_common.py @@ -339,7 +339,7 @@ class ConfigTool(metaclass=ABCMeta): title='Commands') self._common_parser_options(file_type) self.custom_parser_options() - self.parser_args = self.parser.parse_args() + self.args = self.parser.parse_args() self.config = Config() # Make the pylint happy def add_adapter(self, name, function, description): @@ -396,7 +396,7 @@ class ConfigTool(metaclass=ABCMeta): def main(self): """Common main fuction for config manipulation tool.""" - args = self.parser_args + args = self.args config = self.config if args.command is None: From e32956bf2cc928095bdb96e314340eb3cee17f76 Mon Sep 17 00:00:00 2001 From: Gabor Mezei Date: Tue, 17 Sep 2024 13:15:49 +0200 Subject: [PATCH 06/11] Use file path instead of ConfigFile object for parameter Signed-off-by: Gabor Mezei --- scripts/mbedtls_framework/config_common.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/mbedtls_framework/config_common.py b/scripts/mbedtls_framework/config_common.py index 1ad93bcaf..f97b43dbf 100644 --- a/scripts/mbedtls_framework/config_common.py +++ b/scripts/mbedtls_framework/config_common.py @@ -330,14 +330,14 @@ class ConfigTool(metaclass=ABCMeta): Custom parser option can be added by overriding 'custom_parser_options'. """ - def __init__(self, file_type): """Create parser for config manipulation tool.""" + def __init__(self, file): self.parser = argparse.ArgumentParser(description=""" Configuration file manipulation tool.""") self.subparsers = self.parser.add_subparsers(dest='command', title='Commands') - self._common_parser_options(file_type) + self._common_parser_options(file) self.custom_parser_options() self.args = self.parser.parse_args() self.config = Config() # Make the pylint happy @@ -348,13 +348,13 @@ class ConfigTool(metaclass=ABCMeta): subparser = self.subparsers.add_parser(name, help=description) subparser.set_defaults(adapter=function) - def _common_parser_options(self, file_type): + def _common_parser_options(self, file): """Common parser options for config manipulation tool.""" self.parser.add_argument( '--file', '-f', help="""File to read (and modify if requested). Default: {}. - """.format(file_type.default_path)) + """.format(file)) self.parser.add_argument( '--force', '-o', action='store_true', From 3ba299a7a084020879fac6ff14c04b06b82dc78f Mon Sep 17 00:00:00 2001 From: Gabor Mezei Date: Tue, 17 Sep 2024 13:17:14 +0200 Subject: [PATCH 07/11] Fix configfile handling Signed-off-by: Gabor Mezei --- scripts/mbedtls_framework/config_common.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/mbedtls_framework/config_common.py b/scripts/mbedtls_framework/config_common.py index f97b43dbf..70d3b2f85 100644 --- a/scripts/mbedtls_framework/config_common.py +++ b/scripts/mbedtls_framework/config_common.py @@ -101,7 +101,7 @@ class Config: If name is not known, raise KeyError. """ setting = self.settings[name] - if setting != value: + if setting.value != value: setting.configfile.modified = True setting.value = value @@ -176,7 +176,7 @@ class Config: """ if name and name in self.settings: - return self.get(name).configfile + return self.settings[name].configfile return self.configfiles[0] def write(self, filename=None): From 23e4f48d5626b802ddf03dd680b796dfbb9ad9a8 Mon Sep 17 00:00:00 2001 From: Gabor Mezei Date: Tue, 17 Sep 2024 13:28:15 +0200 Subject: [PATCH 08/11] Fix documentation Signed-off-by: Gabor Mezei --- scripts/mbedtls_framework/config_common.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/scripts/mbedtls_framework/config_common.py b/scripts/mbedtls_framework/config_common.py index 70d3b2f85..b31d82b21 100644 --- a/scripts/mbedtls_framework/config_common.py +++ b/scripts/mbedtls_framework/config_common.py @@ -14,7 +14,7 @@ from abc import ABCMeta class Setting: - """Representation of one Mbed TLS mbedtls_config.h pr PSA crypto_config.h setting. + """Representation of one Mbed TLS mbedtls_config.h or PSA crypto_config.h setting. Fields: * name: the symbol name ('MBEDTLS_xxx'). @@ -23,7 +23,7 @@ class Setting: * active: True if name is defined, False if a #define for name is present in mbedtls_config.h but commented out. * section: the name of the section that contains this symbol. - * configfile: the file the settings is defined + * configfile: the representation of the configuration file where the setting is defined """ # pylint: disable=too-few-public-methods, too-many-arguments def __init__(self, configfile, active, name, value='', section=None): @@ -170,9 +170,10 @@ class Config: setting.active = enable def _get_configfile(self, name=None): - """Find a config for a setting name. + """Get the representation of the configuration file name belongs to - If more then one configfile is used this function must be overridden. + If the configuration is spread among several configuration files, this + function may need to be overridden for the case of an unknown setting. """ if name and name in self.settings: @@ -180,16 +181,16 @@ class Config: return self.configfiles[0] def write(self, filename=None): - """Write the whole configuration to the file it was read from. + """Write the whole configuration to the file(s) it was read from. - If filename is specified, write to this file instead. + If filename is specified, write to this file(s) instead. """ for configfile in self.configfiles: configfile.write(self.settings, filename) def filename(self, name=None): - """Get the name of the config file.""" + """Get the name of the config file where the setting name is defined.""" return self._get_configfile(name).filename @@ -327,11 +328,14 @@ class ConfigFile(metaclass=ABCMeta): class ConfigTool(metaclass=ABCMeta): """Command line config manipulation tool. - Custom parser option can be added by overriding 'custom_parser_options'. + Custom parser options can be added by overriding 'custom_parser_options'. """ - """Create parser for config manipulation tool.""" def __init__(self, file): + """Create parser for config manipulation tool. + + 'file' must be the default config file with path + """ self.parser = argparse.ArgumentParser(description=""" Configuration file manipulation tool.""") From b1ec5fd541f62ef2b6fc35141c88a8488649fa49 Mon Sep 17 00:00:00 2001 From: Gabor Mezei Date: Tue, 17 Sep 2024 13:28:51 +0200 Subject: [PATCH 09/11] Fix `get` function functionality To meet the documentation fix the `get` function to return the value of the `default` parameter if an inactive setting is handled. Signed-off-by: Gabor Mezei --- scripts/mbedtls_framework/config_common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/mbedtls_framework/config_common.py b/scripts/mbedtls_framework/config_common.py index b31d82b21..5e2a09d2e 100644 --- a/scripts/mbedtls_framework/config_common.py +++ b/scripts/mbedtls_framework/config_common.py @@ -90,7 +90,7 @@ class Config: If a #define for name is present but commented out, return default. """ - if name in self.settings: + if name in self: return self.settings[name].value else: return default From ebb46d68c2eab9da5a1bd74ffd35b83f2ed35bc3 Mon Sep 17 00:00:00 2001 From: Gabor Mezei Date: Wed, 18 Sep 2024 16:46:52 +0200 Subject: [PATCH 10/11] Only mark config file to modified if actually modified Signed-off-by: Gabor Mezei --- scripts/mbedtls_framework/config_common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/mbedtls_framework/config_common.py b/scripts/mbedtls_framework/config_common.py index 5e2a09d2e..7b06cf6a3 100644 --- a/scripts/mbedtls_framework/config_common.py +++ b/scripts/mbedtls_framework/config_common.py @@ -114,7 +114,7 @@ class Config: """ if name in self.settings: setting = self.settings[name] - if setting.value != value or not setting.active: + if (value is not None and setting.value != value) or not setting.active: setting.configfile.modified = True if value is not None: setting.value = value From 86ae2a778c793eeda78637a52298fe3073940618 Mon Sep 17 00:00:00 2001 From: Gabor Mezei Date: Wed, 18 Sep 2024 16:50:29 +0200 Subject: [PATCH 11/11] Change parameter name Signed-off-by: Gabor Mezei --- scripts/mbedtls_framework/config_common.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/scripts/mbedtls_framework/config_common.py b/scripts/mbedtls_framework/config_common.py index 7b06cf6a3..4dcefb0e6 100644 --- a/scripts/mbedtls_framework/config_common.py +++ b/scripts/mbedtls_framework/config_common.py @@ -331,17 +331,17 @@ class ConfigTool(metaclass=ABCMeta): Custom parser options can be added by overriding 'custom_parser_options'. """ - def __init__(self, file): + def __init__(self, default_file_path): """Create parser for config manipulation tool. - 'file' must be the default config file with path + :param default_file_path: Default configuration file path """ self.parser = argparse.ArgumentParser(description=""" Configuration file manipulation tool.""") self.subparsers = self.parser.add_subparsers(dest='command', title='Commands') - self._common_parser_options(file) + self._common_parser_options(default_file_path) self.custom_parser_options() self.args = self.parser.parse_args() self.config = Config() # Make the pylint happy @@ -352,13 +352,13 @@ class ConfigTool(metaclass=ABCMeta): subparser = self.subparsers.add_parser(name, help=description) subparser.set_defaults(adapter=function) - def _common_parser_options(self, file): + def _common_parser_options(self, default_file_path): """Common parser options for config manipulation tool.""" self.parser.add_argument( '--file', '-f', help="""File to read (and modify if requested). Default: {}. - """.format(file)) + """.format(default_file_path)) self.parser.add_argument( '--force', '-o', action='store_true',