diff options
Diffstat (limited to 'mesonbuild/dependencies/base.py')
-rw-r--r-- | mesonbuild/dependencies/base.py | 533 |
1 files changed, 3 insertions, 530 deletions
diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py index 3c9876c..c8b8735 100644 --- a/mesonbuild/dependencies/base.py +++ b/mesonbuild/dependencies/base.py @@ -19,19 +19,17 @@ # Currently one file, should probably be split into a # package before this gets too big. -import re import sys import os, stat, glob, shutil import shlex -import subprocess import sysconfig from enum import Enum -from collections import OrderedDict from .. import mlog from .. import mesonlib from ..mesonlib import Popen_safe, flatten from ..mesonlib import MesonException, version_compare, version_compare_many -from ..environment import detect_cpu_family, for_windows +from ..environment import detect_cpu_family + class DependencyException(MesonException): '''Exceptions raised while trying to find dependencies''' @@ -353,93 +351,6 @@ class PkgConfigDependency(Dependency): # a path rather than the raw dlname return os.path.basename(dlname) -class WxDependency(Dependency): - wx_found = None - - def __init__(self, environment, kwargs): - Dependency.__init__(self, 'wx', kwargs) - self.is_found = False - # FIXME: use version instead of modversion - self.modversion = 'none' - if WxDependency.wx_found is None: - self.check_wxconfig() - if not WxDependency.wx_found: - # FIXME: this message could be printed after Dependncy found - mlog.log("Neither wx-config-3.0 nor wx-config found; can't detect dependency") - return - - # FIXME: This should print stdout and stderr using mlog.debug - p, out = Popen_safe([self.wxc, '--version'])[0:2] - if p.returncode != 0: - mlog.log('Dependency wxwidgets found:', mlog.red('NO')) - self.cargs = [] - self.libs = [] - else: - self.modversion = out.strip() - version_req = kwargs.get('version', None) - if version_req is not None: - if not version_compare(self.modversion, version_req, strict=True): - mlog.log('Wxwidgets version %s does not fullfill requirement %s' % - (self.modversion, version_req)) - return - mlog.log('Dependency wxwidgets found:', mlog.green('YES')) - self.is_found = True - self.requested_modules = self.get_requested(kwargs) - # wx-config seems to have a cflags as well but since it requires C++, - # this should be good, at least for now. - p, out = Popen_safe([self.wxc, '--cxxflags'])[0:2] - # FIXME: this error should only be raised if required is true - if p.returncode != 0: - raise DependencyException('Could not generate cargs for wxwidgets.') - self.cargs = out.split() - - # FIXME: this error should only be raised if required is true - p, out = Popen_safe([self.wxc, '--libs'] + self.requested_modules)[0:2] - if p.returncode != 0: - raise DependencyException('Could not generate libs for wxwidgets.') - self.libs = out.split() - - def get_requested(self, kwargs): - modules = 'modules' - if modules not in kwargs: - return [] - candidates = kwargs[modules] - if isinstance(candidates, str): - return [candidates] - for c in candidates: - if not isinstance(c, str): - raise DependencyException('wxwidgets module argument is not a string.') - return candidates - - def get_modversion(self): - return self.modversion - - def get_version(self): - return self.modversion - - def get_compile_args(self): - return self.cargs - - def get_link_args(self): - return self.libs - - def check_wxconfig(self): - for wxc in ['wx-config-3.0', 'wx-config']: - try: - p, out = Popen_safe([wxc, '--version'])[0:2] - if p.returncode == 0: - mlog.log('Found wx-config:', mlog.bold(shutil.which(wxc)), - '(%s)' % out.strip()) - self.wxc = wxc - WxDependency.wx_found = True - return - except (FileNotFoundError, PermissionError): - pass - WxDependency.wxconfig_found = False - mlog.log('Found wx-config:', mlog.red('NO')) - - def found(self): - return self.is_found class ExternalProgram: windows_exts = ('exe', 'msc', 'com', 'bat') @@ -1009,334 +920,6 @@ class GMockDependency(Dependency): def found(self): return self.is_found -class QtBaseDependency(Dependency): - def __init__(self, name, env, kwargs): - Dependency.__init__(self, name, kwargs) - self.name = name - self.qtname = name.capitalize() - self.qtver = name[-1] - if self.qtver == "4": - self.qtpkgname = 'Qt' - else: - self.qtpkgname = self.qtname - self.root = '/usr' - self.bindir = None - self.silent = kwargs.get('silent', False) - # We store the value of required here instead of passing it on to - # PkgConfigDependency etc because we want to try the qmake-based - # fallback as well. - self.required = kwargs.pop('required', True) - kwargs['required'] = False - mods = kwargs.get('modules', []) - self.cargs = [] - self.largs = [] - self.is_found = False - if isinstance(mods, str): - mods = [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' - - # Keep track of the detection methods used, for logging purposes. - methods = [] - # Prefer pkg-config, then fallback to `qmake -query` - if DependencyMethods.PKGCONFIG in self.methods: - self._pkgconfig_detect(mods, env, kwargs) - methods.append('pkgconfig') - if not self.is_found and DependencyMethods.QMAKE in self.methods: - from_text = self._qmake_detect(mods, env, kwargs) - methods.append('qmake-' + self.name) - methods.append('qmake') - if not self.is_found: - # Reset compile args and link args - self.cargs = [] - self.largs = [] - from_text = '(checked {})'.format(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" - if self.bindir: - moc = ExternalProgram(os.path.join(self.bindir, 'moc'), silent=True) - uic = ExternalProgram(os.path.join(self.bindir, 'uic'), silent=True) - rcc = ExternalProgram(os.path.join(self.bindir, 'rcc'), silent=True) - else: - # We don't accept unsuffixed 'moc', 'uic', and 'rcc' because they - # are sometimes older, or newer versions. - moc = ExternalProgram('moc-' + self.name, silent=True) - uic = ExternalProgram('uic-' + self.name, silent=True) - rcc = ExternalProgram('rcc-' + self.name, silent=True) - return moc, uic, rcc - - def _pkgconfig_detect(self, mods, env, kwargs): - modules = OrderedDict() - for module in mods: - modules[module] = PkgConfigDependency(self.qtpkgname + module, env, kwargs) - self.is_found = True - for m in modules.values(): - if not m.found(): - self.is_found = False - return - self.cargs += m.get_compile_args() - self.largs += m.get_link_args() - self.version = m.modversion - # Try to detect moc, uic, rcc - if 'Core' in modules: - core = modules['Core'] - else: - corekwargs = {'required': 'false', 'silent': 'true'} - core = PkgConfigDependency(self.qtpkgname + 'Core', env, corekwargs) - # Used by self.compilers_detect() - self.bindir = self.get_pkgconfig_host_bins(core) - if not self.bindir: - # If exec_prefix is not defined, the pkg-config file is broken - prefix = core.get_pkgconfig_variable('exec_prefix') - if prefix: - self.bindir = os.path.join(prefix, 'bin') - - def _find_qmake(self, qmake, env): - # Even when cross-compiling, if we don't get a cross-info qmake, we - # fallback to using the qmake in PATH because that's what we used to do - if env.is_cross_build(): - qmake = env.cross_info.config['binaries'].get('qmake', qmake) - return ExternalProgram(qmake, silent=True) - - def _qmake_detect(self, mods, env, kwargs): - for qmake in ('qmake-' + self.name, 'qmake'): - self.qmake = self._find_qmake(qmake, env) - if not self.qmake.found(): - continue - # Check that the qmake is for qt5 - pc, stdo = Popen_safe(self.qmake.get_command() + ['-v'])[0:2] - if pc.returncode != 0: - continue - if not 'Qt version ' + self.qtver in stdo: - mlog.log('QMake is not for ' + self.qtname) - continue - # Found qmake for Qt5! - break - else: - # Didn't find qmake :( - return - self.version = re.search(self.qtver + '(\.\d+)+', stdo).group(0) - # Query library path, header path, and binary path - mlog.log("Found qmake:", mlog.bold(self.qmake.get_name()), '(%s)' % self.version) - stdo = Popen_safe(self.qmake.get_command() + ['-query'])[1] - qvars = {} - for line in stdo.split('\n'): - line = line.strip() - if line == '': - continue - (k, v) = tuple(line.split(':', 1)) - qvars[k] = v - if mesonlib.is_osx(): - return self._framework_detect(qvars, mods, kwargs) - incdir = qvars['QT_INSTALL_HEADERS'] - self.cargs.append('-I' + incdir) - libdir = qvars['QT_INSTALL_LIBS'] - # Used by self.compilers_detect() - self.bindir = self.get_qmake_host_bins(qvars) - self.is_found = True - for module in mods: - mincdir = os.path.join(incdir, 'Qt' + module) - self.cargs.append('-I' + mincdir) - if for_windows(env.is_cross_build(), env): - libfile = os.path.join(libdir, self.qtpkgname + module + '.lib') - if not os.path.isfile(libfile): - # MinGW can link directly to .dll - libfile = os.path.join(self.bindir, self.qtpkgname + module + '.dll') - if not os.path.isfile(libfile): - self.is_found = False - break - else: - libfile = os.path.join(libdir, 'lib{}{}.so'.format(self.qtpkgname, module)) - if not os.path.isfile(libfile): - self.is_found = False - break - self.largs.append(libfile) - return qmake - - def _framework_detect(self, qvars, modules, kwargs): - libdir = qvars['QT_INSTALL_LIBS'] - for m in modules: - fname = 'Qt' + m - fwdep = ExtraFrameworkDependency(fname, kwargs.get('required', True), libdir, kwargs) - self.cargs.append('-F' + libdir) - if fwdep.found(): - self.is_found = True - self.cargs += fwdep.get_compile_args() - self.largs += fwdep.get_link_args() - # Used by self.compilers_detect() - self.bindir = self.get_qmake_host_bins(qvars) - - def get_qmake_host_bins(self, qvars): - # Prefer QT_HOST_BINS (qt5, correct for cross and native compiling) - # but fall back to QT_INSTALL_BINS (qt4) - if 'QT_HOST_BINS' in qvars: - return qvars['QT_HOST_BINS'] - else: - return qvars['QT_INSTALL_BINS'] - - def get_version(self): - return self.version - - def get_compile_args(self): - return self.cargs - - def get_sources(self): - return [] - - def get_link_args(self): - return self.largs - - def get_methods(self): - return [DependencyMethods.PKGCONFIG, DependencyMethods.QMAKE] - - def found(self): - return self.is_found - - def get_exe_args(self, compiler): - # Originally this was -fPIE but nowadays the default - # for upstream and distros seems to be -reduce-relocations - # which requires -fPIC. This may cause a performance - # penalty when using self-built Qt or on platforms - # where -fPIC is not required. If this is an issue - # for you, patches are welcome. - return compiler.get_pic_args() - -class Qt5Dependency(QtBaseDependency): - def __init__(self, env, kwargs): - QtBaseDependency.__init__(self, 'qt5', env, kwargs) - - def get_pkgconfig_host_bins(self, core): - return core.get_pkgconfig_variable('host_bins') - -class Qt4Dependency(QtBaseDependency): - def __init__(self, env, kwargs): - QtBaseDependency.__init__(self, 'qt4', env, kwargs) - - def get_pkgconfig_host_bins(self, core): - # Only return one bins dir, because the tools are generally all in one - # directory for Qt4, in Qt5, they must all be in one directory. Return - # the first one found among the bin variables, in case one tool is not - # configured to be built. - applications = ['moc', 'uic', 'rcc', 'lupdate', 'lrelease'] - for application in applications: - try: - return os.path.dirname(core.get_pkgconfig_variable('%s_location' % application)) - except MesonException: - pass - -class GnuStepDependency(Dependency): - def __init__(self, environment, kwargs): - Dependency.__init__(self, 'gnustep', kwargs) - self.required = kwargs.get('required', True) - self.modules = kwargs.get('modules', []) - self.detect() - - def detect(self): - self.confprog = 'gnustep-config' - try: - gp = Popen_safe([self.confprog, '--help'])[0] - except (FileNotFoundError, PermissionError): - self.args = None - mlog.log('Dependency GnuStep found:', mlog.red('NO'), '(no gnustep-config)') - return - if gp.returncode != 0: - self.args = None - mlog.log('Dependency GnuStep found:', mlog.red('NO')) - return - if 'gui' in self.modules: - arg = '--gui-libs' - else: - arg = '--base-libs' - fp, flagtxt, flagerr = Popen_safe([self.confprog, '--objc-flags']) - if fp.returncode != 0: - raise DependencyException('Error getting objc-args: %s %s' % (flagtxt, flagerr)) - args = flagtxt.split() - self.args = self.filter_arsg(args) - fp, libtxt, liberr = Popen_safe([self.confprog, arg]) - if fp.returncode != 0: - raise DependencyException('Error getting objc-lib args: %s %s' % (libtxt, liberr)) - self.libs = self.weird_filter(libtxt.split()) - self.version = self.detect_version() - mlog.log('Dependency', mlog.bold('GnuStep'), 'found:', - mlog.green('YES'), self.version) - - def weird_filter(self, elems): - """When building packages, the output of the enclosing Make -is sometimes mixed among the subprocess output. I have no idea -why. As a hack filter out everything that is not a flag.""" - return [e for e in elems if e.startswith('-')] - - def filter_arsg(self, args): - """gnustep-config returns a bunch of garbage args such - as -O2 and so on. Drop everything that is not needed.""" - result = [] - for f in args: - if f.startswith('-D') \ - or f.startswith('-f') \ - or f.startswith('-I') \ - or f == '-pthread' \ - or (f.startswith('-W') and not f == '-Wall'): - result.append(f) - return result - - def detect_version(self): - gmake = self.get_variable('GNUMAKE') - makefile_dir = self.get_variable('GNUSTEP_MAKEFILES') - # This Makefile has the GNUStep version set - base_make = os.path.join(makefile_dir, 'Additional', 'base.make') - # Print the Makefile variable passed as the argument. For instance, if - # you run the make target `print-SOME_VARIABLE`, this will print the - # value of the variable `SOME_VARIABLE`. - printver = "print-%:\n\t@echo '$($*)'" - env = os.environ.copy() - # See base.make to understand why this is set - env['FOUNDATION_LIB'] = 'gnu' - p, o, e = Popen_safe([gmake, '-f', '-', '-f', base_make, - 'print-GNUSTEP_BASE_VERSION'], - env=env, write=printver, stdin=subprocess.PIPE) - version = o.strip() - if not version: - mlog.debug("Couldn't detect GNUStep version, falling back to '1'") - # Fallback to setting some 1.x version - version = '1' - return version - - def get_variable(self, var): - p, o, e = Popen_safe([self.confprog, '--variable=' + var]) - if p.returncode != 0 and self.required: - raise DependencyException('{!r} for variable {!r} failed to run' - ''.format(self.confprog, var)) - return o.strip() - - def found(self): - return self.args is not None - - def get_version(self): - return self.version - - def get_compile_args(self): - if self.args is None: - return [] - return self.args - - def get_link_args(self): - return self.libs class AppleFrameworks(Dependency): def __init__(self, environment, kwargs): @@ -1361,111 +944,6 @@ class AppleFrameworks(Dependency): def get_version(self): return 'unknown' -class GLDependency(Dependency): - def __init__(self, environment, kwargs): - Dependency.__init__(self, 'gl', kwargs) - self.is_found = False - self.cargs = [] - self.linkargs = [] - if DependencyMethods.PKGCONFIG in self.methods: - try: - pcdep = PkgConfigDependency('gl', environment, kwargs) - if pcdep.found(): - self.type_name = 'pkgconfig' - self.is_found = True - self.cargs = pcdep.get_compile_args() - self.linkargs = pcdep.get_link_args() - self.version = pcdep.get_version() - return - except Exception: - pass - if DependencyMethods.SYSTEM in self.methods: - if mesonlib.is_osx(): - self.is_found = True - self.linkargs = ['-framework', 'OpenGL'] - self.version = '1' # FIXME - return - if mesonlib.is_windows(): - self.is_found = True - self.linkargs = ['-lopengl32'] - self.version = '1' # FIXME: unfixable? - return - - def get_link_args(self): - return self.linkargs - - def get_version(self): - return self.version - - def get_methods(self): - if mesonlib.is_osx() or mesonlib.is_windows(): - return [DependencyMethods.PKGCONFIG, DependencyMethods.SYSTEM] - else: - return [DependencyMethods.PKGCONFIG] - -# There are three different ways of depending on SDL2: -# sdl2-config, pkg-config and OSX framework -class SDL2Dependency(Dependency): - def __init__(self, environment, kwargs): - Dependency.__init__(self, 'sdl2', kwargs) - self.is_found = False - self.cargs = [] - self.linkargs = [] - if DependencyMethods.PKGCONFIG in self.methods: - try: - pcdep = PkgConfigDependency('sdl2', environment, kwargs) - if pcdep.found(): - self.type_name = 'pkgconfig' - self.is_found = True - self.cargs = pcdep.get_compile_args() - self.linkargs = pcdep.get_link_args() - self.version = pcdep.get_version() - return - except Exception as e: - mlog.debug('SDL 2 not found via pkgconfig. Trying next, error was:', str(e)) - pass - if DependencyMethods.SDLCONFIG in self.methods: - sdlconf = shutil.which('sdl2-config') - if sdlconf: - stdo = Popen_safe(['sdl2-config', '--cflags'])[1] - self.cargs = stdo.strip().split() - stdo = Popen_safe(['sdl2-config', '--libs'])[1] - self.linkargs = stdo.strip().split() - stdo = Popen_safe(['sdl2-config', '--version'])[1] - self.version = stdo.strip() - self.is_found = True - mlog.log('Dependency', mlog.bold('sdl2'), 'found:', mlog.green('YES'), - self.version, '(%s)' % sdlconf) - return - mlog.debug('Could not find sdl2-config binary, trying next.') - if DependencyMethods.EXTRAFRAMEWORK in self.methods: - if mesonlib.is_osx(): - fwdep = ExtraFrameworkDependency('sdl2', kwargs.get('required', True), None, kwargs) - if fwdep.found(): - self.is_found = True - self.cargs = fwdep.get_compile_args() - self.linkargs = fwdep.get_link_args() - self.version = '2' # FIXME - return - mlog.log('Dependency', mlog.bold('sdl2'), 'found:', mlog.red('NO')) - - def get_compile_args(self): - return self.cargs - - def get_link_args(self): - return self.linkargs - - def found(self): - return self.is_found - - def get_version(self): - return self.version - - def get_methods(self): - if mesonlib.is_osx(): - return [DependencyMethods.PKGCONFIG, DependencyMethods.SDLCONFIG, DependencyMethods.EXTRAFRAMEWORK] - else: - return [DependencyMethods.PKGCONFIG, DependencyMethods.SDLCONFIG] class ExtraFrameworkDependency(Dependency): def __init__(self, name, required, path, kwargs): @@ -1796,18 +1274,13 @@ def find_external_dependency(name, environment, kwargs): mlog.log('Dependency', mlog.bold(name), 'found:', mlog.red('NO')) return pkgdep + # This has to be at the end so the classes it references # are defined. packages = {'boost': BoostDependency, 'gtest': GTestDependency, 'gmock': GMockDependency, - 'qt5': Qt5Dependency, - 'qt4': Qt4Dependency, - 'gnustep': GnuStepDependency, 'appleframeworks': AppleFrameworks, - 'wxwidgets': WxDependency, - 'sdl2': SDL2Dependency, - 'gl': GLDependency, 'threads': ThreadDependency, 'python3': Python3Dependency, 'valgrind': ValgrindDependency, |