diff options
author | Jussi Pakkanen <jpakkane@gmail.com> | 2024-04-14 12:58:30 +0300 |
---|---|---|
committer | Jussi Pakkanen <jpakkane@gmail.com> | 2025-02-13 15:26:44 +0200 |
commit | 518c732ea9b0f1975f6f28accff3286be4106538 (patch) | |
tree | 2be9544828545a2f9f0676efc6eb82ac197ec4bd /mesonbuild/interpreter/interpreter.py | |
parent | ea678ed82938ceac00682b2695b57193d36b71b4 (diff) | |
download | meson-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.py | 141 |
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: |