|
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
|
|
# Copyright 2013-2024 Contributors to the The Meson project
|
|
|
|
|
|
|
|
from collections import OrderedDict
|
|
|
|
from itertools import chain
|
|
|
|
import argparse
|
|
|
|
|
|
|
|
from .mesonlib import (
|
|
|
|
HoldableObject,
|
|
|
|
OptionKey,
|
|
|
|
default_prefix,
|
|
|
|
default_datadir,
|
|
|
|
default_includedir,
|
|
|
|
default_infodir,
|
|
|
|
default_libdir,
|
|
|
|
default_libexecdir,
|
|
|
|
default_localedir,
|
|
|
|
default_mandir,
|
|
|
|
default_sbindir,
|
|
|
|
default_sysconfdir,
|
|
|
|
MesonException,
|
|
|
|
listify_array_value,
|
|
|
|
)
|
|
|
|
|
|
|
|
from . import mlog
|
|
|
|
|
|
|
|
import typing as T
|
|
|
|
from typing import ItemsView
|
|
|
|
|
|
|
|
DEFAULT_YIELDING = False
|
|
|
|
|
|
|
|
# Can't bind this near the class method it seems, sadly.
|
|
|
|
_T = T.TypeVar('_T')
|
|
|
|
|
|
|
|
backendlist = ['ninja', 'vs', 'vs2010', 'vs2012', 'vs2013', 'vs2015', 'vs2017', 'vs2019', 'vs2022', 'xcode', 'none']
|
|
|
|
genvslitelist = ['vs2022']
|
|
|
|
buildtypelist = ['plain', 'debug', 'debugoptimized', 'release', 'minsize', 'custom']
|
|
|
|
|
|
|
|
|
|
|
|
class UserOption(T.Generic[_T], HoldableObject):
|
|
|
|
def __init__(self, name: str, description: str, choices: T.Optional[T.Union[str, T.List[_T]]],
|
|
|
|
yielding: bool,
|
|
|
|
deprecated: T.Union[bool, str, T.Dict[str, str], T.List[str]] = False):
|
|
|
|
super().__init__()
|
|
|
|
self.name = name
|
|
|
|
self.choices = choices
|
|
|
|
self.description = description
|
|
|
|
if not isinstance(yielding, bool):
|
|
|
|
raise MesonException('Value of "yielding" must be a boolean.')
|
|
|
|
self.yielding = yielding
|
|
|
|
self.deprecated = deprecated
|
|
|
|
self.readonly = False
|
|
|
|
|
|
|
|
def listify(self, value: T.Any) -> T.List[T.Any]:
|
|
|
|
return [value]
|
|
|
|
|
|
|
|
def printable_value(self) -> T.Union[str, int, bool, T.List[T.Union[str, int, bool]]]:
|
|
|
|
assert isinstance(self.value, (str, int, bool, list))
|
|
|
|
return self.value
|
|
|
|
|
|
|
|
# Check that the input is a valid value and return the
|
|
|
|
# "cleaned" or "native" version. For example the Boolean
|
|
|
|
# option could take the string "true" and return True.
|
|
|
|
def validate_value(self, value: T.Any) -> _T:
|
|
|
|
raise RuntimeError('Derived option class did not override validate_value.')
|
|
|
|
|
|
|
|
def set_value(self, newvalue: T.Any) -> bool:
|
|
|
|
oldvalue = getattr(self, 'value', None)
|
|
|
|
self.value = self.validate_value(newvalue)
|
|
|
|
return self.value != oldvalue
|
|
|
|
|
|
|
|
_U = T.TypeVar('_U', bound=UserOption[_T])
|
|
|
|
|
|
|
|
|
|
|
|
class UserStringOption(UserOption[str]):
|
|
|
|
def __init__(self, name: str, description: str, value: T.Any, yielding: bool = DEFAULT_YIELDING,
|
|
|
|
deprecated: T.Union[bool, str, T.Dict[str, str], T.List[str]] = False):
|
|
|
|
super().__init__(name, description, None, yielding, deprecated)
|
|
|
|
self.set_value(value)
|
|
|
|
|
|
|
|
def validate_value(self, value: T.Any) -> str:
|
|
|
|
if not isinstance(value, str):
|
|
|
|
raise MesonException(f'The value of option "{self.name}" is "{value}", which is not a string.')
|
|
|
|
return value
|
|
|
|
|
|
|
|
class UserBooleanOption(UserOption[bool]):
|
|
|
|
def __init__(self, name: str, description: str, value: bool, yielding: bool = DEFAULT_YIELDING,
|
|
|
|
deprecated: T.Union[bool, str, T.Dict[str, str], T.List[str]] = False):
|
|
|
|
super().__init__(name, description, [True, False], yielding, deprecated)
|
|
|
|
self.set_value(value)
|
|
|
|
|
|
|
|
def __bool__(self) -> bool:
|
|
|
|
return self.value
|
|
|
|
|
|
|
|
def validate_value(self, value: T.Any) -> bool:
|
|
|
|
if isinstance(value, bool):
|
|
|
|
return value
|
|
|
|
if not isinstance(value, str):
|
|
|
|
raise MesonException(f'Option "{self.name}" value {value} cannot be converted to a boolean')
|
|
|
|
if value.lower() == 'true':
|
|
|
|
return True
|
|
|
|
if value.lower() == 'false':
|
|
|
|
return False
|
|
|
|
raise MesonException(f'Option "{self.name}" value {value} is not boolean (true or false).')
|
|
|
|
|
|
|
|
class UserIntegerOption(UserOption[int]):
|
|
|
|
def __init__(self, name: str, description: str, value: T.Any, yielding: bool = DEFAULT_YIELDING,
|
|
|
|
deprecated: T.Union[bool, str, T.Dict[str, str], T.List[str]] = False):
|
|
|
|
min_value, max_value, default_value = value
|
|
|
|
self.min_value = min_value
|
|
|
|
self.max_value = max_value
|
|
|
|
c: T.List[str] = []
|
|
|
|
if min_value is not None:
|
|
|
|
c.append('>=' + str(min_value))
|
|
|
|
if max_value is not None:
|
|
|
|
c.append('<=' + str(max_value))
|
|
|
|
choices = ', '.join(c)
|
|
|
|
super().__init__(name, description, choices, yielding, deprecated)
|
|
|
|
self.set_value(default_value)
|
|
|
|
|
|
|
|
def validate_value(self, value: T.Any) -> int:
|
|
|
|
if isinstance(value, str):
|
|
|
|
value = self.toint(value)
|
|
|
|
if not isinstance(value, int):
|
|
|
|
raise MesonException(f'Value {value!r} for option "{self.name}" is not an integer.')
|
|
|
|
if self.min_value is not None and value < self.min_value:
|
|
|
|
raise MesonException(f'Value {value} for option "{self.name}" is less than minimum value {self.min_value}.')
|
|
|
|
if self.max_value is not None and value > self.max_value:
|
|
|
|
raise MesonException(f'Value {value} for option "{self.name}" is more than maximum value {self.max_value}.')
|
|
|
|
return value
|
|
|
|
|
|
|
|
def toint(self, valuestring: str) -> int:
|
|
|
|
try:
|
|
|
|
return int(valuestring)
|
|
|
|
except ValueError:
|
|
|
|
raise MesonException(f'Value string "{valuestring}" for option "{self.name}" is not convertible to an integer.')
|
|
|
|
|
|
|
|
class OctalInt(int):
|
|
|
|
# NinjaBackend.get_user_option_args uses str() to converts it to a command line option
|
|
|
|
# UserUmaskOption.toint() uses int(str, 8) to convert it to an integer
|
|
|
|
# So we need to use oct instead of dec here if we do not want values to be misinterpreted.
|
|
|
|
def __str__(self) -> str:
|
|
|
|
return oct(int(self))
|
|
|
|
|
|
|
|
class UserUmaskOption(UserIntegerOption, UserOption[T.Union[str, OctalInt]]):
|
|
|
|
def __init__(self, name: str, description: str, value: T.Any, yielding: bool = DEFAULT_YIELDING,
|
|
|
|
deprecated: T.Union[bool, str, T.Dict[str, str], T.List[str]] = False):
|
|
|
|
super().__init__(name, description, (0, 0o777, value), yielding, deprecated)
|
|
|
|
self.choices = ['preserve', '0000-0777']
|
|
|
|
|
|
|
|
def printable_value(self) -> str:
|
|
|
|
if self.value == 'preserve':
|
|
|
|
return self.value
|
|
|
|
return format(self.value, '04o')
|
|
|
|
|
|
|
|
def validate_value(self, value: T.Any) -> T.Union[str, OctalInt]:
|
|
|
|
if value == 'preserve':
|
|
|
|
return 'preserve'
|
|
|
|
return OctalInt(super().validate_value(value))
|
|
|
|
|
|
|
|
def toint(self, valuestring: T.Union[str, OctalInt]) -> int:
|
|
|
|
try:
|
|
|
|
return int(valuestring, 8)
|
|
|
|
except ValueError as e:
|
|
|
|
raise MesonException(f'Invalid mode for option "{self.name}" {e}')
|
|
|
|
|
|
|
|
class UserComboOption(UserOption[str]):
|
|
|
|
def __init__(self, name: str, description: str, choices: T.List[str], value: T.Any,
|
|
|
|
yielding: bool = DEFAULT_YIELDING,
|
|
|
|
deprecated: T.Union[bool, str, T.Dict[str, str], T.List[str]] = False):
|
|
|
|
super().__init__(name, description, choices, yielding, deprecated)
|
|
|
|
if not isinstance(self.choices, list):
|
|
|
|
raise MesonException(f'Combo choices for option "{self.name}" must be an array.')
|
|
|
|
for i in self.choices:
|
|
|
|
if not isinstance(i, str):
|
|
|
|
raise MesonException(f'Combo choice elements for option "{self.name}" must be strings.')
|
|
|
|
self.set_value(value)
|
|
|
|
|
|
|
|
def validate_value(self, value: T.Any) -> str:
|
|
|
|
if value not in self.choices:
|
|
|
|
if isinstance(value, bool):
|
|
|
|
_type = 'boolean'
|
|
|
|
elif isinstance(value, (int, float)):
|
|
|
|
_type = 'number'
|
|
|
|
else:
|
|
|
|
_type = 'string'
|
|
|
|
optionsstring = ', '.join([f'"{item}"' for item in self.choices])
|
|
|
|
raise MesonException('Value "{}" (of type "{}") for option "{}" is not one of the choices.'
|
|
|
|
' Possible choices are (as string): {}.'.format(
|
|
|
|
value, _type, self.name, optionsstring))
|
|
|
|
return value
|
|
|
|
|
|
|
|
class UserArrayOption(UserOption[T.List[str]]):
|
|
|
|
def __init__(self, name: str, description: str, value: T.Union[str, T.List[str]],
|
|
|
|
split_args: bool = False,
|
|
|
|
allow_dups: bool = False, yielding: bool = DEFAULT_YIELDING,
|
|
|
|
choices: T.Optional[T.List[str]] = None,
|
|
|
|
deprecated: T.Union[bool, str, T.Dict[str, str], T.List[str]] = False):
|
|
|
|
super().__init__(name, description, choices if choices is not None else [], yielding, deprecated)
|
|
|
|
self.split_args = split_args
|
|
|
|
self.allow_dups = allow_dups
|
|
|
|
self.set_value(value)
|
|
|
|
|
|
|
|
def listify(self, value: T.Any) -> T.List[T.Any]:
|
|
|
|
try:
|
|
|
|
return listify_array_value(value, self.split_args)
|
|
|
|
except MesonException as e:
|
|
|
|
raise MesonException(f'error in option "{self.name}": {e!s}')
|
|
|
|
|
|
|
|
def validate_value(self, value: T.Union[str, T.List[str]]) -> T.List[str]:
|
|
|
|
newvalue = self.listify(value)
|
|
|
|
|
|
|
|
if not self.allow_dups and len(set(newvalue)) != len(newvalue):
|
|
|
|
msg = 'Duplicated values in array option is deprecated. ' \
|
|
|
|
'This will become a hard error in the future.'
|
|
|
|
mlog.deprecation(msg)
|
|
|
|
for i in newvalue:
|
|
|
|
if not isinstance(i, str):
|
|
|
|
raise MesonException(f'String array element "{newvalue!s}" for option "{self.name}" is not a string.')
|
|
|
|
if self.choices:
|
|
|
|
bad = [x for x in newvalue if x not in self.choices]
|
|
|
|
if bad:
|
|
|
|
raise MesonException('Value{} "{}" for option "{}" {} not in allowed choices: "{}"'.format(
|
|
|
|
'' if len(bad) == 1 else 's',
|
|
|
|
', '.join(bad),
|
|
|
|
self.name,
|
|
|
|
'is' if len(bad) == 1 else 'are',
|
|
|
|
', '.join(self.choices))
|
|
|
|
)
|
|
|
|
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)
|
|
|
|
|
|
|
|
|
|
|
|
class UserFeatureOption(UserComboOption):
|
|
|
|
static_choices = ['enabled', 'disabled', 'auto']
|
|
|
|
|
|
|
|
def __init__(self, name: str, description: str, value: T.Any, yielding: bool = DEFAULT_YIELDING,
|
|
|
|
deprecated: T.Union[bool, str, T.Dict[str, str], T.List[str]] = False):
|
|
|
|
super().__init__(name, description, self.static_choices, value, yielding, deprecated)
|
|
|
|
self.name: T.Optional[str] = None # TODO: Refactor options to all store their name
|
|
|
|
|
|
|
|
def is_enabled(self) -> bool:
|
|
|
|
return self.value == 'enabled'
|
|
|
|
|
|
|
|
def is_disabled(self) -> bool:
|
|
|
|
return self.value == 'disabled'
|
|
|
|
|
|
|
|
def is_auto(self) -> bool:
|
|
|
|
return self.value == 'auto'
|
|
|
|
|
|
|
|
class UserStdOption(UserComboOption):
|
|
|
|
'''
|
|
|
|
UserOption specific to c_std and cpp_std options. User can set a list of
|
|
|
|
STDs in preference order and it selects the first one supported by current
|
|
|
|
compiler.
|
|
|
|
|
|
|
|
For historical reasons, some compilers (msvc) allowed setting a GNU std and
|
|
|
|
silently fell back to C std. This is now deprecated. Projects that support
|
|
|
|
both GNU and MSVC compilers should set e.g. c_std=gnu11,c11.
|
|
|
|
|
|
|
|
This is not using self.deprecated mechanism we already have for project
|
|
|
|
options because we want to print a warning if ALL values are deprecated, not
|
|
|
|
if SOME values are deprecated.
|
|
|
|
'''
|
|
|
|
def __init__(self, lang: str, all_stds: T.List[str]) -> None:
|
|
|
|
self.lang = lang.lower()
|
|
|
|
self.all_stds = ['none'] + all_stds
|
|
|
|
# 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')
|
|
|
|
|
|
|
|
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)
|
|
|
|
self.choices += versions
|
|
|
|
if gnu:
|
|
|
|
gnu_stds_map = {f'gnu{std[1:]}': std for std in versions}
|
|
|
|
if gnu_deprecated:
|
|
|
|
self.deprecated_stds.update(gnu_stds_map)
|
|
|
|
else:
|
|
|
|
self.choices += gnu_stds_map.keys()
|
|
|
|
|
|
|
|
def validate_value(self, value: T.Union[str, T.List[str]]) -> str:
|
|
|
|
try:
|
|
|
|
candidates = listify_array_value(value)
|
|
|
|
except MesonException as e:
|
|
|
|
raise MesonException(f'error in option "{self.name}": {e!s}')
|
|
|
|
unknown = ','.join(std for std in candidates if std not in self.all_stds)
|
|
|
|
if unknown:
|
|
|
|
raise MesonException(f'Unknown option "{self.name}" value {unknown}. Possible values are {self.all_stds}.')
|
|
|
|
# Check first if any of the candidates are not deprecated
|
|
|
|
for std in candidates:
|
|
|
|
if std in self.choices:
|
|
|
|
return std
|
|
|
|
# Fallback to a deprecated std if any
|
|
|
|
for std in candidates:
|
|
|
|
newstd = self.deprecated_stds.get(std)
|
|
|
|
if newstd is not None:
|
|
|
|
mlog.deprecation(
|
|
|
|
f'None of the values {candidates} are supported by the {self.lang} compiler.\n' +
|
|
|
|
f'However, the deprecated {std} std currently falls back to {newstd}.\n' +
|
|
|
|
'This will be an error in the future.\n' +
|
|
|
|
'If the project supports both GNU and MSVC compilers, a value such as\n' +
|
|
|
|
'"c_std=gnu11,c11" specifies that GNU is preferred but it can safely fallback to plain c11.')
|
|
|
|
return newstd
|
|
|
|
raise MesonException(f'None of values {candidates} are supported by the {self.lang.upper()} compiler. ' +
|
|
|
|
f'Possible values for option "{self.name}" are {self.choices}')
|
|
|
|
|
|
|
|
|
|
|
|
class BuiltinOption(T.Generic[_T, _U]):
|
|
|
|
|
|
|
|
"""Class for a builtin option type.
|
|
|
|
|
|
|
|
There are some cases that are not fully supported yet.
|
|
|
|
"""
|
|
|
|
|
|
|
|
def __init__(self, opt_type: T.Type[_U], description: str, default: T.Any, yielding: bool = True, *,
|
|
|
|
choices: T.Any = None, readonly: bool = False):
|
|
|
|
self.opt_type = opt_type
|
|
|
|
self.description = description
|
|
|
|
self.default = default
|
|
|
|
self.choices = choices
|
|
|
|
self.yielding = yielding
|
|
|
|
self.readonly = readonly
|
|
|
|
|
|
|
|
def init_option(self, name: 'OptionKey', value: T.Optional[T.Any], prefix: str) -> _U:
|
|
|
|
"""Create an instance of opt_type and return it."""
|
|
|
|
if value is None:
|
|
|
|
value = self.prefixed_default(name, prefix)
|
|
|
|
keywords = {'yielding': self.yielding, 'value': value}
|
|
|
|
if self.choices:
|
|
|
|
keywords['choices'] = self.choices
|
|
|
|
o = self.opt_type(name.name, self.description, **keywords)
|
|
|
|
o.readonly = self.readonly
|
|
|
|
return o
|
|
|
|
|
|
|
|
def _argparse_action(self) -> T.Optional[str]:
|
|
|
|
# If the type is a boolean, the presence of the argument in --foo form
|
|
|
|
# is to enable it. Disabling happens by using -Dfoo=false, which is
|
|
|
|
# parsed under `args.projectoptions` and does not hit this codepath.
|
|
|
|
if isinstance(self.default, bool):
|
|
|
|
return 'store_true'
|
|
|
|
return None
|
|
|
|
|
|
|
|
def _argparse_choices(self) -> T.Any:
|
|
|
|
if self.opt_type is UserBooleanOption:
|
|
|
|
return [True, False]
|
|
|
|
elif self.opt_type is UserFeatureOption:
|
|
|
|
return UserFeatureOption.static_choices
|
|
|
|
return self.choices
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def argparse_name_to_arg(name: str) -> str:
|
|
|
|
if name == 'warning_level':
|
|
|
|
return '--warnlevel'
|
|
|
|
else:
|
|
|
|
return '--' + name.replace('_', '-')
|
|
|
|
|
|
|
|
def prefixed_default(self, name: 'OptionKey', prefix: str = '') -> T.Any:
|
|
|
|
if self.opt_type in [UserComboOption, UserIntegerOption]:
|
|
|
|
return self.default
|
|
|
|
try:
|
|
|
|
return BUILTIN_DIR_NOPREFIX_OPTIONS[name][prefix]
|
|
|
|
except KeyError:
|
|
|
|
pass
|
|
|
|
return self.default
|
|
|
|
|
|
|
|
def add_to_argparse(self, name: str, parser: argparse.ArgumentParser, help_suffix: str) -> None:
|
|
|
|
kwargs = OrderedDict()
|
|
|
|
|
|
|
|
c = self._argparse_choices()
|
|
|
|
b = self._argparse_action()
|
|
|
|
h = self.description
|
|
|
|
if not b:
|
|
|
|
h = '{} (default: {}).'.format(h.rstrip('.'), self.prefixed_default(name))
|
|
|
|
else:
|
|
|
|
kwargs['action'] = b
|
|
|
|
if c and not b:
|
|
|
|
kwargs['choices'] = c
|
|
|
|
kwargs['default'] = argparse.SUPPRESS
|
|
|
|
kwargs['dest'] = name
|
|
|
|
|
|
|
|
cmdline_name = self.argparse_name_to_arg(name)
|
|
|
|
parser.add_argument(cmdline_name, help=h + help_suffix, **kwargs)
|
|
|
|
|
|
|
|
|
|
|
|
# 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('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', (2, None, 4))),
|
|
|
|
(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('vsenv'), BuiltinOption(UserBooleanOption, 'Activate Visual Studio environment', False, readonly=True)),
|
|
|
|
|
|
|
|
# Pkgconfig module
|
|
|
|
(OptionKey('relocatable', module='pkgconfig'),
|
|
|
|
BuiltinOption(UserBooleanOption, 'Generate pkgconfig files as relocatable', False)),
|
|
|
|
|
|
|
|
# Python module
|
|
|
|
(OptionKey('bytecompile', module='python'),
|
|
|
|
BuiltinOption(UserIntegerOption, 'Whether to compile bytecode', (-1, 2, 0))),
|
|
|
|
(OptionKey('install_env', module='python'),
|
|
|
|
BuiltinOption(UserComboOption, 'Which python environment to install to', 'prefix', choices=['auto', 'prefix', 'system', 'venv'])),
|
|
|
|
(OptionKey('platlibdir', module='python'),
|
|
|
|
BuiltinOption(UserStringOption, 'Directory for site-specific, platform-specific files.', '')),
|
|
|
|
(OptionKey('purelibdir', module='python'),
|
|
|
|
BuiltinOption(UserStringOption, 'Directory for site-specific, non-platform-specific files.', '')),
|
|
|
|
(OptionKey('allow_limited_api', module='python'),
|
|
|
|
BuiltinOption(UserBooleanOption, '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(UserArrayOption, 'List of additional paths for pkg-config to search', [])),
|
|
|
|
(OptionKey('cmake_prefix_path'), BuiltinOption(UserArrayOption, '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.
|
|
|
|
BUILTIN_DIR_NOPREFIX_OPTIONS: T.Dict[OptionKey, T.Dict[str, str]] = {
|
|
|
|
OptionKey('sysconfdir'): {'/usr': '/etc'},
|
|
|
|
OptionKey('localstatedir'): {'/usr': '/var', '/usr/local': '/var/local'},
|
|
|
|
OptionKey('sharedstatedir'): {'/usr': '/var/lib', '/usr/local': '/var/local/lib'},
|
|
|
|
OptionKey('platlibdir', module='python'): {},
|
|
|
|
OptionKey('purelibdir', module='python'): {},
|
|
|
|
}
|
|
|
|
|
|
|
|
class OptionStore:
|
|
|
|
def __init__(self):
|
|
|
|
self.d: T.Dict['OptionKey', 'UserOption[T.Any]'] = {}
|
|
|
|
|
|
|
|
def __len__(self):
|
|
|
|
return len(self.d)
|
|
|
|
|
|
|
|
def ensure_key(self, key: T.Union[OptionKey, str]) -> OptionKey:
|
|
|
|
if isinstance(key, str):
|
|
|
|
return OptionKey(key)
|
|
|
|
return key
|
|
|
|
|
|
|
|
def get_value_object(self, key: T.Union[OptionKey, str]) -> 'UserOption[T.Any]':
|
|
|
|
return self.d[self.ensure_key(key)]
|
|
|
|
|
|
|
|
def get_value(self, key: T.Union[OptionKey, str]) -> 'T.Any':
|
|
|
|
return self.get_value_object(key).value
|
|
|
|
|
|
|
|
def add_system_option(self, key: T.Union[OptionKey, str], valobj: 'UserOption[T.Any]'):
|
|
|
|
key = self.ensure_key(key)
|
|
|
|
self.d[key] = valobj
|
|
|
|
|
|
|
|
def add_project_option(self, key: T.Union[OptionKey, str], valobj: 'UserOption[T.Any]'):
|
|
|
|
key = self.ensure_key(key)
|
|
|
|
self.d[key] = valobj
|
|
|
|
|
|
|
|
def set_value(self, key: T.Union[OptionKey, str], new_value: 'T.Any') -> bool:
|
|
|
|
key = self.ensure_key(key)
|
|
|
|
return self.d[key].set_value(new_value)
|
|
|
|
|
|
|
|
# FIXME, this should be removed.or renamed to "change_type_of_existing_object" or something like that
|
|
|
|
def set_value_object(self, key: T.Union[OptionKey, str], new_object: 'UserOption[T.Any]') -> bool:
|
|
|
|
key = self.ensure_key(key)
|
|
|
|
self.d[key] = new_object
|
|
|
|
|
|
|
|
def remove(self, key):
|
|
|
|
del self.d[key]
|
|
|
|
|
|
|
|
def __contains__(self, key):
|
|
|
|
key = self.ensure_key(key)
|
|
|
|
return key in self.d
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
return repr(self.d)
|
|
|
|
|
|
|
|
def keys(self):
|
|
|
|
return self.d.keys()
|
|
|
|
|
|
|
|
def values(self):
|
|
|
|
return self.d.values()
|
|
|
|
|
|
|
|
def items(self) -> ItemsView['OptionKey', 'UserOption[T.Any]']:
|
|
|
|
return self.d.items()
|
|
|
|
|
|
|
|
def update(self, *args, **kwargs):
|
|
|
|
return self.d.update(*args, **kwargs)
|
|
|
|
|
|
|
|
def setdefault(self, k, o):
|
|
|
|
return self.d.setdefault(k, o)
|
|
|
|
|
|
|
|
def get(self, *args, **kwargs) -> UserOption:
|
|
|
|
return self.d.get(*args, **kwargs)
|