options: Add an EnumeratedUserOption class

This will allow us to take choices out of the UserOption class, which
doesn't actually use this attribute.
pull/14227/head
Dylan Baker 8 months ago committed by Jussi Pakkanen
parent 0e11b90d6f
commit ba3460eb11
  1. 12
      mesonbuild/cmake/interpreter.py
  2. 14
      mesonbuild/compilers/c.py
  3. 13
      mesonbuild/compilers/compilers.py
  4. 28
      mesonbuild/compilers/cpp.py
  5. 3
      mesonbuild/compilers/cuda.py
  6. 4
      mesonbuild/compilers/cython.py
  7. 15
      mesonbuild/compilers/fortran.py
  8. 2
      mesonbuild/compilers/rust.py
  9. 2
      mesonbuild/coredata.py
  10. 2
      mesonbuild/mconf.py
  11. 2
      mesonbuild/mintro.py
  12. 17
      mesonbuild/optinterpreter.py
  13. 61
      mesonbuild/options.py

@ -19,6 +19,7 @@ from .toolchain import CMakeToolchain, CMakeExecScope
from .traceparser import CMakeTraceParser
from .tracetargets import resolve_cmake_trace_targets
from .. import mlog, mesonlib
from .. import options
from ..mesonlib import MachineChoice, OrderedSet, path_is_in_root, relative_to_if_possible
from ..options import OptionKey
from ..mesondata import DataFile
@ -533,17 +534,12 @@ class ConverterTarget:
@lru_cache(maxsize=None)
def _all_lang_stds(self, lang: str) -> 'ImmutableListProtocol[str]':
try:
res = self.env.coredata.optstore.get_value_object(OptionKey(f'{lang}_std', machine=MachineChoice.BUILD)).choices
opt = self.env.coredata.optstore.get_value_object(OptionKey(f'{lang}_std', machine=MachineChoice.BUILD))
assert isinstance(opt, (options.UserStdOption, options.UserComboOption)), 'for mypy'
return opt.choices or []
except KeyError:
return []
# TODO: Get rid of this once we have proper typing for options
assert isinstance(res, list)
for i in res:
assert isinstance(i, str)
return res
def process_inter_target_dependencies(self) -> None:
# Move the dependencies from all TRANSFER_DEPENDENCIES_FROM to the target
to_process = list(self.depends)

