From be20e0809f3cee518a49f4c22ce3ca19319ebb33 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 1 Mar 2023 12:21:56 -0800 Subject: [PATCH] interpreter: allow default_options and override_options as a dict --- docs/markdown/snippets/option_dict.md | 5 +++ docs/yaml/functions/_build_target_base.yaml | 3 +- docs/yaml/functions/dependency.yaml | 3 +- docs/yaml/functions/project.yaml | 4 +- docs/yaml/functions/subproject.yaml | 12 +++--- mesonbuild/interpreter/dependencyfallbacks.py | 13 +++---- mesonbuild/interpreter/interpreter.py | 25 ++++++++++--- mesonbuild/interpreter/kwargs.py | 8 ++-- mesonbuild/interpreter/type_checking.py | 37 +++++++++---------- mesonbuild/modules/cmake.py | 2 +- 10 files changed, 66 insertions(+), 46 deletions(-) create mode 100644 docs/markdown/snippets/option_dict.md diff --git a/docs/markdown/snippets/option_dict.md b/docs/markdown/snippets/option_dict.md new file mode 100644 index 000000000..79cca5f76 --- /dev/null +++ b/docs/markdown/snippets/option_dict.md @@ -0,0 +1,5 @@ +## default_options and override_options may now be dictionaries + +Instead of passing them as `default_options : ['key=value']`, they can now be +passed as `default_options : {'key': 'value'}`, and the same for +`override_options`. diff --git a/docs/yaml/functions/_build_target_base.yaml b/docs/yaml/functions/_build_target_base.yaml index abc5bf94a..f8632889c 100644 --- a/docs/yaml/functions/_build_target_base.yaml +++ b/docs/yaml/functions/_build_target_base.yaml @@ -228,12 +228,13 @@ kwargs: Set this to `[]`, or omit the keyword argument for the default behaviour. override_options: - type: list[str] + type: list[str] | dict[str] since: 0.40.0 description: | takes an array of strings in the same format as `project`'s `default_options` overriding the values of these options for this target only. + *(since 1.2.0)*: A dictionary may now be passed. gnu_symbol_visibility: type: str diff --git a/docs/yaml/functions/dependency.yaml b/docs/yaml/functions/dependency.yaml index 85255b9f1..6a628bd6a 100644 --- a/docs/yaml/functions/dependency.yaml +++ b/docs/yaml/functions/dependency.yaml @@ -78,7 +78,7 @@ varargs: kwargs: default_options: - type: list[str] + type: list[str] | dict[str] since: 0.38.0 description: | An array of default option values @@ -86,6 +86,7 @@ kwargs: (like `default_options` in [[project]], they only have effect when Meson is run for the first time, and command line arguments override any default options in build files) + *(since 1.2.0)*: A dictionary may now be passed. allow_fallback: type: bool diff --git a/docs/yaml/functions/project.yaml b/docs/yaml/functions/project.yaml index 59595cd87..6eb7adc1a 100644 --- a/docs/yaml/functions/project.yaml +++ b/docs/yaml/functions/project.yaml @@ -38,7 +38,7 @@ varargs: kwargs: default_options: - type: list[str] + type: list[str] | dict[str] description: | Accepts strings in the form `key=value` which have the same format as options to `meson configure`. @@ -54,6 +54,8 @@ kwargs: environment variable is not used. Consider using [[add_project_arguments()]] instead. + *(since 1.2.0)*: A dictionary may now be passed. + version: type: str | file description: | diff --git a/docs/yaml/functions/subproject.yaml b/docs/yaml/functions/subproject.yaml index 14a778dd8..3017c20f3 100644 --- a/docs/yaml/functions/subproject.yaml +++ b/docs/yaml/functions/subproject.yaml @@ -12,8 +12,9 @@ description: | that override those set in the subproject's `meson.options` (like `default_options` in `project`, they only have effect when Meson is run for the first time, and command line arguments override - any default options in build files). *(since 0.54.0)*: `default_library` - built-in option can also be overridden. + any default options in build files). + *(since 0.54.0)*: `default_library` built-in option can also be overridden. + *(since 1.2.0)*: A dictionary may be passed instead of array. - `version`: works just like the same as in `dependency`. It specifies what version the subproject should be, as an example `>=1.0.1` - `required` *(since 0.48.0)*: By default, `required` is `true` and @@ -41,15 +42,16 @@ posargs: kwargs: default_options: - type: list[str] + type: list[str] | dict[str] since: 0.37.0 description: | An array of default option values that override those set in the subproject's `meson.options` (like `default_options` in [[project]], they only have effect when Meson is run for the first time, and command line arguments override - any default options in build files). *(since 0.54.0)*: `default_library` - built-in option can also be overridden. + any default options in build files). + *(since 0.54.0)*: `default_library` built-in option can also be overridden. + *(since 1.2.0)*: A dictionary may now be passed. version: type: str diff --git a/mesonbuild/interpreter/dependencyfallbacks.py b/mesonbuild/interpreter/dependencyfallbacks.py index 79ca884c0..7ef152796 100644 --- a/mesonbuild/interpreter/dependencyfallbacks.py +++ b/mesonbuild/interpreter/dependencyfallbacks.py @@ -19,7 +19,7 @@ if T.TYPE_CHECKING: class DependencyFallbacksHolder(MesonInterpreterObject): def __init__(self, interpreter: 'Interpreter', names: T.List[str], allow_fallback: T.Optional[bool] = None, - default_options: T.Optional[T.List[str]] = None) -> None: + default_options: T.Optional[T.Dict[OptionKey, str]] = None) -> None: super().__init__(subproject=interpreter.subproject) self.interpreter = interpreter self.subproject = interpreter.subproject @@ -30,7 +30,7 @@ class DependencyFallbacksHolder(MesonInterpreterObject): self.allow_fallback = allow_fallback self.subproject_name: T.Optional[str] = None self.subproject_varname: T.Optional[str] = None - self.subproject_kwargs = {'default_options': default_options or []} + self.subproject_kwargs = {'default_options': default_options or {}} self.names: T.List[str] = [] self.forcefallback: bool = False self.nofallback: bool = False @@ -114,12 +114,11 @@ class DependencyFallbacksHolder(MesonInterpreterObject): # dependency('foo', static: true) should implicitly add # default_options: ['default_library=static'] static = kwargs.get('static') - default_options = stringlistify(func_kwargs.get('default_options', [])) - if static is not None and not any('default_library' in i for i in default_options): + default_options = func_kwargs.get('default_options', {}) + if static is not None and 'default_library' not in default_options: default_library = 'static' if static else 'shared' - opt = f'default_library={default_library}' - mlog.log(f'Building fallback subproject with {opt}') - default_options.append(opt) + mlog.log(f'Building fallback subproject with default_library={default_library}') + default_options[OptionKey('default_library')] = default_library func_kwargs['default_options'] = default_options # Configure the subproject diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 5908becd2..c5e25d303 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -80,6 +80,7 @@ from .type_checking import ( INSTALL_TAG_KW, LANGUAGE_KW, NATIVE_KW, + OVERRIDE_OPTIONS_KW, PRESERVE_PATH_KW, REQUIRED_KW, SOURCES_KW, @@ -890,7 +891,7 @@ class Interpreter(InterpreterBase, HoldableObject): mlog.log('Subproject', mlog.bold(subp_name), ':', 'skipped: feature', mlog.bold(feature), 'disabled') return self.disabled_subproject(subp_name, disabled_feature=feature) - default_options = coredata.create_options_dict(kwargs['default_options'], subp_name) + default_options = {k.evolve(subproject=subp_name): v for k, v in kwargs['default_options'].items()} if subp_name == '': raise InterpreterException('Subproject name must not be empty.') @@ -1196,13 +1197,17 @@ class Interpreter(InterpreterBase, HoldableObject): self.coredata.update_project_options(oi.options) self.add_build_def_file(option_file) + 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. - self.project_default_options = coredata.create_options_dict( - kwargs['default_options'], self.subproject) - + # # 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 @@ -1694,7 +1699,7 @@ class Interpreter(InterpreterBase, HoldableObject): mlog.bold(' '.join(args))) sp_kwargs: kwtypes.DoSubproject = { 'required': required, - 'default_options': [], + 'default_options': {}, 'version': [], 'cmake_options': [], 'options': None, @@ -1739,10 +1744,10 @@ class Interpreter(InterpreterBase, HoldableObject): @FeatureNewKwargs('dependency', '0.50.0', ['not_found_message', 'cmake_module_path', 'cmake_args']) @FeatureNewKwargs('dependency', '0.49.0', ['disabler']) @FeatureNewKwargs('dependency', '0.40.0', ['method']) - @FeatureNewKwargs('dependency', '0.38.0', ['default_options']) @disablerIfNotFound @permittedKwargs(permitted_dependency_kwargs) @typed_pos_args('dependency', varargs=str, min_varargs=1) + @typed_kwargs('dependency', DEFAULT_OPTIONS.evolve(since='0.38.0'), allow_unknown=True) def func_dependency(self, node: mparser.BaseNode, args: T.Tuple[T.List[str]], kwargs) -> Dependency: # Replace '' by empty list of names names = [n for n in args[0] if n] @@ -1794,6 +1799,7 @@ class Interpreter(InterpreterBase, HoldableObject): @FeatureDeprecatedKwargs('executable', '0.56.0', ['gui_app'], extra_message="Use 'win_subsystem' instead.") @permittedKwargs(build.known_exe_kwargs) @typed_pos_args('executable', str, varargs=(str, mesonlib.File, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList, build.StructuredSources, build.ExtractedObjects, build.BuildTarget)) + @typed_kwargs('executable', OVERRIDE_OPTIONS_KW, allow_unknown=True) def func_executable(self, node: mparser.BaseNode, args: T.Tuple[str, T.List[BuildTargetSource]], kwargs) -> build.Executable: @@ -1801,6 +1807,7 @@ class Interpreter(InterpreterBase, HoldableObject): @permittedKwargs(build.known_stlib_kwargs) @typed_pos_args('static_library', str, varargs=(str, mesonlib.File, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList, build.StructuredSources, build.ExtractedObjects, build.BuildTarget)) + @typed_kwargs('static_library', OVERRIDE_OPTIONS_KW, allow_unknown=True) def func_static_lib(self, node: mparser.BaseNode, args: T.Tuple[str, T.List[BuildTargetSource]], kwargs) -> build.StaticLibrary: @@ -1808,6 +1815,7 @@ class Interpreter(InterpreterBase, HoldableObject): @permittedKwargs(build.known_shlib_kwargs) @typed_pos_args('shared_library', str, varargs=(str, mesonlib.File, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList, build.StructuredSources, build.ExtractedObjects, build.BuildTarget)) + @typed_kwargs('shared_library', OVERRIDE_OPTIONS_KW, allow_unknown=True) def func_shared_lib(self, node: mparser.BaseNode, args: T.Tuple[str, T.List[BuildTargetSource]], kwargs) -> build.SharedLibrary: @@ -1817,6 +1825,7 @@ class Interpreter(InterpreterBase, HoldableObject): @permittedKwargs(known_library_kwargs) @typed_pos_args('both_libraries', str, varargs=(str, mesonlib.File, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList, build.StructuredSources, build.ExtractedObjects, build.BuildTarget)) + @typed_kwargs('both_libraries', OVERRIDE_OPTIONS_KW, allow_unknown=True) def func_both_lib(self, node: mparser.BaseNode, args: T.Tuple[str, T.List[BuildTargetSource]], kwargs) -> build.BothLibraries: @@ -1825,6 +1834,7 @@ class Interpreter(InterpreterBase, HoldableObject): @FeatureNew('shared_module', '0.37.0') @permittedKwargs(build.known_shmod_kwargs) @typed_pos_args('shared_module', str, varargs=(str, mesonlib.File, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList, build.StructuredSources, build.ExtractedObjects, build.BuildTarget)) + @typed_kwargs('shared_module', OVERRIDE_OPTIONS_KW, allow_unknown=True) def func_shared_module(self, node: mparser.BaseNode, args: T.Tuple[str, T.List[BuildTargetSource]], kwargs) -> build.SharedModule: @@ -1832,6 +1842,7 @@ class Interpreter(InterpreterBase, HoldableObject): @permittedKwargs(known_library_kwargs) @typed_pos_args('library', str, varargs=(str, mesonlib.File, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList, build.StructuredSources, build.ExtractedObjects, build.BuildTarget)) + @typed_kwargs('library', OVERRIDE_OPTIONS_KW, allow_unknown=True) def func_library(self, node: mparser.BaseNode, args: T.Tuple[str, T.List[BuildTargetSource]], kwargs) -> build.Executable: @@ -1839,6 +1850,7 @@ class Interpreter(InterpreterBase, HoldableObject): @permittedKwargs(build.known_jar_kwargs) @typed_pos_args('jar', str, varargs=(str, mesonlib.File, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList, build.ExtractedObjects, build.BuildTarget)) + @typed_kwargs('jar', OVERRIDE_OPTIONS_KW, allow_unknown=True) def func_jar(self, node: mparser.BaseNode, args: T.Tuple[str, T.List[T.Union[str, mesonlib.File, build.GeneratedTypes]]], kwargs) -> build.Jar: @@ -1847,6 +1859,7 @@ class Interpreter(InterpreterBase, HoldableObject): @FeatureNewKwargs('build_target', '0.40.0', ['link_whole', 'override_options']) @permittedKwargs(known_build_target_kwargs) @typed_pos_args('build_target', str, varargs=(str, mesonlib.File, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList, build.StructuredSources, build.ExtractedObjects, build.BuildTarget)) + @typed_kwargs('build_target', OVERRIDE_OPTIONS_KW, allow_unknown=True) def func_build_target(self, node: mparser.BaseNode, args: T.Tuple[str, T.List[BuildTargetSource]], kwargs) -> T.Union[build.Executable, build.StaticLibrary, build.SharedLibrary, diff --git a/mesonbuild/interpreter/kwargs.py b/mesonbuild/interpreter/kwargs.py index 3c1cb00d9..972b0f3d9 100644 --- a/mesonbuild/interpreter/kwargs.py +++ b/mesonbuild/interpreter/kwargs.py @@ -12,7 +12,7 @@ from typing_extensions import TypedDict, Literal, Protocol from .. import build from .. import coredata from ..compilers import Compiler -from ..mesonlib import MachineChoice, File, FileMode, FileOrString +from ..mesonlib import MachineChoice, File, FileMode, FileOrString, OptionKey from ..modules.cmake import CMakeSubprojectOptions from ..programs import ExternalProgram @@ -203,7 +203,7 @@ class Project(TypedDict): version: T.Optional[FileOrString] meson_version: T.Optional[str] - default_options: T.List[str] + default_options: T.Dict[OptionKey, str] license: T.List[str] subproject_dir: str @@ -298,13 +298,13 @@ class ConfigureFile(TypedDict): class Subproject(ExtractRequired): - default_options: T.List[str] + default_options: T.Dict[OptionKey, str] version: T.List[str] class DoSubproject(ExtractRequired): - default_options: T.List[str] + default_options: T.Dict[OptionKey, str] version: T.List[str] cmake_options: T.List[str] options: T.Optional[CMakeSubprojectOptions] diff --git a/mesonbuild/interpreter/type_checking.py b/mesonbuild/interpreter/type_checking.py index 2d2b916eb..e950da06f 100644 --- a/mesonbuild/interpreter/type_checking.py +++ b/mesonbuild/interpreter/type_checking.py @@ -281,21 +281,25 @@ COMMAND_KW: KwargInfo[T.List[T.Union[str, BuildTarget, CustomTarget, CustomTarge default=[], ) -def _override_options_convertor(raw: T.List[str]) -> T.Dict[OptionKey, str]: - output: T.Dict[OptionKey, str] = {} - for each in raw: - k, v = split_equal_string(each) - output[OptionKey.from_string(k)] = v - return output - - -OVERRIDE_OPTIONS_KW: KwargInfo[T.List[str]] = KwargInfo( +def _override_options_convertor(raw: T.Union[str, T.List[str], T.Dict[str, str]]) -> T.Dict[OptionKey, str]: + if isinstance(raw, str): + raw = [raw] + if isinstance(raw, list): + output: T.Dict[OptionKey, str] = {} + for each in raw: + k, v = split_equal_string(each) + output[OptionKey.from_string(k)] = v + return output + return {OptionKey.from_string(k): v for k, v in raw.items()} + + +OVERRIDE_OPTIONS_KW: KwargInfo[T.Union[str, T.Dict[str, str], T.List[str]]] = KwargInfo( 'override_options', - ContainerTypeInfo(list, str), - listify=True, - default=[], + (str, ContainerTypeInfo(list, str), ContainerTypeInfo(dict, str)), + default={}, validator=_options_validator, convertor=_override_options_convertor, + since_values={dict: '1.2.0'}, ) @@ -385,14 +389,7 @@ INCLUDE_DIRECTORIES: KwargInfo[T.List[T.Union[str, IncludeDirs]]] = KwargInfo( default=[], ) -# for cases like default_options and override_options -DEFAULT_OPTIONS: KwargInfo[T.List[str]] = KwargInfo( - 'default_options', - ContainerTypeInfo(list, str), - listify=True, - default=[], - validator=_options_validator, -) +DEFAULT_OPTIONS = OVERRIDE_OPTIONS_KW.evolve(name='default_options') ENV_METHOD_KW = KwargInfo('method', str, default='set', since='0.62.0', validator=in_set_validator({'set', 'prepend', 'append'})) diff --git a/mesonbuild/modules/cmake.py b/mesonbuild/modules/cmake.py index 25129e64a..d37c832ab 100644 --- a/mesonbuild/modules/cmake.py +++ b/mesonbuild/modules/cmake.py @@ -431,7 +431,7 @@ class CmakeModule(ExtensionModule): 'required': kwargs_['required'], 'options': kwargs_['options'], 'cmake_options': kwargs_['cmake_options'], - 'default_options': [], + 'default_options': {}, 'version': [], } subp = self.interpreter.do_subproject(dirname, 'cmake', kw)