options: merge set_value and set_option

Which are basically the same, except for handling of deprecated options,
and various bugs that only existed in one implementation or the other.
This commit is contained in:
Dylan Baker 2025-03-04 10:04:55 -08:00 committed by Eli Schwartz
parent 251f74dcf3
commit c56e42c119
3 changed files with 33 additions and 57 deletions

View File

@ -417,7 +417,7 @@ class CoreData:
def set_option(self, key: OptionKey, value, first_invocation: bool = False) -> bool: def set_option(self, key: OptionKey, value, first_invocation: bool = False) -> bool:
dirty = False dirty = False
try: try:
changed = self.optstore.set_value(key, value, first_invocation) changed = self.optstore.set_option(key, value, first_invocation)
except KeyError: except KeyError:
raise MesonException(f'Tried to set unknown builtin option {str(key)}') raise MesonException(f'Tried to set unknown builtin option {str(key)}')
dirty |= changed dirty |= changed
@ -484,8 +484,8 @@ class CoreData:
assert value == 'custom' assert value == 'custom'
return False return False
dirty |= self.optstore.set_value('optimization', opt) dirty |= self.optstore.set_option(OptionKey('optimization'), opt)
dirty |= self.optstore.set_value('debug', debug) dirty |= self.optstore.set_option(OptionKey('debug'), debug)
return dirty return dirty
@ -517,7 +517,7 @@ class CoreData:
oldval = self.optstore.get_value_object(key) oldval = self.optstore.get_value_object(key)
if type(oldval) is not type(value): if type(oldval) is not type(value):
self.optstore.set_value(key, value.value) self.optstore.set_option(key, value.value)
elif options.choices_are_different(oldval, value): elif options.choices_are_different(oldval, value):
# If the choices have changed, use the new value, but attempt # If the choices have changed, use the new value, but attempt
# to keep the old options. If they are not valid keep the new # to keep the old options. If they are not valid keep the new
@ -548,7 +548,7 @@ class CoreData:
assert not self.is_cross_build() assert not self.is_cross_build()
for k in options.BUILTIN_OPTIONS_PER_MACHINE: for k in options.BUILTIN_OPTIONS_PER_MACHINE:
o = self.optstore.get_value_object_for(k.name) o = self.optstore.get_value_object_for(k.name)
dirty |= self.optstore.set_value(k, o.value, True) dirty |= self.optstore.set_option(k, o.value, True)
for bk, bv in self.optstore.items(): for bk, bv in self.optstore.items():
if bk.machine is MachineChoice.BUILD: if bk.machine is MachineChoice.BUILD:
hk = bk.as_host() hk = bk.as_host()
@ -692,16 +692,16 @@ class CoreData:
if skey not in self.optstore: if skey not in self.optstore:
self.optstore.add_system_option(skey, copy.deepcopy(compilers.BASE_OPTIONS[key])) self.optstore.add_system_option(skey, copy.deepcopy(compilers.BASE_OPTIONS[key]))
if skey in env.options: if skey in env.options:
self.optstore.set_value(skey, env.options[skey]) self.optstore.set_option(skey, env.options[skey])
elif subproject and key in env.options: elif subproject and key in env.options:
self.optstore.set_value(skey, env.options[key]) self.optstore.set_option(skey, env.options[key])
# FIXME # FIXME
#if subproject and not self.optstore.has_option(key): #if subproject and not self.optstore.has_option(key):
# self.optstore[key] = copy.deepcopy(self.optstore[skey]) # self.optstore[key] = copy.deepcopy(self.optstore[skey])
elif skey in env.options: elif skey in env.options:
self.optstore.set_value(skey, env.options[skey]) self.optstore.set_option(skey, env.options[skey])
elif subproject and key in env.options: elif subproject and key in env.options:
self.optstore.set_value(skey, env.options[key]) self.optstore.set_option(skey, env.options[key])
self.emit_base_options_warnings() self.emit_base_options_warnings()
def emit_base_options_warnings(self) -> None: def emit_base_options_warnings(self) -> None:

View File