@ -124,7 +124,7 @@ class ClangCCompiler(ClangCStds, ClangCompiler, CCompiler):
opts = super().get_options()
if self.info.is_windows() or self.info.is_cygwin():
key = self.form_compileropt_key('winlibs')
opts[key] = options.UserArrayOption(
opts[key] = options.UserStringArrayOption(
self.make_option_name(key),
'Standard Windows libraries to link against',
gnu_winlibs)
@ -257,7 +257,7 @@ class GnuCCompiler(GnuCStds, GnuCompiler, CCompiler):
opts = super().get_options()
if self.info.is_windows() or self.info.is_cygwin():
key = self.form_compileropt_key('winlibs')
opts[key] = options.UserArrayOption(
opts[key] = options.UserStringArrayOption(
self.make_option_name(key),
'Standard Windows libraries to link against',
gnu_winlibs)
@ -406,7 +406,7 @@ class VisualStudioLikeCCompilerMixin(CompilerMixinBase):
def get_options(self) -> MutableKeyedOptionDictType:
opts = super().get_options()
key = self.form_compileropt_key('winlibs')
opts[key] = options.UserArrayOption(
opts[key] = options.UserStringArrayOption(
self.make_option_name(key),
'Standard Windows libraries to link against',
msvc_winlibs)
@ -733,9 +733,7 @@ class MetrowerksCCompilerARM(MetrowerksCompiler, CCompiler):
def get_options(self) -> 'MutableKeyedOptionDictType':
opts = super().get_options()
c_stds = ['c99']
key = self.form_compileropt_key('std')
opts[key].choices = ['none'] + c_stds
self._update_language_stds(opts, ['c99'])
return opts
def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
@ -763,9 +761,7 @@ class MetrowerksCCompilerEmbeddedPowerPC(MetrowerksCompiler, CCompiler):
def get_options(self) -> 'MutableKeyedOptionDictType':
opts = super().get_options()
c_stds = ['c99']
key = self.form_compileropt_key('std')
opts[key].choices = ['none'] + c_stds
self._update_language_stds(opts, ['c99'])
return opts
def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:

@ -1359,6 +1359,15 @@ class Compiler(HoldableObject, metaclass=abc.ABCMeta):
def form_compileropt_key(self, basename: str) -> OptionKey:
return OptionKey(f'{self.language}_{basename}', machine=self.for_machine)
def _update_language_stds(self, opts: MutableKeyedOptionDictType, value: T.List[str]) -> None:
key = self.form_compileropt_key('std')
std = opts[key]
assert isinstance(std, (options.UserStdOption, options.UserComboOption)), 'for mypy'
if 'none' not in value:
value = ['none'] + value
std.choices = value
def get_global_options(lang: str,
comp: T.Type[Compiler],
for_machine: MachineChoice,
@ -1374,12 +1383,12 @@ def get_global_options(lang: str,
comp_options = env.options.get(comp_key, [])
link_options = env.options.get(largkey, [])
cargs = options.UserArrayOption(
cargs = options.UserStringArrayOption(
f'{lang}_{argkey.name}',
description + ' compiler',
comp_options, split_args=True, allow_dups=True)
largs = options.UserArrayOption(
largs = options.UserStringArrayOption(
f'{lang}_{largkey.name}',
description + ' linker',
link_options, split_args=True, allow_dups=True)

@ -243,7 +243,7 @@ class ClangCPPCompiler(_StdCPPLibMixin, ClangCPPStds, ClangCompiler, CPPCompiler
self.make_option_name(key),
'C++ exception handling type.',
'default',
['none', 'default', 'a', 's', 'sc'])
choices=['none', 'default', 'a', 's', 'sc'])
key = self.form_compileropt_key('rtti')
opts[key] = options.UserBooleanOption(
@ -259,7 +259,7 @@ class ClangCPPCompiler(_StdCPPLibMixin, ClangCPPStds, ClangCompiler, CPPCompiler
if self.info.is_windows() or self.info.is_cygwin():
key = self.form_compileropt_key('winlibs')
opts[key] = options.UserArrayOption(
opts[key] = options.UserStringArrayOption(
self.make_option_name(key),
'Standard Win libraries to link against',
gnu_winlibs)
@ -399,7 +399,7 @@ class ArmclangCPPCompiler(ArmclangCompiler, CPPCompiler):
self.make_option_name(key),
'C++ exception handling type.',
'default',
['none', 'default', 'a', 's', 'sc'])
choices=['none', 'default', 'a', 's', 'sc'])
key = self.form_compileropt_key('std')
std_opt = opts[key]
@ -449,7 +449,7 @@ class GnuCPPCompiler(_StdCPPLibMixin, GnuCPPStds, GnuCompiler, CPPCompiler):
self.make_option_name(key),
'C++ exception handling type.',
'default',
['none', 'default', 'a', 's', 'sc'])
choices=['none', 'default', 'a', 's', 'sc'])
key = self.form_compileropt_key('rtti')
opts[key] = options.UserBooleanOption(
@ -465,7 +465,7 @@ class GnuCPPCompiler(_StdCPPLibMixin, GnuCPPStds, GnuCompiler, CPPCompiler):
if self.info.is_windows() or self.info.is_cygwin():
key = key.evolve(name='cpp_winlibs')
opts[key] = options.UserArrayOption(
opts[key] = options.UserStringArrayOption(
self.make_option_name(key),
'Standard Win libraries to link against',
gnu_winlibs)
@ -578,7 +578,7 @@ class ElbrusCPPCompiler(ElbrusCompiler, CPPCompiler):
self.make_option_name(key),
'C++ exception handling type.',
'default',
['none', 'default', 'a', 's', 'sc'])
choices=['none', 'default', 'a', 's', 'sc'])
key = self.form_compileropt_key('debugstl')
opts[key] = options.UserBooleanOption(
@ -661,7 +661,7 @@ class IntelCPPCompiler(IntelGnuLikeCompiler, CPPCompiler):
self.make_option_name(key),
'C++ exception handling type.',
'default',
['none', 'default', 'a', 's', 'sc'])
choices=['none', 'default', 'a', 's', 'sc'])
key = self.form_compileropt_key('rtti')
opts[key] = options.UserBooleanOption(
@ -691,9 +691,7 @@ class IntelCPPCompiler(IntelGnuLikeCompiler, CPPCompiler):
c_stds += ['c++2a']
g_stds += ['gnu++2a']
std_opt = opts[self.form_compileropt_key('std')]
assert isinstance(std_opt, options.UserStdOption), 'for mypy'
std_opt.set_versions(c_stds + g_stds)
self._update_language_stds(opts, c_stds + g_stds)
return opts
def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
@ -754,7 +752,7 @@ class VisualStudioLikeCPPCompilerMixin(CompilerMixinBase):
self.make_option_name(key),
'C++ exception handling type.',
'default',
['none', 'default', 'a', 's', 'sc'])
choices=['none', 'default', 'a', 's', 'sc'])
key = self.form_compileropt_key('rtti')
opts[key] = options.UserBooleanOption(
@ -763,7 +761,7 @@ class VisualStudioLikeCPPCompilerMixin(CompilerMixinBase):
True)
key = self.form_compileropt_key('winlibs')
opts[key] = options.UserArrayOption(
opts[key] = options.UserStringArrayOption(
self.make_option_name(key),
'Standard Win libraries to link against',
msvc_winlibs)
@ -1040,8 +1038,7 @@ class MetrowerksCPPCompilerARM(MetrowerksCompiler, CPPCompiler):
def get_options(self) -> 'MutableKeyedOptionDictType':
opts = super().get_options()
key = self.form_compileropt_key('std')
opts[key].choices = ['none']
self._update_language_stds(opts, [])
return opts
def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
@ -1069,8 +1066,7 @@ class MetrowerksCPPCompilerEmbeddedPowerPC(MetrowerksCompiler, CPPCompiler):
def get_options(self) -> 'MutableKeyedOptionDictType':
opts = super().get_options()
key = self.form_compileropt_key('std')
opts[key].choices = ['none']
self._update_language_stds(opts, [])
return opts
def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:

@ -648,13 +648,12 @@ class CudaCompiler(Compiler):
opts = super().get_options()
# XXX: cpp_std is correct, the annotations are wrong
key = self.form_compileropt_key('std')
opts[key] = options.UserComboOption(
self.make_option_name(key),
'C++ language standard to use with CUDA',
'none',
cpp_stds)
choices=cpp_stds)
key = self.form_compileropt_key('ccbindir')
opts[key] = options.UserStringOption(

@ -74,14 +74,14 @@ class CythonCompiler(Compiler):
self.make_option_name(key),
'Python version to target',
'3',
['2', '3'])
choices=['2', '3'])
key = self.form_compileropt_key('language')
opts[key] = options.UserComboOption(
self.make_option_name(key),
'Output C or C++ files',
'c',
['c', 'cpp'])
choices=['c', 'cpp'])
return opts

@ -121,7 +121,7 @@ class FortranCompiler(CLikeCompiler, Compiler):
self.make_option_name(key),
'Fortran language standard to use',
'none',
['none'])
choices=['none'])
return opts
@ -281,8 +281,7 @@ class GnuFortranCompiler(GnuCompiler, FortranCompiler):
fortran_stds += ['f2008']
if version_compare(self.version, '>=8.0.0'):
fortran_stds += ['f2018']
key = self.form_compileropt_key('std')
opts[key].choices = ['none'] + fortran_stds
self._update_language_stds(opts, fortran_stds)
return opts
def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
@ -338,9 +337,7 @@ class ElbrusFortranCompiler(ElbrusCompiler, FortranCompiler):
def get_options(self) -> 'MutableKeyedOptionDictType':
opts = super().get_options()
fortran_stds = ['f95', 'f2003', 'f2008', 'gnu', 'legacy', 'f2008ts']
key = self.form_compileropt_key('std')
opts[key].choices = ['none'] + fortran_stds
self._update_language_stds(opts, ['f95', 'f2003', 'f2008', 'gnu', 'legacy', 'f2008ts'])
return opts
def get_module_outdir_args(self, path: str) -> T.List[str]:
@ -418,8 +415,7 @@ class IntelFortranCompiler(IntelGnuLikeCompiler, FortranCompiler):
def get_options(self) -> 'MutableKeyedOptionDictType':
opts = super().get_options()
key = self.form_compileropt_key('std')
opts[key].choices = ['none', 'legacy', 'f95', 'f2003', 'f2008', 'f2018']
self._update_language_stds(opts, ['none', 'legacy', 'f95', 'f2003', 'f2008', 'f2018'])
return opts
def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
@ -473,8 +469,7 @@ class IntelClFortranCompiler(IntelVisualStudioLikeCompiler, FortranCompiler):
def get_options(self) -> 'MutableKeyedOptionDictType':
opts = super().get_options()
key = self.form_compileropt_key('std')
opts[key].choices = ['none', 'legacy', 'f95', 'f2003', 'f2008', 'f2018']
self._update_language_stds(opts, ['none', 'legacy', 'f95', 'f2003', 'f2008', 'f2018'])
return opts
def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:

