Hoist trying several dependency detection methods up to find_external_dependency()

find_external_dependency() now makes and iterates over a list of callables
which are constructors with bound arguments for the dependency objects we
are going to attempt to make, so we can consolidate reporting on these
attempts and handling failures in that function.
pull/3657/head
Jon Turney 7 years ago
parent 983263e82c
commit 82bdf07a9d
No known key found for this signature in database
GPG Key ID: C7C86F0370285C81
  1. 101
      mesonbuild/dependencies/base.py
  2. 100
      mesonbuild/dependencies/misc.py
  3. 201
      mesonbuild/dependencies/ui.py

@ -16,6 +16,7 @@
# Custom logic for several other packages are in separate files. # Custom logic for several other packages are in separate files.
import copy import copy
import functools
import os import os
import re import re
import stat import stat
@ -1279,47 +1280,79 @@ def find_external_dependency(name, env, kwargs):
raise DependencyException('Keyword "required" must be a boolean.') raise DependencyException('Keyword "required" must be a boolean.')
if not isinstance(kwargs.get('method', ''), str): if not isinstance(kwargs.get('method', ''), str):
raise DependencyException('Keyword "method" must be a string.') raise DependencyException('Keyword "method" must be a string.')
method = kwargs.get('method', '') if name.lower() not in _packages_accept_language and 'language' in kwargs:
raise DependencyException('%s dependency does not accept "language" keyword argument' % (name, ))
# build a list of dependency methods to try
candidates = _build_external_dependency_list(name, env, kwargs)
pkg_exc = None
pkgdep = []
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:
# if the dependency was found
if d.found():
return d
# otherwise, the dependency could not be found
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
tried_methods = ','.join([d.type_name for d in pkgdep])
raise DependencyException('Dependency "%s" not found, tried %s' % (name, tried_methods))
# 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() lname = name.lower()
if lname in packages: if lname in packages:
if lname not in _packages_accept_language and 'language' in kwargs: # Create the list of dependency object constructors using a factory
raise DependencyException('%s dependency does not accept "language" keyword argument' % (lname, )) # class method, if one exists, otherwise the list just consists of the
# Create the dependency object using a factory class method, if one # constructor
# exists, otherwise it is just constructed directly.
if getattr(packages[lname], '_factory', None): if getattr(packages[lname], '_factory', None):
dep = packages[lname]._factory(env, kwargs) dep = packages[lname]._factory(env, kwargs)
else: else:
dep = packages[lname](env, kwargs) dep = [functools.partial(packages[lname], env, kwargs)]
if required and not dep.found():
raise DependencyException('Dependency "%s" not found' % name)
return dep return dep
if 'language' in kwargs:
# Remove check when PkgConfigDependency supports language. candidates = []
raise DependencyException('%s dependency does not accept "language" keyword argument' % (lname, ))
if 'dub' == method: # If it's explicitly requested, use the dub detection method (only)
dubdep = DubDependency(name, env, kwargs) if 'dub' == kwargs.get('method', ''):
if required and not dubdep.found(): candidates.append(functools.partial(DubDependency, name, env, kwargs))
mlog.log('Dependency', mlog.bold(name), 'found:', mlog.red('NO')) return candidates
return dubdep # TBD: other values of method should control what method(s) are used
pkg_exc = None
pkgdep = None # Otherwise, just use the pkgconfig dependency detector
try: candidates.append(functools.partial(PkgConfigDependency, name, env, kwargs))
pkgdep = PkgConfigDependency(name, env, kwargs)
if pkgdep.found(): # On OSX, also try framework dependency detector
return pkgdep
except Exception as e:
pkg_exc = e
if mesonlib.is_osx(): if mesonlib.is_osx():
fwdep = ExtraFrameworkDependency(name, False, None, env, None, kwargs) candidates.append(functools.partial(ExtraFrameworkDependency, name,
if required and not fwdep.found(): False, None, env, None, kwargs))
m = 'Dependency {!r} not found, tried Extra Frameworks ' \
'and Pkg-Config:\n\n' + str(pkg_exc) return candidates
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
def strip_system_libdirs(environment, link_args): def strip_system_libdirs(environment, link_args):

