aboutsummaryrefslogtreecommitdiff
path: root/mesonbuild/interpreter/interpreter.py
diff options
context:
space:
mode:
authorJussi Pakkanen <jpakkane@gmail.com>2024-04-14 12:58:30 +0300
committerJussi Pakkanen <jpakkane@gmail.com>2025-02-13 15:26:44 +0200
commit518c732ea9b0f1975f6f28accff3286be4106538 (patch)
tree2be9544828545a2f9f0676efc6eb82ac197ec4bd /mesonbuild/interpreter/interpreter.py
parentea678ed82938ceac00682b2695b57193d36b71b4 (diff)
downloadmeson-optionrefactor3.zip
meson-optionrefactor3.tar.gz
meson-optionrefactor3.tar.bz2
Make all Meson level options overridable per subproject.optionrefactor3
Diffstat (limited to 'mesonbuild/interpreter/interpreter.py')
-rw-r--r--mesonbuild/interpreter/interpreter.py141
1 files changed, 54 insertions, 87 deletions
diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py
index f6e1bfa..4b023a8 100644
--- a/mesonbuild/interpreter/interpreter.py
+++ b/mesonbuild/interpreter/interpreter.py
@@ -299,10 +299,12 @@ class Interpreter(InterpreterBase, HoldableObject):
self.configure_file_outputs: T.Dict[str, int] = {}
# Passed from the outside, only used in subprojects.
if default_project_options:
- self.default_project_options = default_project_options.copy()
+ self.default_project_options = default_project_options if isinstance(default_project_options, str) else default_project_options.copy()
+ if isinstance(default_project_options, dict):
+ pass
else:
self.default_project_options = {}
- self.project_default_options: T.Dict[OptionKey, str] = {}
+ self.project_default_options: T.List[str] = []
self.build_func_dict()
self.build_holder_map()
self.user_defined_options = user_defined_options
@@ -878,13 +880,15 @@ class Interpreter(InterpreterBase, HoldableObject):
return sub
def do_subproject(self, subp_name: str, kwargs: kwtypes.DoSubproject, force_method: T.Optional[wrap.Method] = None) -> SubprojectHolder:
+ if subp_name == 'sub_static':
+ pass
disabled, required, feature = extract_required_kwarg(kwargs, self.subproject)
if disabled:
assert feature, 'for mypy'
mlog.log('Subproject', mlog.bold(subp_name), ':', 'skipped: feature', mlog.bold(feature), 'disabled')
return self.disabled_subproject(subp_name, disabled_feature=feature)
- default_options = {k.evolve(subproject=subp_name): v for k, v in kwargs['default_options'].items()}
+ default_options = kwargs['default_options']
if subp_name == '':
raise InterpreterException('Subproject name must not be empty.')
@@ -959,7 +963,7 @@ class Interpreter(InterpreterBase, HoldableObject):
raise e
def _do_subproject_meson(self, subp_name: str, subdir: str,
- default_options: T.Dict[OptionKey, str],
+ default_options: T.List[str],
kwargs: kwtypes.DoSubproject,
ast: T.Optional[mparser.CodeBlockNode] = None,
build_def_files: T.Optional[T.List[str]] = None,
@@ -1019,21 +1023,21 @@ class Interpreter(InterpreterBase, HoldableObject):
return self.subprojects[subp_name]
def _do_subproject_cmake(self, subp_name: str, subdir: str,
- default_options: T.Dict[OptionKey, str],
+ default_options: T.List[str],
kwargs: kwtypes.DoSubproject) -> SubprojectHolder:
from ..cmake import CMakeInterpreter
with mlog.nested(subp_name):
- prefix = self.coredata.optstore.get_value('prefix')
+ prefix = self.coredata.optstore.get_value_for('prefix')
from ..modules.cmake import CMakeSubprojectOptions
- options = kwargs.get('options') or CMakeSubprojectOptions()
- cmake_options = kwargs.get('cmake_options', []) + options.cmake_options
+ kw_opts = kwargs.get('options') or CMakeSubprojectOptions()
+ cmake_options = kwargs.get('cmake_options', []) + kw_opts.cmake_options
cm_int = CMakeInterpreter(Path(subdir), Path(prefix), self.build.environment, self.backend)
cm_int.initialise(cmake_options)
cm_int.analyse()
# Generate a meson ast and execute it with the normal do_subproject_meson
- ast = cm_int.pretend_to_be_meson(options.target_options)
+ ast = cm_int.pretend_to_be_meson(kw_opts.target_options)
result = self._do_subproject_meson(
subp_name, subdir, default_options,
kwargs, ast,
@@ -1046,7 +1050,7 @@ class Interpreter(InterpreterBase, HoldableObject):
return result
def _do_subproject_cargo(self, subp_name: str, subdir: str,
- default_options: T.Dict[OptionKey, str],
+ default_options: T.List[str],
kwargs: kwtypes.DoSubproject) -> SubprojectHolder:
from .. import cargo
FeatureNew.single_use('Cargo subproject', '1.3.0', self.subproject, location=self.current_node)
@@ -1061,46 +1065,13 @@ class Interpreter(InterpreterBase, HoldableObject):
# FIXME: Are there other files used by cargo interpreter?
[os.path.join(subdir, 'Cargo.toml')])
- def get_option_internal(self, optname: str) -> options.UserOption:
- key = OptionKey.from_string(optname).evolve(subproject=self.subproject)
-
- if not self.environment.coredata.optstore.is_project_option(key):
- for opts in [self.coredata.optstore, compilers.base_options]:
- v = opts.get(key)
- if v is None or v.yielding:
- v = opts.get(key.as_root())
- if v is not None:
- assert isinstance(v, options.UserOption), 'for mypy'
- return v
-
- try:
- opt = self.coredata.optstore.get_value_object(key)
- if opt.yielding and key.subproject and key.as_root() in self.coredata.optstore:
- popt = self.coredata.optstore.get_value_object(key.as_root())
- if type(opt) is type(popt):
- opt = popt
- else:
- # Get class name, then option type as a string
- opt_type = opt.__class__.__name__[4:][:-6].lower()
- popt_type = popt.__class__.__name__[4:][:-6].lower()
- # This is not a hard error to avoid dependency hell, the workaround
- # when this happens is to simply set the subproject's option directly.
- mlog.warning('Option {0!r} of type {1!r} in subproject {2!r} cannot yield '
- 'to parent option of type {3!r}, ignoring parent value. '
- 'Use -D{2}:{0}=value to set the value for this option manually'
- '.'.format(optname, opt_type, self.subproject, popt_type),
- location=self.current_node)
- return opt
- except KeyError:
- pass
-
- raise InterpreterException(f'Tried to access unknown option {optname!r}.')
-
@typed_pos_args('get_option', str)
@noKwargs
def func_get_option(self, nodes: mparser.BaseNode, args: T.Tuple[str],
kwargs: 'TYPE_kwargs') -> T.Union[options.UserOption, 'TYPE_var']:
optname = args[0]
+ if optname == 'optimization' and self.subproject == 'sub2':
+ pass
if ':' in optname:
raise InterpreterException('Having a colon in option name is forbidden, '
'projects are not allowed to directly access '
@@ -1109,15 +1080,19 @@ class Interpreter(InterpreterBase, HoldableObject):
if optname_regex.search(optname.split('.', maxsplit=1)[-1]) is not None:
raise InterpreterException(f'Invalid option name {optname!r}')
- opt = self.get_option_internal(optname)
- if isinstance(opt, options.UserFeatureOption):
- opt.name = optname
- return opt
- elif isinstance(opt, options.UserOption):
- if isinstance(opt.value, str):
- return P_OBJ.OptionString(opt.value, f'{{{optname}}}')
- return opt.value
- return opt
+ value_object, value = self.coredata.optstore.get_option_from_meson_file(options.OptionKey(optname, self.subproject))
+ if isinstance(value_object, options.UserFeatureOption):
+ ocopy = copy.copy(value_object)
+ ocopy.name = optname
+ ocopy.value = value
+ return ocopy
+ elif isinstance(value_object, options.UserOption):
+ if isinstance(value_object.value, str):
+ return P_OBJ.OptionString(value, f'{{{optname}}}')
+ return value
+ ocopy = copy.copy(value_object)
+ ocopy.value = value
+ return ocopy
@typed_pos_args('configuration_data', optargs=[dict])
@noKwargs
@@ -1220,28 +1195,22 @@ class Interpreter(InterpreterBase, HoldableObject):
else:
self.coredata.options_files[self.subproject] = None
- if self.subproject:
- self.project_default_options = {k.evolve(subproject=self.subproject): v
- for k, v in kwargs['default_options'].items()}
- else:
- self.project_default_options = kwargs['default_options']
-
- # Do not set default_options on reconfigure otherwise it would override
- # values previously set from command line. That means that changing
- # default_options in a project will trigger a reconfigure but won't
- # have any effect.
- #
- # If this is the first invocation we always need to initialize
- # builtins, if this is a subproject that is new in a re-invocation we
- # need to initialize builtins for that
+ self.project_default_options = kwargs['default_options']
+ if isinstance(self.project_default_options, str):
+ self.project_default_options = [self.project_default_options]
+ assert isinstance(self.project_default_options, (list, dict))
if self.environment.first_invocation or (self.subproject != '' and self.subproject not in self.coredata.initialized_subprojects):
- default_options = self.project_default_options.copy()
- default_options.update(self.default_project_options)
- self.coredata.init_builtins(self.subproject)
- self.coredata.initialized_subprojects.add(self.subproject)
- else:
- default_options = {}
- self.coredata.set_default_options(default_options, self.subproject, self.environment)
+ if self.subproject == '':
+ self.coredata.optstore.initialize_from_top_level_project_call(self.project_default_options,
+ self.user_defined_options.cmd_line_options,
+ self.environment.options)
+ else:
+ invoker_method_default_options = self.default_project_options
+ self.coredata.optstore.initialize_from_subproject_call(self.subproject,
+ invoker_method_default_options,
+ self.project_default_options,
+ self.user_defined_options.cmd_line_options)
+ self.coredata.initialized_subprojects.add(self.subproject)
if not self.is_subproject():
self.build.project_name = proj_name
@@ -1353,8 +1322,8 @@ class Interpreter(InterpreterBase, HoldableObject):
mlog.warning('add_languages is missing native:, assuming languages are wanted for both host and build.',
location=node)
- success = self.add_languages(langs, False, MachineChoice.BUILD)
- success &= self.add_languages(langs, required, MachineChoice.HOST)
+ success = self.add_languages(langs, required, MachineChoice.HOST)
+ success &= self.add_languages(langs, False, MachineChoice.BUILD)
return success
def _stringify_user_arguments(self, args: T.List[TYPE_var], func_name: str) -> T.List[str]:
@@ -1428,7 +1397,14 @@ class Interpreter(InterpreterBase, HoldableObject):
values['Cross files'] = self.user_defined_options.cross_file
if self.user_defined_options.native_file:
values['Native files'] = self.user_defined_options.native_file
- sorted_options = sorted(self.user_defined_options.cmd_line_options.items())
+
+ def compatibility_sort_helper(s):
+ if isinstance(s, tuple):
+ s = s[0]
+ if isinstance(s, str):
+ return s
+ return s.name
+ sorted_options = sorted(self.user_defined_options.cmd_line_options.items(), key=compatibility_sort_helper)
values.update({str(k): v for k, v in sorted_options})
if values:
self.summary_impl('User defined options', values, {'bool_yn': False, 'list_sep': None})
@@ -1566,15 +1542,6 @@ class Interpreter(InterpreterBase, HoldableObject):
# update new values from commandline, if it applies
self.coredata.process_compiler_options(lang, comp, self.environment, self.subproject)
- # Add per-subproject compiler options. They inherit value from main project.
- if self.subproject:
- options = {}
- for k in comp.get_options():
- v = copy.copy(self.coredata.optstore.get_value_object(k))
- k = k.evolve(subproject=self.subproject)
- options[k] = v
- self.coredata.add_compiler_options(options, lang, for_machine, self.environment, self.subproject)
-
if for_machine == MachineChoice.HOST or self.environment.is_cross_build():
logger_fun = mlog.log
else: