diff options
-rw-r--r-- | docs/markdown/Reference-manual.md | 3 | ||||
-rw-r--r-- | docs/markdown/snippets/per_subproject_builtin.md | 15 | ||||
-rw-r--r-- | mesonbuild/coredata.py | 58 | ||||
-rw-r--r-- | mesonbuild/interpreter.py | 13 | ||||
-rw-r--r-- | mesonbuild/mintro.py | 15 | ||||
-rw-r--r-- | test cases/common/229 persubproject options/foo.c | 5 | ||||
-rw-r--r-- | test cases/common/229 persubproject options/meson.build | 10 | ||||
-rw-r--r-- | test cases/common/229 persubproject options/subprojects/sub1/foo.c | 5 | ||||
-rw-r--r-- | test cases/common/229 persubproject options/subprojects/sub1/meson.build | 7 | ||||
-rw-r--r-- | test cases/common/229 persubproject options/subprojects/sub2/foo.c | 5 | ||||
-rw-r--r-- | test cases/common/229 persubproject options/subprojects/sub2/meson.build | 7 |
11 files changed, 117 insertions, 26 deletions
diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md index ae6d1c5..510d443 100644 --- a/docs/markdown/Reference-manual.md +++ b/docs/markdown/Reference-manual.md @@ -1553,7 +1553,8 @@ arguments: that override those set in the subproject's `meson_options.txt` (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) + any default options in build files). *Since 0.54.0* `default_library` + built-in option can also be overridden. - `version` keyword argument that works just like the one in `dependency`. It specifies what version the subproject should be, as an example `>=1.0.1` diff --git a/docs/markdown/snippets/per_subproject_builtin.md b/docs/markdown/snippets/per_subproject_builtin.md new file mode 100644 index 0000000..44ed1c8 --- /dev/null +++ b/docs/markdown/snippets/per_subproject_builtin.md @@ -0,0 +1,15 @@ +## Per subproject `default_library` option + +The `default_library` built-in option can now be defined per subproject. This is +useful for example when building shared libraries in the main project, but static +link a subproject. + +Most of the time this would be used either by the parent project by setting +subproject's default_options (e.g. `subproject('foo', default_options: 'default_library=static')`), +or by the user using the command line `-Dfoo:default_library=static`. + +The value is overriden in this order: +- Value from parent project +- Value from subproject's default_options if set +- Value from subproject() default_options if set +- Value from command line if set diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index cdee20d..aea2d90 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -364,11 +364,12 @@ class CoreData: self.install_guid = str(uuid.uuid4()).upper() self.target_guids = {} self.version = version - self.init_builtins() - self.backend_options = {} # : T.Dict[str, UserOption] - self.user_options = {} # : T.Dict[str, UserOption] + self.builtins = {} # : OptionDictType + self.builtins_per_machine = PerMachine({}, {}) + self.backend_options = {} # : OptionDictType + self.user_options = {} # : OptionDictType self.compiler_options = PerMachine({}, {}) - self.base_options = {} # : T.Dict[str, UserOption] + self.base_options = {} # : OptionDictType self.cross_files = self.__load_config_files(options, scratch_dir, 'cross') self.compilers = PerMachine(OrderedDict(), OrderedDict()) @@ -378,6 +379,7 @@ class CoreData: self.compiler_check_cache = OrderedDict() # Only to print a warning if it changes between Meson invocations. self.config_files = self.__load_config_files(options, scratch_dir, 'native') + self.init_builtins('') self.libdir_cross_fixup() @staticmethod @@ -497,15 +499,25 @@ class CoreData: raise MesonException(msg.format(option, value, prefix)) return value.as_posix() - def init_builtins(self): + def init_builtins(self, subproject: str): # Create builtin options with default values - self.builtins = {} for key, opt in builtin_options.items(): - self.builtins[key] = opt.init_option(key, default_prefix()) - self.builtins_per_machine = PerMachine({}, {}) + self.add_builtin_option(self.builtins, key, opt, subproject) for for_machine in iter(MachineChoice): for key, opt in builtin_options_per_machine.items(): - self.builtins_per_machine[for_machine][key] = opt.init_option() + self.add_builtin_option(self.builtins_per_machine[for_machine], key, opt, subproject) + + def add_builtin_option(self, opts_map, key, opt, subproject): + if subproject: + if opt.yielding: + # This option is global and not per-subproject + return + optname = subproject + ':' + key + value = opts_map[key].value + else: + optname = key + value = None + opts_map[optname] = opt.init_option(key, value, default_prefix()) def init_backend_options(self, backend_name): if backend_name == 'ninja': @@ -520,15 +532,20 @@ class CoreData: 'Default project to execute in Visual Studio', '') - def get_builtin_option(self, optname): + def get_builtin_option(self, optname, subproject=''): + raw_optname = optname + if subproject: + optname = subproject + ':' + optname for opts in self._get_all_builtin_options(): v = opts.get(optname) + if v is None or v.yielding: + v = opts.get(raw_optname) if v is None: continue - if optname == 'wrap_mode': + if raw_optname == 'wrap_mode': return WrapMode.from_string(v.value) return v.value - raise RuntimeError('Tried to get unknown builtin option %s.' % optname) + raise RuntimeError('Tried to get unknown builtin option %s.' % raw_optname) def _try_set_builtin_option(self, optname, value): for opts in self._get_all_builtin_options(): @@ -707,11 +724,13 @@ class CoreData: env.cmd_line_options.setdefault(k, v) # Set default options as if they were passed to the command line. - # Subprojects can only define default for user options. + # Subprojects can only define default for user options and not yielding + # builtin option. from . import optinterpreter for k, v in default_options.items(): if subproject: - if optinterpreter.is_invalid_name(k, log=False): + if (k not in builtin_options or builtin_options[k].yielding) \ + and optinterpreter.is_invalid_name(k, log=False): continue k = subproject + ':' + k env.cmd_line_options.setdefault(k, v) @@ -951,7 +970,7 @@ class BuiltinOption(T.Generic[_T, _U]): Currently doesn't support UserIntegerOption, or a few other cases. """ - def __init__(self, opt_type: T.Type[_U], description: str, default: T.Any, yielding: T.Optional[bool] = None, *, + def __init__(self, opt_type: T.Type[_U], description: str, default: T.Any, yielding: bool = True, *, choices: T.Any = None): self.opt_type = opt_type self.description = description @@ -959,9 +978,11 @@ class BuiltinOption(T.Generic[_T, _U]): self.choices = choices self.yielding = yielding - def init_option(self, name: str = 'prefix', prefix: str = '') -> _U: + def init_option(self, name: str, value: T.Optional[T.Any], prefix: str) -> _U: """Create an instance of opt_type and return it.""" - keywords = {'yielding': self.yielding, 'value': self.prefixed_default(name, prefix)} + if value is None: + value = self.prefixed_default(name, prefix) + keywords = {'yielding': self.yielding, 'value': value} if self.choices: keywords['choices'] = self.choices return self.opt_type(self.description, **keywords) @@ -1036,7 +1057,8 @@ builtin_options = OrderedDict([ ('buildtype', BuiltinOption(UserComboOption, 'Build type to use', 'debug', choices=['plain', 'debug', 'debugoptimized', 'release', 'minsize', 'custom'])), ('debug', BuiltinOption(UserBooleanOption, 'Debug', True)), - ('default_library', BuiltinOption(UserComboOption, 'Default library type', 'shared', choices=['shared', 'static', 'both'])), + ('default_library', BuiltinOption(UserComboOption, 'Default library type', 'shared', choices=['shared', 'static', 'both'], + yielding=False)), ('errorlogs', BuiltinOption(UserBooleanOption, "Whether to print the logs from failing tests", True)), ('install_umask', BuiltinOption(UserUmaskOption, 'Default umask to apply on permissions of installed files', '022')), ('layout', BuiltinOption(UserComboOption, 'Build directory layout', 'mirror', choices=['mirror', 'flat'])), diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index 74882b2..d824e3c 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -2750,19 +2750,21 @@ external dependencies (including libraries) must go to "dependencies".''') return result def get_option_internal(self, optname): + raw_optname = optname + if self.is_subproject(): + optname = self.subproject + ':' + optname + for opts in chain( [self.coredata.base_options, compilers.base_options, self.coredata.builtins], self.coredata.get_prefixed_options_per_machine(self.coredata.builtins_per_machine), self.coredata.get_prefixed_options_per_machine(self.coredata.compiler_options), ): v = opts.get(optname) + if v is None or v.yielding: + v = opts.get(raw_optname) if v is not None: return v - raw_optname = optname - if self.is_subproject(): - optname = self.subproject + ':' + optname - try: opt = self.coredata.user_options[optname] if opt.yielding and ':' in optname and raw_optname in self.coredata.user_options: @@ -2869,6 +2871,7 @@ external dependencies (including libraries) must go to "dependencies".''') if self.environment.first_invocation: default_options = self.project_default_options default_options.update(self.default_project_options) + self.coredata.init_builtins(self.subproject) else: default_options = {} self.coredata.set_default_options(default_options, self.subproject, self.environment) @@ -4368,7 +4371,7 @@ Try setting b_lundef to false instead.'''.format(self.coredata.base_options['b_s return BothLibrariesHolder(shared_holder, static_holder, self) def build_library(self, node, args, kwargs): - default_library = self.coredata.get_builtin_option('default_library') + default_library = self.coredata.get_builtin_option('default_library', self.subproject) if default_library == 'shared': return self.build_target(node, args, kwargs, SharedLibraryHolder) elif default_library == 'static': diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index dff5ecc..cfa4574 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -180,9 +180,10 @@ def list_targets(builddata: build.Build, installdata, backend: backends.Backend) return tlist def list_buildoptions_from_source(intr: IntrospectionInterpreter) -> T.List[T.Dict[str, T.Union[str, bool, int, T.List[str]]]]: - return list_buildoptions(intr.coredata) + subprojects = [i['name'] for i in intr.project_data['subprojects']] + return list_buildoptions(intr.coredata, subprojects) -def list_buildoptions(coredata: cdata.CoreData) -> T.List[T.Dict[str, T.Union[str, bool, int, T.List[str]]]]: +def list_buildoptions(coredata: cdata.CoreData, subprojects: T.Optional[T.List[str]] = None) -> T.List[T.Dict[str, T.Union[str, bool, int, T.List[str]]]]: optlist = [] # type: T.List[T.Dict[str, T.Union[str, bool, int, T.List[str]]]] dir_option_names = ['bindir', @@ -206,6 +207,16 @@ def list_buildoptions(coredata: cdata.CoreData) -> T.List[T.Dict[str, T.Union[st test_options = {k: o for k, o in coredata.builtins.items() if k in test_option_names} core_options = {k: o for k, o in coredata.builtins.items() if k in core_option_names} + if subprojects: + # Add per subproject built-in options + sub_core_options = {} + for sub in subprojects: + for k, o in core_options.items(): + if o.yielding: + continue + sub_core_options[sub + ':' + k] = o + core_options.update(sub_core_options) + def add_keys(options: T.Dict[str, cdata.UserOption], section: str, machine: str = 'any') -> None: for key in sorted(options.keys()): opt = options[key] diff --git a/test cases/common/229 persubproject options/foo.c b/test cases/common/229 persubproject options/foo.c new file mode 100644 index 0000000..63e4de6 --- /dev/null +++ b/test cases/common/229 persubproject options/foo.c @@ -0,0 +1,5 @@ +int foo(void); + +int foo(void) { + return 0; +} diff --git a/test cases/common/229 persubproject options/meson.build b/test cases/common/229 persubproject options/meson.build new file mode 100644 index 0000000..6a76697 --- /dev/null +++ b/test cases/common/229 persubproject options/meson.build @@ -0,0 +1,10 @@ +project('persubproject options', 'c', default_options : ['default_library=both']) + +assert(get_option('default_library') == 'both', 'Parent default_library should be "both"') + +# Check it build both by calling a method only both_libraries target implement +lib = library('lib1', 'foo.c') +lib.get_static_lib() + +subproject('sub1') +subproject('sub2', default_options : ['default_library=static']) diff --git a/test cases/common/229 persubproject options/subprojects/sub1/foo.c b/test cases/common/229 persubproject options/subprojects/sub1/foo.c new file mode 100644 index 0000000..63e4de6 --- /dev/null +++ b/test cases/common/229 persubproject options/subprojects/sub1/foo.c @@ -0,0 +1,5 @@ +int foo(void); + +int foo(void) { + return 0; +} diff --git a/test cases/common/229 persubproject options/subprojects/sub1/meson.build b/test cases/common/229 persubproject options/subprojects/sub1/meson.build new file mode 100644 index 0000000..7afc934 --- /dev/null +++ b/test cases/common/229 persubproject options/subprojects/sub1/meson.build @@ -0,0 +1,7 @@ +project('sub1', 'c') + +assert(get_option('default_library') == 'both', 'Should inherit parent project default_library') + +# Check it build both by calling a method only both_libraries target implement +lib = library('lib1', 'foo.c') +lib.get_static_lib() diff --git a/test cases/common/229 persubproject options/subprojects/sub2/foo.c b/test cases/common/229 persubproject options/subprojects/sub2/foo.c new file mode 100644 index 0000000..63e4de6 --- /dev/null +++ b/test cases/common/229 persubproject options/subprojects/sub2/foo.c @@ -0,0 +1,5 @@ +int foo(void); + +int foo(void) { + return 0; +} diff --git a/test cases/common/229 persubproject options/subprojects/sub2/meson.build b/test cases/common/229 persubproject options/subprojects/sub2/meson.build new file mode 100644 index 0000000..546884d --- /dev/null +++ b/test cases/common/229 persubproject options/subprojects/sub2/meson.build @@ -0,0 +1,7 @@ +project('sub2', 'c', default_options : ['default_library=shared']) + +assert(get_option('default_library') == 'static', 'Parent should override default_library') + +# If it doesn't build only a static library, it would make target name clash. +library('lib1', 'foo.c') +shared_library('lib1', 'foo.c') |