Make 'default_library' per-subproject builtin option

Currently it's just like if all builtin/base/compiler options are
yielding. This patch makes possible to have non-yielding builtin
options. The value in 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
pull/6595/head
Xavier Claessens 5 years ago committed by Xavier Claessens
parent 0bf3c4ac4d
commit 3ba0073df6
  1. 3
      docs/markdown/Reference-manual.md
  2. 15
      docs/markdown/snippets/per_subproject_builtin.md
  3. 58
      mesonbuild/coredata.py
  4. 13
      mesonbuild/interpreter.py
  5. 15
      mesonbuild/mintro.py
  6. 5
      test cases/common/229 persubproject options/foo.c
  7. 10
      test cases/common/229 persubproject options/meson.build
  8. 5
      test cases/common/229 persubproject options/subprojects/sub1/foo.c
  9. 7
      test cases/common/229 persubproject options/subprojects/sub1/meson.build
  10. 5
      test cases/common/229 persubproject options/subprojects/sub2/foo.c
  11. 7
      test cases/common/229 persubproject options/subprojects/sub2/meson.build

@ -1553,7 +1553,8 @@ arguments:
that override those set in the subproject's `meson_options.txt` that override those set in the subproject's `meson_options.txt`
(like `default_options` in `project`, they only have effect when (like `default_options` in `project`, they only have effect when
Meson is run for the first time, and command line arguments override 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 - `version` keyword argument that works just like the one in
`dependency`. It specifies what version the subproject should be, `dependency`. It specifies what version the subproject should be,
as an example `>=1.0.1` as an example `>=1.0.1`

@ -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

@ -364,11 +364,12 @@ class CoreData:
self.install_guid = str(uuid.uuid4()).upper() self.install_guid = str(uuid.uuid4()).upper()
self.target_guids = {} self.target_guids = {}
self.version = version self.version = version
self.init_builtins() self.builtins = {} # : OptionDictType
self.backend_options = {} # : T.Dict[str, UserOption] self.builtins_per_machine = PerMachine({}, {})
self.user_options = {} # : T.Dict[str, UserOption] self.backend_options = {} # : OptionDictType
self.user_options = {} # : OptionDictType
self.compiler_options = PerMachine({}, {}) 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.cross_files = self.__load_config_files(options, scratch_dir, 'cross')
self.compilers = PerMachine(OrderedDict(), OrderedDict()) self.compilers = PerMachine(OrderedDict(), OrderedDict())
@ -378,6 +379,7 @@ class CoreData:
self.compiler_check_cache = OrderedDict() self.compiler_check_cache = OrderedDict()
# Only to print a warning if it changes between Meson invocations. # Only to print a warning if it changes between Meson invocations.
self.config_files = self.__load_config_files(options, scratch_dir, 'native') self.config_files = self.__load_config_files(options, scratch_dir, 'native')
self.init_builtins('')
self.libdir_cross_fixup() self.libdir_cross_fixup()
@staticmethod @staticmethod
@ -497,15 +499,25 @@ class CoreData:
raise MesonException(msg.format(option, value, prefix)) raise MesonException(msg.format(option, value, prefix))
return value.as_posix() return value.as_posix()
def init_builtins(self): def init_builtins(self, subproject: str):
# Create builtin options with default values # Create builtin options with default values
self.builtins = {}
for key, opt in builtin_options.items(): for key, opt in builtin_options.items():
self.builtins[key] = opt.init_option(key, default_prefix()) self.add_builtin_option(self.builtins, key, opt, subproject)
self.builtins_per_machine = PerMachine({}, {})
for for_machine in iter(MachineChoice): for for_machine in iter(MachineChoice):
for key, opt in builtin_options_per_machine.items(): 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): def init_backend_options(self, backend_name):
if backend_name == 'ninja': if backend_name == 'ninja':
@ -520,15 +532,20 @@ class CoreData:
'Default project to execute in Visual Studio', '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(): for opts in self._get_all_builtin_options():
v = opts.get(optname) v = opts.get(optname)
if v is None or v.yielding:
v = opts.get(raw_optname)
if v is None: if v is None:
continue continue
if optname == 'wrap_mode': if raw_optname == 'wrap_mode':
return WrapMode.from_string(v.value) return WrapMode.from_string(v.value)
return 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): def _try_set_builtin_option(self, optname, value):
for opts in self._get_all_builtin_options(): for opts in self._get_all_builtin_options():
@ -707,11 +724,13 @@ class CoreData:
env.cmd_line_options.setdefault(k, v) env.cmd_line_options.setdefault(k, v)
# Set default options as if they were passed to the command line. # 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 from . import optinterpreter
for k, v in default_options.items(): for k, v in default_options.items():
if subproject: 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 continue
k = subproject + ':' + k k = subproject + ':' + k
env.cmd_line_options.setdefault(k, v) 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. 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): choices: T.Any = None):
self.opt_type = opt_type self.opt_type = opt_type
self.description = description self.description = description
@ -959,9 +978,11 @@ class BuiltinOption(T.Generic[_T, _U]):
self.choices = choices self.choices = choices
self.yielding = yielding 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.""" """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: if self.choices:
keywords['choices'] = self.choices keywords['choices'] = self.choices
return self.opt_type(self.description, **keywords) return self.opt_type(self.description, **keywords)
@ -1036,7 +1057,8 @@ builtin_options = OrderedDict([
('buildtype', BuiltinOption(UserComboOption, 'Build type to use', 'debug', ('buildtype', BuiltinOption(UserComboOption, 'Build type to use', 'debug',
choices=['plain', 'debug', 'debugoptimized', 'release', 'minsize', 'custom'])), choices=['plain', 'debug', 'debugoptimized', 'release', 'minsize', 'custom'])),
('debug', BuiltinOption(UserBooleanOption, 'Debug', True)), ('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)), ('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')), ('install_umask', BuiltinOption(UserUmaskOption, 'Default umask to apply on permissions of installed files', '022')),
('layout', BuiltinOption(UserComboOption, 'Build directory layout', 'mirror', choices=['mirror', 'flat'])), ('layout', BuiltinOption(UserComboOption, 'Build directory layout', 'mirror', choices=['mirror', 'flat'])),

@ -2750,19 +2750,21 @@ external dependencies (including libraries) must go to "dependencies".''')
return result return result
def get_option_internal(self, optname): def get_option_internal(self, optname):
raw_optname = optname
if self.is_subproject():
optname = self.subproject + ':' + optname
for opts in chain( for opts in chain(
[self.coredata.base_options, compilers.base_options, self.coredata.builtins], [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.builtins_per_machine),
self.coredata.get_prefixed_options_per_machine(self.coredata.compiler_options), self.coredata.get_prefixed_options_per_machine(self.coredata.compiler_options),
): ):
v = opts.get(optname) v = opts.get(optname)
if v is None or v.yielding:
v = opts.get(raw_optname)
if v is not None: if v is not None:
return v return v
raw_optname = optname
if self.is_subproject():
optname = self.subproject + ':' + optname
try: try:
opt = self.coredata.user_options[optname] opt = self.coredata.user_options[optname]
if opt.yielding and ':' in optname and raw_optname in self.coredata.user_options: 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: if self.environment.first_invocation:
default_options = self.project_default_options default_options = self.project_default_options
default_options.update(self.default_project_options) default_options.update(self.default_project_options)
self.coredata.init_builtins(self.subproject)
else: else:
default_options = {} default_options = {}
self.coredata.set_default_options(default_options, self.subproject, self.environment) 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) return BothLibrariesHolder(shared_holder, static_holder, self)
def build_library(self, node, args, kwargs): 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': if default_library == 'shared':
return self.build_target(node, args, kwargs, SharedLibraryHolder) return self.build_target(node, args, kwargs, SharedLibraryHolder)
elif default_library == 'static': elif default_library == 'static':

@ -180,9 +180,10 @@ def list_targets(builddata: build.Build, installdata, backend: backends.Backend)
return tlist return tlist
def list_buildoptions_from_source(intr: IntrospectionInterpreter) -> T.List[T.Dict[str, T.Union[str, bool, int, T.List[str]]]]: 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]]]] optlist = [] # type: T.List[T.Dict[str, T.Union[str, bool, int, T.List[str]]]]
dir_option_names = ['bindir', 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} 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} 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: def add_keys(options: T.Dict[str, cdata.UserOption], section: str, machine: str = 'any') -> None:
for key in sorted(options.keys()): for key in sorted(options.keys()):
opt = options[key] opt = options[key]

@ -0,0 +1,5 @@
int foo(void);
int foo(void) {
return 0;
}

@ -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'])

@ -0,0 +1,5 @@
int foo(void);
int foo(void) {
return 0;
}

@ -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()

@ -0,0 +1,5 @@
int foo(void);
int foo(void) {
return 0;
}

@ -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')
Loading…
Cancel
Save