options: Handle updates to choices in options

Currently if you change the `choices` field in the meson_options.txt
file, no update will be done until `meson setup --wipe` is called. Now
if the choices change then the options will be properly merged.

If the currently select value is still valid it is guaranteed to be
kept, if it is now invalid the new default value will be used and a
warning will be printed.

Fixes #7386
pull/7817/head
Dylan Baker 4 years ago committed by Jussi Pakkanen
parent c4fa876925
commit 94ac51fdda
  1. 20
      mesonbuild/coredata.py
  2. 40
      run_unittests.py
  3. 1
      test cases/unit/84 change option choices/meson.build
  4. 13
      test cases/unit/84 change option choices/meson_options.1.txt
  5. 13
      test cases/unit/84 change option choices/meson_options.2.txt

@ -689,14 +689,24 @@ class CoreData:
def get_external_link_args(self, for_machine: MachineChoice, lang):
return self.compiler_options[for_machine][lang]['link_args'].value
def merge_user_options(self, options: T.Dict[str, T.Union[str, bool, int]]) -> None:
def merge_user_options(self, options: T.Dict[str, UserOption[T.Any]]) -> None:
for (name, value) in options.items():
if name not in self.user_options:
self.user_options[name] = value
else:
oldval = self.user_options[name]
if type(oldval) != type(value):
self.user_options[name] = value
continue
oldval = self.user_options[name]
if type(oldval) != type(value):
self.user_options[name] = 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.user_options[name] = value
try:
value.set_value(oldval.value)
except MesonException as e:
mlog.warning('Old value(s) of {} are no longer valid, resetting to default ({}).'.format(name, value.value))
def is_cross_build(self, when_building_for: MachineChoice = MachineChoice.HOST) -> bool:
if when_building_for == MachineChoice.BUILD:

@ -3325,6 +3325,46 @@ int main(int argc, char **argv) {
self.setconf("-Dfree_array_opt=['a,b', 'c,d']", will_build=False)
self.opt_has('free_array_opt', ['a,b', 'c,d'])
def test_options_with_choices_changing(self) -> None:
"""Detect when options like arrays or combos have their choices change."""
testdir = Path(os.path.join(self.unit_test_dir, '84 change option choices'))
options1 = str(testdir / 'meson_options.1.txt')
options2 = str(testdir / 'meson_options.2.txt')
# Test that old options are changed to the new defaults if they are not valid
real_options = str(testdir / 'meson_options.txt')
self.addCleanup(os.unlink, real_options)
shutil.copy(options1, real_options)
self.init(str(testdir))
shutil.copy(options2, real_options)
self.build()
opts = self.introspect('--buildoptions')
for item in opts:
if item['name'] == 'combo':
self.assertEqual(item['value'], 'b')
self.assertEqual(item['choices'], ['b', 'c', 'd'])
elif item['name'] == 'arr':
self.assertEqual(item['value'], ['b'])
self.assertEqual(item['choices'], ['b', 'c', 'd'])
self.wipe()
# When the old options are valid they should remain
shutil.copy(options1, real_options)
self.init(str(testdir), extra_args=['-Dcombo=c', '-Darray=b,c'])
shutil.copy(options2, real_options)
self.build()
opts = self.introspect('--buildoptions')
for item in opts:
if item['name'] == 'combo':
self.assertEqual(item['value'], 'c')
self.assertEqual(item['choices'], ['b', 'c', 'd'])
elif item['name'] == 'arr':
self.assertEqual(item['value'], ['b', 'c'])
self.assertEqual(item['choices'], ['b', 'c', 'd'])
def test_subproject_promotion(self):
testdir = os.path.join(self.unit_test_dir, '12 promote')
workdir = os.path.join(self.builddir, 'work')

@ -0,0 +1 @@
project('change option choices')

@ -0,0 +1,13 @@
option(
'combo',
type : 'combo',
choices : ['a', 'b', 'c'],
value : 'a',
)
option(
'array',
type : 'array',
choices : ['a', 'b', 'c'],
value : ['a'],
)

@ -0,0 +1,13 @@
option(
'combo',
type : 'combo',
choices : ['b', 'c', 'd'],
value : 'b',
)
option(
'array',
type : 'array',
choices : ['b', 'c', 'd'],
value : ['b'],
)
Loading…
Cancel
Save