diff options
| author | Dylan Baker <dylan@pnwbakers.com> | 2025-03-03 11:26:32 -0800 |
|---|---|---|
| committer | Jussi Pakkanen <jpakkane@gmail.com> | 2025-03-05 18:06:11 +0200 |
| commit | 9c252002658660c48e7b05cd280a4c751a54250c (patch) | |
| tree | 83b53aaeb890f85d5deb67b25aa95312e4a22774 | |
| parent | 51e51eb40c089cc594525864a981b8b9aca10646 (diff) | |
| download | meson-9c252002658660c48e7b05cd280a4c751a54250c.tar.gz | |
options: Remove BuiltinOption class
It basically served two purposes, to allow us to initialize UserOptions,
and to convert those options into Argparse values. The latter has been
transformed into a standalone function, the former can be easily
achieved without this class. This reduces LOC, simplifies setup, and
helps with our type safety.
| -rw-r--r-- | mesonbuild/coredata.py | 19 | ||||
| -rw-r--r-- | mesonbuild/options.py | 196 | ||||
| -rw-r--r-- | unittests/datatests.py | 10 |
3 files changed, 102 insertions, 123 deletions
diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index fb2c07521..cf3262146 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -17,6 +17,7 @@ from .mesonlib import ( MesonBugException, MesonException, MachineChoice, PerMachine, PerMachineDefaultable, + default_prefix, stringlistify, pickle_load ) @@ -363,19 +364,23 @@ class CoreData: @staticmethod def add_builtin_option(optstore: options.OptionStore, key: OptionKey, - opt: 'options.BuiltinOption') -> None: + opt: options.AnyOptionType) -> None: + # Create a copy of the object, as we're going to mutate it + opt = copy.copy(opt) if key.subproject: if opt.yielding: # This option is global and not per-subproject return - value = optstore.get_value(key.as_root()) else: - value = None - if key.has_module_prefix(): - modulename = key.get_module_prefix() - optstore.add_module_option(modulename, key, opt.init_option(key, value, options.default_prefix())) + new_value = options.argparse_prefixed_default( + opt, key, default_prefix()) + opt.set_value(new_value) + + modulename = key.get_module_prefix() + if modulename: + optstore.add_module_option(modulename, key, opt) else: - optstore.add_system_option(key, opt.init_option(key, value, options.default_prefix())) + optstore.add_system_option(key, opt) def init_backend_options(self, backend_name: str) -> None: if backend_name == 'ninja': diff --git a/mesonbuild/options.py b/mesonbuild/options.py index 05a6c2835..d7997ffcc 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -302,12 +302,6 @@ class OptionKey: return self.name.split('.', 1)[0] return None - def without_module_prefix(self) -> 'OptionKey': - if self.has_module_prefix(): - newname = self.name.split('.', 1)[1] - return self.evolve(newname) - return self - def is_for_build(self) -> bool: return self.machine is MachineChoice.BUILD @@ -635,60 +629,32 @@ class UserStdOption(UserComboOption): f'Possible values for option "{self.name}" are {self.choices}') -class BuiltinOption(T.Generic[_T]): - - """Class for a builtin option type. - - There are some cases that are not fully supported yet. - """ - - def __init__(self, opt_type: T.Type[UserOption[_T]], description: str, default: T.Any, yielding: bool = True, *, - choices: T.Any = None, readonly: bool = False, **kwargs: object): - self.opt_type = opt_type - self.description = description - self.default = default - self.choices = choices - self.yielding = yielding - self.readonly = readonly - self.kwargs = kwargs - - def init_option(self, name: 'OptionKey', value: T.Optional[T.Any], prefix: str) -> UserOption[_T]: - """Create an instance of opt_type and return it.""" - if value is None: - value = _argparse_prefixed_default(self, name, prefix) - keywords = {'yielding': self.yielding, 'value_': value} - keywords.update(self.kwargs) - if self.choices: - keywords['choices'] = self.choices - # This will be fixed when the BuiltinOption class is deleted - o = self.opt_type(name.name, self.description, **keywords) # type: ignore - o.readonly = self.readonly - return o - - def argparse_name_to_arg(name: str) -> str: if name == 'warning_level': return '--warnlevel' return '--' + name.replace('_', '-') -def _argparse_prefixed_default(opt: BuiltinOption, name: OptionKey, prefix: str = '') -> ElementaryOptionValues: - if opt.opt_type in {UserComboOption, UserIntegerOption, UserUmaskOption}: - return opt.default +def argparse_prefixed_default(opt: AnyOptionType, name: OptionKey, prefix: str = '') -> ElementaryOptionValues: + if isinstance(opt, (UserComboOption, UserIntegerOption, UserUmaskOption)): + return T.cast('ElementaryOptionValues', opt.default) try: return BUILTIN_DIR_NOPREFIX_OPTIONS[name][prefix] except KeyError: return T.cast('ElementaryOptionValues', opt.default) -def option_to_argparse(option: BuiltinOption, name: OptionKey, parser: argparse.ArgumentParser, help_suffix: str) -> None: +def option_to_argparse(option: AnyOptionType, name: OptionKey, parser: argparse.ArgumentParser, help_suffix: str) -> None: kwargs: ArgparseKWs = {} - c = option.choices + if isinstance(option, (EnumeratedUserOption, UserArrayOption)): + c = option.choices + else: + c = None b = 'store_true' if isinstance(option.default, bool) else None h = option.description if not b: - h = '{} (default: {}).'.format(h.rstrip('.'), _argparse_prefixed_default(option, name)) + h = '{} (default: {}).'.format(h.rstrip('.'), argparse_prefixed_default(option, name)) else: kwargs['action'] = b if c and not b: @@ -703,79 +669,79 @@ def option_to_argparse(option: BuiltinOption, name: OptionKey, parser: argparse. # Update `docs/markdown/Builtin-options.md` after changing the options below # Also update mesonlib._BUILTIN_NAMES. See the comment there for why this is required. # Please also update completion scripts in $MESONSRC/data/shell-completions/ -BUILTIN_DIR_OPTIONS: T.Dict['OptionKey', 'BuiltinOption'] = OrderedDict([ - (OptionKey('prefix'), BuiltinOption(UserStringOption, 'Installation prefix', default_prefix())), - (OptionKey('bindir'), BuiltinOption(UserStringOption, 'Executable directory', 'bin')), - (OptionKey('datadir'), BuiltinOption(UserStringOption, 'Data file directory', default_datadir())), - (OptionKey('includedir'), BuiltinOption(UserStringOption, 'Header file directory', default_includedir())), - (OptionKey('infodir'), BuiltinOption(UserStringOption, 'Info page directory', default_infodir())), - (OptionKey('libdir'), BuiltinOption(UserStringOption, 'Library directory', default_libdir())), - (OptionKey('licensedir'), BuiltinOption(UserStringOption, 'Licenses directory', '')), - (OptionKey('libexecdir'), BuiltinOption(UserStringOption, 'Library executable directory', default_libexecdir())), - (OptionKey('localedir'), BuiltinOption(UserStringOption, 'Locale data directory', default_localedir())), - (OptionKey('localstatedir'), BuiltinOption(UserStringOption, 'Localstate data directory', 'var')), - (OptionKey('mandir'), BuiltinOption(UserStringOption, 'Manual page directory', default_mandir())), - (OptionKey('sbindir'), BuiltinOption(UserStringOption, 'System executable directory', default_sbindir())), - (OptionKey('sharedstatedir'), BuiltinOption(UserStringOption, 'Architecture-independent data directory', 'com')), - (OptionKey('sysconfdir'), BuiltinOption(UserStringOption, 'Sysconf data directory', default_sysconfdir())), -]) - -BUILTIN_CORE_OPTIONS: T.Dict['OptionKey', 'BuiltinOption'] = OrderedDict([ - (OptionKey('auto_features'), BuiltinOption(UserFeatureOption, "Override value of all 'auto' features", 'auto')), - (OptionKey('backend'), BuiltinOption(UserComboOption, 'Backend to use', 'ninja', choices=backendlist, - readonly=True)), - (OptionKey('genvslite'), - BuiltinOption( - UserComboOption, - 'Setup multiple buildtype-suffixed ninja-backend build directories, ' - 'and a [builddir]_vs containing a Visual Studio meta-backend with multiple configurations that calls into them', - 'vs2022', - choices=genvslitelist) - ), - (OptionKey('buildtype'), BuiltinOption(UserComboOption, 'Build type to use', 'debug', - choices=buildtypelist)), - (OptionKey('debug'), BuiltinOption(UserBooleanOption, 'Enable debug symbols and other information', True)), - (OptionKey('default_library'), BuiltinOption(UserComboOption, 'Default library type', 'shared', choices=['shared', 'static', 'both'], - yielding=False)), - (OptionKey('default_both_libraries'), BuiltinOption(UserComboOption, 'Default library type for both_libraries', 'shared', choices=['shared', 'static', 'auto'])), - (OptionKey('errorlogs'), BuiltinOption(UserBooleanOption, "Whether to print the logs from failing tests", True)), - (OptionKey('install_umask'), BuiltinOption(UserUmaskOption, 'Default umask to apply on permissions of installed files', '022')), - (OptionKey('layout'), BuiltinOption(UserComboOption, 'Build directory layout', 'mirror', choices=['mirror', 'flat'])), - (OptionKey('optimization'), BuiltinOption(UserComboOption, 'Optimization level', '0', choices=['plain', '0', 'g', '1', '2', '3', 's'])), - (OptionKey('prefer_static'), BuiltinOption(UserBooleanOption, 'Whether to try static linking before shared linking', False)), - (OptionKey('stdsplit'), BuiltinOption(UserBooleanOption, 'Split stdout and stderr in test logs', True)), - (OptionKey('strip'), BuiltinOption(UserBooleanOption, 'Strip targets on install', False)), - (OptionKey('unity'), BuiltinOption(UserComboOption, 'Unity build', 'off', choices=['on', 'off', 'subprojects'])), - (OptionKey('unity_size'), BuiltinOption(UserIntegerOption, 'Unity block size', 4, min_value=2)), - (OptionKey('warning_level'), BuiltinOption(UserComboOption, 'Compiler warning level to use', '1', choices=['0', '1', '2', '3', 'everything'], yielding=False)), - (OptionKey('werror'), BuiltinOption(UserBooleanOption, 'Treat warnings as errors', False, yielding=False)), - (OptionKey('wrap_mode'), BuiltinOption(UserComboOption, 'Wrap mode', 'default', choices=['default', 'nofallback', 'nodownload', 'forcefallback', 'nopromote'])), - (OptionKey('force_fallback_for'), BuiltinOption(UserStringArrayOption, 'Force fallback for those subprojects', [])), - (OptionKey('vsenv'), BuiltinOption(UserBooleanOption, 'Activate Visual Studio environment', False, readonly=True)), - - # Pkgconfig module - (OptionKey('pkgconfig.relocatable'), - BuiltinOption(UserBooleanOption, 'Generate pkgconfig files as relocatable', False)), - - # Python module - (OptionKey('python.bytecompile'), - BuiltinOption(UserIntegerOption, 'Whether to compile bytecode', 0, min_value=-1, max_value=2)), - (OptionKey('python.install_env'), - BuiltinOption(UserComboOption, 'Which python environment to install to', 'prefix', choices=['auto', 'prefix', 'system', 'venv'])), - (OptionKey('python.platlibdir'), - BuiltinOption(UserStringOption, 'Directory for site-specific, platform-specific files.', '')), - (OptionKey('python.purelibdir'), - BuiltinOption(UserStringOption, 'Directory for site-specific, non-platform-specific files.', '')), - (OptionKey('python.allow_limited_api'), - BuiltinOption(UserBooleanOption, 'Whether to allow use of the Python Limited API', True)), -]) +BUILTIN_DIR_OPTIONS: T.Mapping[OptionKey, AnyOptionType] = { + OptionKey(o.name): o for o in [ + UserStringOption('prefix', 'Installation prefix', default_prefix()), + UserStringOption('bindir', 'Executable directory', 'bin'), + UserStringOption('datadir', 'Data file directory', default_datadir()), + UserStringOption('includedir', 'Header file directory', default_includedir()), + UserStringOption('infodir', 'Info page directory', default_infodir()), + UserStringOption('libdir', 'Library directory', default_libdir()), + UserStringOption('licensedir', 'Licenses directory', ''), + UserStringOption('libexecdir', 'Library executable directory', default_libexecdir()), + UserStringOption('localedir', 'Locale data directory', default_localedir()), + UserStringOption('localstatedir', 'Localstate data directory', 'var'), + UserStringOption('mandir', 'Manual page directory', default_mandir()), + UserStringOption('sbindir', 'System executable directory', default_sbindir()), + UserStringOption('sharedstatedir', 'Architecture-independent data directory', 'com'), + UserStringOption('sysconfdir', 'Sysconf data directory', default_sysconfdir()), + ] +} + +BUILTIN_CORE_OPTIONS: T.Mapping[OptionKey, AnyOptionType] = { + OptionKey(o.name): o for o in T.cast('T.List[AnyOptionType]', [ + UserFeatureOption('auto_features', "Override value of all 'auto' features", 'auto'), + UserComboOption('backend', 'Backend to use', 'ninja', choices=backendlist, readonly=True), + UserComboOption( + 'genvslite', + 'Setup multiple buildtype-suffixed ninja-backend build directories, ' + 'and a [builddir]_vs containing a Visual Studio meta-backend with multiple configurations that calls into them', + 'vs2022', + choices=genvslitelist + ), + UserComboOption('buildtype', 'Build type to use', 'debug', choices=buildtypelist), + UserBooleanOption('debug', 'Enable debug symbols and other information', True), + UserComboOption('default_library', 'Default library type', 'shared', choices=['shared', 'static', 'both'], + yielding=False), + UserComboOption('default_both_libraries', 'Default library type for both_libraries', 'shared', + choices=['shared', 'static', 'auto']), + UserBooleanOption('errorlogs', "Whether to print the logs from failing tests", True), + UserUmaskOption('install_umask', 'Default umask to apply on permissions of installed files', OctalInt(0o022)), + UserComboOption('layout', 'Build directory layout', 'mirror', choices=['mirror', 'flat']), + UserComboOption('optimization', 'Optimization level', '0', choices=['plain', '0', 'g', '1', '2', '3', 's']), + UserBooleanOption('prefer_static', 'Whether to try static linking before shared linking', False), + UserBooleanOption('stdsplit', 'Split stdout and stderr in test logs', True), + UserBooleanOption('strip', 'Strip targets on install', False), + UserComboOption('unity', 'Unity build', 'off', choices=['on', 'off', 'subprojects']), + UserIntegerOption('unity_size', 'Unity block size', 4, min_value=2), + UserComboOption('warning_level', 'Compiler warning level to use', '1', choices=['0', '1', '2', '3', 'everything'], + yielding=False), + UserBooleanOption('werror', 'Treat warnings as errors', False, yielding=False), + UserComboOption('wrap_mode', 'Wrap mode', 'default', choices=['default', 'nofallback', 'nodownload', 'forcefallback', 'nopromote']), + UserStringArrayOption('force_fallback_for', 'Force fallback for those subprojects', []), + UserBooleanOption('vsenv', 'Activate Visual Studio environment', False, readonly=True), + + # Pkgconfig module + UserBooleanOption('pkgconfig.relocatable', 'Generate pkgconfig files as relocatable', False), + + # Python module + UserIntegerOption('python.bytecompile', 'Whether to compile bytecode', 0, min_value=-1, max_value=2), + UserComboOption('python.install_env', 'Which python environment to install to', 'prefix', + choices=['auto', 'prefix', 'system', 'venv']), + UserStringOption('python.platlibdir', 'Directory for site-specific, platform-specific files.', ''), + UserStringOption('python.purelibdir', 'Directory for site-specific, non-platform-specific files.', ''), + UserBooleanOption('python.allow_limited_api', 'Whether to allow use of the Python Limited API', True), + ]) +} BUILTIN_OPTIONS = OrderedDict(chain(BUILTIN_DIR_OPTIONS.items(), BUILTIN_CORE_OPTIONS.items())) -BUILTIN_OPTIONS_PER_MACHINE: T.Dict['OptionKey', 'BuiltinOption'] = OrderedDict([ - (OptionKey('pkg_config_path'), BuiltinOption(UserStringArrayOption, 'List of additional paths for pkg-config to search', [])), - (OptionKey('cmake_prefix_path'), BuiltinOption(UserStringArrayOption, 'List of additional prefixes for cmake to search', [])), -]) +BUILTIN_OPTIONS_PER_MACHINE: T.Mapping[OptionKey, AnyOptionType] = { + OptionKey(o.name): o for o in [ + UserStringArrayOption('pkg_config_path', 'List of additional paths for pkg-config to search', []), + UserStringArrayOption('cmake_prefix_path', 'List of additional prefixes for cmake to search', []), + ] +} # Special prefix-dependent defaults for installation directories that reside in # a path outside of the prefix in FHS and common usage. @@ -1291,7 +1257,9 @@ class OptionStore: if prefix in prefix_mapping: new_value = prefix_mapping[prefix] else: - new_value = BUILTIN_OPTIONS[optkey].default + _v = BUILTIN_OPTIONS[optkey].default + assert isinstance(_v, str), 'for mypy' + new_value = _v valobj.set_value(new_value) self.options[OptionKey('prefix')].set_value(prefix) diff --git a/unittests/datatests.py b/unittests/datatests.py index cb6542db8..173f718ea 100644 --- a/unittests/datatests.py +++ b/unittests/datatests.py @@ -138,9 +138,15 @@ class DataTests(unittest.TestCase): self.assertEqual(len(found_entries & options), 0) found_entries |= options + # TODO: put the module name back in the OptionKey + def remove_module_name(key: OptionKey) -> OptionKey: + if '.' in key.name: + return key.evolve(name=key.name.split('.', 1)[1]) + return key + self.assertEqual(found_entries, { - *(str(k.without_module_prefix()) for k in mesonbuild.options.BUILTIN_OPTIONS), - *(str(k.without_module_prefix()) for k in mesonbuild.options.BUILTIN_OPTIONS_PER_MACHINE), + *(str(remove_module_name(k)) for k in mesonbuild.options.BUILTIN_OPTIONS), + *(str(remove_module_name(k)) for k in mesonbuild.options.BUILTIN_OPTIONS_PER_MACHINE), }) # Check that `buildtype` table inside `Core options` matches how |