@ -941,53 +941,27 @@ class OptionStore:
# .as_posix() keeps the posix-like file separators Meson uses. # .as_posix() keeps the posix-like file separators Meson uses.
return value.as_posix() return value.as_posix()
def set_value(self, key: T.Union[OptionKey, str], new_value: 'T.Any', first_invocation: bool = False) -> bool: def _set_dependents(self, key: OptionKey, value: str) -> None:
key = self.ensure_and_validate_key(key)
if key.name == 'prefix':
new_value = self.sanitize_prefix(new_value)
elif self.is_builtin_option(key):
prefix = self.get_value_for('prefix')
assert isinstance(prefix, str)
new_value = self.sanitize_dir_option_value(prefix, key, new_value)
if key not in self.options:
raise MesonException(f'Unknown options: "{key.name}" not found.')
valobj = self.options[key]
old_value = valobj.value
changed = valobj.set_value(new_value)
if valobj.readonly and changed and not first_invocation:
raise MesonException(f'Tried to modify read only option {str(key)!r}')
if key.name == 'prefix' and first_invocation and changed:
assert isinstance(old_value, str)
self.reset_prefixed_options(old_value, new_value)
if changed:
self.set_dependents(key, new_value)
return changed
def set_dependents(self, key: OptionKey, value: str) -> None:
if key.name != 'buildtype':
return
opt, debug = self.DEFAULT_DEPENDENTS[value] opt, debug = self.DEFAULT_DEPENDENTS[value]
dkey = key.evolve(name='debug') dkey = key.evolve(name='debug')
optkey = key.evolve(name='optimization') optkey = key.evolve(name='optimization')
self.options[dkey].set_value(debug) self.options[dkey].set_value(debug)
self.options[optkey].set_value(opt) self.options[optkey].set_value(opt)
def set_option(self, key: OptionKey, new_value: str, first_invocation: bool = False) -> bool: def set_option(self, key: OptionKey, new_value: ElementaryOptionValues, first_invocation: bool = False) -> bool:
assert isinstance(key, OptionKey)
# FIXME, dupe of set_value
# Remove one of the two before merging to master.
if key.name == 'prefix': if key.name == 'prefix':
assert isinstance(new_value, str), 'for mypy'
new_value = self.sanitize_prefix(new_value) new_value = self.sanitize_prefix(new_value)
elif self.is_builtin_option(key): elif self.is_builtin_option(key):
prefix = self.get_value_for('prefix') prefix = self.get_value_for('prefix')
assert isinstance(prefix, str) assert isinstance(prefix, str), 'for mypy'
new_value = self.sanitize_dir_option_value(prefix, key, new_value) new_value = self.sanitize_dir_option_value(prefix, key, new_value)
opt = self.get_value_object_for(key)
try:
opt = self.get_value_object_for(key)
except KeyError:
raise MesonException(f'Unknown options: "{key!s}" not found.')
if opt.deprecated is True: if opt.deprecated is True:
mlog.deprecation(f'Option {key.name!r} is deprecated') mlog.deprecation(f'Option {key.name!r} is deprecated')
elif isinstance(opt.deprecated, list): elif isinstance(opt.deprecated, list):
@ -1014,15 +988,17 @@ class OptionStore:
old_value = opt.value old_value = opt.value
changed = opt.set_value(new_value) changed = opt.set_value(new_value)
if opt.readonly and changed: if opt.readonly and changed and not first_invocation:
raise MesonException(f'Tried modify read only option {str(key)!r}') raise MesonException(f'Tried to modify read only option {str(key)!r}')
if key.name == 'prefix' and first_invocation and changed: if key.name == 'prefix' and first_invocation and changed:
assert isinstance(old_value, str), 'for mypy' assert isinstance(old_value, str), 'for mypy'
assert isinstance(new_value, str), 'for mypy'
self.reset_prefixed_options(old_value, new_value) self.reset_prefixed_options(old_value, new_value)
if changed: if changed and key.name == 'buildtype':
self.set_dependents(key, new_value) assert isinstance(new_value, str), 'for mypy'
self._set_dependents(key, new_value)
return changed return changed
@ -1032,9 +1008,9 @@ class OptionStore:
else: else:
o = OptionKey.from_string(keystr) o = OptionKey.from_string(keystr)
if o in self.options: if o in self.options:
return self.set_value(o, new_value) return self.set_option(o, new_value)
o = o.as_root() o = o.as_root()
return self.set_value(o, new_value) return self.set_option(o, new_value)
def set_from_configure_command(self, D_args: T.List[str], U_args: T.List[str]) -> bool: def set_from_configure_command(self, D_args: T.List[str], U_args: T.List[str]) -> bool:
dirty = False dirty = False
@ -1279,7 +1255,7 @@ class OptionStore:
augstr = str(key) augstr = str(key)
self.augments[augstr] = valstr self.augments[augstr] = valstr
elif key in self.options: elif key in self.options:
self.set_value(key, valstr, first_invocation) self.set_option(key, valstr, first_invocation)
else: else:
proj_key = key.as_root() proj_key = key.as_root()
if proj_key in self.options: if proj_key in self.options:
@ -1330,7 +1306,7 @@ class OptionStore:
if not self.is_cross and key.is_for_build(): if not self.is_cross and key.is_for_build():
continue continue
if key in self.options: if key in self.options:
self.set_value(key, valstr, True) self.set_option(key, valstr, True)
elif key.subproject is None: elif key.subproject is None:
projectkey = key.as_root() projectkey = key.as_root()
if projectkey in self.options: if projectkey in self.options:
@ -1371,7 +1347,7 @@ class OptionStore:
# If the key points to a project option, set the value from that. # If the key points to a project option, set the value from that.
# Otherwise set an augment. # Otherwise set an augment.
if key in self.project_options: if key in self.project_options:
self.set_value(key, valstr, is_first_invocation) self.set_option(key, valstr, is_first_invocation)
else: else:
self.pending_project_options.pop(key, None) self.pending_project_options.pop(key, None)
aug_str = f'{subproject}:{keystr}' aug_str = f'{subproject}:{keystr}'
@ -1385,6 +1361,6 @@ class OptionStore:
continue continue
self.pending_project_options.pop(key, None) self.pending_project_options.pop(key, None)
if key in self.options: if key in self.options:
self.set_value(key, valstr, is_first_invocation) self.set_option(key, valstr, is_first_invocation)
else: else:
self.augments[str(key)] = valstr self.augments[str(key)] = valstr