@ -14,6 +14,7 @@
# This file contains the detection logic for miscellaneous external dependencies. # This file contains the detection logic for miscellaneous external dependencies.
import functools
import os import os
import re import re
import shlex import shlex
@ -436,26 +437,21 @@ class PcapDependency(ExternalDependency):
@classmethod @classmethod
def _factory(cls, environment, kwargs): def _factory(cls, environment, kwargs):
methods = cls._process_method_kw(kwargs) methods = cls._process_method_kw(kwargs)
candidates = []
if DependencyMethods.PKGCONFIG in methods: if DependencyMethods.PKGCONFIG in methods:
try: candidates.append(functools.partial(PkgConfigDependency, 'pcap', environment, kwargs))
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))
if DependencyMethods.CONFIG_TOOL in methods: if DependencyMethods.CONFIG_TOOL in methods:
try: candidates.append(functools.partial(ConfigToolDependency.factory,
ctdep = ConfigToolDependency.factory( 'pcap', environment, None, kwargs, ['pcap-config'], 'pcap-config'))
'pcap', environment, None, kwargs, ['pcap-config'], 'pcap-config') # if ctdep.found():
if ctdep.found(): # ctdep.compile_args = ctdep.get_config_value(['--cflags'], 'compile_args')
ctdep.compile_args = ctdep.get_config_value(['--cflags'], 'compile_args') # ctdep.link_args = ctdep.get_config_value(['--libs'], 'link_args')
ctdep.link_args = ctdep.get_config_value(['--libs'], 'link_args') # ctdep.version = cls.get_pcap_lib_version(ctdep)
ctdep.version = cls.get_pcap_lib_version(ctdep) # return ctdep
return ctdep
except Exception as e: return candidates
mlog.debug('Pcap not found via pcap-config. Trying next, error was:', str(e))
return PcapDependency(environment, kwargs)
@staticmethod @staticmethod
def get_methods(): def get_methods():
@ -474,32 +470,27 @@ class CupsDependency(ExternalDependency):
@classmethod @classmethod
def _factory(cls, environment, kwargs): def _factory(cls, environment, kwargs):
methods = cls._process_method_kw(kwargs) methods = cls._process_method_kw(kwargs)
candidates = []
if DependencyMethods.PKGCONFIG in methods: if DependencyMethods.PKGCONFIG in methods:
try: candidates.append(functools.partial(PkgConfigDependency, 'cups', environment, kwargs))
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))
if DependencyMethods.CONFIG_TOOL in methods: if DependencyMethods.CONFIG_TOOL in methods:
try: candidates.append(functools.partial(ConfigToolDependency.factory,
ctdep = ConfigToolDependency.factory( 'cups', environment, None,
'cups', environment, None, kwargs, ['cups-config'], 'cups-config') kwargs, ['cups-config'],
if ctdep.found(): 'cups-config'))
ctdep.compile_args = ctdep.get_config_value(['--cflags'], 'compile_args') # if ctdep.found():
ctdep.link_args = ctdep.get_config_value(['--ldflags', '--libs'], 'link_args') # ctdep.compile_args = ctdep.get_config_value(['--cflags'], 'compile_args')
return ctdep # ctdep.link_args = ctdep.get_config_value(['--ldflags', '--libs'], 'link_args')
except Exception as e:
mlog.debug('cups not found via cups-config. Trying next, error was:', str(e))
if DependencyMethods.EXTRAFRAMEWORK in methods: if DependencyMethods.EXTRAFRAMEWORK in methods:
if mesonlib.is_osx(): if mesonlib.is_osx():
fwdep = ExtraFrameworkDependency('cups', False, None, environment, candidates.append(functools.partial(
kwargs.get('language', None), kwargs) ExtraFrameworkDependency, 'cups', False, None, environment,
if fwdep.found(): kwargs.get('language', None), kwargs))
return fwdep
mlog.log('Dependency', mlog.bold('cups'), 'found:', mlog.red('NO'))
return CupsDependency(environment, kwargs) return candidates
@staticmethod @staticmethod
def get_methods(): def get_methods():
@ -516,26 +507,21 @@ class LibWmfDependency(ExternalDependency):
@classmethod @classmethod
def _factory(cls, environment, kwargs): def _factory(cls, environment, kwargs):
methods = cls._process_method_kw(kwargs) methods = cls._process_method_kw(kwargs)
candidates = []
if DependencyMethods.PKGCONFIG in methods: if DependencyMethods.PKGCONFIG in methods:
try: candidates.append(functools.partial(PkgConfigDependency, 'libwmf', environment, kwargs))
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))
if DependencyMethods.CONFIG_TOOL in methods: if DependencyMethods.CONFIG_TOOL in methods:
try: candidates.append(functools.partial(ConfigToolDependency.factory,
ctdep = ConfigToolDependency.factory( 'libwmf', environment, None, kwargs, ['libwmf-config'], 'libwmf-config'))
'libwmf', environment, None, kwargs, ['libwmf-config'], 'libwmf-config')
if ctdep.found(): # if ctdep.found():
ctdep.compile_args = ctdep.get_config_value(['--cflags'], 'compile_args') # ctdep.compile_args = ctdep.get_config_value(['--cflags'], 'compile_args')
ctdep.link_args = ctdep.get_config_value(['--libs'], 'link_args') # ctdep.link_args = ctdep.get_config_value(['--libs'], 'link_args')
return ctdep # return ctdep
except Exception as e:
mlog.debug('cups not found via libwmf-config. Trying next, error was:', str(e)) return candidates
return LibWmfDependency(environment, kwargs)
@staticmethod @staticmethod
def get_methods(): def get_methods():

