From 0d7bb776e2d97d406b726b90090bbfa8df13232b Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Sun, 30 Jun 2024 13:16:42 +0300 Subject: Move OptionKey in the option source file. --- mesonbuild/utils/universal.py | 292 ++++++++---------------------------------- 1 file changed, 53 insertions(+), 239 deletions(-) (limited to 'mesonbuild/utils/universal.py') diff --git a/mesonbuild/utils/universal.py b/mesonbuild/utils/universal.py index 4db209a73..b5310b8c3 100644 --- a/mesonbuild/utils/universal.py +++ b/mesonbuild/utils/universal.py @@ -15,7 +15,7 @@ import time import abc import platform, subprocess, operator, os, shlex, shutil, re import collections -from functools import lru_cache, wraps, total_ordering +from functools import lru_cache, wraps from itertools import tee from tempfile import TemporaryDirectory, NamedTemporaryFile import typing as T @@ -67,9 +67,7 @@ __all__ = [ 'EnvironmentException', 'FileOrString', 'GitException', - 'OptionKey', 'dump_conf_header', - 'OptionType', 'OrderedSet', 'PerMachine', 'PerMachineDefaultable', @@ -1981,7 +1979,58 @@ class LibType(enum.IntEnum): class ProgressBarFallback: # lgtm [py/iter-returns-non-self] ''' - Fallback progress bar implementation when tqdm is not found + Fallback progress bar implementation when tqdm is not foundclass OptionType(enum.IntEnum): + + """Enum used to specify what kind of argument a thing is.""" + + BUILTIN = 0 + BACKEND = 1 + BASE = 2 + COMPILER = 3 + PROJECT = 4 + +# This is copied from coredata. There is no way to share this, because this +# is used in the OptionKey constructor, and the coredata lists are +# OptionKeys... +_BUILTIN_NAMES = { + 'prefix', + 'bindir', + 'datadir', + 'includedir', + 'infodir', + 'libdir', + 'licensedir', + 'libexecdir', + 'localedir', + 'localstatedir', + 'mandir', + 'sbindir', + 'sharedstatedir', + 'sysconfdir', + 'auto_features', + 'backend', + 'buildtype', + 'debug', + 'default_library', + 'errorlogs', + 'genvslite', + 'install_umask', + 'layout', + 'optimization', + 'prefer_static', + 'stdsplit', + 'strip', + 'unity', + 'unity_size', + 'warning_level', + 'werror', + 'wrap_mode', + 'force_fallback_for', + 'pkg_config_path', + 'cmake_prefix_path', + 'vsenv', +} + Since this class is not an actual iterator, but only provides a minimal fallback, it is safe to ignore the 'Iterator does not return self from @@ -2157,241 +2206,6 @@ def generate_list(func: T.Callable[..., T.Generator[_T, None, None]]) -> T.Calla return wrapper -class OptionType(enum.IntEnum): - - """Enum used to specify what kind of argument a thing is.""" - - BUILTIN = 0 - BACKEND = 1 - BASE = 2 - COMPILER = 3 - PROJECT = 4 - -# This is copied from coredata. There is no way to share this, because this -# is used in the OptionKey constructor, and the coredata lists are -# OptionKeys... -_BUILTIN_NAMES = { - 'prefix', - 'bindir', - 'datadir', - 'includedir', - 'infodir', - 'libdir', - 'licensedir', - 'libexecdir', - 'localedir', - 'localstatedir', - 'mandir', - 'sbindir', - 'sharedstatedir', - 'sysconfdir', - 'auto_features', - 'backend', - 'buildtype', - 'debug', - 'default_library', - 'errorlogs', - 'genvslite', - 'install_umask', - 'layout', - 'optimization', - 'prefer_static', - 'stdsplit', - 'strip', - 'unity', - 'unity_size', - 'warning_level', - 'werror', - 'wrap_mode', - 'force_fallback_for', - 'pkg_config_path', - 'cmake_prefix_path', - 'vsenv', -} - - -def _classify_argument(key: 'OptionKey') -> OptionType: - """Classify arguments into groups so we know which dict to assign them to.""" - - if key.name.startswith('b_'): - return OptionType.BASE - elif key.lang is not None: - return OptionType.COMPILER - elif key.name in _BUILTIN_NAMES or key.module: - return OptionType.BUILTIN - elif key.name.startswith('backend_'): - assert key.machine is MachineChoice.HOST, str(key) - return OptionType.BACKEND - else: - assert key.machine is MachineChoice.HOST, str(key) - return OptionType.PROJECT - - -@total_ordering -class OptionKey: - - """Represents an option key in the various option dictionaries. - - This provides a flexible, powerful way to map option names from their - external form (things like subproject:build.option) to something that - internally easier to reason about and produce. - """ - - __slots__ = ['name', 'subproject', 'machine', 'lang', '_hash', 'type', 'module'] - - name: str - subproject: str - machine: MachineChoice - lang: T.Optional[str] - _hash: int - type: OptionType - module: T.Optional[str] - - def __init__(self, name: str, subproject: str = '', - machine: MachineChoice = MachineChoice.HOST, - lang: T.Optional[str] = None, - module: T.Optional[str] = None, - _type: T.Optional[OptionType] = None): - # the _type option to the constructor is kinda private. We want to be - # able tos ave the state and avoid the lookup function when - # pickling/unpickling, but we need to be able to calculate it when - # constructing a new OptionKey - object.__setattr__(self, 'name', name) - object.__setattr__(self, 'subproject', subproject) - object.__setattr__(self, 'machine', machine) - object.__setattr__(self, 'lang', lang) - object.__setattr__(self, 'module', module) - object.__setattr__(self, '_hash', hash((name, subproject, machine, lang, module))) - if _type is None: - _type = _classify_argument(self) - object.__setattr__(self, 'type', _type) - - def __setattr__(self, key: str, value: T.Any) -> None: - raise AttributeError('OptionKey instances do not support mutation.') - - def __getstate__(self) -> T.Dict[str, T.Any]: - return { - 'name': self.name, - 'subproject': self.subproject, - 'machine': self.machine, - 'lang': self.lang, - '_type': self.type, - 'module': self.module, - } - - def __setstate__(self, state: T.Dict[str, T.Any]) -> None: - """De-serialize the state of a pickle. - - This is very clever. __init__ is not a constructor, it's an - initializer, therefore it's safe to call more than once. We create a - state in the custom __getstate__ method, which is valid to pass - splatted to the initializer. - """ - # Mypy doesn't like this, because it's so clever. - self.__init__(**state) # type: ignore - - def __hash__(self) -> int: - return self._hash - - def _to_tuple(self) -> T.Tuple[str, OptionType, str, str, MachineChoice, str]: - return (self.subproject, self.type, self.lang or '', self.module or '', self.machine, self.name) - - def __eq__(self, other: object) -> bool: - if isinstance(other, OptionKey): - return self._to_tuple() == other._to_tuple() - return NotImplemented - - def __lt__(self, other: object) -> bool: - if isinstance(other, OptionKey): - return self._to_tuple() < other._to_tuple() - return NotImplemented - - def __str__(self) -> str: - out = self.name - if self.lang: - out = f'{self.lang}_{out}' - if self.machine is MachineChoice.BUILD: - out = f'build.{out}' - if self.module: - out = f'{self.module}.{out}' - if self.subproject: - out = f'{self.subproject}:{out}' - return out - - def __repr__(self) -> str: - return f'OptionKey({self.name!r}, {self.subproject!r}, {self.machine!r}, {self.lang!r}, {self.module!r}, {self.type!r})' - - @classmethod - def from_string(cls, raw: str) -> 'OptionKey': - """Parse the raw command line format into a three part tuple. - - This takes strings like `mysubproject:build.myoption` and Creates an - OptionKey out of them. - """ - try: - subproject, raw2 = raw.split(':') - except ValueError: - subproject, raw2 = '', raw - - module = None - for_machine = MachineChoice.HOST - try: - prefix, raw3 = raw2.split('.') - if prefix == 'build': - for_machine = MachineChoice.BUILD - else: - module = prefix - except ValueError: - raw3 = raw2 - - from ..compilers import all_languages - if any(raw3.startswith(f'{l}_') for l in all_languages): - lang, opt = raw3.split('_', 1) - else: - lang, opt = None, raw3 - assert ':' not in opt - assert '.' not in opt - - return cls(opt, subproject, for_machine, lang, module) - - def evolve(self, name: T.Optional[str] = None, subproject: T.Optional[str] = None, - machine: T.Optional[MachineChoice] = None, lang: T.Optional[str] = '', - module: T.Optional[str] = '') -> 'OptionKey': - """Create a new copy of this key, but with altered members. - - For example: - >>> a = OptionKey('foo', '', MachineChoice.Host) - >>> b = OptionKey('foo', 'bar', MachineChoice.Host) - >>> b == a.evolve(subproject='bar') - True - """ - # We have to be a little clever with lang here, because lang is valid - # as None, for non-compiler options - return OptionKey( - name if name is not None else self.name, - subproject if subproject is not None else self.subproject, - machine if machine is not None else self.machine, - lang if lang != '' else self.lang, - module if module != '' else self.module - ) - - def as_root(self) -> 'OptionKey': - """Convenience method for key.evolve(subproject='').""" - return self.evolve(subproject='') - - def as_build(self) -> 'OptionKey': - """Convenience method for key.evolve(machine=MachineChoice.BUILD).""" - return self.evolve(machine=MachineChoice.BUILD) - - def as_host(self) -> 'OptionKey': - """Convenience method for key.evolve(machine=MachineChoice.HOST).""" - return self.evolve(machine=MachineChoice.HOST) - - def is_project_hack_for_optionsview(self) -> bool: - """This method will be removed once we can delete OptionsView.""" - return self.type is OptionType.PROJECT - - def pickle_load(filename: str, object_name: str, object_type: T.Type[_PL], suggest_reconfigure: bool = True) -> _PL: load_fail_msg = f'{object_name} file {filename!r} is corrupted.' extra_msg = ' Consider reconfiguring the directory with "meson setup --reconfigure".' if suggest_reconfigure else '' -- cgit v1.2.3