Remove option type from OptionKey and get it from OptionStore instead.

pull/12487/head
Jussi Pakkanen 5 months ago
parent 978a58ea1e
commit de8e3d65e0
  1. 22
      mesonbuild/coredata.py
  2. 76
      mesonbuild/options.py
  3. 2
      unittests/allplatformstests.py
  4. 14
      unittests/internaltests.py

@ -22,7 +22,7 @@ from .mesonlib import (
pickle_load pickle_load
) )
from .options import OptionKey, OptionType from .options import OptionKey
from .machinefile import CmdLineFileParser from .machinefile import CmdLineFileParser
@ -586,8 +586,6 @@ class CoreData:
def update_project_options(self, project_options: 'MutableKeyedOptionDictType', subproject: SubProject) -> None: def update_project_options(self, project_options: 'MutableKeyedOptionDictType', subproject: SubProject) -> None:
for key, value in project_options.items(): for key, value in project_options.items():
if not self.optstore.is_project_option(key):
continue
if key not in self.optstore: if key not in self.optstore:
self.optstore.add_project_option(key, value) self.optstore.add_project_option(key, value)
continue continue
@ -654,7 +652,7 @@ class CoreData:
continue continue
elif k in self.optstore: elif k in self.optstore:
dirty |= self.set_option(k, v, first_invocation) dirty |= self.set_option(k, v, first_invocation)
elif k.machine != MachineChoice.BUILD and k.type != OptionType.COMPILER: elif k.machine != MachineChoice.BUILD and not self.optstore.is_compiler_option(k):
unknown_options.append(k) unknown_options.append(k)
if unknown_options: if unknown_options:
unknown_options_str = ', '.join(sorted(str(s) for s in unknown_options)) unknown_options_str = ', '.join(sorted(str(s) for s in unknown_options))
@ -700,9 +698,9 @@ class CoreData:
continue continue
# Skip base, compiler, and backend options, they are handled when # Skip base, compiler, and backend options, they are handled when
# adding languages and setting backend. # adding languages and setting backend.
if k.type in {OptionType.COMPILER, OptionType.BACKEND}: if self.optstore.is_compiler_option(k) or self.optstore.is_backend_option(k):
continue continue
if k.type == OptionType.BASE and k.as_root() in base_options: if self.optstore.is_base_option(k) and k.as_root() in base_options:
# set_options will report unknown base options # set_options will report unknown base options
continue continue
options[k] = v options[k] = v
@ -908,7 +906,17 @@ class OptionsView(abc.Mapping):
# FIXME: This is fundamentally the same algorithm than interpreter.get_option_internal(). # FIXME: This is fundamentally the same algorithm than interpreter.get_option_internal().
# We should try to share the code somehow. # We should try to share the code somehow.
key = key.evolve(subproject=self.subproject) key = key.evolve(subproject=self.subproject)
if not key.is_project_hack_for_optionsview(): if not isinstance(self.original_options, options.OptionStore):
# This is only used by CUDA currently.
# This entire class gets removed when option refactor
# is finished.
if '_' in key.name or key.lang is not None:
is_project_option = False
else:
sys.exit(f'FAIL {key}.')
else:
is_project_option = self.original_options.is_project_option(key)
if not is_project_option:
opt = self.original_options.get(key) opt = self.original_options.get(key)
if opt is None or opt.yielding: if opt is None or opt.yielding:
key2 = key.as_root() key2 = key.as_root()