View File

@ -451,7 +451,7 @@ class PlatformAgnosticTests(BasePlatformTests):
f.write(line) f.write(line)
with self.assertRaises(subprocess.CalledProcessError) as e: with self.assertRaises(subprocess.CalledProcessError) as e:
self.setconf('-Dneg_int_opt=0') self.setconf('-Dneg_int_opt=0')
self.assertIn('Unknown options: "neg_int_opt"', e.exception.stdout) self.assertIn('Unknown options: ":neg_int_opt"', e.exception.stdout)
def test_configure_option_changed_constraints(self) -> None: def test_configure_option_changed_constraints(self) -> None:
"""Changing the constraints of an option without reconfiguring should work.""" """Changing the constraints of an option without reconfiguring should work."""
@ -491,7 +491,7 @@ class PlatformAgnosticTests(BasePlatformTests):
os.unlink(os.path.join(testdir, 'meson_options.txt')) os.unlink(os.path.join(testdir, 'meson_options.txt'))
with self.assertRaises(subprocess.CalledProcessError) as e: with self.assertRaises(subprocess.CalledProcessError) as e:
self.setconf('-Dneg_int_opt=0') self.setconf('-Dneg_int_opt=0')
self.assertIn('Unknown options: "neg_int_opt"', e.exception.stdout) self.assertIn('Unknown options: ":neg_int_opt"', e.exception.stdout)
def test_configure_options_file_added(self) -> None: def test_configure_options_file_added(self) -> None:
"""A new project option file should be detected.""" """A new project option file should be detected."""