diff options
Diffstat (limited to 'mesonbuild/coredata.py')
-rw-r--r-- | mesonbuild/coredata.py | 154 |
1 files changed, 88 insertions, 66 deletions
diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index 782e770..36191d9 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -32,6 +32,7 @@ import typing as T if T.TYPE_CHECKING: from typing_extensions import Protocol + from typing import Any from . import dependencies from .compilers.compilers import Compiler, CompileResult, RunResult, CompileCheckMode @@ -40,6 +41,7 @@ if T.TYPE_CHECKING: from .mesonlib import FileOrString from .cmake.traceparser import CMakeCacheEntry from .interpreterbase import SubProject + from .options import UserOption class SharedCMDOptions(Protocol): @@ -58,7 +60,7 @@ if T.TYPE_CHECKING: OptionDictType = T.Union[T.Dict[str, 'options.UserOption[T.Any]'], 'OptionsView'] MutableKeyedOptionDictType = T.Dict['OptionKey', 'options.UserOption[T.Any]'] - KeyedOptionDictType = T.Union[MutableKeyedOptionDictType, 'OptionsView'] + KeyedOptionDictType = T.Union['options.OptionStore', 'OptionsView'] CompilerCheckCacheKey = T.Tuple[T.Tuple[str, ...], str, FileOrString, T.Tuple[str, ...], CompileCheckMode] # code, args RunCheckCacheKey = T.Tuple[str, T.Tuple[str, ...]] @@ -150,8 +152,8 @@ class DependencyCache: def __calculate_subkey(self, type_: DependencyCacheType) -> T.Tuple[str, ...]: data: T.Dict[DependencyCacheType, T.List[str]] = { - DependencyCacheType.PKG_CONFIG: stringlistify(self.__builtins[self.__pkg_conf_key].value), - DependencyCacheType.CMAKE: stringlistify(self.__builtins[self.__cmake_key].value), + DependencyCacheType.PKG_CONFIG: stringlistify(self.__builtins.get_value(self.__pkg_conf_key)), + DependencyCacheType.CMAKE: stringlistify(self.__builtins.get_value(self.__cmake_key)), DependencyCacheType.OTHER: [], } assert type_ in data, 'Someone forgot to update subkey calculations for a new type' @@ -241,7 +243,7 @@ _V = T.TypeVar('_V') class CoreData: - def __init__(self, options: SharedCMDOptions, scratch_dir: str, meson_command: T.List[str]): + def __init__(self, cmd_options: SharedCMDOptions, scratch_dir: str, meson_command: T.List[str]): self.lang_guids = { 'default': '8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942', 'c': '8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942', @@ -255,8 +257,8 @@ class CoreData: self.meson_command = meson_command self.target_guids = {} self.version = version - self.options: 'MutableKeyedOptionDictType' = {} - self.cross_files = self.__load_config_files(options, scratch_dir, 'cross') + self.optstore = options.OptionStore() + self.cross_files = self.__load_config_files(cmd_options, scratch_dir, 'cross') self.compilers: PerMachine[T.Dict[str, Compiler]] = PerMachine(OrderedDict(), OrderedDict()) # Stores the (name, hash) of the options file, The name will be either @@ -272,8 +274,8 @@ class CoreData: # For host == build configurations these caches should be the same. self.deps: PerMachine[DependencyCache] = PerMachineDefaultable.default( self.is_cross_build(), - DependencyCache(self.options, MachineChoice.BUILD), - DependencyCache(self.options, MachineChoice.HOST)) + DependencyCache(self.optstore, MachineChoice.BUILD), + DependencyCache(self.optstore, MachineChoice.HOST)) self.compiler_check_cache: T.Dict['CompilerCheckCacheKey', 'CompileResult'] = OrderedDict() self.run_check_cache: T.Dict['RunCheckCacheKey', 'RunResult'] = OrderedDict() @@ -282,18 +284,18 @@ class CoreData: self.cmake_cache: PerMachine[CMakeStateCache] = PerMachine(CMakeStateCache(), CMakeStateCache()) # Only to print a warning if it changes between Meson invocations. - self.config_files = self.__load_config_files(options, scratch_dir, 'native') + self.config_files = self.__load_config_files(cmd_options, scratch_dir, 'native') self.builtin_options_libdir_cross_fixup() self.init_builtins('') @staticmethod - def __load_config_files(options: SharedCMDOptions, scratch_dir: str, ftype: str) -> T.List[str]: + def __load_config_files(cmd_options: SharedCMDOptions, scratch_dir: str, ftype: str) -> T.List[str]: # Need to try and make the passed filenames absolute because when the # files are parsed later we'll have chdir()d. if ftype == 'cross': - filenames = options.cross_file + filenames = cmd_options.cross_file else: - filenames = options.native_file + filenames = cmd_options.native_file if not filenames: return [] @@ -403,10 +405,10 @@ class CoreData: def init_builtins(self, subproject: str) -> None: # Create builtin options with default values for key, opt in options.BUILTIN_OPTIONS.items(): - self.add_builtin_option(self.options, key.evolve(subproject=subproject), opt) + self.add_builtin_option(self.optstore, key.evolve(subproject=subproject), opt) for for_machine in iter(MachineChoice): for key, opt in options.BUILTIN_OPTIONS_PER_MACHINE.items(): - self.add_builtin_option(self.options, key.evolve(subproject=subproject, machine=for_machine), opt) + self.add_builtin_option(self.optstore, key.evolve(subproject=subproject, machine=for_machine), opt) @staticmethod def add_builtin_option(opts_map: 'MutableKeyedOptionDictType', key: OptionKey, @@ -415,33 +417,33 @@ class CoreData: if opt.yielding: # This option is global and not per-subproject return - value = opts_map[key.as_root()].value + value = opts_map.get_value(key.as_root()) else: value = None - opts_map[key] = opt.init_option(key, value, options.default_prefix()) + opts_map.add_system_option(key, opt.init_option(key, value, options.default_prefix())) def init_backend_options(self, backend_name: str) -> None: if backend_name == 'ninja': - self.options[OptionKey('backend_max_links')] = options.UserIntegerOption( + self.optstore.add_system_option('backend_max_links', options.UserIntegerOption( 'backend_max_links', 'Maximum number of linker processes to run or 0 for no ' 'limit', - (0, None, 0)) + (0, None, 0))) elif backend_name.startswith('vs'): - self.options[OptionKey('backend_startup_project')] = options.UserStringOption( + self.optstore.add_system_option('backend_startup_project', options.UserStringOption( 'backend_startup_project', 'Default project to execute in Visual Studio', - '') + '')) def get_option(self, key: OptionKey) -> T.Union[T.List[str], str, int, bool]: try: - v = self.options[key].value + v = self.optstore.get_value(key) return v except KeyError: pass try: - v = self.options[key.as_root()] + v = self.optstore.get_value_object(key.as_root()) if v.yielding: return v.value except KeyError: @@ -455,11 +457,11 @@ class CoreData: if key.name == 'prefix': value = self.sanitize_prefix(value) else: - prefix = self.options[OptionKey('prefix')].value + prefix = self.optstore.get_value('prefix') value = self.sanitize_dir_option_value(prefix, key, value) try: - opt = self.options[key] + opt = self.optstore.get_value_object(key) except KeyError: raise MesonException(f'Tried to set unknown builtin option {str(key)}') @@ -510,7 +512,7 @@ class CoreData: def get_nondefault_buildtype_args(self) -> T.List[T.Union[T.Tuple[str, str, str], T.Tuple[str, bool, bool]]]: result: T.List[T.Union[T.Tuple[str, str, str], T.Tuple[str, bool, bool]]] = [] - value = self.options[OptionKey('buildtype')].value + value = self.optstore.get_value('buildtype') if value == 'plain': opt = 'plain' debug = False @@ -529,8 +531,8 @@ class CoreData: else: assert value == 'custom' return [] - actual_opt = self.options[OptionKey('optimization')].value - actual_debug = self.options[OptionKey('debug')].value + actual_opt = self.optstore.get_value('optimization') + actual_debug = self.optstore.get_value('debug') if actual_opt != opt: result.append(('optimization', actual_opt, opt)) if actual_debug != debug: @@ -559,8 +561,8 @@ class CoreData: assert value == 'custom' return False - dirty |= self.options[OptionKey('optimization')].set_value(opt) - dirty |= self.options[OptionKey('debug')].set_value(debug) + dirty |= self.optstore.set_value('optimization', opt) + dirty |= self.optstore.set_value('debug', debug) return dirty @@ -572,30 +574,32 @@ class CoreData: def get_external_args(self, for_machine: MachineChoice, lang: str) -> T.List[str]: # mypy cannot analyze type of OptionKey - return T.cast('T.List[str]', self.options[OptionKey('args', machine=for_machine, lang=lang)].value) + key = OptionKey('args', machine=for_machine, lang=lang) + return T.cast('T.List[str]', self.optstore.get_value(key)) def get_external_link_args(self, for_machine: MachineChoice, lang: str) -> T.List[str]: # mypy cannot analyze type of OptionKey - return T.cast('T.List[str]', self.options[OptionKey('link_args', machine=for_machine, lang=lang)].value) + key = OptionKey('link_args', machine=for_machine, lang=lang) + return T.cast('T.List[str]', self.optstore.get_value(key)) - def update_project_options(self, options: 'MutableKeyedOptionDictType', subproject: SubProject) -> None: - for key, value in options.items(): + def update_project_options(self, project_options: 'MutableKeyedOptionDictType', subproject: SubProject) -> None: + for key, value in project_options.items(): if not key.is_project(): continue - if key not in self.options: - self.options[key] = value + if key not in self.optstore: + self.optstore.add_project_option(key, value) continue if key.subproject != subproject: raise MesonBugException(f'Tried to set an option for subproject {key.subproject} from {subproject}!') - oldval = self.options[key] + oldval = self.optstore.get_value_object(key) if type(oldval) is not type(value): - self.options[key] = value + self.optstore.set_value(key, value.value) elif oldval.choices != value.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. - self.options[key] = value + self.optstore.set_value_object(key, value) try: value.set_value(oldval.value) except MesonException: @@ -603,9 +607,9 @@ class CoreData: fatal=False) # Find any extranious keys for this project and remove them - for key in self.options.keys() - options.keys(): + for key in self.optstore.keys() - project_options.keys(): if key.is_project() and key.subproject == subproject: - del self.options[key] + self.optstore.remove(key) def is_cross_build(self, when_building_for: MachineChoice = MachineChoice.HOST) -> bool: if when_building_for == MachineChoice.BUILD: @@ -616,13 +620,13 @@ class CoreData: dirty = False assert not self.is_cross_build() for k in options.BUILTIN_OPTIONS_PER_MACHINE: - o = self.options[k] - dirty |= self.options[k.as_build()].set_value(o.value) - for bk, bv in self.options.items(): + o = self.optstore.get_value_object(k) + dirty |= self.optstore.set_value(k.as_build(), o.value) + for bk, bv in self.optstore.items(): if bk.machine is MachineChoice.BUILD: hk = bk.as_host() try: - hv = self.options[hk] + hv = self.optstore.get_value_object(hk) dirty |= bv.set_value(hv.value) except KeyError: continue @@ -637,16 +641,16 @@ class CoreData: pfk = OptionKey('prefix') if pfk in opts_to_set: prefix = self.sanitize_prefix(opts_to_set[pfk]) - dirty |= self.options[OptionKey('prefix')].set_value(prefix) + dirty |= self.optstore.set_value('prefix', prefix) for key in options.BUILTIN_DIR_NOPREFIX_OPTIONS: if key not in opts_to_set: - dirty |= self.options[key].set_value(options.BUILTIN_OPTIONS[key].prefixed_default(key, prefix)) + dirty |= self.optstore.set_value(key, options.BUILTIN_OPTIONS[key].prefixed_default(key, prefix)) unknown_options: T.List[OptionKey] = [] for k, v in opts_to_set.items(): if k == pfk: continue - elif k in self.options: + elif k in self.optstore: dirty |= self.set_option(k, v, first_invocation) elif k.machine != MachineChoice.BUILD and k.type != OptionType.COMPILER: unknown_options.append(k) @@ -690,7 +694,7 @@ class CoreData: # Always test this using the HOST machine, as many builtin options # are not valid for the BUILD machine, but the yielding value does # not differ between them even when they are valid for both. - if subproject and k.is_builtin() and self.options[k.evolve(subproject='', machine=MachineChoice.HOST)].yielding: + if subproject and k.is_builtin() and self.optstore.get_value_object(k.evolve(subproject='', machine=MachineChoice.HOST)).yielding: continue # Skip base, compiler, and backend options, they are handled when # adding languages and setting backend. @@ -703,23 +707,23 @@ class CoreData: self.set_options(options, subproject=subproject, first_invocation=env.first_invocation) - def add_compiler_options(self, options: MutableKeyedOptionDictType, lang: str, for_machine: MachineChoice, + def add_compiler_options(self, c_options: MutableKeyedOptionDictType, lang: str, for_machine: MachineChoice, env: Environment, subproject: str) -> None: - for k, o in options.items(): + for k, o in c_options.items(): value = env.options.get(k) if value is not None: o.set_value(value) if not subproject: - self.options[k] = o # override compiler option on reconfigure - self.options.setdefault(k, o) + self.optstore.set_value_object(k, o) # override compiler option on reconfigure + self.optstore.setdefault(k, o) if subproject: sk = k.evolve(subproject=subproject) value = env.options.get(sk) or value if value is not None: o.set_value(value) - self.options[sk] = o # override compiler option on reconfigure - self.options.setdefault(sk, o) + self.optstore.set_value_object(sk, o) # override compiler option on reconfigure + self.optstore.setdefault(sk, o) def add_lang_args(self, lang: str, comp: T.Type['Compiler'], for_machine: MachineChoice, env: 'Environment') -> None: @@ -727,8 +731,8 @@ class CoreData: from .compilers import compilers # These options are all new at this point, because the compiler is # responsible for adding its own options, thus calling - # `self.options.update()`` is perfectly safe. - self.options.update(compilers.get_global_options(lang, comp, for_machine, env)) + # `self.optstore.update()`` is perfectly safe. + self.optstore.update(compilers.get_global_options(lang, comp, for_machine, env)) def process_compiler_options(self, lang: str, comp: Compiler, env: Environment, subproject: str) -> None: from . import compilers @@ -741,20 +745,20 @@ class CoreData: skey = key.evolve(subproject=subproject) else: skey = key - if skey not in self.options: - self.options[skey] = copy.deepcopy(compilers.base_options[key]) + if skey not in self.optstore: + self.optstore.add_system_option(skey, copy.deepcopy(compilers.base_options[key])) if skey in env.options: - self.options[skey].set_value(env.options[skey]) + self.optstore.set_value(skey, env.options[skey]) enabled_opts.append(skey) elif subproject and key in env.options: - self.options[skey].set_value(env.options[key]) + self.optstore.set_value(skey, env.options[key]) enabled_opts.append(skey) - if subproject and key not in self.options: - self.options[key] = copy.deepcopy(self.options[skey]) + if subproject and key not in self.optstore: + self.optstore.add_system_option(key, copy.deepcopy(self.optstore.get_value_object(skey))) elif skey in env.options: - self.options[skey].set_value(env.options[skey]) + self.optstore.set_value(skey, env.options[skey]) elif subproject and key in env.options: - self.options[skey].set_value(env.options[key]) + self.optstore.set_value(skey, env.options[key]) self.emit_base_options_warnings(enabled_opts) def emit_base_options_warnings(self, enabled_opts: T.List[OptionKey]) -> None: @@ -894,7 +898,7 @@ class OptionsView(abc.Mapping): # TODO: the typing here could be made more explicit using a TypeDict from # python 3.8 or typing_extensions - original_options: KeyedOptionDictType + original_options: T.Union[KeyedOptionDictType, 'dict[OptionKey, UserOption[Any]]'] subproject: T.Optional[str] = None overrides: T.Optional[T.Mapping[OptionKey, T.Union[str, int, bool, T.List[str]]]] = None @@ -905,7 +909,15 @@ class OptionsView(abc.Mapping): if not key.is_project(): opt = self.original_options.get(key) if opt is None or opt.yielding: - opt = self.original_options[key.as_root()] + key2 = key.as_root() + # This hack goes away once wi start using OptionStore + # to hold overrides. + if isinstance(self.original_options, options.OptionStore): + if key2 not in self.original_options: + raise KeyError + opt = self.original_options.get_value_object(key2) + else: + opt = self.original_options[key2] else: opt = self.original_options[key] if opt.yielding: @@ -917,6 +929,16 @@ class OptionsView(abc.Mapping): opt.set_value(override_value) return opt + def get_value(self, key: T.Union[str, OptionKey]): + if isinstance(key, str): + key = OptionKey(key) + return self[key].value + + def set_value(self, key: T.Union[str, OptionKey], value: T.Union[str, int, bool, T.List[str]]): + if isinstance(key, str): + key = OptionKey(key) + self.overrides[key] = value + def __iter__(self) -> T.Iterator[OptionKey]: return iter(self.original_options) |