holders: Introduce BothLibraries

pull/8903/head
Daniel Mensinger 4 years ago
parent c2c7f7c9d7
commit 84a3e459a8
  1. 20
      mesonbuild/build.py
  2. 12
      mesonbuild/interpreter/interpreter.py
  3. 121
      mesonbuild/interpreter/interpreterobjects.py
  4. 29
      mesonbuild/modules/pkgconfig.py

@ -1251,6 +1251,8 @@ You probably should put it in link_with instead.''')
def link(self, target):
for t in unholder(listify(target)):
if isinstance(t, BothLibraries):
t = t.get_preferred_library()
if isinstance(self, StaticLibrary) and self.need_install:
if isinstance(t, (CustomTarget, CustomTargetIndex)):
if not t.should_install():
@ -1279,6 +1281,9 @@ You probably should put it in link_with instead.''')
def link_whole(self, target):
for t in unholder(listify(target)):
# Always use the static library from BothLibraries, since shared libs aren't supported anyway
if isinstance(t, BothLibraries):
t = t.static
if isinstance(t, (CustomTarget, CustomTargetIndex)):
if not t.is_linkable_target():
raise InvalidArguments(f'Custom target {t!r} is not linkable.')
@ -1840,6 +1845,8 @@ class SharedLibrary(BuildTarget):
self.gcc_import_filename = None
# The debugging information file this target will generate
self.debug_filename = None
# Use by the pkgconfig module
self.shared_library_only = False
super().__init__(name, subdir, subproject, for_machine, sources, objects, environment, kwargs)
if 'rust' in self.compilers:
# If no crate type is specified, or it's the generic lib type, use dylib
@ -2159,6 +2166,19 @@ class SharedModule(SharedLibrary):
def get_default_install_dir(self, environment):
return environment.get_shared_module_dir()
class BothLibraries(HoldableObject):
def __init__(self, shared: SharedLibrary, static: StaticLibrary) -> None:
self._preferred_library = 'shared'
self.shared = shared
self.static = static
def get_preferred_library(self) -> BuildTarget:
if self._preferred_library == 'shared':
return self.shared
elif self._preferred_library == 'static':
return self.static
raise MesonBugException(f'self._preferred_library == "{self._preferred_library}" is neither "shared" nor "static".')
class CommandBase:
def flatten_command(self, cmd):
cmd = unholder(listify(cmd))