@ -15,6 +15,7 @@
# This file contains the detection logic for external dependencies that # This file contains the detection logic for external dependencies that
# are UI-related. # are UI-related.
import functools
import os import os
import re import re
import subprocess import subprocess
@ -37,32 +38,34 @@ from .base import ConfigToolDependency
class GLDependency(ExternalDependency): class GLDependency(ExternalDependency):
def __init__(self, environment, kwargs): def __init__(self, environment, kwargs):
super().__init__('gl', environment, None, kwargs) super().__init__('gl', environment, None, kwargs)
if DependencyMethods.SYSTEM in self.methods:
if mesonlib.is_osx(): if mesonlib.is_osx():
self.is_found = True self.is_found = True
# FIXME: Use AppleFrameworks dependency # FIXME: Use AppleFrameworks dependency
self.link_args = ['-framework', 'OpenGL'] self.link_args = ['-framework', 'OpenGL']
# FIXME: Detect version using self.clib_compiler # FIXME: Detect version using self.clib_compiler
self.version = '1' self.version = '1'
return return
if mesonlib.is_windows(): if mesonlib.is_windows():
self.is_found = True self.is_found = True
# FIXME: Use self.clib_compiler.find_library() # FIXME: Use self.clib_compiler.find_library()
self.link_args = ['-lopengl32'] self.link_args = ['-lopengl32']
# FIXME: Detect version using self.clib_compiler # FIXME: Detect version using self.clib_compiler
self.version = '1' self.version = '1'
return return
@classmethod @classmethod
def _factory(cls, environment, kwargs): def _factory(cls, environment, kwargs):
if DependencyMethods.PKGCONFIG in cls._process_method_kw(kwargs): methods = cls._process_method_kw(kwargs)
try: candidates = []
pcdep = PkgConfigDependency('gl', environment, kwargs)
if pcdep.found(): if DependencyMethods.PKGCONFIG in methods:
return pcdep candidates.append(functools.partial(PkgConfigDependency, 'gl', environment, kwargs))
except Exception:
pass if DependencyMethods.SYSTEM in methods:
return GLDependency(environment, kwargs) candidates.append(functools.partial(GLDependency), environment, kwargs)
return candidates
@staticmethod @staticmethod
def get_methods(): def get_methods():
@ -452,33 +455,27 @@ class SDL2Dependency(ExternalDependency):
@classmethod @classmethod
def _factory(cls, environment, kwargs): def _factory(cls, environment, kwargs):
methods = cls._process_method_kw(kwargs) methods = cls._process_method_kw(kwargs)
candidates = []
if DependencyMethods.PKGCONFIG in methods: if DependencyMethods.PKGCONFIG in methods:
try: candidates.append(functools.partial(PkgConfigDependency, 'sdl2', environment, kwargs))
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))
if DependencyMethods.CONFIG_TOOL in methods: if DependencyMethods.CONFIG_TOOL in methods:
try: candidates.append(functools.partial(ConfigToolDependency.factory,
ctdep = ConfigToolDependency.factory( 'sdl2', environment, None,
'sdl2', environment, None, kwargs, ['sdl2-config'], 'sdl2-config') kwargs, ['sdl2-config'],
if ctdep.found(): 'sdl2-config'))
ctdep.compile_args = ctdep.get_config_value(['--cflags'], 'compile_args') # if ctdep.found():
ctdep.link_args = ctdep.get_config_value(['--libs'], 'link_args') # ctdep.compile_args = ctdep.get_config_value(['--cflags'], 'compile_args')
return ctdep # ctdep.link_args = ctdep.get_config_value(['--libs'], 'link_args')
except Exception as e:
mlog.debug('SDL 2 not found via sdl2-config. Trying next, error was:', str(e))
if DependencyMethods.EXTRAFRAMEWORK in methods: if DependencyMethods.EXTRAFRAMEWORK in methods:
if mesonlib.is_osx(): if mesonlib.is_osx():
fwdep = ExtraFrameworkDependency('sdl2', False, None, environment, candidates.append(functools.partial(ExtraFrameworkDependency,
kwargs.get('language', None), kwargs) 'sdl2', False, None, environment,
if fwdep.found(): kwargs.get('language', None), kwargs))
fwdep.version = '2' # FIXME # fwdep.version = '2' # FIXME
return fwdep return candidates
mlog.log('Dependency', mlog.bold('sdl2'), 'found:', mlog.red('NO'))
return SDL2Dependency(environment, kwargs)
@staticmethod @staticmethod
def get_methods(): def get_methods():
@ -518,73 +515,73 @@ class VulkanDependency(ExternalDependency):
def __init__(self, environment, kwargs): def __init__(self, environment, kwargs):
super().__init__('vulkan', environment, None, kwargs) super().__init__('vulkan', environment, None, kwargs)
if DependencyMethods.SYSTEM in self.methods: try:
try: self.vulkan_sdk = os.environ['VULKAN_SDK']
self.vulkan_sdk = os.environ['VULKAN_SDK'] if not os.path.isabs(self.vulkan_sdk):
if not os.path.isabs(self.vulkan_sdk): raise DependencyException('VULKAN_SDK must be an absolute path.')
raise DependencyException('VULKAN_SDK must be an absolute path.') except KeyError:
except KeyError: self.vulkan_sdk = None
self.vulkan_sdk = None
if self.vulkan_sdk:
if self.vulkan_sdk: # TODO: this config might not work on some platforms, fix bugs as reported
# 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)
# 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' lib_name = 'vulkan'
if mesonlib.is_windows(): lib_dir = 'lib'
lib_name = 'vulkan-1' inc_dir = 'include'
lib_dir = 'Lib32'
inc_dir = 'Include'
if detect_cpu({}) == 'x86_64':
lib_dir = 'Lib'
else:
lib_name = 'vulkan'
lib_dir = 'lib'
inc_dir = 'include'
# make sure header and lib are valid # make sure header and lib are valid
inc_path = os.path.join(self.vulkan_sdk, inc_dir) inc_path = os.path.join(self.vulkan_sdk, inc_dir)
header = os.path.join(inc_path, 'vulkan', 'vulkan.h') header = os.path.join(inc_path, 'vulkan', 'vulkan.h')
lib_path = os.path.join(self.vulkan_sdk, lib_dir) lib_path = os.path.join(self.vulkan_sdk, lib_dir)
find_lib = self.clib_compiler.find_library(lib_name, environment, lib_path) find_lib = self.clib_compiler.find_library(lib_name, environment, lib_path)
if not find_lib: if not find_lib:
raise DependencyException('VULKAN_SDK point to invalid directory (no lib)') raise DependencyException('VULKAN_SDK point to invalid directory (no lib)')
if not os.path.isfile(header): if not os.path.isfile(header):
raise DependencyException('VULKAN_SDK point to invalid directory (no include)') raise DependencyException('VULKAN_SDK point to invalid directory (no include)')
self.type_name = 'vulkan_sdk' self.type_name = 'vulkan_sdk'
self.is_found = True self.is_found = True
self.compile_args.append('-I' + inc_path) self.compile_args.append('-I' + inc_path)
self.link_args.append('-L' + lib_path) self.link_args.append('-L' + lib_path)
self.link_args.append('-l' + lib_name) self.link_args.append('-l' + lib_name)
# TODO: find a way to retrieve the version from the sdk? # 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) # Usually it is a part of the path to it (but does not have to be)
self.version = '1' 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 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 @classmethod
def _factory(cls, environment, kwargs): def _factory(cls, environment, kwargs):
if DependencyMethods.PKGCONFIG in cls._process_method_kw(kwargs): methods = cls._process_method_kw(kwargs)
try: candidates = []
pcdep = PkgConfigDependency('vulkan', environment, kwargs)
if pcdep.found():
return pcdep
except Exception:
pass
return VulkanDependency(environment, kwargs) if DependencyMethods.PKGCONFIG in methods:
candidates.append(functools.partial(PkgConfigDependency, 'vulkan', environment, kwargs))
if DependencyMethods.PKGCONFIG in methods:
candidates.append(functools.partial(VulkanDependency, environment, kwargs))
return candidates
@staticmethod @staticmethod
def get_methods(): def get_methods():

Loading…
Cancel
Save