@ -5,7 +5,6 @@ from collections import OrderedDict
from itertools import chain from itertools import chain
from functools import total_ordering from functools import total_ordering
import argparse import argparse
import enum
from .mesonlib import ( from .mesonlib import (
HoldableObject, HoldableObject,
@ -39,32 +38,6 @@ genvslitelist = ['vs2022']
buildtypelist = ['plain', 'debug', 'debugoptimized', 'release', 'minsize', 'custom'] buildtypelist = ['plain', 'debug', 'debugoptimized', 'release', 'minsize', 'custom']
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
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
# This is copied from coredata. There is no way to share this, because this # 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 # is used in the OptionKey constructor, and the coredata lists are
# OptionKeys... # OptionKeys...
@ -117,21 +90,19 @@ class OptionKey:
internally easier to reason about and produce. internally easier to reason about and produce.
""" """
__slots__ = ['name', 'subproject', 'machine', 'lang', '_hash', 'type', 'module'] __slots__ = ['name', 'subproject', 'machine', 'lang', '_hash', 'module']
name: str name: str
subproject: str subproject: str
machine: MachineChoice machine: MachineChoice
lang: T.Optional[str] lang: T.Optional[str]
_hash: int _hash: int
type: OptionType
module: T.Optional[str] module: T.Optional[str]
def __init__(self, name: str, subproject: str = '', def __init__(self, name: str, subproject: str = '',
machine: MachineChoice = MachineChoice.HOST, machine: MachineChoice = MachineChoice.HOST,
lang: T.Optional[str] = None, lang: T.Optional[str] = None,
module: 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 # the _type option to the constructor is kinda private. We want to be
# able tos ave the state and avoid the lookup function when # able tos ave the state and avoid the lookup function when
# pickling/unpickling, but we need to be able to calculate it when # pickling/unpickling, but we need to be able to calculate it when
@ -142,9 +113,6 @@ class OptionKey:
object.__setattr__(self, 'lang', lang) object.__setattr__(self, 'lang', lang)
object.__setattr__(self, 'module', module) object.__setattr__(self, 'module', module)
object.__setattr__(self, '_hash', hash((name, subproject, machine, lang, 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: def __setattr__(self, key: str, value: T.Any) -> None:
raise AttributeError('OptionKey instances do not support mutation.') raise AttributeError('OptionKey instances do not support mutation.')
@ -155,7 +123,6 @@ class OptionKey:
'subproject': self.subproject, 'subproject': self.subproject,
'machine': self.machine, 'machine': self.machine,
'lang': self.lang, 'lang': self.lang,
'_type': self.type,
'module': self.module, 'module': self.module,
} }
@ -173,8 +140,8 @@ class OptionKey:
def __hash__(self) -> int: def __hash__(self) -> int:
return self._hash return self._hash
def _to_tuple(self) -> T.Tuple[str, OptionType, str, str, MachineChoice, str]: def _to_tuple(self) -> T.Tuple[str, str, str, MachineChoice, str]:
return (self.subproject, self.type, self.lang or '', self.module or '', self.machine, self.name) return (self.subproject, self.lang or '', self.module or '', self.machine, self.name)
def __eq__(self, other: object) -> bool: def __eq__(self, other: object) -> bool:
if isinstance(other, OptionKey): if isinstance(other, OptionKey):
@ -199,7 +166,7 @@ class OptionKey:
return out return out
def __repr__(self) -> str: 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})' return f'OptionKey({self.name!r}, {self.subproject!r}, {self.machine!r}, {self.lang!r}, {self.module!r})'
@classmethod @classmethod
def from_string(cls, raw: str) -> 'OptionKey': def from_string(cls, raw: str) -> 'OptionKey':
@ -269,7 +236,8 @@ class OptionKey:
def is_project_hack_for_optionsview(self) -> bool: def is_project_hack_for_optionsview(self) -> bool:
"""This method will be removed once we can delete OptionsView.""" """This method will be removed once we can delete OptionsView."""
return self.type is OptionType.PROJECT import sys
sys.exit('FATAL internal error. This should not make it into an actual release. File a bug.')
class UserOption(T.Generic[_T], HoldableObject): class UserOption(T.Generic[_T], HoldableObject):
@ -712,6 +680,7 @@ BUILTIN_DIR_NOPREFIX_OPTIONS: T.Dict[OptionKey, T.Dict[str, str]] = {
class OptionStore: class OptionStore:
def __init__(self): def __init__(self):
self.d: T.Dict['OptionKey', 'UserOption[T.Any]'] = {} self.d: T.Dict['OptionKey', 'UserOption[T.Any]'] = {}
self.project_options = set()
def __len__(self): def __len__(self):
return len(self.d) return len(self.d)
@ -734,6 +703,7 @@ class OptionStore:
def add_project_option(self, key: T.Union[OptionKey, str], valobj: 'UserOption[T.Any]'): def add_project_option(self, key: T.Union[OptionKey, str], valobj: 'UserOption[T.Any]'):
key = self.ensure_key(key) key = self.ensure_key(key)
self.d[key] = valobj self.d[key] = valobj
self.project_options.add(key)
def set_value(self, key: T.Union[OptionKey, str], new_value: 'T.Any') -> bool: def set_value(self, key: T.Union[OptionKey, str], new_value: 'T.Any') -> bool:
key = self.ensure_key(key) key = self.ensure_key(key)
@ -774,23 +744,39 @@ class OptionStore:
def is_project_option(self, key: OptionKey) -> bool: def is_project_option(self, key: OptionKey) -> bool:
"""Convenience method to check if this is a project option.""" """Convenience method to check if this is a project option."""
return key.type is OptionType.PROJECT return key in self.project_options
def is_reserved_name(self, key: OptionKey) -> bool: def is_reserved_name(self, key: OptionKey) -> bool:
return not self.is_project_option(key) if key.name in _BUILTIN_NAMES:
return True
# FIXME, this hack is needed until the lang field is removed from OptionKey.
if key.lang is not None:
return True
if '_' not in key.name:
return False
prefix = key.name.split('_')[0]
# Pylint seems to think that it is faster to build a set object
# and all related work just to test whether a string has one of two
# values. It is not, thank you very much.
if prefix in ('b', 'backend'): # pylint: disable=R6201
return True
from .compilers import all_languages
if prefix in all_languages:
return True
return False
def is_builtin_option(self, key: OptionKey) -> bool: def is_builtin_option(self, key: OptionKey) -> bool:
"""Convenience method to check if this is a builtin option.""" """Convenience method to check if this is a builtin option."""
return key.type is OptionType.BUILTIN return key.name in _BUILTIN_NAMES or key.module
def is_base_option(self, key: OptionKey) -> bool: def is_base_option(self, key: OptionKey) -> bool:
"""Convenience method to check if this is a base option.""" """Convenience method to check if this is a base option."""
return key.type is OptionType.BASE return key.name.startswith('b_')
def is_backend_option(self, key: OptionKey) -> bool: def is_backend_option(self, key: OptionKey) -> bool:
"""Convenience method to check if this is a backend option.""" """Convenience method to check if this is a backend option."""
return key.type is OptionType.BACKEND return key.name.startswith('backend_')
def is_compiler_option(self, key: OptionKey) -> bool: def is_compiler_option(self, key: OptionKey) -> bool:
"""Convenience method to check if this is a compiler option.""" """Convenience method to check if this is a compiler option."""
return key.type is OptionType.COMPILER return key.lang is not None

@ -3758,9 +3758,9 @@ class AllPlatformTests(BasePlatformTests):
User defined options User defined options
backend : ''' + self.backend_name + ''' backend : ''' + self.backend_name + '''
enabled_opt : enabled
libdir : lib libdir : lib
prefix : /usr prefix : /usr
enabled_opt : enabled
python : ''' + sys.executable + ''' python : ''' + sys.executable + '''
''') ''')
expected_lines = expected.split('\n')[1:] expected_lines = expected.split('\n')[1:]

@ -34,7 +34,7 @@ from mesonbuild.mesonlib import (
LibType, MachineChoice, PerMachine, Version, is_windows, is_osx, LibType, MachineChoice, PerMachine, Version, is_windows, is_osx,
is_cygwin, is_openbsd, search_version, MesonException, is_cygwin, is_openbsd, search_version, MesonException,
) )
from mesonbuild.options import OptionKey, OptionType from mesonbuild.options import OptionKey
from mesonbuild.interpreter.type_checking import in_set_validator, NoneType from mesonbuild.interpreter.type_checking import in_set_validator, NoneType
from mesonbuild.dependencies.pkgconfig import PkgConfigDependency, PkgConfigInterface, PkgConfigCLI from mesonbuild.dependencies.pkgconfig import PkgConfigDependency, PkgConfigInterface, PkgConfigCLI
from mesonbuild.programs import ExternalProgram from mesonbuild.programs import ExternalProgram
@ -1704,16 +1704,16 @@ class InternalTests(unittest.TestCase):
def test_option_key_from_string(self) -> None: def test_option_key_from_string(self) -> None:
cases = [ cases = [
('c_args', OptionKey('args', lang='c', _type=OptionType.COMPILER)), ('c_args', OptionKey('args', lang='c')),
('build.cpp_args', OptionKey('args', machine=MachineChoice.BUILD, lang='cpp', _type=OptionType.COMPILER)), ('build.cpp_args', OptionKey('args', machine=MachineChoice.BUILD, lang='cpp')),
('prefix', OptionKey('prefix', _type=OptionType.BUILTIN)), ('prefix', OptionKey('prefix')),
('made_up', OptionKey('made_up', _type=OptionType.PROJECT)), ('made_up', OptionKey('made_up')),
# TODO: the from_String method should be splitting the prefix off of # TODO: the from_String method should be splitting the prefix off of
# these, as we have the type already, but it doesn't. For now have a # these, as we have the type already, but it doesn't. For now have a
# test so that we don't change the behavior un-intentionally # test so that we don't change the behavior un-intentionally
('b_lto', OptionKey('b_lto', _type=OptionType.BASE)), ('b_lto', OptionKey('b_lto')),
('backend_startup_project', OptionKey('backend_startup_project', _type=OptionType.BACKEND)), ('backend_startup_project', OptionKey('backend_startup_project')),
] ]
for raw, expected in cases: for raw, expected in cases:

Loading…
Cancel
Save