@ -2517,7 +2517,7 @@ Try setting b_lundef to false instead.'''.format(self.coredata.options[OptionKey
@FeatureNew('both_libraries', '0.46.0')
def build_both_libraries(self, node, args, kwargs):
shared_holder = self.build_target(node, args, kwargs, SharedLibraryHolder)
shared_lib = self.build_target(node, args, kwargs, build.SharedLibrary)
# Check if user forces non-PIC static library.
pic = True
@ -2543,21 +2543,21 @@ Try setting b_lundef to false instead.'''.format(self.coredata.options[OptionKey
static_args = [args[0]]
static_kwargs = kwargs.copy()
static_kwargs['sources'] = []
static_kwargs['objects'] = shared_holder.held_object.extract_all_objects()
static_kwargs['objects'] = shared_lib.extract_all_objects()
else:
static_args = args
static_kwargs = kwargs
static_holder = self.build_target(node, static_args, static_kwargs, StaticLibraryHolder)
static_lib = self.build_target(node, static_args, static_kwargs, build.StaticLibrary)
return BothLibrariesHolder(shared_holder, static_holder, self)
return build.BothLibraries(shared_lib, static_lib)
def build_library(self, node, args, kwargs):
default_library = self.coredata.get_option(OptionKey('default_library', subproject=self.subproject))
if default_library == 'shared':
return self.build_target(node, args, kwargs, SharedLibraryHolder)
return self.build_target(node, args, kwargs, build.SharedLibrary)
elif default_library == 'static':
return self.build_target(node, args, kwargs, StaticLibraryHolder)
return self.build_target(node, args, kwargs, build.StaticLibrary)
elif default_library == 'both':
return self.build_both_libraries(node, args, kwargs)
else:

@ -799,18 +799,10 @@ class MutableModuleObjectHolder(ModuleObjectHolder, MutableInterpreterObject):
modobj = copy.deepcopy(self.held_object, memo)
return MutableModuleObjectHolder(modobj, self.interpreter)
_Target = T.TypeVar('_Target', bound=build.Target)
_BuildTarget = T.TypeVar('_BuildTarget', bound=T.Union[build.BuildTarget, build.BothLibraries])
class TargetHolder(ObjectHolder[_Target]):
def __init__(self, target: _Target, interp: 'Interpreter'):
super().__init__(target, subproject=interp.subproject)
self.interpreter = interp
_BuildTarget = T.TypeVar('_BuildTarget', bound=build.BuildTarget)
class BuildTargetHolder(TargetHolder[_BuildTarget]):
class BuildTargetHolder(ObjectHolder[_BuildTarget]):
def __init__(self, target: _BuildTarget, interp: 'Interpreter'):
super().__init__(target, interp)
self.methods.update({'extract_objects': self.extract_objects_method,
@ -822,59 +814,67 @@ class BuildTargetHolder(TargetHolder[_BuildTarget]):
'private_dir_include': self.private_dir_include_method,
})
def __repr__(self):
def __repr__(self) -> str:
r = '<{} {}: {}>'
h = self.held_object
return r.format(self.__class__.__name__, h.get_id(), h.filename)
def is_cross(self):
return not self.held_object.environment.machines.matches_build_machine(self.held_object.for_machine)
@property
def _target_object(self) -> build.BuildTarget:
if isinstance(self.held_object, build.BothLibraries):
return self.held_object.get_preferred_library()
assert isinstance(self.held_object, build.BuildTarget)
return self.held_object
def is_cross(self) -> bool:
return not self._target_object.environment.machines.matches_build_machine(self._target_object.for_machine)
@noPosargs
@permittedKwargs({})
def private_dir_include_method(self, args, kwargs):
return IncludeDirsHolder(build.IncludeDirs('', [], False,
[self.interpreter.backend.get_target_private_dir(self.held_object)]))
@noKwargs
def private_dir_include_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> build.IncludeDirs:
return build.IncludeDirs('', [], False, [self.interpreter.backend.get_target_private_dir(self._target_object)])
@noPosargs
@permittedKwargs({})
def full_path_method(self, args, kwargs):
return self.interpreter.backend.get_target_filename_abs(self.held_object)
@noKwargs
def full_path_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
return self.interpreter.backend.get_target_filename_abs(self._target_object)
@noPosargs
@permittedKwargs({})
def outdir_method(self, args, kwargs):
return self.interpreter.backend.get_target_dir(self.held_object)
@noKwargs
def outdir_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
return self.interpreter.backend.get_target_dir(self._target_object)
@permittedKwargs({})
def extract_objects_method(self, args, kwargs):
gobjs = self.held_object.extract_objects(args)
return GeneratedObjectsHolder(gobjs)
@noKwargs
@typed_pos_args('extract_objects', varargs=(mesonlib.File, str))
def extract_objects_method(self, args: T.Tuple[T.List[mesonlib.FileOrString]], kwargs: TYPE_nkwargs) -> build.ExtractedObjects:
return self._target_object.extract_objects(args[0])
@FeatureNewKwargs('extract_all_objects', '0.46.0', ['recursive'])
@noPosargs
@permittedKwargs({'recursive'})
def extract_all_objects_method(self, args, kwargs):
recursive = kwargs.get('recursive', False)
gobjs = self.held_object.extract_all_objects(recursive)
if gobjs.objlist and 'recursive' not in kwargs:
mlog.warning('extract_all_objects called without setting recursive '
'keyword argument. Meson currently defaults to '
'non-recursive to maintain backward compatibility but '
'the default will be changed in the future.',
location=self.current_node)
return GeneratedObjectsHolder(gobjs)
@typed_kwargs(
'extract_all_objects',
KwargInfo(
'recursive', bool, default=False, since='0.46.0',
not_set_warning=textwrap.dedent('''\
extract_all_objects called without setting recursive
keyword argument. Meson currently defaults to
non-recursive to maintain backward compatibility but
the default will be changed in the future.
''')
)
)
def extract_all_objects_method(self, args: T.List[TYPE_nvar], kwargs: 'kwargs.BuildTargeMethodExtractAllObjects') -> build.ExtractedObjects:
return self._target_object.extract_all_objects(kwargs['recursive'])
@noPosargs
@permittedKwargs({})
def get_id_method(self, args, kwargs):
return self.held_object.get_id()
@noKwargs
def get_id_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
return self._target_object.get_id()
@FeatureNew('name', '0.54.0')
@noPosargs
@permittedKwargs({})
def name_method(self, args, kwargs):
return self.held_object.name
@noKwargs
def name_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
return self._target_object.name
class ExecutableHolder(BuildTargetHolder[build.Executable]):
pass
@ -883,37 +883,32 @@ class StaticLibraryHolder(BuildTargetHolder[build.StaticLibrary]):
pass
class SharedLibraryHolder(BuildTargetHolder[build.SharedLibrary]):
def __init__(self, target: build.SharedLibrary, interp: 'Interpreter'):
super().__init__(target, interp)
# Set to True only when called from self.func_shared_lib().
target.shared_library_only = False
pass
class BothLibrariesHolder(BuildTargetHolder):
def __init__(self, shared_holder, static_holder, interp):
class BothLibrariesHolder(BuildTargetHolder[build.BothLibraries]):
def __init__(self, libs: build.BothLibraries, interp: 'Interpreter'):
# FIXME: This build target always represents the shared library, but
# that should be configurable.
super().__init__(shared_holder.held_object, interp)
self.shared_holder = shared_holder
self.static_holder = static_holder
super().__init__(libs, interp)
self.methods.update({'get_shared_lib': self.get_shared_lib_method,
'get_static_lib': self.get_static_lib_method,
})
def __repr__(self):
def __repr__(self) -> str:
r = '<{} {}: {}, {}: {}>'
h1 = self.shared_holder.held_object
h2 = self.static_holder.held_object
h1 = self.held_object.shared
h2 = self.held_object.static
return r.format(self.__class__.__name__, h1.get_id(), h1.filename, h2.get_id(), h2.filename)
@noPosargs
@permittedKwargs({})
def get_shared_lib_method(self, args, kwargs):
return self.shared_holder
@noKwargs
def get_shared_lib_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> build.SharedLibrary:
return self.held_object.shared
@noPosargs
@permittedKwargs({})
def get_static_lib_method(self, args, kwargs):
return self.static_holder
@noKwargs
def get_static_lib_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> build.StaticLibrary:
return self.held_object.static
class SharedModuleHolder(BuildTargetHolder[build.SharedModule]):
pass

@ -110,6 +110,7 @@ class DependenciesHelper:
def _process_libs(self, libs, public):
libs = mesonlib.unholder(mesonlib.listify(libs))
libs = [x.get_preferred_library() if isinstance(x, build.BothLibraries) else x for x in libs]
processed_libs = []
processed_reqs = []
processed_cflags = []
@ -136,7 +137,7 @@ class DependenciesHelper:
if obj.found():
processed_libs += obj.get_link_args()
processed_cflags += obj.get_compile_args()
elif isinstance(obj, build.SharedLibrary) and shared_library_only:
elif isinstance(obj, build.SharedLibrary) and obj.shared_library_only:
# Do not pull dependencies for shared libraries because they are
# only required for static linking. Adding private requires has
# the side effect of exposing their cflags, which is the
@ -161,7 +162,7 @@ class DependenciesHelper:
elif isinstance(obj, str):
processed_libs.append(obj)
else:
raise mesonlib.MesonException('library argument not a string, library or dependency object.')
raise mesonlib.MesonException(f'library argument of type {type(obj).__name__} not a string, library or dependency object.')
return processed_libs, processed_reqs, processed_cflags
@ -330,9 +331,9 @@ class PkgConfigModule(ExtensionModule):
except ValueError:
return subdir.as_posix()
def generate_pkgconfig_file(self, state, deps, subdirs, name, description,
url, version, pcfile, conflicts, variables,
unescaped_variables, uninstalled=False, dataonly=False):
def _generate_pkgconfig_file(self, state, deps, subdirs, name, description,
url, version, pcfile, conflicts, variables,
unescaped_variables, uninstalled=False, dataonly=False):
coredata = state.environment.get_coredata()
if uninstalled:
outdir = os.path.join(state.environment.build_dir, 'meson-uninstalled')
@ -452,6 +453,19 @@ class PkgConfigModule(ExtensionModule):
if cflags and not dataonly:
ofile.write('Cflags: {}\n'.format(' '.join(cflags)))
@staticmethod
def _handle_both_libraries(args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> T.Tuple[T.List[TYPE_var], TYPE_kwargs]:
def _do_extract(arg: TYPE_var) -> TYPE_var:
if isinstance(arg, list):
return [_do_extract(x) for x in arg]
elif isinstance(arg, dict):
return {k: _do_extract(v) for k, v in arg.items()}
elif isinstance(arg, build.BothLibraries):
return arg.get_preferred_library()
return arg
return [_do_extract(x) for x in args], {k: _do_extract(v) for k, v in kwargs.items()}
@FeatureNewKwargs('pkgconfig.generate', '0.59.0', ['unescaped_variables', 'unescaped_uninstalled_variables'])
@FeatureNewKwargs('pkgconfig.generate', '0.54.0', ['uninstalled_variables'])
@FeatureNewKwargs('pkgconfig.generate', '0.42.0', ['extra_cflags'])
@ -469,6 +483,7 @@ class PkgConfigModule(ExtensionModule):
default_name = None
mainlib = None
default_subdirs = ['.']
args, kwargs = PkgConfigModule._handle_both_libraries(args, kwargs)
if not args and 'version' not in kwargs:
FeatureNew.single_use('pkgconfig.generate implicit version keyword', '0.46.0', state.subproject)
elif len(args) == 1:
@ -556,7 +571,7 @@ class PkgConfigModule(ExtensionModule):
pkgroot = os.path.join(state.environment.coredata.get_option(mesonlib.OptionKey('libdir')), 'pkgconfig')
if not isinstance(pkgroot, str):
raise mesonlib.MesonException('Install_dir must be a string.')
self.generate_pkgconfig_file(state, deps, subdirs, name, description, url,
self._generate_pkgconfig_file(state, deps, subdirs, name, description, url,
version, pcfile, conflicts, variables,
unescaped_variables, False, dataonly)
res = build.Data([mesonlib.File(True, state.environment.get_scratch_dir(), pcfile)], pkgroot, None, state.subproject)
@ -566,7 +581,7 @@ class PkgConfigModule(ExtensionModule):
unescaped_variables = parse_variable_list(unescaped_variables)
pcfile = filebase + '-uninstalled.pc'
self.generate_pkgconfig_file(state, deps, subdirs, name, description, url,
self._generate_pkgconfig_file(state, deps, subdirs, name, description, url,
version, pcfile, conflicts, variables,
unescaped_variables, uninstalled=True, dataonly=dataonly)
# Associate the main library with this generated pc file. If the library

Loading…
Cancel
Save