diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 5f5dd6b38..9837d5a8d 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -20,7 +20,7 @@ from . import environment from . import dependencies from . import mlog from .mesonlib import File, MesonException, listify, extract_as_list -from .mesonlib import flatten, typeslistify, stringlistify, classify_unity_sources +from .mesonlib import typeslistify, stringlistify, classify_unity_sources from .mesonlib import get_filenames_templates_dict, substitute_values from .environment import for_windows, for_darwin, for_cygwin from .compilers import is_object, clike_langs, sort_clike, lang_suffixes @@ -682,7 +682,7 @@ class BuildTarget(Target): if 'd' in self.compilers: self.add_compiler_args('d', self.compilers['d'].get_feature_args(dfeatures)) - self.link_args = flatten(kwargs.get('link_args', [])) + self.link_args = extract_as_list(kwargs, 'link_args') for i in self.link_args: if not isinstance(i, str): raise InvalidArguments('Link_args arguments must be strings.') @@ -856,9 +856,7 @@ You probably should put it in link_with instead.''') return self.external_deps def link(self, target): - for t in flatten(target): - if hasattr(t, 'held_object'): - t = t.held_object + for t in listify(target, unholder=True): if not t.is_linkable_target(): raise InvalidArguments('Link target {!r} is not linkable.'.format(t)) if isinstance(self, SharedLibrary) and isinstance(t, StaticLibrary) and not t.pic: @@ -870,9 +868,7 @@ You probably should put it in link_with instead.''') self.link_targets.append(t) def link_whole(self, target): - for t in flatten(target): - if hasattr(t, 'held_object'): - t = t.held_object + for t in listify(target, unholder=True): if not isinstance(t, StaticLibrary): raise InvalidArguments('{!r} is not a static library.'.format(t)) if isinstance(self, SharedLibrary) and not t.pic: @@ -915,7 +911,7 @@ You probably should put it in link_with instead.''') self.include_dirs += ids def add_compiler_args(self, language, args): - args = flatten(args) + args = listify(args) for a in args: if not isinstance(a, (str, File)): raise InvalidArguments('A non-string passed to compiler args.') @@ -1546,11 +1542,9 @@ class CustomTarget(Target): return deps def flatten_command(self, cmd): - cmd = listify(cmd) + cmd = listify(cmd, unholder=True) final_cmd = [] for c in cmd: - if hasattr(c, 'held_object'): - c = c.held_object if isinstance(c, str): final_cmd.append(c) elif isinstance(c, File): @@ -1573,12 +1567,7 @@ class CustomTarget(Target): def process_kwargs(self, kwargs): super().process_kwargs(kwargs) - sources = flatten(kwargs.get('input', [])) - self.sources = [] - for s in sources: - if hasattr(s, 'held_object'): - s = s.held_object - self.sources.append(s) + self.sources = extract_as_list(kwargs, 'input', unholder=True) if 'output' not in kwargs: raise InvalidArguments('Missing keyword argument "output".') self.outputs = listify(kwargs['output']) diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py index 7c7f986f0..0d9742d42 100644 --- a/mesonbuild/dependencies/base.py +++ b/mesonbuild/dependencies/base.py @@ -23,7 +23,7 @@ from enum import Enum from .. import mlog from .. import mesonlib -from ..mesonlib import MesonException, Popen_safe, flatten, version_compare_many, listify +from ..mesonlib import MesonException, Popen_safe, version_compare_many, listify # These must be defined in this file to avoid cyclical references. @@ -586,7 +586,7 @@ class ExtraFrameworkDependency(ExternalDependency): def get_dep_identifier(name, kwargs, want_cross): # Need immutable objects since the identifier will be used as a dict key - version_reqs = flatten(kwargs.get('version', [])) + version_reqs = listify(kwargs.get('version', [])) if isinstance(version_reqs, list): version_reqs = frozenset(version_reqs) identifier = (name, version_reqs, want_cross) @@ -599,7 +599,7 @@ def get_dep_identifier(name, kwargs, want_cross): continue # All keyword arguments are strings, ints, or lists (or lists of lists) if isinstance(value, list): - value = frozenset(flatten(value)) + value = frozenset(listify(value)) identifier += (key, value) return identifier diff --git a/mesonbuild/dependencies/dev.py b/mesonbuild/dependencies/dev.py index d2dd1075e..387300a37 100644 --- a/mesonbuild/dependencies/dev.py +++ b/mesonbuild/dependencies/dev.py @@ -21,7 +21,7 @@ import shutil from .. import mlog from .. import mesonlib -from ..mesonlib import version_compare, Popen_safe +from ..mesonlib import version_compare, Popen_safe, stringlistify, extract_as_list from .base import DependencyException, ExternalDependency, PkgConfigDependency class GTestDependency(ExternalDependency): @@ -185,7 +185,7 @@ class LLVMDependency(ExternalDependency): raise DependencyException('Could not generate modules for LLVM.') self.modules = shlex.split(out) - modules = mesonlib.stringlistify(mesonlib.flatten(kwargs.get('modules', []))) + modules = stringlistify(extract_as_list(kwargs, 'modules')) for mod in sorted(set(modules)): if mod not in self.modules: mlog.log('LLVM module', mod, 'found:', mlog.red('NO')) diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index 7641514d0..b9380807b 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -48,6 +48,14 @@ def stringifyUserArguments(args): raise InvalidArguments('Function accepts only strings, integers, lists and lists thereof.') +class ObjectHolder: + def __init__(self, obj): + self.held_object = obj + + def __repr__(self): + return ''.format(self.held_object) + + class TryRunResultHolder(InterpreterObject): def __init__(self, res): super().__init__() @@ -117,17 +125,18 @@ class RunProcess(InterpreterObject): def stderr_method(self, args, kwargs): return self.stderr -class ConfigureFileHolder(InterpreterObject): +class ConfigureFileHolder(InterpreterObject, ObjectHolder): def __init__(self, subdir, sourcename, targetname, configuration_data): InterpreterObject.__init__(self) - self.held_object = build.ConfigureFile(subdir, sourcename, targetname, configuration_data) + ObjectHolder.__init__(self, build.ConfigureFile(subdir, sourcename, + targetname, configuration_data)) -class EnvironmentVariablesHolder(MutableInterpreterObject): +class EnvironmentVariablesHolder(MutableInterpreterObject, ObjectHolder): def __init__(self): - super().__init__() - self.held_object = build.EnvironmentVariables() + MutableInterpreterObject.__init__(self) + ObjectHolder.__init__(self, build.EnvironmentVariables()) self.methods.update({'set': self.set_method, 'append': self.append_method, 'prepend': self.prepend_method, @@ -158,11 +167,11 @@ class EnvironmentVariablesHolder(MutableInterpreterObject): self.add_var(self.held_object.prepend, args, kwargs) -class ConfigurationDataHolder(MutableInterpreterObject): +class ConfigurationDataHolder(MutableInterpreterObject, ObjectHolder): def __init__(self): - super().__init__() + MutableInterpreterObject.__init__(self) self.used = False # These objects become immutable after use in configure_file. - self.held_object = build.ConfigurationData() + ObjectHolder.__init__(self, build.ConfigurationData()) self.methods.update({'set': self.set_method, 'set10': self.set10_method, 'set_quoted': self.set_quoted_method, @@ -242,10 +251,10 @@ class ConfigurationDataHolder(MutableInterpreterObject): # Interpreter objects can not be pickled so we must have # these wrappers. -class DependencyHolder(InterpreterObject): +class DependencyHolder(InterpreterObject, ObjectHolder): def __init__(self, dep): InterpreterObject.__init__(self) - self.held_object = dep + ObjectHolder.__init__(self, dep) self.methods.update({'found': self.found_method, 'type_name': self.type_name_method, 'version': self.version_method, @@ -272,10 +281,10 @@ class DependencyHolder(InterpreterObject): raise InterpreterException('Variable name must be a string.') return self.held_object.get_pkgconfig_variable(varname) -class InternalDependencyHolder(InterpreterObject): +class InternalDependencyHolder(InterpreterObject, ObjectHolder): def __init__(self, dep): InterpreterObject.__init__(self) - self.held_object = dep + ObjectHolder.__init__(self, dep) self.methods.update({'found': self.found_method, 'version': self.version_method, }) @@ -286,10 +295,10 @@ class InternalDependencyHolder(InterpreterObject): def version_method(self, args, kwargs): return self.held_object.get_version() -class ExternalProgramHolder(InterpreterObject): +class ExternalProgramHolder(InterpreterObject, ObjectHolder): def __init__(self, ep): InterpreterObject.__init__(self) - self.held_object = ep + ObjectHolder.__init__(self, ep) self.methods.update({'found': self.found_method, 'path': self.path_method}) @@ -308,10 +317,10 @@ class ExternalProgramHolder(InterpreterObject): def get_name(self): return self.held_object.get_name() -class ExternalLibraryHolder(InterpreterObject): +class ExternalLibraryHolder(InterpreterObject, ObjectHolder): def __init__(self, el): InterpreterObject.__init__(self) - self.held_object = el + ObjectHolder.__init__(self, el) self.methods.update({'found': self.found_method}) def found(self): @@ -332,11 +341,11 @@ class ExternalLibraryHolder(InterpreterObject): def get_exe_args(self): return self.held_object.get_exe_args() -class GeneratorHolder(InterpreterObject): +class GeneratorHolder(InterpreterObject, ObjectHolder): def __init__(self, interpreter, args, kwargs): - super().__init__() + InterpreterObject.__init__(self) self.interpreter = interpreter - self.held_object = build.Generator(args, kwargs) + ObjectHolder.__init__(self, build.Generator(args, kwargs)) self.methods.update({'process': self.process_method}) def process_method(self, args, kwargs): @@ -345,13 +354,13 @@ class GeneratorHolder(InterpreterObject): return GeneratedListHolder(gl) -class GeneratedListHolder(InterpreterObject): +class GeneratedListHolder(InterpreterObject, ObjectHolder): def __init__(self, arg1, extra_args=[]): - super().__init__() + InterpreterObject.__init__(self) if isinstance(arg1, GeneratorHolder): - self.held_object = build.GeneratedList(arg1.held_object, extra_args) + ObjectHolder.__init__(self, build.GeneratedList(arg1.held_object, extra_args)) else: - self.held_object = arg1 + ObjectHolder.__init__(self, arg1) def __repr__(self): r = '<{}: {!r}>' @@ -360,14 +369,15 @@ class GeneratedListHolder(InterpreterObject): def add_file(self, a): self.held_object.add_file(a) -class BuildMachine(InterpreterObject): +class BuildMachine(InterpreterObject, ObjectHolder): def __init__(self, compilers): self.compilers = compilers InterpreterObject.__init__(self) - self.held_object = environment.MachineInfo(environment.detect_system(), - environment.detect_cpu_family(self.compilers), - environment.detect_cpu(self.compilers), - sys.byteorder) + held_object = environment.MachineInfo(environment.detect_system(), + environment.detect_cpu_family(self.compilers), + environment.detect_cpu(self.compilers), + sys.byteorder) + ObjectHolder.__init__(self, held_object) self.methods.update({'system': self.system_method, 'cpu_family': self.cpu_family_method, 'cpu': self.cpu_method, @@ -388,7 +398,7 @@ class BuildMachine(InterpreterObject): # This class will provide both host_machine and # target_machine -class CrossMachineInfo(InterpreterObject): +class CrossMachineInfo(InterpreterObject, ObjectHolder): def __init__(self, cross_info): InterpreterObject.__init__(self) minimum_cross_info = {'cpu', 'cpu_family', 'endian', 'system'} @@ -397,10 +407,11 @@ class CrossMachineInfo(InterpreterObject): 'Machine info is currently {}\n'.format(cross_info) + 'but is missing {}.'.format(minimum_cross_info - set(cross_info))) self.info = cross_info - self.held_object = environment.MachineInfo(cross_info['system'], - cross_info['cpu_family'], - cross_info['cpu'], - cross_info['endian']) + minfo = environment.MachineInfo(cross_info['system'], + cross_info['cpu_family'], + cross_info['cpu'], + cross_info['endian']) + ObjectHolder.__init__(self, minfo) self.methods.update({'system': self.system_method, 'cpu': self.cpu_method, 'cpu_family': self.cpu_family_method, @@ -419,10 +430,10 @@ class CrossMachineInfo(InterpreterObject): def endian_method(self, args, kwargs): return self.held_object.endian -class IncludeDirsHolder(InterpreterObject): +class IncludeDirsHolder(InterpreterObject, ObjectHolder): def __init__(self, idobj): - super().__init__() - self.held_object = idobj + InterpreterObject.__init__(self) + ObjectHolder.__init__(self, idobj) class Headers(InterpreterObject): @@ -447,10 +458,10 @@ class Headers(InterpreterObject): def get_custom_install_dir(self): return self.custom_install_dir -class DataHolder(InterpreterObject): +class DataHolder(InterpreterObject, ObjectHolder): def __init__(self, data): - super().__init__() - self.held_object = data + InterpreterObject.__init__(self) + ObjectHolder.__init__(self, data) def get_source_subdir(self): return self.held_object.source_subdir @@ -495,20 +506,20 @@ class Man(InterpreterObject): def get_sources(self): return self.sources -class GeneratedObjectsHolder(InterpreterObject): +class GeneratedObjectsHolder(InterpreterObject, ObjectHolder): def __init__(self, held_object): - super().__init__() - self.held_object = held_object + InterpreterObject.__init__(self) + ObjectHolder.__init__(self, held_object) -class TargetHolder(InterpreterObject): - def __init__(self): - super().__init__() +class TargetHolder(InterpreterObject, ObjectHolder): + def __init__(self, target, interp): + InterpreterObject.__init__(self) + ObjectHolder.__init__(self, target) + self.interpreter = interp class BuildTargetHolder(TargetHolder): def __init__(self, target, interp): - super().__init__() - self.held_object = target - self.interpreter = interp + super().__init__(target, interp) self.methods.update({'extract_objects': self.extract_objects_method, 'extract_all_objects': self.extract_all_objects_method, 'get_id': self.get_id_method, @@ -566,16 +577,14 @@ class JarHolder(BuildTargetHolder): def __init__(self, target, interp): super().__init__(target, interp) -class CustomTargetIndexHolder(InterpreterObject): +class CustomTargetIndexHolder(InterpreterObject, ObjectHolder): def __init__(self, object_to_hold): - super().__init__() - self.held_object = object_to_hold + InterpreterObject.__init__(self) + ObjectHolder.__init__(self, object_to_hold) class CustomTargetHolder(TargetHolder): - def __init__(self, object_to_hold, interp): - super().__init__() - self.held_object = object_to_hold - self.interpreter = interp + def __init__(self, target, interp): + super().__init__(target, interp) self.methods.update({'full_path': self.full_path_method, }) @@ -596,10 +605,10 @@ class CustomTargetHolder(TargetHolder): def __delitem__(self, index): raise InterpreterException('Cannot delete a member of a CustomTarget') -class RunTargetHolder(InterpreterObject): +class RunTargetHolder(InterpreterObject, ObjectHolder): def __init__(self, name, command, args, dependencies, subdir): - super().__init__() - self.held_object = build.RunTarget(name, command, args, dependencies, subdir) + InterpreterObject.__init__(self) + ObjectHolder.__init__(self, build.RunTarget(name, command, args, dependencies, subdir)) def __repr__(self): r = '<{} {}: {}>' @@ -625,11 +634,11 @@ class Test(InterpreterObject): def get_name(self): return self.name -class SubprojectHolder(InterpreterObject): +class SubprojectHolder(InterpreterObject, ObjectHolder): def __init__(self, subinterpreter): - super().__init__() - self.held_object = subinterpreter + InterpreterObject.__init__(self) + ObjectHolder.__init__(self, subinterpreter) self.methods.update({'get_variable': self.get_variable_method, }) @@ -1056,11 +1065,11 @@ ModuleState = namedtuple('ModuleState', [ 'man', 'global_args', 'project_args', 'build_machine', 'host_machine', 'target_machine']) -class ModuleHolder(InterpreterObject): +class ModuleHolder(InterpreterObject, ObjectHolder): def __init__(self, modname, module, interpreter): InterpreterObject.__init__(self) + ObjectHolder.__init__(self, module) self.modname = modname - self.held_object = module self.interpreter = interpreter def method_call(self, method_name, args, kwargs): @@ -1551,12 +1560,11 @@ class Interpreter(InterpreterBase): version = kwargs.get('version', self.project_version) if not isinstance(version, str): raise InterpreterException('Version must be a string.') - incs = extract_as_list(kwargs, 'include_directories') - libs = extract_as_list(kwargs, 'link_with') + incs = extract_as_list(kwargs, 'include_directories', unholder=True) + libs = extract_as_list(kwargs, 'link_with', unholder=True) sources = extract_as_list(kwargs, 'sources') - sources = self.source_strings_to_files(self.flatten(sources)) - deps = self.flatten(kwargs.get('dependencies', [])) - deps = listify(deps) + sources = listify(self.source_strings_to_files(sources), unholder=True) + deps = extract_as_list(kwargs, 'dependencies', unholder=True) compile_args = mesonlib.stringlistify(kwargs.get('compile_args', [])) link_args = mesonlib.stringlistify(kwargs.get('link_args', [])) final_deps = [] @@ -1568,13 +1576,8 @@ class Interpreter(InterpreterBase): if not isinstance(d, (dependencies.Dependency, dependencies.ExternalLibrary, dependencies.InternalDependency)): raise InterpreterException('Dependencies must be external deps') final_deps.append(d) - dep = dependencies.InternalDependency(version, - mesonlib.unholder_array(incs), - compile_args, - link_args, - mesonlib.unholder_array(libs), - mesonlib.unholder_array(sources), - final_deps) + dep = dependencies.InternalDependency(version, incs, compile_args, + link_args, libs, sources, final_deps) return DependencyHolder(dep) @noKwargs @@ -1629,7 +1632,7 @@ class Interpreter(InterpreterBase): 'or not executable'.format(cmd)) cmd = prog expanded_args = [] - for a in mesonlib.flatten(cargs): + for a in listify(cargs): if isinstance(a, str): expanded_args.append(a) elif isinstance(a, mesonlib.File): @@ -2299,11 +2302,7 @@ to directly access options of other subprojects.''') raise InterpreterException('Run_target needs at least one positional argument.') cleaned_args = [] - for i in mesonlib.flatten(all_args): - try: - i = i.held_object - except AttributeError: - pass + for i in listify(all_args, unholder=True): if not isinstance(i, (str, build.BuildTarget, build.CustomTarget, dependencies.ExternalProgram, mesonlib.File)): mlog.debug('Wrong type:', str(i)) raise InterpreterException('Invalid argument to run_target.') @@ -2374,11 +2373,10 @@ to directly access options of other subprojects.''') par = kwargs.get('is_parallel', True) if not isinstance(par, bool): raise InterpreterException('Keyword argument is_parallel must be a boolean.') - cmd_args = extract_as_list(kwargs, 'args') + cmd_args = extract_as_list(kwargs, 'args', unholder=True) for i in cmd_args: - if not isinstance(i, (str, mesonlib.File, TargetHolder)): + if not isinstance(i, (str, mesonlib.File, build.Target)): raise InterpreterException('Command line arguments must be strings, files or targets.') - cmd_args = mesonlib.unholder_array(cmd_args) env = self.unpack_env_kwarg(kwargs) should_fail = kwargs.get('should_fail', False) if not isinstance(should_fail, bool): @@ -2796,7 +2794,8 @@ different subdirectory. elif isinstance(s, str): s = mesonlib.File.from_source_file(self.environment.source_dir, self.subdir, s) else: - raise InterpreterException("Source item is not string or File-type object.") + raise InterpreterException('Source item is {!r} instead of ' + 'string or File-type object'.format(s)) results.append(s) return results @@ -2822,7 +2821,7 @@ different subdirectory. if not args: raise InterpreterException('Target does not have a name.') name = args[0] - sources = args[1:] + sources = listify(args[1:]) if self.environment.is_cross_build(): if kwargs.get('native', False): is_cross = False @@ -2830,19 +2829,14 @@ different subdirectory. is_cross = True else: is_cross = False - try: - kw_src = self.flatten(kwargs['sources']) - kw_src = listify(kw_src) - except KeyError: - kw_src = [] - sources += kw_src + if 'sources' in kwargs: + sources += listify(kwargs['sources']) sources = self.source_strings_to_files(sources) - objs = self.flatten(kwargs.get('objects', [])) - kwargs['dependencies'] = self.flatten(kwargs.get('dependencies', [])) + objs = extract_as_list(kwargs, 'objects') + kwargs['dependencies'] = extract_as_list(kwargs, 'dependencies') if 'extra_files' in kwargs: ef = extract_as_list(kwargs, 'extra_files') kwargs['extra_files'] = self.source_strings_to_files(ef) - objs = listify(objs) self.check_sources_exist(os.path.join(self.source_root, self.subdir), sources) if targetholder is ExecutableHolder: targetclass = build.Executable diff --git a/mesonbuild/mesonlib.py b/mesonbuild/mesonlib.py index 71134a8a9..5c4c3747b 100644 --- a/mesonbuild/mesonlib.py +++ b/mesonbuild/mesonlib.py @@ -199,17 +199,6 @@ def classify_unity_sources(compilers, sources): compsrclist[comp].append(src) return compsrclist -def flatten(item): - if not isinstance(item, list): - return [item] - result = [] - for i in item: - if isinstance(i, list): - result += flatten(i) - else: - result.append(i) - return result - def is_osx(): return platform.system().lower() == 'darwin' @@ -474,24 +463,45 @@ def replace_if_different(dst, dst_tmp): else: os.unlink(dst_tmp) - -def listify(*args): +def listify(item, flatten=True, unholder=False): ''' - Returns a list with all args embedded in a list if they are not of type list. + Returns a list with all args embedded in a list if they are not a list. This function preserves order. + @flatten: Convert lists of lists to a flat list + @unholder: Replace each item with the object it holds, if required + + Note: unholding only works recursively when flattening ''' - if len(args) == 1: # Special case with one single arg - return args[0] if type(args[0]) is list else [args[0]] - return [item if type(item) is list else [item] for item in args] + if not isinstance(item, list): + if unholder and hasattr(item, 'held_object'): + item = item.held_object + return [item] + result = [] + for i in item: + if unholder and hasattr(i, 'held_object'): + i = i.held_object + if flatten and isinstance(i, list): + result += listify(i, flatten=True, unholder=unholder) + else: + result.append(i) + return result -def extract_as_list(dict_object, *keys, pop = False): +def extract_as_list(dict_object, *keys, pop=False, **kwargs): ''' Extracts all values from given dict_object and listifies them. ''' + result = [] + fetch = dict_object.get if pop: - return listify(*[dict_object.pop(key, []) for key in keys]) - return listify(*[dict_object.get(key, []) for key in keys]) + fetch = dict_object.pop + # If there's only one key, we don't return a list with one element + if len(keys) == 1: + return listify(fetch(keys[0], []), **kwargs) + # Return a list of values corresponding to *keys + for key in keys: + result.append(listify(fetch(key, []), **kwargs)) + return result def typeslistify(item, types): @@ -750,15 +760,6 @@ def windows_proof_rmtree(f): # Try one last time and throw if it fails. shutil.rmtree(f) -def unholder_array(entries): - result = [] - entries = flatten(entries) - for e in entries: - if hasattr(e, 'held_object'): - e = e.held_object - result.append(e) - return result - class OrderedSet(collections.MutableSet): """A set that preserves the order in which items are added, by first insertion. diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index fa2bbb4f1..1f813da92 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -20,7 +20,7 @@ import os import copy import subprocess from . import ModuleReturnValue -from ..mesonlib import MesonException, OrderedSet, Popen_safe +from ..mesonlib import MesonException, OrderedSet, Popen_safe, extract_as_list from ..dependencies import Dependency, PkgConfigDependency, InternalDependency from .. import mlog from .. import mesonlib @@ -323,11 +323,9 @@ class GnomeModule(ExtensionModule): cflags = OrderedSet() ldflags = OrderedSet() gi_includes = OrderedSet() - deps = mesonlib.listify(deps) + deps = mesonlib.listify(deps, unholder=True) for dep in deps: - if hasattr(dep, 'held_object'): - dep = dep.held_object if isinstance(dep, InternalDependency): cflags.update(get_include_args(dep.include_directories)) for lib in dep.libraries: @@ -378,7 +376,7 @@ class GnomeModule(ExtensionModule): elif isinstance(dep, (build.StaticLibrary, build.SharedLibrary)): cflags.update(get_include_args(dep.get_include_dirs())) else: - mlog.log('dependency %s not handled to build gir files' % dep) + mlog.log('dependency {!r} not handled to build gir files'.format(dep)) continue if gir_has_extra_lib_arg() and use_gir_args: @@ -417,7 +415,7 @@ class GnomeModule(ExtensionModule): raise MesonException('gobject-introspection dependency was not found, gir cannot be generated.') ns = kwargs.pop('namespace') nsversion = kwargs.pop('nsversion') - libsources = mesonlib.flatten(kwargs.pop('sources')) + libsources = mesonlib.extract_as_list(kwargs, 'sources', pop=True) girfile = '%s-%s.gir' % (ns, nsversion) srcdir = os.path.join(state.environment.get_source_dir(), state.subdir) builddir = os.path.join(state.environment.get_build_dir(), state.subdir) @@ -531,9 +529,8 @@ class GnomeModule(ExtensionModule): else: raise MesonException('Gir export packages must be str or list') - deps = mesonlib.extract_as_list(kwargs, 'dependencies', pop = True) deps = (girtarget.get_all_link_deps() + girtarget.get_external_deps() + - deps) + extract_as_list(kwargs, 'dependencies', pop=True, unholder=True)) # Need to recursively add deps on GirTarget sources from our # dependencies and also find the include directories needed for the # typelib generation custom target below. @@ -800,7 +797,8 @@ This will become a hard error in the future.''') def _get_build_args(self, kwargs, state): args = [] - cflags, ldflags, gi_includes = self._get_dependencies_flags(kwargs.get('dependencies', []), state, include_rpath=True) + deps = extract_as_list(kwargs, 'dependencies', unholder=True) + cflags, ldflags, gi_includes = self._get_dependencies_flags(deps, state, include_rpath=True) inc_dirs = mesonlib.extract_as_list(kwargs, 'include_directories') for incd in inc_dirs: if not isinstance(incd.held_object, (str, build.IncludeDirs)): diff --git a/run_unittests.py b/run_unittests.py index b21771446..7ae994726 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -31,6 +31,7 @@ import mesonbuild.compilers import mesonbuild.environment import mesonbuild.mesonlib import mesonbuild.coredata +from mesonbuild.interpreter import ObjectHolder from mesonbuild.mesonlib import is_linux, is_windows, is_osx, is_cygwin, windows_proof_rmtree from mesonbuild.environment import Environment from mesonbuild.dependencies import DependencyException @@ -62,7 +63,6 @@ def get_soname(fname): def get_rpath(fname): return get_dynamic_section_entry(fname, r'(?:rpath|runpath)') - class InternalTests(unittest.TestCase): def test_version_number(self): @@ -398,6 +398,49 @@ class InternalTests(unittest.TestCase): self.assertEqual(forced_value, desired_value) + def test_listify(self): + listify = mesonbuild.mesonlib.listify + # Test sanity + self.assertEqual([1], listify(1)) + self.assertEqual([], listify([])) + self.assertEqual([1], listify([1])) + # Test flattening + self.assertEqual([1, 2, 3], listify([1, [2, 3]])) + self.assertEqual([1, 2, 3], listify([1, [2, [3]]])) + self.assertEqual([1, [2, [3]]], listify([1, [2, [3]]], flatten=False)) + # Test flattening and unholdering + holder1 = ObjectHolder(1) + holder3 = ObjectHolder(3) + self.assertEqual([holder1], listify(holder1)) + self.assertEqual([holder1], listify([holder1])) + self.assertEqual([holder1, 2], listify([holder1, 2])) + self.assertEqual([holder1, 2, 3], listify([holder1, 2, [3]])) + self.assertEqual([1], listify(holder1, unholder=True)) + self.assertEqual([1], listify([holder1], unholder=True)) + self.assertEqual([1, 2], listify([holder1, 2], unholder=True)) + self.assertEqual([1, 2, 3], listify([holder1, 2, [holder3]], unholder=True)) + # Unholding doesn't work recursively when not flattening + self.assertEqual([1, [2], [holder3]], listify([holder1, [2], [holder3]], unholder=True, flatten=False)) + + def test_extract_as_list(self): + extract = mesonbuild.mesonlib.extract_as_list + # Test sanity + kwargs = {'sources': [1, 2, 3]} + self.assertEqual([1, 2, 3], extract(kwargs, 'sources')) + self.assertEqual(kwargs, {'sources': [1, 2, 3]}) + self.assertEqual([1, 2, 3], extract(kwargs, 'sources', pop=True)) + self.assertEqual(kwargs, {}) + # Test unholding + holder3 = ObjectHolder(3) + kwargs = {'sources': [1, 2, holder3]} + self.assertEqual([1, 2, 3], extract(kwargs, 'sources', unholder=True)) + self.assertEqual(kwargs, {'sources': [1, 2, holder3]}) + self.assertEqual([1, 2, 3], extract(kwargs, 'sources', unholder=True, pop=True)) + self.assertEqual(kwargs, {}) + # Test listification + kwargs = {'sources': [1, 2, 3], 'pch_sources': [4, 5, 6]} + self.assertEqual([[1, 2, 3], [4, 5, 6]], extract(kwargs, 'sources', 'pch_sources')) + class BasePlatformTests(unittest.TestCase): def setUp(self): diff --git a/test cases/frameworks/7 gnome/gir/meson.build b/test cases/frameworks/7 gnome/gir/meson.build index 3598b663d..a91cb977a 100644 --- a/test cases/frameworks/7 gnome/gir/meson.build +++ b/test cases/frameworks/7 gnome/gir/meson.build @@ -27,7 +27,7 @@ gnome.generate_gir( identifier_prefix : 'Meson', includes : ['GObject-2.0', 'MesonDep1-1.0'], # dep1_dep pulls in dep2_dep for us - dependencies : [fake_dep, dep1_dep], + dependencies : [[fake_dep, dep1_dep]], install : true, build_by_default : true, # Test that unknown kwargs do not crash the parser.