Remove module type from OptionKey.

pull/12487/head
Jussi Pakkanen 5 months ago
parent 61c742fae9
commit 6e20022295
  1. 6
      mesonbuild/coredata.py
  2. 7
      mesonbuild/mconf.py
  3. 10
      mesonbuild/modules/__init__.py
  4. 2
      mesonbuild/modules/pkgconfig.py
  5. 10
      mesonbuild/modules/python.py
  6. 78
      mesonbuild/options.py
  7. 4
      unittests/datatests.py

@ -422,7 +422,11 @@ class CoreData:
value = opts_map.get_value(key.as_root()) value = opts_map.get_value(key.as_root())
else: else:
value = None value = None
opts_map.add_system_option(key, opt.init_option(key, value, options.default_prefix())) if key.has_module_prefix():
modulename = key.get_module_prefix()
opts_map.add_module_option(modulename, key, opt.init_option(key, value, options.default_prefix()))
else:
opts_map.add_system_option(key, opt.init_option(key, value, options.default_prefix()))
def init_backend_options(self, backend_name: str) -> None: def init_backend_options(self, backend_name: str) -> None:
if backend_name == 'ninja': if backend_name == 'ninja':

@ -272,12 +272,13 @@ class Conf:
dir_options[k] = v dir_options[k] = v
elif k in test_option_names: elif k in test_option_names:
test_options[k] = v test_options[k] = v
elif k.module: elif k.has_module_prefix():
# Ignore module options if we did not use that module during # Ignore module options if we did not use that module during
# configuration. # configuration.
if self.build and k.module not in self.build.modules: modname = k.get_module_prefix()
if self.build and modname not in self.build.modules:
continue continue
module_options[k.module][k] = v module_options[modname][k] = v
elif self.coredata.optstore.is_builtin_option(k): elif self.coredata.optstore.is_builtin_option(k):
core_options[k] = v core_options[k] = v

@ -132,15 +132,13 @@ class ModuleState:
self._interpreter.func_test(self.current_node, real_args, kwargs) self._interpreter.func_test(self.current_node, real_args, kwargs)
def get_option(self, name: str, subproject: str = '', def get_option(self, name: str, subproject: str = '',
machine: MachineChoice = MachineChoice.HOST, machine: MachineChoice = MachineChoice.HOST) -> T.Union[T.List[str], str, int, bool]:
module: T.Optional[str] = None) -> T.Union[T.List[str], str, int, bool]: return self.environment.coredata.get_option(OptionKey(name, subproject, machine))
return self.environment.coredata.get_option(OptionKey(name, subproject, machine, module))
def is_user_defined_option(self, name: str, subproject: str = '', def is_user_defined_option(self, name: str, subproject: str = '',
machine: MachineChoice = MachineChoice.HOST, machine: MachineChoice = MachineChoice.HOST,
lang: T.Optional[str] = None, lang: T.Optional[str] = None) -> bool:
module: T.Optional[str] = None) -> bool: key = OptionKey(name, subproject, machine)
key = OptionKey(name, subproject, machine, module)
return key in self._interpreter.user_defined_options.cmd_line_options return key in self._interpreter.user_defined_options.cmd_line_options
def process_include_dirs(self, dirs: T.Iterable[T.Union[str, IncludeDirs]]) -> T.Iterable[IncludeDirs]: def process_include_dirs(self, dirs: T.Iterable[T.Union[str, IncludeDirs]]) -> T.Iterable[IncludeDirs]:

