diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index e99f99e17eba..5206d9625d5c 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -289,7 +289,7 @@ def __init__(self, cmd_options: SharedCMDOptions, scratch_dir: str, meson_comman # Only to print a warning if it changes between Meson invocations. self.config_files = self.__load_config_files(cmd_options, scratch_dir, 'native') self.builtin_options_libdir_cross_fixup() - self.init_builtins('') + self.init_builtins() @staticmethod def __load_config_files(cmd_options: SharedCMDOptions, scratch_dir: str, ftype: str) -> T.List[str]: @@ -356,7 +356,7 @@ def builtin_options_libdir_cross_fixup(self) -> None: if self.cross_files: options.BUILTIN_OPTIONS[OptionKey('libdir')].default = 'lib' - def init_builtins(self, subproject: str) -> None: + def init_builtins(self) -> None: # Create builtin options with default values for key, opt in options.BUILTIN_OPTIONS.items(): self.add_builtin_option(self.optstore, key, opt) @@ -448,9 +448,7 @@ def set_option(self, key: OptionKey, value, first_invocation: bool = False) -> b except KeyError: raise MesonException(f'Tried to set unknown builtin option {str(key)}') - changed = self.optstore.set_value(key.name, key.subproject, value) - if changed and opt.readonly and not first_invocation: - raise MesonException(f'Tried modify read only option {str(key)!r}') + changed = self.optstore.set_value(key, value) dirty |= changed if key.name == 'buildtype': @@ -692,6 +690,8 @@ def set_default_options(self, default_options: T.MutableMapping[OptionKey, str], options = OrderedDict() for k, v in env.options.items(): + if isinstance(k, str): + k = OptionKey.from_string(k) # If this is a subproject, don't use other subproject options if k.subproject and k.subproject != subproject: continue diff --git a/mesonbuild/options.py b/mesonbuild/options.py index d8c351e5b8b3..a0a216be2a75 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -829,7 +829,7 @@ def sanitize_dir_option_value(self, prefix: str, option: OptionKey, value: T.Any except TypeError: return value if option.name.endswith('dir') and value.is_absolute() and \ - option not in options.BUILTIN_DIR_NOPREFIX_OPTIONS: + option not in BUILTIN_DIR_NOPREFIX_OPTIONS: try: # Try to relativize the path. value = value.relative_to(prefix) @@ -856,7 +856,11 @@ def set_value(self, key: T.Union[OptionKey, str], new_value: 'T.Any') -> bool: new_value = self.sanitize_dir_option_value(prefix, key, new_value) if key not in self.options: raise MesonException(f'Internal error, tried to access non-existing option {key.name}.') - return self.options[key].set_value(new_value) + valobj = self.options[key] + changed = valobj.set_value(new_value) + if valobj.readonly and changed: + raise MesonException(f'Tried modify read only option {str(key)!r}') + return changed def set_option(self, name: str, subproject: T.Optional[str], new_value: str): key = OptionKey(name, subproject) @@ -886,7 +890,13 @@ def replace(v): dirty |= opt.set_value(new_value) return dirty - return opt.set_value(new_value) + changed = opt.set_value(new_value) + + if opt.readonly and changed: + raise MesonException(f'Tried modify read only option {str(key)!r}') + + + return changed # 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: @@ -1094,7 +1104,10 @@ def set_from_top_level_project_call(self, project_default_options, cmd_line_opti else: self.pending_project_options[key] = valstr for keystr, valstr in cmd_line_options.items(): - key = OptionKey.from_string(keystr) + if isinstance(keystr, str): + key = OptionKey.from_string(keystr) + else: + key = keystr if key.subproject is None: projectkey = key.evolve(subproject='') if key in self.options: diff --git a/unittests/platformagnostictests.py b/unittests/platformagnostictests.py index b97ebfc9895f..4ca289881c24 100644 --- a/unittests/platformagnostictests.py +++ b/unittests/platformagnostictests.py @@ -174,14 +174,14 @@ def test_change_backend(self): self.setconf('-Dbackend=none') self.assertIn("ERROR: Tried modify read only option 'backend'", cm.exception.stdout) - # Reconfigure with a different backend is not allowed - with self.assertRaises(subprocess.CalledProcessError) as cm: - self.init(testdir, extra_args=['--reconfigure', '--backend=none']) - self.assertIn("ERROR: Tried modify read only option 'backend'", cm.exception.stdout) + # Check that the new value was not written in the store. + self.assertEqual(self.getconf('backend'), 'ninja') # Wipe with a different backend is allowed self.init(testdir, extra_args=['--wipe', '--backend=none']) + self.assertEqual(self.getconf('backend'), 'none') + def test_validate_dirs(self): testdir = os.path.join(self.common_test_dir, '1 trivial')