@ -241,7 +241,7 @@ class RustCompiler(Compiler):
self.make_option_name(key),
'Rust edition to use',
'none',
['none', '2015', '2018', '2021', '2024'])
choices=['none', '2015', '2018', '2021', '2024'])
return opts

@ -599,7 +599,7 @@ class CoreData:
oldval = self.optstore.get_value_object(key)
if type(oldval) is not type(value):
self.optstore.set_value(key, value.value)
elif oldval.choices != value.choices:
elif oldval.printable_choices() != value.printable_choices():
# If the choices have changed, use the new value, but attempt
# to keep the old options. If they are not valid keep the new
# defaults but warn.

@ -240,7 +240,7 @@ class Conf:
printable_value = '<inherited from main project>'
if isinstance(o, options.UserFeatureOption) and o.is_auto():
printable_value = auto.printable_value()
self.add_option(str(root), o.description, printable_value, o.choices)
self.add_option(str(root), o.description, printable_value, o.printable_choices())
def print_conf(self, pager: bool) -> None:
if pager:

@ -319,7 +319,7 @@ def list_buildoptions(coredata: cdata.CoreData, subprojects: T.Optional[T.List[s
typestr = 'combo'
elif isinstance(opt, options.UserIntegerOption):
typestr = 'integer'
elif isinstance(opt, options.UserArrayOption):
elif isinstance(opt, options.UserStringArrayOption):
typestr = 'array'
c = opt.printable_choices()
if c:

@ -213,7 +213,7 @@ class OptionInterpreter:
KwargInfo('value', str, default=''),
)
def string_parser(self, name: str, description: str, args: T.Tuple[bool, _DEPRECATED_ARGS], kwargs: StringArgs) -> options.UserOption:
return options.UserStringOption(name, description, kwargs['value'], None, *args)
return options.UserStringOption(name, description, kwargs['value'], *args)
@typed_kwargs(
'boolean option',
@ -239,7 +239,7 @@ class OptionInterpreter:
value = kwargs['value']
if value is None:
value = kwargs['choices'][0]
return options.UserComboOption(name, description, value, choices, *args)
return options.UserComboOption(name, description, value, *args, choices)
@typed_kwargs(
'integer option',
@ -255,7 +255,7 @@ class OptionInterpreter:
)
def integer_parser(self, name: str, description: str, args: T.Tuple[bool, _DEPRECATED_ARGS], kwargs: IntegerArgs) -> options.UserOption:
return options.UserIntegerOption(
name, description, kwargs['value'], None, *args, min_value=kwargs['min'], max_value=kwargs['max'])
name, description, kwargs['value'], *args, min_value=kwargs['min'], max_value=kwargs['max'])
@typed_kwargs(
'string array option',
@ -270,12 +270,11 @@ class OptionInterpreter:
FeatureDeprecated('String value for array option', '1.3.0').use(self.subproject)
else:
raise mesonlib.MesonException('Value does not define an array: ' + value)
# XXX: the value of choices is correct, the annotation is wrong.
# the annotation will be fixed in a later commit
return options.UserArrayOption(name, description, value,
choices=choices, # type: ignore[arg-type]
yielding=args[0],
deprecated=args[1])
return options.UserStringArrayOption(
name, description, value,
choices=choices,
yielding=args[0],
deprecated=args[1])
@typed_kwargs(
'feature option',

@ -253,7 +253,6 @@ class UserOption(T.Generic[_T], HoldableObject):
name: str
description: str
value_: dataclasses.InitVar[_T]
choices: T.Optional[T.Union[str, T.List[_T]]] = None
yielding: bool = DEFAULT_YIELDING
deprecated: DeprecatedType = False
readonly: bool = dataclasses.field(default=False, init=False)
@ -269,11 +268,7 @@ class UserOption(T.Generic[_T], HoldableObject):
return self.value
def printable_choices(self) -> T.Optional[T.List[str]]:
if not self.choices:
return None
if isinstance(self.choices, str):
return [self.choices]
return [str(c) for c in self.choices]
return None
# Check that the input is a valid value and return the
# "cleaned" or "native" version. For example the Boolean
@ -287,6 +282,17 @@ class UserOption(T.Generic[_T], HoldableObject):
return self.value != oldvalue
@dataclasses.dataclass
class EnumeratedUserOption(UserOption[_T]):
"""A generic UserOption that has enumerated values."""
choices: T.List[_T] = dataclasses.field(default_factory=list)
def printable_choices(self) -> T.Optional[T.List[str]]:
return [str(c) for c in self.choices]
@dataclasses.dataclass
class UserStringOption(UserOption[str]):
@ -296,7 +302,7 @@ class UserStringOption(UserOption[str]):
return value
@dataclasses.dataclass
class UserBooleanOption(UserOption[bool]):
class UserBooleanOption(EnumeratedUserOption[bool]):
choices: T.List[bool] = dataclasses.field(default_factory=lambda: [True, False])
@ -327,7 +333,10 @@ class UserIntegerOption(UserOption[int]):
choices.append(f'>= {self.min_value!s}')
if self.max_value is not None:
choices.append(f'<= {self.max_value!s}')
self.choices = ', '.join(choices)
self.__choices: str = ', '.join(choices)
def printable_choices(self) -> T.Optional[T.List[str]]:
return [self.__choices]
def validate_value(self, value: T.Any) -> int:
if isinstance(value, str):
@ -376,7 +385,7 @@ class UserUmaskOption(UserIntegerOption, UserOption[T.Union[str, OctalInt]]):
raise MesonException(f'Invalid mode for option "{self.name}" {e}')
@dataclasses.dataclass
class UserComboOption(UserOption[str]):
class UserComboOption(EnumeratedUserOption[str]):
def validate_value(self, value: T.Any) -> str:
if value not in self.choices:
@ -390,15 +399,32 @@ class UserComboOption(UserOption[str]):
raise MesonException('Value "{}" (of type "{}") for option "{}" is not one of the choices.'
' Possible choices are (as string): {}.'.format(
value, _type, self.name, optionsstring))
assert isinstance(value, str), 'for mypy'
return value
@dataclasses.dataclass
class UserArrayOption(UserOption[T.List[str]]):
class UserArrayOption(UserOption[T.List[_T]]):
value_: dataclasses.InitVar[T.Union[str, T.List[str]]]
value_: dataclasses.InitVar[T.Union[_T, T.List[_T]]]
choices: T.Optional[T.List[_T]] = None
split_args: bool = False
allow_dups: bool = False
def extend_value(self, value: T.Union[str, T.List[str]]) -> None:
"""Extend the value with an additional value."""
new = self.validate_value(value)
self.set_value(self.value + new)
def printable_choices(self) -> T.Optional[T.List[str]]:
if self.choices is None:
return None
return [str(c) for c in self.choices]
@dataclasses.dataclass
class UserStringArrayOption(UserArrayOption[str]):
def listify(self, value: T.Any) -> T.List[T.Any]:
try:
return listify_array_value(value, self.split_args)
@ -427,11 +453,6 @@ class UserArrayOption(UserOption[T.List[str]]):
)
return newvalue
def extend_value(self, value: T.Union[str, T.List[str]]) -> None:
"""Extend the value with an additional value."""
new = self.validate_value(value)
self.set_value(self.value + new)
@dataclasses.dataclass
class UserFeatureOption(UserComboOption):
@ -471,7 +492,7 @@ class UserStdOption(UserComboOption):
# Map a deprecated std to its replacement. e.g. gnu11 -> c11.
self.deprecated_stds: T.Dict[str, str] = {}
opt_name = 'cpp_std' if lang == 'c++' else f'{lang}_std'
super().__init__(opt_name, f'{lang} language standard to use', 'none', ['none'])
super().__init__(opt_name, f'{lang} language standard to use', 'none', choices=['none'])
def set_versions(self, versions: T.List[str], gnu: bool = False, gnu_deprecated: bool = False) -> None:
assert all(std in self.all_stds for std in versions)
@ -637,7 +658,7 @@ BUILTIN_CORE_OPTIONS: T.Dict['OptionKey', 'BuiltinOption'] = OrderedDict([
(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(UserArrayOption, 'Force fallback for those subprojects', [])),
(OptionKey('force_fallback_for'), BuiltinOption(UserStringArrayOption, 'Force fallback for those subprojects', [])),
(OptionKey('vsenv'), BuiltinOption(UserBooleanOption, 'Activate Visual Studio environment', False, readonly=True)),
# Pkgconfig module
@ -660,8 +681,8 @@ BUILTIN_CORE_OPTIONS: T.Dict['OptionKey', 'BuiltinOption'] = OrderedDict([
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(UserArrayOption, 'List of additional paths for pkg-config to search', [])),
(OptionKey('cmake_prefix_path'), BuiltinOption(UserArrayOption, 'List of additional prefixes for cmake to search', [])),
(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', [])),
])
# Special prefix-dependent defaults for installation directories that reside in

Loading…
Cancel
Save