@ -703,7 +703,7 @@ class PkgConfigModule(NewExtensionModule):
else: else:
pkgroot = os.path.join(_as_str(state.environment.coredata.get_option(OptionKey('libdir'))), 'pkgconfig') pkgroot = os.path.join(_as_str(state.environment.coredata.get_option(OptionKey('libdir'))), 'pkgconfig')
pkgroot_name = os.path.join('{libdir}', 'pkgconfig') pkgroot_name = os.path.join('{libdir}', 'pkgconfig')
relocatable = state.get_option('relocatable', module='pkgconfig') relocatable = state.get_option('pkgconfig.relocatable')
self._generate_pkgconfig_file(state, deps, subdirs, name, description, url, self._generate_pkgconfig_file(state, deps, subdirs, name, description, url,
version, pcfile, conflicts, variables, version, pcfile, conflicts, variables,
unescaped_variables, False, dataonly, unescaped_variables, False, dataonly,

@ -83,13 +83,13 @@ class PythonExternalProgram(BasicPythonExternalProgram):
if not state: if not state:
# This happens only from run_project_tests.py # This happens only from run_project_tests.py
return rel_path return rel_path
value = T.cast('str', state.get_option(f'{key}dir', module='python')) value = T.cast('str', state.get_option(f'python.{key}dir'))
if value: if value:
if state.is_user_defined_option('install_env', module='python'): if state.is_user_defined_option('python.install_env'):
raise mesonlib.MesonException(f'python.{key}dir and python.install_env are mutually exclusive') raise mesonlib.MesonException(f'python.{key}dir and python.install_env are mutually exclusive')
return value return value
install_env = state.get_option('install_env', module='python') install_env = state.get_option('python.install_env')
if install_env == 'auto': if install_env == 'auto':
install_env = 'venv' if self.info['is_venv'] else 'system' install_env = 'venv' if self.info['is_venv'] else 'system'
@ -169,7 +169,7 @@ class PythonInstallation(_ExternalProgramHolder['PythonExternalProgram']):
self.current_node) self.current_node)
limited_api_version = kwargs.pop('limited_api') limited_api_version = kwargs.pop('limited_api')
allow_limited_api = self.interpreter.environment.coredata.get_option(OptionKey('allow_limited_api', module='python')) allow_limited_api = self.interpreter.environment.coredata.get_option(OptionKey('python.allow_limited_api'))
if limited_api_version != '' and allow_limited_api: if limited_api_version != '' and allow_limited_api:
target_suffix = self.limited_api_suffix target_suffix = self.limited_api_suffix
@ -374,7 +374,7 @@ class PythonModule(ExtensionModule):
def _get_install_scripts(self) -> T.List[mesonlib.ExecutableSerialisation]: def _get_install_scripts(self) -> T.List[mesonlib.ExecutableSerialisation]:
backend = self.interpreter.backend backend = self.interpreter.backend
ret = [] ret = []
optlevel = self.interpreter.environment.coredata.get_option(OptionKey('bytecompile', module='python')) optlevel = self.interpreter.environment.coredata.get_option(OptionKey('python.bytecompile'))
if optlevel == -1: if optlevel == -1:
return ret return ret
if not any(PythonExternalProgram.run_bytecompile.values()): if not any(PythonExternalProgram.run_bytecompile.values()):

