Skip to content

Commit

Permalink
Add general override functionality. Closes #3001.
Browse files Browse the repository at this point in the history
  • Loading branch information
jpakkane committed May 13, 2021
1 parent 1730778 commit db7787e
Show file tree
Hide file tree
Showing 8 changed files with 102 additions and 20 deletions.
61 changes: 42 additions & 19 deletions mesonbuild/coredata.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,27 @@ def validate_value(self, value: T.Any) -> str:
raise MesonException('Value "%s" for string option is not a string.' % str(value))
return value

class UserFilePathOption(UserOption[str]):
def __init__(self, description: str, value: T.Any, yielding: T.Optional[bool] = None):
super().__init__(description, None, yielding)
self.path_must_exist = True
self.set_value(value)

def validate_value(self, value: T.Any) -> str:
if not isinstance(value, str):
raise MesonException('Value "%s" for file path option is not a string.' % str(value))
if not value:
return value
# Store absolute paths, because the original Meson command
# might not have been run from the build root.
if not os.path.isabs(value):
value = os.path.join(os.getcwd(), value)
if not self.path_must_exist:
return value
if not os.path.exists(value):
raise MesonException(f'File path "{value}" for does not exist on the file system.')
return value

class UserBooleanOption(UserOption[bool]):
def __init__(self, description: str, value, yielding: T.Optional[bool] = None) -> None:
super().__init__(description, [True, False], yielding)
Expand Down Expand Up @@ -374,6 +395,7 @@ def items(self) -> T.Iterator[T.Tuple['CacheKeyType', T.List['dependencies.Depen
def clear(self) -> None:
self.__cache.clear()


# Can't bind this near the class method it seems, sadly.
_V = T.TypeVar('_V')

Expand Down Expand Up @@ -831,7 +853,7 @@ class MachineFileParser():
def __init__(self, filenames: T.List[str]) -> None:
self.parser = CmdLineFileParser()
self.constants = {'True': True, 'False': False}
self.sections = {}
self.sections = {}

self.parser.read(filenames)

Expand Down Expand Up @@ -1124,24 +1146,25 @@ def add_to_argparse(self, name: str, parser: argparse.ArgumentParser, help_suffi
])

BUILTIN_CORE_OPTIONS: 'KeyedOptionDictType' = OrderedDict([
(OptionKey('auto_features'), BuiltinOption(UserFeatureOption, "Override value of all 'auto' features", 'auto')),
(OptionKey('backend'), BuiltinOption(UserComboOption, 'Backend to use', 'ninja', choices=backendlist)),
(OptionKey('buildtype'), BuiltinOption(UserComboOption, 'Build type to use', 'debug',
choices=['plain', 'debug', 'debugoptimized', 'release', 'minsize', 'custom'])),
(OptionKey('debug'), BuiltinOption(UserBooleanOption, 'Debug', True)),
(OptionKey('default_library'), BuiltinOption(UserComboOption, 'Default library type', 'shared', choices=['shared', 'static', 'both'],
yielding=False)),
(OptionKey('errorlogs'), BuiltinOption(UserBooleanOption, "Whether to print the logs from failing tests", True)),
(OptionKey('install_umask'), BuiltinOption(UserUmaskOption, 'Default umask to apply on permissions of installed files', '022')),
(OptionKey('layout'), BuiltinOption(UserComboOption, 'Build directory layout', 'mirror', choices=['mirror', 'flat'])),
(OptionKey('optimization'), BuiltinOption(UserComboOption, 'Optimization level', '0', choices=['0', 'g', '1', '2', '3', 's'])),
(OptionKey('stdsplit'), BuiltinOption(UserBooleanOption, 'Split stdout and stderr in test logs', True)),
(OptionKey('strip'), BuiltinOption(UserBooleanOption, 'Strip targets on install', False)),
(OptionKey('unity'), BuiltinOption(UserComboOption, 'Unity build', 'off', choices=['on', 'off', 'subprojects'])),
(OptionKey('unity_size'), BuiltinOption(UserIntegerOption, 'Unity block size', (2, None, 4))),
(OptionKey('warning_level'), BuiltinOption(UserComboOption, 'Compiler warning level to use', '1', choices=['0', '1', '2', '3'], yielding=False)),
(OptionKey('werror'), BuiltinOption(UserBooleanOption, 'Treat warnings as errors', False, yielding=False)),
(OptionKey('wrap_mode'), BuiltinOption(UserComboOption, 'Wrap mode', 'default', choices=['default', 'nofallback', 'nodownload', 'forcefallback', 'nopromote'])),
(OptionKey('auto_features'), BuiltinOption(UserFeatureOption, "Override value of all 'auto' features", 'auto')),
(OptionKey('backend'), BuiltinOption(UserComboOption, 'Backend to use', 'ninja', choices=backendlist)),
(OptionKey('buildtype'), BuiltinOption(UserComboOption, 'Build type to use', 'debug',
choices=['plain', 'debug', 'debugoptimized', 'release', 'minsize', 'custom'])),
(OptionKey('debug'), BuiltinOption(UserBooleanOption, 'Debug', True)),
(OptionKey('default_library'), BuiltinOption(UserComboOption, 'Default library type', 'shared', choices=['shared', 'static', 'both'],
yielding=False)),
(OptionKey('errorlogs'), BuiltinOption(UserBooleanOption, "Whether to print the logs from failing tests", True)),
(OptionKey('install_umask'), BuiltinOption(UserUmaskOption, 'Default umask to apply on permissions of installed files', '022')),
(OptionKey('layout'), BuiltinOption(UserComboOption, 'Build directory layout', 'mirror', choices=['mirror', 'flat'])),
(OptionKey('optimization'), BuiltinOption(UserComboOption, 'Optimization level', '0', choices=['0', 'g', '1', '2', '3', 's'])),
(OptionKey('override_file'), BuiltinOption(UserFilePathOption, 'Override file path', '')),
(OptionKey('stdsplit'), BuiltinOption(UserBooleanOption, 'Split stdout and stderr in test logs', True)),
(OptionKey('strip'), BuiltinOption(UserBooleanOption, 'Strip targets on install', False)),
(OptionKey('unity'), BuiltinOption(UserComboOption, 'Unity build', 'off', choices=['on', 'off', 'subprojects'])),
(OptionKey('unity_size'), BuiltinOption(UserIntegerOption, 'Unity block size', (2, None, 4))),
(OptionKey('warning_level'), BuiltinOption(UserComboOption, 'Compiler warning level to use', '1', choices=['0', '1', '2', '3'], yielding=False)),
(OptionKey('werror'), BuiltinOption(UserBooleanOption, 'Treat warnings as errors', False, yielding=False)),
(OptionKey('wrap_mode'), BuiltinOption(UserComboOption, 'Wrap mode', 'default', choices=['default', 'nofallback', 'nodownload', 'forcefallback', 'nopromote'])),
(OptionKey('force_fallback_for'), BuiltinOption(UserArrayOption, 'Force fallback for those subprojects', [])),
])

Expand Down
30 changes: 30 additions & 0 deletions mesonbuild/environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,23 @@ def search_version(text: str) -> str:

return 'unknown version'

class OptionOverrides:
def __init__(self):
self.subproject_overrides = {} # Overrides option value in all subprojects but not the main project.
# FIXME add per-project overrides here

def add_subprojects_override(self, key, value):
self.subproject_overrides[key] = value

def value_if_overridden(self, key, subproject): # FIXME add target as an argument?
if subproject == '':
return None
if key not in self.subproject_overrides:
return None
return self.subproject_overrides[key]



class Environment:
private_dir = 'meson-private'
log_dir = 'meson-logs'
Expand Down Expand Up @@ -675,6 +692,9 @@ def __init__(self, source_dir: T.Optional[str], build_dir: T.Optional[str], opti
# Command line options override those from cross/native files
self.options.update(options.cmd_line_options)

self.overrides = OptionOverrides()
self.init_overrides()

# Take default value from env if not set in cross/native files or command line.
self._set_default_options_from_env()
self._set_default_binaries_from_env()
Expand Down Expand Up @@ -2185,3 +2205,13 @@ def get_exe_wrapper(self):
if not self.need_exe_wrapper():
return EmptyExternalProgram()
return self.exe_wrapper

def init_overrides(self) -> None:
k = OptionKey('override_file')
override_file = self.options[k]
if override_file:
if not os.path.exists(override_file):
raise mesonlib.MesonException(f'Override file {override_file} does not exist.')
sections = coredata.MachineFileParser(override_file).sections
for k, v in sections.get('override_subprojects', {}).items():
self.overrides.add_subprojects_override(k, v)
4 changes: 4 additions & 0 deletions mesonbuild/interpreter/interpreter.py
Original file line number Diff line number Diff line change
Expand Up @@ -858,6 +858,10 @@ def _do_subproject_cmake(self, subp_name, subdir, subdir_abs, default_options, k
def get_option_internal(self, optname: str):
key = OptionKey.from_string(optname).evolve(subproject=self.subproject)

maybe_v = self.environment.overrides.value_if_overridden(optname, self.subproject)
if maybe_v is not None:
return maybe_v

if not key.is_project():
for opts in [self.coredata.options, compilers.base_options]:
v = opts.get(key)
Expand Down
2 changes: 2 additions & 0 deletions mesonbuild/mintro.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,8 @@ def add_keys(options: 'cdata.KeyedOptionDictType', section: str) -> None:
typestr = 'integer'
elif isinstance(opt, cdata.UserArrayOption):
typestr = 'array'
elif isinstance(opt, cdata.UserFilePathOption):
typestr = 'filepath'
else:
raise RuntimeError("Unknown option type")
optdict['type'] = typestr
Expand Down
16 changes: 15 additions & 1 deletion run_unittests.py
Original file line number Diff line number Diff line change
Expand Up @@ -1720,7 +1720,8 @@ def setUp(self):
self.common_test_dir = os.path.join(src_root, 'test cases/common')
self.vala_test_dir = os.path.join(src_root, 'test cases/vala')
self.framework_test_dir = os.path.join(src_root, 'test cases/frameworks')
self.unit_test_dir = os.path.join(src_root, 'test cases/unit')
self.unit_test_rel_dir = 'test cases/unit'
self.unit_test_dir = os.path.join(src_root, self.unit_test_rel_dir)
self.rewrite_test_dir = os.path.join(src_root, 'test cases/rewrite')
self.linuxlike_test_dir = os.path.join(src_root, 'test cases/linuxlike')
self.objc_test_dir = os.path.join(src_root, 'test cases/objc')
Expand Down Expand Up @@ -5705,6 +5706,19 @@ def test_env_flags_to_linker(self) -> None:
link_args = env.coredata.get_external_link_args(cc.for_machine, cc.language)
self.assertEqual(sorted(link_args), sorted(['-flto']))

def test_overrides(self):
d = '96 overriding'
testdir = os.path.join(self.unit_test_dir, d)
# Use a relative path to the override file to test that path
# preservation works.
override_file = os.path.join(self.unit_test_rel_dir, d, 'overrides.txt')
self.init(testdir, extra_args=['-Doverride_file='+ override_file])
for i in self.introspect('--buildoptions'):
if i['name'] == 'override_file':
self.assertTrue(os.path.isabs(i['value']))
return
self.assertTrue(False, 'Unreachable code.')

class FailureTests(BasePlatformTests):
'''
Tests that test failure conditions. Build files here should be dynamically
Expand Down
4 changes: 4 additions & 0 deletions test cases/unit/96 overriding/meson.build
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
project('mainprog', 'c')

assert(get_option('buildtype') == 'debug')
subproject('somesub')
2 changes: 2 additions & 0 deletions test cases/unit/96 overriding/overrides.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[override_subprojects]
buildtype = 'debugoptimized'
3 changes: 3 additions & 0 deletions test cases/unit/96 overriding/subprojects/somesub/meson.build
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
project('somesub', 'c')

assert(get_option('buildtype') == 'debugoptimized')

0 comments on commit db7787e

Please sign in to comment.