Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make the new options module fully type safe #13620

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
12 changes: 4 additions & 8 deletions mesonbuild/cmake/interpreter.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
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
Expand Down Expand Up @@ -533,17 +534,12 @@ def _all_source_suffixes(self) -> 'ImmutableListProtocol[str]':
@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)
Expand Down
48 changes: 19 additions & 29 deletions mesonbuild/compilers/c.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,13 +154,11 @@ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_
def get_options(self) -> 'MutableKeyedOptionDictType':
opts = super().get_options()
if self.info.is_windows() or self.info.is_cygwin():
self.update_options(
opts,
self.create_option(options.UserArrayOption,
self.form_compileropt_key('winlibs'),
'Standard Win libraries to link against',
gnu_winlibs),
)
key = self.form_compileropt_key('winlibs')
opts[key] = options.UserStringArrayOption(
self.make_option_name(key),
'Standard Win libraries to link against',
gnu_winlibs)
return opts

def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
Expand Down Expand Up @@ -307,13 +305,11 @@ def get_options(self) -> 'MutableKeyedOptionDictType':
assert isinstance(std_opt, options.UserStdOption), 'for mypy'
std_opt.set_versions(stds, gnu=True)
if self.info.is_windows() or self.info.is_cygwin():
self.update_options(
opts,
self.create_option(options.UserArrayOption,
key.evolve('c_winlibs'),
'Standard Win libraries to link against',
gnu_winlibs),
)
key = self.form_compileropt_key('winlibs')
opts[key] = options.UserStringArrayOption(
self.make_option_name(key),
'Standard Win libraries to link against',
gnu_winlibs)
return opts

def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
Expand Down Expand Up @@ -449,15 +445,13 @@ class VisualStudioLikeCCompilerMixin(CompilerMixinBase):
"""Shared methods that apply to MSVC-like C compilers."""

def get_options(self) -> MutableKeyedOptionDictType:
return self.update_options(
super().get_options(),
self.create_option(
options.UserArrayOption,
self.form_compileropt_key('winlibs'),
'Windows libs to link against.',
msvc_winlibs,
),
)
opts = super().get_options()
key = self.form_compileropt_key('winlibs')
opts[key] = options.UserStringArrayOption(
self.make_option_name(key),
'Standard Win libraries to link against',
msvc_winlibs)
return opts

def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
# need a TypeDict to make this work
Expand Down Expand Up @@ -780,9 +774,7 @@ def get_instruction_set_args(self, instruction_set: str) -> T.Optional[T.List[st

def get_options(self) -> 'MutableKeyedOptionDictType':
opts = CCompiler.get_options(self)
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]:
Expand Down Expand Up @@ -810,9 +802,7 @@ def get_instruction_set_args(self, instruction_set: str) -> T.Optional[T.List[st

def get_options(self) -> 'MutableKeyedOptionDictType':
opts = CCompiler.get_options(self)
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]:
Expand Down
39 changes: 24 additions & 15 deletions mesonbuild/compilers/compilers.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,7 @@
EnvironmentException, MesonException,
Popen_safe_logged, LibType, TemporaryDirectoryWinProof,
)

from ..options import OptionKey

from ..arglist import CompilerArgs

if T.TYPE_CHECKING:
Expand All @@ -37,8 +35,8 @@
from ..dependencies import Dependency

CompilerType = T.TypeVar('CompilerType', bound='Compiler')
_T = T.TypeVar('_T')
UserOptionType = T.TypeVar('UserOptionType', bound=options.UserOption)

_T = T.TypeVar('_T')

"""This file contains the data files of all compilers Meson knows
about. To support a new compiler, add its information below.
Expand Down Expand Up @@ -211,23 +209,25 @@ class CompileCheckMode(enum.Enum):

MSCRT_VALS = ['none', 'md', 'mdd', 'mt', 'mtd']


@dataclass
class BaseOption(T.Generic[options._T, options._U]):
opt_type: T.Type[options._U]
class BaseOption(T.Generic[_T]):
opt_type: T.Type[options.UserOption[_T]]
description: str
default: T.Any = None
choices: T.Any = None

def init_option(self, name: OptionKey) -> options._U:
keywords = {'value': self.default}
def init_option(self, name: OptionKey) -> options.UserOption[_T]:
keywords = {}
if self.choices:
keywords['choices'] = self.choices
return self.opt_type(name.name, self.description, **keywords)
return self.opt_type(name.name, self.description, self.default, **keywords)


BASE_OPTIONS: T.Mapping[OptionKey, BaseOption] = {
OptionKey('b_pch'): BaseOption(options.UserBooleanOption, 'Use precompiled headers', True),
OptionKey('b_lto'): BaseOption(options.UserBooleanOption, 'Use link time optimization', False),
OptionKey('b_lto_threads'): BaseOption(options.UserIntegerOption, 'Use multiple threads for Link Time Optimization', (None, None, 0)),
OptionKey('b_lto_threads'): BaseOption(options.UserIntegerOption, 'Use multiple threads for Link Time Optimization', 0),
OptionKey('b_lto_mode'): BaseOption(options.UserComboOption, 'Select between different LTO modes.', 'default',
choices=['default', 'thin']),
OptionKey('b_thinlto_cache'): BaseOption(options.UserBooleanOption, 'Use LLVM ThinLTO caching for faster incremental builds', False),
Expand Down Expand Up @@ -585,11 +585,11 @@ def gen_import_library_args(self, implibname: str) -> T.List[str]:
"""
return []

def create_option(self, option_type: T.Type[UserOptionType], option_key: OptionKey, *args: T.Any, **kwargs: T.Any) -> T.Tuple[OptionKey, UserOptionType]:
return option_key, option_type(f'{self.language}_{option_key.name}', *args, **kwargs)
def make_option_name(self, key: OptionKey) -> str:
return f'{self.language}_{key.name}'

@staticmethod
def update_options(options: MutableKeyedOptionDictType, *args: T.Tuple[OptionKey, UserOptionType]) -> MutableKeyedOptionDictType:
def update_options(options: MutableKeyedOptionDictType, *args: T.Tuple[OptionKey, options.AnyOptionType]) -> MutableKeyedOptionDictType:
options.update(args)
return options

Expand Down Expand Up @@ -1352,6 +1352,15 @@ def get_preprocessor(self) -> Compiler:
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,
Expand All @@ -1367,12 +1376,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)
Expand Down
Loading
Loading