@ -90,17 +90,15 @@ class OptionKey:
internally easier to reason about and produce. internally easier to reason about and produce.
""" """
__slots__ = ['name', 'subproject', 'machine', '_hash', 'module'] __slots__ = ['name', 'subproject', 'machine', '_hash']
name: str name: str
subproject: str subproject: str
machine: MachineChoice machine: MachineChoice
_hash: int _hash: int
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):
module: T.Optional[str] = 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
@ -108,8 +106,7 @@ class OptionKey:
object.__setattr__(self, 'name', name) object.__setattr__(self, 'name', name)
object.__setattr__(self, 'subproject', subproject) object.__setattr__(self, 'subproject', subproject)
object.__setattr__(self, 'machine', machine) object.__setattr__(self, 'machine', machine)
object.__setattr__(self, 'module', module) object.__setattr__(self, '_hash', hash((name, subproject, machine)))
object.__setattr__(self, '_hash', hash((name, subproject, machine, module)))
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.')
@ -119,7 +116,6 @@ class OptionKey:
'name': self.name, 'name': self.name,
'subproject': self.subproject, 'subproject': self.subproject,
'machine': self.machine, 'machine': self.machine,
'module': self.module,
} }
def __setstate__(self, state: T.Dict[str, T.Any]) -> None: def __setstate__(self, state: T.Dict[str, T.Any]) -> None:
@ -137,7 +133,7 @@ class OptionKey:
return self._hash return self._hash
def _to_tuple(self) -> T.Tuple[str, str, str, MachineChoice, str]: def _to_tuple(self) -> T.Tuple[str, str, str, MachineChoice, str]:
return (self.subproject, self.module or '', self.machine, self.name) return (self.subproject, self.machine, self.name)
def __eq__(self, other: object) -> bool: def __eq__(self, other: object) -> bool:
if isinstance(other, OptionKey): if isinstance(other, OptionKey):
@ -153,14 +149,12 @@ class OptionKey:
out = self.name out = self.name
if self.machine is MachineChoice.BUILD: if self.machine is MachineChoice.BUILD:
out = f'build.{out}' out = f'build.{out}'
if self.module:
out = f'{self.module}.{out}'
if self.subproject: if self.subproject:
out = f'{self.subproject}:{out}' out = f'{self.subproject}:{out}'
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.module!r})' return f'OptionKey({self.name!r}, {self.subproject!r}, {self.machine!r})'
@classmethod @classmethod
def from_string(cls, raw: str) -> 'OptionKey': def from_string(cls, raw: str) -> 'OptionKey':
@ -174,26 +168,24 @@ class OptionKey:
except ValueError: except ValueError:
subproject, raw2 = '', raw subproject, raw2 = '', raw
module = None
for_machine = MachineChoice.HOST for_machine = MachineChoice.HOST
try: try:
prefix, raw3 = raw2.split('.') prefix, raw3 = raw2.split('.')
if prefix == 'build': if prefix == 'build':
for_machine = MachineChoice.BUILD for_machine = MachineChoice.BUILD
else: else:
module = prefix raw3 = raw2
except ValueError: except ValueError:
raw3 = raw2 raw3 = raw2
opt = raw3 opt = raw3
assert ':' not in opt assert ':' not in opt
assert '.' not in opt assert opt.count('.') < 2
return cls(opt, subproject, for_machine, module) return cls(opt, subproject, for_machine)
def evolve(self, name: T.Optional[str] = None, subproject: T.Optional[str] = None, def evolve(self, name: T.Optional[str] = None, subproject: T.Optional[str] = None,
machine: T.Optional[MachineChoice] = None, machine: T.Optional[MachineChoice] = None) -> 'OptionKey':
module: T.Optional[str] = '') -> 'OptionKey':
"""Create a new copy of this key, but with altered members. """Create a new copy of this key, but with altered members.
For example: For example:
@ -208,7 +200,6 @@ class OptionKey:
name if name is not None else self.name, name if name is not None else self.name,
subproject if subproject is not None else self.subproject, subproject if subproject is not None else self.subproject,
machine if machine is not None else self.machine, machine if machine is not None else self.machine,
module if module != '' else self.module
) )
def as_root(self) -> 'OptionKey': def as_root(self) -> 'OptionKey':
@ -228,6 +219,20 @@ class OptionKey:
import sys import sys
sys.exit('FATAL internal error. This should not make it into an actual release. File a bug.') sys.exit('FATAL internal error. This should not make it into an actual release. File a bug.')
def has_module_prefix(self) -> bool:
return '.' in self.name
def get_module_prefix(self) -> T.Optional[str]:
if self.has_module_prefix():
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
class UserOption(T.Generic[_T], HoldableObject): class UserOption(T.Generic[_T], HoldableObject):
def __init__(self, name: str, description: str, choices: T.Optional[T.Union[str, T.List[_T]]], def __init__(self, name: str, description: str, choices: T.Optional[T.Union[str, T.List[_T]]],
@ -633,19 +638,19 @@ BUILTIN_CORE_OPTIONS: T.Dict['OptionKey', 'BuiltinOption'] = OrderedDict([
(OptionKey('vsenv'), BuiltinOption(UserBooleanOption, 'Activate Visual Studio environment', False, readonly=True)), (OptionKey('vsenv'), BuiltinOption(UserBooleanOption, 'Activate Visual Studio environment', False, readonly=True)),
# Pkgconfig module # Pkgconfig module
(OptionKey('relocatable', module='pkgconfig'), (OptionKey('pkgconfig.relocatable'),
BuiltinOption(UserBooleanOption, 'Generate pkgconfig files as relocatable', False)), BuiltinOption(UserBooleanOption, 'Generate pkgconfig files as relocatable', False)),
# Python module # Python module
(OptionKey('bytecompile', module='python'), (OptionKey('python.bytecompile'),
BuiltinOption(UserIntegerOption, 'Whether to compile bytecode', (-1, 2, 0))), BuiltinOption(UserIntegerOption, 'Whether to compile bytecode', (-1, 2, 0))),
(OptionKey('install_env', module='python'), (OptionKey('python.install_env'),
BuiltinOption(UserComboOption, 'Which python environment to install to', 'prefix', choices=['auto', 'prefix', 'system', 'venv'])), BuiltinOption(UserComboOption, 'Which python environment to install to', 'prefix', choices=['auto', 'prefix', 'system', 'venv'])),
(OptionKey('platlibdir', module='python'), (OptionKey('python.platlibdir'),
BuiltinOption(UserStringOption, 'Directory for site-specific, platform-specific files.', '')), BuiltinOption(UserStringOption, 'Directory for site-specific, platform-specific files.', '')),
(OptionKey('purelibdir', module='python'), (OptionKey('python.purelibdir'),
BuiltinOption(UserStringOption, 'Directory for site-specific, non-platform-specific files.', '')), BuiltinOption(UserStringOption, 'Directory for site-specific, non-platform-specific files.', '')),
(OptionKey('allow_limited_api', module='python'), (OptionKey('python.allow_limited_api'),
BuiltinOption(UserBooleanOption, 'Whether to allow use of the Python Limited API', True)), BuiltinOption(UserBooleanOption, 'Whether to allow use of the Python Limited API', True)),
]) ])
@ -662,8 +667,8 @@ BUILTIN_DIR_NOPREFIX_OPTIONS: T.Dict[OptionKey, T.Dict[str, str]] = {
OptionKey('sysconfdir'): {'/usr': '/etc'}, OptionKey('sysconfdir'): {'/usr': '/etc'},
OptionKey('localstatedir'): {'/usr': '/var', '/usr/local': '/var/local'}, OptionKey('localstatedir'): {'/usr': '/var', '/usr/local': '/var/local'},
OptionKey('sharedstatedir'): {'/usr': '/var/lib', '/usr/local': '/var/local/lib'}, OptionKey('sharedstatedir'): {'/usr': '/var/lib', '/usr/local': '/var/local/lib'},
OptionKey('platlibdir', module='python'): {}, OptionKey('python.platlibdir'): {},
OptionKey('purelibdir', module='python'): {}, OptionKey('python.purelibdir'): {},
} }
class OptionStore: class OptionStore:
@ -671,6 +676,7 @@ class OptionStore:
self.d: T.Dict['OptionKey', 'UserOption[T.Any]'] = {} self.d: T.Dict['OptionKey', 'UserOption[T.Any]'] = {}
self.project_options = set() self.project_options = set()
self.all_languages = set() self.all_languages = set()
self.module_options = set()
from .compilers import all_languages from .compilers import all_languages
for lang in all_languages: for lang in all_languages:
self.all_languages.add(lang) self.all_languages.add(lang)
@ -690,6 +696,12 @@ class OptionStore:
return self.get_value_object(key).value return self.get_value_object(key).value
def add_system_option(self, key: T.Union[OptionKey, str], valobj: 'UserOption[T.Any]'): def add_system_option(self, key: T.Union[OptionKey, str], valobj: 'UserOption[T.Any]'):
key = self.ensure_key(key)
if '.' in key.name:
raise MesonException(f'Internal error: non-module option has a period in its name {key.name}.')
self.add_system_option_internal(key, valobj)
def add_system_option_internal(self, key: T.Union[OptionKey, str], valobj: 'UserOption[T.Any]'):
key = self.ensure_key(key) key = self.ensure_key(key)
assert isinstance(valobj, UserOption) assert isinstance(valobj, UserOption)
self.d[key] = valobj self.d[key] = valobj
@ -705,6 +717,15 @@ class OptionStore:
self.d[key] = valobj self.d[key] = valobj
self.project_options.add(key) self.project_options.add(key)
def add_module_option(self, modulename: str, key: T.Union[OptionKey, str], valobj: 'UserOption[T.Any]'):
key = self.ensure_key(key)
if key.name.startswith('build.'):
raise MesonException('FATAL internal error: somebody goofed option handling.')
if not key.name.startswith(modulename + '.'):
raise MesonException('Internal error: module option name {key.name} does not start with module prefix {modulename}.')
self.add_system_option_internal(key, valobj)
self.module_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)
return self.d[key].set_value(new_value) return self.d[key].set_value(new_value)
@ -764,7 +785,7 @@ class OptionStore:
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.name in _BUILTIN_NAMES or key.module return key.name in _BUILTIN_NAMES or self.is_module_option(key)
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."""
@ -784,3 +805,6 @@ class OptionStore:
if prefix in self.all_languages: if prefix in self.all_languages:
return True return True
return False return False
def is_module_option(self, key: OptionKey) -> bool:
return key in self.module_options

@ -139,8 +139,8 @@ class DataTests(unittest.TestCase):
found_entries |= options found_entries |= options
self.assertEqual(found_entries, { self.assertEqual(found_entries, {
*(str(k.evolve(module=None)) for k in mesonbuild.options.BUILTIN_OPTIONS), *(str(k.without_module_prefix()) for k in mesonbuild.options.BUILTIN_OPTIONS),
*(str(k.evolve(module=None)) for k in mesonbuild.options.BUILTIN_OPTIONS_PER_MACHINE), *(str(k.without_module_prefix()) for k in mesonbuild.options.BUILTIN_OPTIONS_PER_MACHINE),
}) })
# Check that `buildtype` table inside `Core options` matches how # Check that `buildtype` table inside `Core options` matches how

Loading…
Cancel
Save