diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py index 45ff96bd0..c4f963000 100644 --- a/mesonbuild/dependencies/base.py +++ b/mesonbuild/dependencies/base.py @@ -16,6 +16,7 @@ # Custom logic for several other packages are in separate files. import copy +import functools import os import re import stat @@ -265,6 +266,15 @@ class ExternalDependency(Dependency): return new + def log_details(self): + return '' + + def log_info(self): + return '' + + def log_tried(self): + return '' + class NotFoundDependency(Dependency): def __init__(self, environment): @@ -296,6 +306,8 @@ class ConfigToolDependency(ExternalDependency): self.config = None return self.version = version + if getattr(self, 'finish_init', None): + self.finish_init(self) def _sanitize_version(self, version): """Remove any non-numeric, non-point version suffixes.""" @@ -307,7 +319,7 @@ class ConfigToolDependency(ExternalDependency): return version @classmethod - def factory(cls, name, environment, language, kwargs, tools, tool_name): + def factory(cls, name, environment, language, kwargs, tools, tool_name, finish_init=None): """Constructor for use in dependencies that can be found multiple ways. In addition to the standard constructor values, this constructor sets @@ -322,7 +334,7 @@ class ConfigToolDependency(ExternalDependency): def reduce(self): return (cls._unpickle, (), self.__dict__) sub = type('{}Dependency'.format(name.capitalize()), (cls, ), - {'tools': tools, 'tool_name': tool_name, '__reduce__': reduce}) + {'tools': tools, 'tool_name': tool_name, '__reduce__': reduce, 'finish_init': staticmethod(finish_init)}) return sub(name, environment, language, kwargs) @@ -388,13 +400,9 @@ class ConfigToolDependency(ExternalDependency): else: mlog.log('Found', mlog.bold(self.tool_name), repr(req_version), mlog.red('NO')) - mlog.log('Dependency', mlog.bold(self.name), 'found:', mlog.red('NO')) - if self.required: - raise DependencyException('Dependency {} not found'.format(self.name)) return False mlog.log('Found {}:'.format(self.tool_name), mlog.bold(shutil.which(self.config)), '({})'.format(version)) - mlog.log('Dependency', mlog.bold(self.name), 'found:', mlog.green('YES')) return True def get_config_value(self, args, stage): @@ -422,6 +430,9 @@ class ConfigToolDependency(ExternalDependency): mlog.debug('Got config-tool variable {} : {}'.format(variable_name, variable)) return variable + def log_tried(self): + return self.type_name + class PkgConfigDependency(ExternalDependency): # The class's copy of the pkg-config path. Avoids having to search for it @@ -463,20 +474,12 @@ class PkgConfigDependency(ExternalDependency): if self.required: raise DependencyException('Pkg-config not found.') return - if self.want_cross: - self.type_string = 'Cross' - else: - self.type_string = 'Native' mlog.debug('Determining dependency {!r} with pkg-config executable ' '{!r}'.format(name, self.pkgbin.get_path())) ret, self.version = self._call_pkgbin(['--modversion', name]) if ret != 0: - if self.required: - raise DependencyException('{} dependency {!r} not found' - ''.format(self.type_string, name)) return - found_msg = [self.type_string + ' dependency', mlog.bold(name), 'found:'] if self.version_reqs is None: self.is_found = True else: @@ -487,14 +490,6 @@ class PkgConfigDependency(ExternalDependency): (self.is_found, not_found, found) = \ version_compare_many(self.version, self.version_reqs) if not self.is_found: - found_msg += [mlog.red('NO'), - 'found {!r} but need:'.format(self.version), - ', '.join(["'{}'".format(e) for e in not_found])] - if found: - found_msg += ['; matched:', - ', '.join(["'{}'".format(e) for e in found])] - if not self.silent: - mlog.log(*found_msg) if self.required: m = 'Invalid version of dependency, need {!r} {!r} found {!r}.' raise DependencyException(m.format(name, not_found, self.version)) @@ -505,7 +500,6 @@ class PkgConfigDependency(ExternalDependency): self._set_cargs() # Fetch the libraries and library paths needed for using this self._set_libs() - found_msg += [mlog.green('YES'), self.version] except DependencyException as e: if self.required: raise @@ -513,12 +507,7 @@ class PkgConfigDependency(ExternalDependency): self.compile_args = [] self.link_args = [] self.is_found = False - found_msg += [mlog.red('NO'), '; reason: {}'.format(str(e))] - - # Print the found message only at the very end because fetching cflags - # and libs can also fail if other needed pkg-config files aren't found. - if not self.silent: - mlog.log(*found_msg) + self.reason = e def __repr__(self): s = '<{0} {1}: {2} {3}>' @@ -712,8 +701,8 @@ class PkgConfigDependency(ExternalDependency): variable = '' if ret != 0: if self.required: - raise DependencyException('%s dependency %s not found.' % - (self.type_string, self.name)) + raise DependencyException('dependency %s not found.' % + (self.name)) else: variable = out.strip() @@ -797,6 +786,9 @@ class PkgConfigDependency(ExternalDependency): # a path rather than the raw dlname return os.path.basename(dlname) + def log_tried(self): + return self.type_name + class DubDependency(ExternalDependency): class_dubbin = None @@ -818,7 +810,6 @@ class DubDependency(ExternalDependency): if self.required: raise DependencyException('DUB not found.') self.is_found = False - mlog.log('Dependency', mlog.bold(name), 'found:', mlog.red('NO')) return mlog.debug('Determining dependency {!r} with DUB executable ' @@ -828,10 +819,7 @@ class DubDependency(ExternalDependency): ret, res = self._call_dubbin(['describe', name]) if ret != 0: - if self.required: - raise DependencyException('Dependency {!r} not found'.format(name)) self.is_found = False - mlog.log('Dependency', mlog.bold(name), 'found:', mlog.red('NO')) return j = json.loads(res) @@ -842,10 +830,7 @@ class DubDependency(ExternalDependency): msg = ['Dependency', mlog.bold(name), 'found but it was compiled with'] msg += [mlog.bold(j['compiler']), 'and we are using', mlog.bold(comp)] mlog.error(*msg) - if self.required: - raise DependencyException('Dependency {!r} not found'.format(name)) self.is_found = False - mlog.log('Dependency', mlog.bold(name), 'found:', mlog.red('NO')) return self.version = package['version'] @@ -853,7 +838,6 @@ class DubDependency(ExternalDependency): break # Check if package version meets the requirements - found_msg = ['Dependency', mlog.bold(name), 'found:'] if self.version_reqs is None: self.is_found = True else: @@ -864,21 +848,11 @@ class DubDependency(ExternalDependency): (self.is_found, not_found, found) = \ version_compare_many(self.version, self.version_reqs) if not self.is_found: - found_msg += [mlog.red('NO'), - 'found {!r} but need:'.format(self.version), - ', '.join(["'{}'".format(e) for e in not_found])] - if found: - found_msg += ['; matched:', - ', '.join(["'{}'".format(e) for e in found])] - if not self.silent: - mlog.log(*found_msg) if self.required: m = 'Invalid version of dependency, need {!r} {!r} found {!r}.' raise DependencyException(m.format(name, not_found, self.version)) return - found_msg += [mlog.green('YES'), self.version] - if self.pkg['targetFileName'].endswith('.a'): self.static = True @@ -899,14 +873,8 @@ class DubDependency(ExternalDependency): self.link_args.append(file) if not found: - if self.required: - raise DependencyException('Dependency {!r} not found'.format(name)) - self.is_found = False - mlog.log('Dependency', mlog.bold(name), 'found:', mlog.red('NO')) - return - - if not self.silent: - mlog.log(*found_msg) + self.is_found = False + return def get_compiler(self): return self.compiler @@ -952,7 +920,7 @@ class DubDependency(ExternalDependency): @staticmethod def get_methods(): - return [DependencyMethods.PKGCONFIG, DependencyMethods.DUB] + return [DependencyMethods.DUB] class ExternalProgram: windows_exts = ('exe', 'msc', 'com', 'bat', 'cmd') @@ -1229,10 +1197,6 @@ class ExtraFrameworkDependency(ExternalDependency): if self.found(): self.compile_args = ['-I' + os.path.join(self.path, self.name, 'Headers')] self.link_args = ['-F' + self.path, '-framework', self.name.split('.')[0]] - mlog.log('Dependency', mlog.bold(name), 'found:', mlog.green('YES'), - os.path.join(self.path, self.name)) - else: - mlog.log('Dependency', name, 'found:', mlog.red('NO')) def detect(self, name, path): lname = name.lower() @@ -1251,12 +1215,16 @@ class ExtraFrameworkDependency(ExternalDependency): self.name = d self.is_found = True return - if not self.found() and self.required: - raise DependencyException('Framework dependency %s not found.' % (name, )) def get_version(self): return 'unknown' + def log_info(self): + return os.path.join(self.path, self.name) + + def log_tried(self): + return 'framework' + def get_dep_identifier(name, kwargs, want_cross): # Need immutable objects since the identifier will be used as a dict key @@ -1277,54 +1245,131 @@ def get_dep_identifier(name, kwargs, want_cross): identifier += (key, value) return identifier +display_name_map = { + 'boost': 'Boost', + 'dub': 'DUB', + 'gmock': 'GMock', + 'gtest': 'GTest', + 'llvm': 'LLVM', + 'mpi': 'MPI', + 'openmp': 'OpenMP', + 'wxwidgets': 'WxWidgets', +} def find_external_dependency(name, env, kwargs): + assert(name) required = kwargs.get('required', True) if not isinstance(required, bool): raise DependencyException('Keyword "required" must be a boolean.') if not isinstance(kwargs.get('method', ''), str): raise DependencyException('Keyword "method" must be a string.') - method = kwargs.get('method', '') + lname = name.lower() + if lname not in _packages_accept_language and 'language' in kwargs: + raise DependencyException('%s dependency does not accept "language" keyword argument' % (name, )) + + # display the dependency name with correct casing + display_name = display_name_map.get(lname, lname) + + # if this isn't a cross-build, it's uninteresting if native: is used or not + if not env.is_cross_build(): + type_text = 'Dependency' + else: + type_text = 'Native' if kwargs.get('native', False) else 'Cross' + type_text += ' dependency' + + # build a list of dependency methods to try + candidates = _build_external_dependency_list(name, env, kwargs) + + pkg_exc = None + pkgdep = [] + details = '' + + for c in candidates: + # try this dependency method + try: + d = c() + pkgdep.append(d) + except Exception as e: + mlog.debug(str(e)) + # store the first exception we see + if not pkg_exc: + pkg_exc = e + else: + details = d.log_details() + if details: + details = '(' + details + ') ' + if 'language' in kwargs: + details += 'for ' + d.language + ' ' + + # if the dependency was found + if d.found(): + + info = d.log_info() + if info: + info = ', ' + info + + mlog.log(type_text, mlog.bold(display_name), details + 'found:', mlog.green('YES'), d.version + info) + + return d + + # otherwise, the dependency could not be found + tried_methods = [d.log_tried() for d in pkgdep if d.log_tried()] + if tried_methods: + tried = '{}'.format(mlog.format_list(tried_methods)) + else: + tried = '' + + mlog.log(type_text, mlog.bold(display_name), details + 'found:', mlog.red('NO'), + '(tried {})'.format(tried) if tried else '') + + if required: + # if exception(s) occurred, re-raise the first one (on the grounds that + # it came from a preferred dependency detection method) + if pkg_exc: + raise pkg_exc + + # we have a list of failed ExternalDependency objects, so we can report + # the methods we tried to find the dependency + raise DependencyException('Dependency "%s" not found, tried %s' % (name, tried)) + + # return the last failed dependency object + if pkgdep: + return pkgdep[-1] + + # this should never happen + raise DependencyException('Dependency "%s" not found, but no dependency object to return' % (name)) + + +def _build_external_dependency_list(name, env, kwargs): + # Is there a specific dependency detector for this dependency? lname = name.lower() if lname in packages: - if lname not in _packages_accept_language and 'language' in kwargs: - raise DependencyException('%s dependency does not accept "language" keyword argument' % (lname, )) - # Create the dependency object using a factory class method, if one - # exists, otherwise it is just constructed directly. + # Create the list of dependency object constructors using a factory + # class method, if one exists, otherwise the list just consists of the + # constructor if getattr(packages[lname], '_factory', None): dep = packages[lname]._factory(env, kwargs) else: - dep = packages[lname](env, kwargs) - if required and not dep.found(): - raise DependencyException('Dependency "%s" not found' % name) + dep = [functools.partial(packages[lname], env, kwargs)] return dep - if 'language' in kwargs: - # Remove check when PkgConfigDependency supports language. - raise DependencyException('%s dependency does not accept "language" keyword argument' % (lname, )) - if 'dub' == method: - dubdep = DubDependency(name, env, kwargs) - if required and not dubdep.found(): - mlog.log('Dependency', mlog.bold(name), 'found:', mlog.red('NO')) - return dubdep - pkg_exc = None - pkgdep = None - try: - pkgdep = PkgConfigDependency(name, env, kwargs) - if pkgdep.found(): - return pkgdep - except Exception as e: - pkg_exc = e + + candidates = [] + + # If it's explicitly requested, use the dub detection method (only) + if 'dub' == kwargs.get('method', ''): + candidates.append(functools.partial(DubDependency, name, env, kwargs)) + return candidates + # TBD: other values of method should control what method(s) are used + + # Otherwise, just use the pkgconfig dependency detector + candidates.append(functools.partial(PkgConfigDependency, name, env, kwargs)) + + # On OSX, also try framework dependency detector if mesonlib.is_osx(): - fwdep = ExtraFrameworkDependency(name, False, None, env, None, kwargs) - if required and not fwdep.found(): - m = 'Dependency {!r} not found, tried Extra Frameworks ' \ - 'and Pkg-Config:\n\n' + str(pkg_exc) - raise DependencyException(m.format(name)) - return fwdep - if pkg_exc is not None: - raise pkg_exc - mlog.log('Dependency', mlog.bold(name), 'found:', mlog.red('NO')) - return pkgdep + candidates.append(functools.partial(ExtraFrameworkDependency, name, + False, None, env, None, kwargs)) + + return candidates def strip_system_libdirs(environment, link_args): diff --git a/mesonbuild/dependencies/boost.py b/mesonbuild/dependencies/boost.py index 7acde2dc1..17f92400f 100644 --- a/mesonbuild/dependencies/boost.py +++ b/mesonbuild/dependencies/boost.py @@ -132,7 +132,6 @@ class BoostDependency(ExternalDependency): self.incdir = self.detect_nix_incdir() if self.check_invalid_modules(): - self.log_fail() return mlog.debug('Boost library root dir is', mlog.bold(self.boost_root)) @@ -146,12 +145,6 @@ class BoostDependency(ExternalDependency): self.detect_lib_modules() mlog.debug('Boost library directory is', mlog.bold(self.libdir)) - # 3. Report success or failure - if self.is_found: - self.log_success() - else: - self.log_fail() - def check_invalid_modules(self): invalid_modules = [c for c in self.requested_modules if 'boost_' + c not in BOOST_LIBS] @@ -172,17 +165,14 @@ class BoostDependency(ExternalDependency): else: return False - def log_fail(self): + def log_details(self): module_str = ', '.join(self.requested_modules) - mlog.log("Dependency Boost (%s) found:" % module_str, mlog.red('NO')) + return module_str - def log_success(self): - module_str = ', '.join(self.requested_modules) + def log_info(self): if self.boost_root: - info = self.version + ', ' + self.boost_root - else: - info = self.version - mlog.log('Dependency Boost (%s) found:' % module_str, mlog.green('YES'), info) + return self.boost_root + return '' def detect_nix_roots(self): return [os.path.abspath(os.path.join(x, '..')) diff --git a/mesonbuild/dependencies/dev.py b/mesonbuild/dependencies/dev.py index 4ea338575..0cd3c2bcb 100644 --- a/mesonbuild/dependencies/dev.py +++ b/mesonbuild/dependencies/dev.py @@ -45,7 +45,7 @@ class GTestDependency(ExternalDependency): if self.main: self.link_args += gtest_main_detect self.sources = [] - mlog.log('Dependency GTest found:', mlog.green('YES'), '(prebuilt)') + self.prebuilt = True elif self.detect_srcdir(): self.is_found = True self.compile_args = ['-I' + d for d in self.src_include_dirs] @@ -54,9 +54,8 @@ class GTestDependency(ExternalDependency): self.sources = [self.all_src, self.main_src] else: self.sources = [self.all_src] - mlog.log('Dependency GTest found:', mlog.green('YES'), '(building self)') + self.prebuilt = False else: - mlog.log('Dependency GTest found:', mlog.red('NO')) self.is_found = False def detect_srcdir(self): @@ -76,6 +75,12 @@ class GTestDependency(ExternalDependency): def need_threads(self): return True + def log_info(self): + if self.prebuilt: + return 'prebuilt' + else: + return 'building self' + class GMockDependency(ExternalDependency): def __init__(self, environment, kwargs): @@ -89,7 +94,7 @@ class GMockDependency(ExternalDependency): self.compile_args = [] self.link_args = gmock_detect self.sources = [] - mlog.log('Dependency GMock found:', mlog.green('YES'), '(prebuilt)') + self.prebuilt = True return for d in ['/usr/src/googletest/googlemock/src', '/usr/src/gmock/src', '/usr/src/gmock']: @@ -106,11 +111,17 @@ class GMockDependency(ExternalDependency): self.sources = [all_src, main_src] else: self.sources = [all_src] - mlog.log('Dependency GMock found:', mlog.green('YES'), '(building self)') + self.prebuilt = False return - mlog.log('Dependency GMock found:', mlog.red('NO')) + self.is_found = False + def log_info(self): + if self.prebuilt: + return 'prebuilt' + else: + return 'building self' + class LLVMDependency(ConfigToolDependency): """ @@ -145,6 +156,7 @@ class LLVMDependency(ConfigToolDependency): super().__init__('LLVM', environment, 'cpp', kwargs) self.provided_modules = [] self.required_modules = set() + self.module_details = [] if not self.is_found: return self.static = kwargs.get('static', False) @@ -237,21 +249,30 @@ class LLVMDependency(ConfigToolDependency): is required. """ for mod in sorted(set(modules)): + status = '' + if mod not in self.provided_modules: - mlog.log('LLVM module', mlog.bold(mod), 'found:', mlog.red('NO'), - '(optional)' if not required else '') if required: self.is_found = False if self.required: raise DependencyException( 'Could not find required LLVM Component: {}'.format(mod)) + status = '(missing)' + else: + status = '(missing but optional)' else: self.required_modules.add(mod) - mlog.log('LLVM module', mlog.bold(mod), 'found:', mlog.green('YES')) + + self.module_details.append(mod + status) def need_threads(self): return True + def log_details(self): + if self.module_details: + return 'modules: ' + ', '.join(self.module_details) + return '' + class ValgrindDependency(PkgConfigDependency): ''' diff --git a/mesonbuild/dependencies/misc.py b/mesonbuild/dependencies/misc.py index 745dff011..78ce51b67 100644 --- a/mesonbuild/dependencies/misc.py +++ b/mesonbuild/dependencies/misc.py @@ -14,6 +14,7 @@ # This file contains the detection logic for miscellaneous external dependencies. +import functools import os import re import shlex @@ -37,7 +38,6 @@ class MPIDependency(ExternalDependency): def __init__(self, environment, kwargs): language = kwargs.get('language', 'c') super().__init__('mpi', environment, language, kwargs) - required = kwargs.pop('required', True) kwargs['required'] = False kwargs['silent'] = True self.is_found = False @@ -103,13 +103,6 @@ class MPIDependency(ExternalDependency): self.is_found = True self.version, self.compile_args, self.link_args = result - if self.is_found: - mlog.log('Dependency', mlog.bold(self.name), 'for', self.language, 'found:', mlog.green('YES'), self.version) - else: - mlog.log('Dependency', mlog.bold(self.name), 'for', self.language, 'found:', mlog.red('NO')) - if required: - raise DependencyException('MPI dependency {!r} not found'.format(self.name)) - def _filter_compile_args(self, args): """ MPI wrappers return a bunch of garbage args. @@ -267,10 +260,6 @@ class OpenMPDependency(ExternalDependency): self.is_found = True else: mlog.log(mlog.yellow('WARNING:'), 'OpenMP found but omp.h missing.') - if self.is_found: - mlog.log('Dependency', mlog.bold(self.name), 'found:', mlog.green('YES'), self.version) - else: - mlog.log('Dependency', mlog.bold(self.name), 'found:', mlog.red('NO')) def need_openmp(self): return True @@ -326,10 +315,6 @@ class Python3Dependency(ExternalDependency): self.compile_args = fw.get_compile_args() self.link_args = fw.get_link_args() self.is_found = True - if self.is_found: - mlog.log('Dependency', mlog.bold(self.name), 'found:', mlog.green('YES')) - else: - mlog.log('Dependency', mlog.bold(self.name), 'found:', mlog.red('NO')) @staticmethod def get_windows_python_arch(): @@ -436,33 +421,29 @@ class PcapDependency(ExternalDependency): @classmethod def _factory(cls, environment, kwargs): methods = cls._process_method_kw(kwargs) + candidates = [] + if DependencyMethods.PKGCONFIG in methods: - try: - pcdep = PkgConfigDependency('pcap', environment, kwargs) - if pcdep.found(): - return pcdep - except Exception as e: - mlog.debug('Pcap not found via pkgconfig. Trying next, error was:', str(e)) + candidates.append(functools.partial(PkgConfigDependency, 'pcap', environment, kwargs)) + if DependencyMethods.CONFIG_TOOL in methods: - try: - ctdep = ConfigToolDependency.factory( - 'pcap', environment, None, kwargs, ['pcap-config'], 'pcap-config') - if ctdep.found(): - ctdep.compile_args = ctdep.get_config_value(['--cflags'], 'compile_args') - ctdep.link_args = ctdep.get_config_value(['--libs'], 'link_args') - ctdep.version = cls.get_pcap_lib_version(ctdep) - return ctdep - except Exception as e: - mlog.debug('Pcap not found via pcap-config. Trying next, error was:', str(e)) - - return PcapDependency(environment, kwargs) + candidates.append(functools.partial(ConfigToolDependency.factory, + 'pcap', environment, None, + kwargs, ['pcap-config'], + 'pcap-config', + PcapDependency.tool_finish_init)) + + return candidates + + @staticmethod + def tool_finish_init(ctdep): + ctdep.compile_args = ctdep.get_config_value(['--cflags'], 'compile_args') + ctdep.link_args = ctdep.get_config_value(['--libs'], 'link_args') + ctdep.version = PcapDependency.get_pcap_lib_version(ctdep) @staticmethod def get_methods(): - if mesonlib.is_osx(): - return [DependencyMethods.PKGCONFIG, DependencyMethods.CONFIG_TOOL, DependencyMethods.EXTRAFRAMEWORK] - else: - return [DependencyMethods.PKGCONFIG, DependencyMethods.CONFIG_TOOL] + return [DependencyMethods.PKGCONFIG, DependencyMethods.CONFIG_TOOL] @staticmethod def get_pcap_lib_version(ctdep): @@ -477,32 +458,29 @@ class CupsDependency(ExternalDependency): @classmethod def _factory(cls, environment, kwargs): methods = cls._process_method_kw(kwargs) + candidates = [] + if DependencyMethods.PKGCONFIG in methods: - try: - pcdep = PkgConfigDependency('cups', environment, kwargs) - if pcdep.found(): - return pcdep - except Exception as e: - mlog.debug('cups not found via pkgconfig. Trying next, error was:', str(e)) + candidates.append(functools.partial(PkgConfigDependency, 'cups', environment, kwargs)) + if DependencyMethods.CONFIG_TOOL in methods: - try: - ctdep = ConfigToolDependency.factory( - 'cups', environment, None, kwargs, ['cups-config'], 'cups-config') - if ctdep.found(): - ctdep.compile_args = ctdep.get_config_value(['--cflags'], 'compile_args') - ctdep.link_args = ctdep.get_config_value(['--ldflags', '--libs'], 'link_args') - return ctdep - except Exception as e: - mlog.debug('cups not found via cups-config. Trying next, error was:', str(e)) + candidates.append(functools.partial(ConfigToolDependency.factory, + 'cups', environment, None, + kwargs, ['cups-config'], + 'cups-config', CupsDependency.tool_finish_init)) + if DependencyMethods.EXTRAFRAMEWORK in methods: if mesonlib.is_osx(): - fwdep = ExtraFrameworkDependency('cups', False, None, environment, - kwargs.get('language', None), kwargs) - if fwdep.found(): - return fwdep - mlog.log('Dependency', mlog.bold('cups'), 'found:', mlog.red('NO')) + candidates.append(functools.partial( + ExtraFrameworkDependency, 'cups', False, None, environment, + kwargs.get('language', None), kwargs)) - return CupsDependency(environment, kwargs) + return candidates + + @staticmethod + def tool_finish_init(ctdep): + ctdep.compile_args = ctdep.get_config_value(['--cflags'], 'compile_args') + ctdep.link_args = ctdep.get_config_value(['--ldflags', '--libs'], 'link_args') @staticmethod def get_methods(): @@ -519,30 +497,22 @@ class LibWmfDependency(ExternalDependency): @classmethod def _factory(cls, environment, kwargs): methods = cls._process_method_kw(kwargs) + candidates = [] + if DependencyMethods.PKGCONFIG in methods: - try: - kwargs['required'] = False - pcdep = PkgConfigDependency('libwmf', environment, kwargs) - if pcdep.found(): - return pcdep - except Exception as e: - mlog.debug('LibWmf not found via pkgconfig. Trying next, error was:', str(e)) + candidates.append(functools.partial(PkgConfigDependency, 'libwmf', environment, kwargs)) + if DependencyMethods.CONFIG_TOOL in methods: - try: - ctdep = ConfigToolDependency.factory( - 'libwmf', environment, None, kwargs, ['libwmf-config'], 'libwmf-config') - if ctdep.found(): - ctdep.compile_args = ctdep.get_config_value(['--cflags'], 'compile_args') - ctdep.link_args = ctdep.get_config_value(['--libs'], 'link_args') - return ctdep - except Exception as e: - mlog.debug('cups not found via libwmf-config. Trying next, error was:', str(e)) + candidates.append(functools.partial(ConfigToolDependency.factory, + 'libwmf', environment, None, kwargs, ['libwmf-config'], 'libwmf-config', LibWmfDependency.tool_finish_init)) - return LibWmfDependency(environment, kwargs) + return candidates + + @staticmethod + def tool_finish_init(ctdep): + ctdep.compile_args = ctdep.get_config_value(['--cflags'], 'compile_args') + ctdep.link_args = ctdep.get_config_value(['--libs'], 'link_args') @staticmethod def get_methods(): - if mesonlib.is_osx(): - return [DependencyMethods.PKGCONFIG, DependencyMethods.CONFIG_TOOL, DependencyMethods.EXTRAFRAMEWORK] - else: - return [DependencyMethods.PKGCONFIG, DependencyMethods.CONFIG_TOOL] + return [DependencyMethods.PKGCONFIG, DependencyMethods.CONFIG_TOOL] diff --git a/mesonbuild/dependencies/ui.py b/mesonbuild/dependencies/ui.py index 197d22cb1..c877f510c 100644 --- a/mesonbuild/dependencies/ui.py +++ b/mesonbuild/dependencies/ui.py @@ -15,6 +15,7 @@ # This file contains the detection logic for external dependencies that # are UI-related. +import functools import os import re import subprocess @@ -37,32 +38,34 @@ from .base import ConfigToolDependency class GLDependency(ExternalDependency): def __init__(self, environment, kwargs): super().__init__('gl', environment, None, kwargs) - if DependencyMethods.SYSTEM in self.methods: - if mesonlib.is_osx(): - self.is_found = True - # FIXME: Use AppleFrameworks dependency - self.link_args = ['-framework', 'OpenGL'] - # FIXME: Detect version using self.clib_compiler - self.version = '1' - return - if mesonlib.is_windows(): - self.is_found = True - # FIXME: Use self.clib_compiler.find_library() - self.link_args = ['-lopengl32'] - # FIXME: Detect version using self.clib_compiler - self.version = '1' - return + + if mesonlib.is_osx(): + self.is_found = True + # FIXME: Use AppleFrameworks dependency + self.link_args = ['-framework', 'OpenGL'] + # FIXME: Detect version using self.clib_compiler + self.version = '1' + return + if mesonlib.is_windows(): + self.is_found = True + # FIXME: Use self.clib_compiler.find_library() + self.link_args = ['-lopengl32'] + # FIXME: Detect version using self.clib_compiler + self.version = '1' + return @classmethod def _factory(cls, environment, kwargs): - if DependencyMethods.PKGCONFIG in cls._process_method_kw(kwargs): - try: - pcdep = PkgConfigDependency('gl', environment, kwargs) - if pcdep.found(): - return pcdep - except Exception: - pass - return GLDependency(environment, kwargs) + methods = cls._process_method_kw(kwargs) + candidates = [] + + if DependencyMethods.PKGCONFIG in methods: + candidates.append(functools.partial(PkgConfigDependency, 'gl', environment, kwargs)) + + if DependencyMethods.SYSTEM in methods: + candidates.append(functools.partial(GLDependency), environment, kwargs) + + return candidates @staticmethod def get_methods(): @@ -201,12 +204,10 @@ class QtBaseDependency(ExternalDependency): self.bindir = None self.private_headers = kwargs.get('private_headers', False) mods = extract_as_list(kwargs, 'modules') + self.requested_modules = mods if not mods: raise DependencyException('No ' + self.qtname + ' modules specified.') - type_text = 'cross' if env.is_cross_build() else 'native' - found_msg = '{} {} {{}} dependency (modules: {}) found:' \ - ''.format(self.qtname, type_text, ', '.join(mods)) - from_text = 'pkg-config' + self.from_text = 'pkg-config' # Keep track of the detection methods used, for logging purposes. methods = [] @@ -215,25 +216,15 @@ class QtBaseDependency(ExternalDependency): self._pkgconfig_detect(mods, kwargs) methods.append('pkgconfig') if not self.is_found and DependencyMethods.QMAKE in self.methods: - from_text = self._qmake_detect(mods, kwargs) + self.from_text = self._qmake_detect(mods, kwargs) methods.append('qmake-' + self.name) methods.append('qmake') if not self.is_found: # Reset compile args and link args self.compile_args = [] self.link_args = [] - from_text = '(checked {})'.format(mlog.format_list(methods)) + self.from_text = mlog.format_list(methods) self.version = 'none' - if self.required: - err_msg = '{} {} dependency not found {}' \ - ''.format(self.qtname, type_text, from_text) - raise DependencyException(err_msg) - if not self.silent: - mlog.log(found_msg.format(from_text), mlog.red('NO')) - return - from_text = '`{}`'.format(from_text) - if not self.silent: - mlog.log(found_msg.format(from_text), mlog.green('YES')) def compilers_detect(self): "Detect Qt (4 or 5) moc, uic, rcc in the specified bindir or in PATH" @@ -414,6 +405,16 @@ class QtBaseDependency(ExternalDependency): def get_private_includes(self, mod_inc_dir, module): return tuple() + def log_details(self): + module_str = ', '.join(self.requested_modules) + return 'modules: ' + module_str + + def log_info(self): + return '`{}`'.format(self.from_text) + + def log_tried(self): + return self.from_text + class Qt4Dependency(QtBaseDependency): def __init__(self, env, kwargs): @@ -452,33 +453,29 @@ class SDL2Dependency(ExternalDependency): @classmethod def _factory(cls, environment, kwargs): methods = cls._process_method_kw(kwargs) + candidates = [] + if DependencyMethods.PKGCONFIG in methods: - try: - pcdep = PkgConfigDependency('sdl2', environment, kwargs) - if pcdep.found(): - return pcdep - except Exception as e: - mlog.debug('SDL 2 not found via pkgconfig. Trying next, error was:', str(e)) + candidates.append(functools.partial(PkgConfigDependency, 'sdl2', environment, kwargs)) + if DependencyMethods.CONFIG_TOOL in methods: - try: - ctdep = ConfigToolDependency.factory( - 'sdl2', environment, None, kwargs, ['sdl2-config'], 'sdl2-config') - if ctdep.found(): - ctdep.compile_args = ctdep.get_config_value(['--cflags'], 'compile_args') - ctdep.link_args = ctdep.get_config_value(['--libs'], 'link_args') - return ctdep - except Exception as e: - mlog.debug('SDL 2 not found via sdl2-config. Trying next, error was:', str(e)) + candidates.append(functools.partial(ConfigToolDependency.factory, + 'sdl2', environment, None, + kwargs, ['sdl2-config'], + 'sdl2-config', SDL2Dependency.tool_finish_init)) + if DependencyMethods.EXTRAFRAMEWORK in methods: if mesonlib.is_osx(): - fwdep = ExtraFrameworkDependency('sdl2', False, None, environment, - kwargs.get('language', None), kwargs) - if fwdep.found(): - fwdep.version = '2' # FIXME - return fwdep - mlog.log('Dependency', mlog.bold('sdl2'), 'found:', mlog.red('NO')) + candidates.append(functools.partial(ExtraFrameworkDependency, + 'sdl2', False, None, environment, + kwargs.get('language', None), kwargs)) + # fwdep.version = '2' # FIXME + return candidates - return SDL2Dependency(environment, kwargs) + @staticmethod + def tool_finish_init(ctdep): + ctdep.compile_args = ctdep.get_config_value(['--cflags'], 'compile_args') + ctdep.link_args = ctdep.get_config_value(['--libs'], 'link_args') @staticmethod def get_methods(): @@ -518,73 +515,73 @@ class VulkanDependency(ExternalDependency): def __init__(self, environment, kwargs): super().__init__('vulkan', environment, None, kwargs) - if DependencyMethods.SYSTEM in self.methods: - try: - self.vulkan_sdk = os.environ['VULKAN_SDK'] - if not os.path.isabs(self.vulkan_sdk): - raise DependencyException('VULKAN_SDK must be an absolute path.') - except KeyError: - self.vulkan_sdk = None - - if self.vulkan_sdk: - # TODO: this config might not work on some platforms, fix bugs as reported - # we should at least detect other 64-bit platforms (e.g. armv8) + try: + self.vulkan_sdk = os.environ['VULKAN_SDK'] + if not os.path.isabs(self.vulkan_sdk): + raise DependencyException('VULKAN_SDK must be an absolute path.') + except KeyError: + self.vulkan_sdk = None + + if self.vulkan_sdk: + # TODO: this config might not work on some platforms, fix bugs as reported + # we should at least detect other 64-bit platforms (e.g. armv8) + lib_name = 'vulkan' + if mesonlib.is_windows(): + lib_name = 'vulkan-1' + lib_dir = 'Lib32' + inc_dir = 'Include' + if detect_cpu({}) == 'x86_64': + lib_dir = 'Lib' + else: lib_name = 'vulkan' - if mesonlib.is_windows(): - lib_name = 'vulkan-1' - lib_dir = 'Lib32' - inc_dir = 'Include' - if detect_cpu({}) == 'x86_64': - lib_dir = 'Lib' - else: - lib_name = 'vulkan' - lib_dir = 'lib' - inc_dir = 'include' + lib_dir = 'lib' + inc_dir = 'include' - # make sure header and lib are valid - inc_path = os.path.join(self.vulkan_sdk, inc_dir) - header = os.path.join(inc_path, 'vulkan', 'vulkan.h') - lib_path = os.path.join(self.vulkan_sdk, lib_dir) - find_lib = self.clib_compiler.find_library(lib_name, environment, lib_path) + # make sure header and lib are valid + inc_path = os.path.join(self.vulkan_sdk, inc_dir) + header = os.path.join(inc_path, 'vulkan', 'vulkan.h') + lib_path = os.path.join(self.vulkan_sdk, lib_dir) + find_lib = self.clib_compiler.find_library(lib_name, environment, lib_path) - if not find_lib: - raise DependencyException('VULKAN_SDK point to invalid directory (no lib)') + if not find_lib: + raise DependencyException('VULKAN_SDK point to invalid directory (no lib)') - if not os.path.isfile(header): - raise DependencyException('VULKAN_SDK point to invalid directory (no include)') + if not os.path.isfile(header): + raise DependencyException('VULKAN_SDK point to invalid directory (no include)') - self.type_name = 'vulkan_sdk' - self.is_found = True - self.compile_args.append('-I' + inc_path) - self.link_args.append('-L' + lib_path) - self.link_args.append('-l' + lib_name) + self.type_name = 'vulkan_sdk' + self.is_found = True + self.compile_args.append('-I' + inc_path) + self.link_args.append('-L' + lib_path) + self.link_args.append('-l' + lib_name) - # TODO: find a way to retrieve the version from the sdk? - # Usually it is a part of the path to it (but does not have to be) - self.version = '1' + # TODO: find a way to retrieve the version from the sdk? + # Usually it is a part of the path to it (but does not have to be) + self.version = '1' + return + else: + # simply try to guess it, usually works on linux + libs = self.clib_compiler.find_library('vulkan', environment, []) + if libs is not None and self.clib_compiler.has_header('vulkan/vulkan.h', '', environment): + self.type_name = 'system' + self.is_found = True + self.version = 1 # TODO + for lib in libs: + self.link_args.append(lib) return - else: - # simply try to guess it, usually works on linux - libs = self.clib_compiler.find_library('vulkan', environment, []) - if libs is not None and self.clib_compiler.has_header('vulkan/vulkan.h', '', environment): - self.type_name = 'system' - self.is_found = True - self.version = 1 # TODO - for lib in libs: - self.link_args.append(lib) - return @classmethod def _factory(cls, environment, kwargs): - if DependencyMethods.PKGCONFIG in cls._process_method_kw(kwargs): - try: - pcdep = PkgConfigDependency('vulkan', environment, kwargs) - if pcdep.found(): - return pcdep - except Exception: - pass + methods = cls._process_method_kw(kwargs) + candidates = [] + + if DependencyMethods.PKGCONFIG in methods: + candidates.append(functools.partial(PkgConfigDependency, 'vulkan', environment, kwargs)) - return VulkanDependency(environment, kwargs) + if DependencyMethods.PKGCONFIG in methods: + candidates.append(functools.partial(VulkanDependency, environment, kwargs)) + + return candidates @staticmethod def get_methods(): diff --git a/run_unittests.py b/run_unittests.py index efb69ad73..6e5017271 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -3025,11 +3025,12 @@ class LinuxlikeTests(BasePlatformTests): raise unittest.SkipTest('Qt not found with pkg-config') testdir = os.path.join(self.framework_test_dir, '4 qt') self.init(testdir, ['-Dmethod=pkg-config']) - # Confirm that the dependency was found with qmake - msg = 'Qt4 native `pkg-config` dependency (modules: Core, Gui) found: YES\n' - msg2 = 'Qt5 native `pkg-config` dependency (modules: Core, Gui) found: YES\n' + # Confirm that the dependency was found with pkg-config mesonlog = self.get_meson_log() - self.assertTrue(msg in mesonlog or msg2 in mesonlog) + self.assertRegex('\n'.join(mesonlog), + r'Dependency qt4 \(modules: Core\) found: YES .*, `pkg-config`\n') + self.assertRegex('\n'.join(mesonlog), + r'Dependency qt5 \(modules: Core\) found: YES .*, `pkg-config`\n') def test_qt5dependency_qmake_detection(self): ''' @@ -3047,10 +3048,9 @@ class LinuxlikeTests(BasePlatformTests): testdir = os.path.join(self.framework_test_dir, '4 qt') self.init(testdir, ['-Dmethod=qmake']) # Confirm that the dependency was found with qmake - msg = 'Qt5 native `qmake-qt5` dependency (modules: Core) found: YES\n' - msg2 = 'Qt5 native `qmake` dependency (modules: Core) found: YES\n' mesonlog = self.get_meson_log() - self.assertTrue(msg in mesonlog or msg2 in mesonlog) + self.assertRegex('\n'.join(mesonlog), + r'Dependency qt5 \(modules: Core\) found: YES .*, `(qmake|qmake-qt5)`\n') def _test_soname_impl(self, libpath, install): if is_cygwin() or is_osx():