diff options
author | Elliott Sales de Andrade <quantum.analyst@gmail.com> | 2017-04-15 16:22:29 -0400 |
---|---|---|
committer | Elliott Sales de Andrade <quantum.analyst@gmail.com> | 2017-05-09 18:13:09 -0400 |
commit | 23f9b42d3b9fd2248c405e64467bb54f85b07ec5 (patch) | |
tree | 729f7fb95e16bcf5d177d601fe04020d69e54b0b | |
parent | 92557e1c2a97be6fb0b5c4aa7a12a43b5d43462c (diff) | |
download | meson-23f9b42d3b9fd2248c405e64467bb54f85b07ec5.zip meson-23f9b42d3b9fd2248c405e64467bb54f85b07ec5.tar.gz meson-23f9b42d3b9fd2248c405e64467bb54f85b07ec5.tar.bz2 |
Split UI-related into a separate file.
Some of these are a bit bigger than just UI libraries, but this division
seems close enough.
-rw-r--r-- | mesonbuild/dependencies/__init__.py | 12 | ||||
-rw-r--r-- | mesonbuild/dependencies/base.py | 533 | ||||
-rw-r--r-- | mesonbuild/dependencies/ui.py | 560 |
3 files changed, 575 insertions, 530 deletions
diff --git a/mesonbuild/dependencies/__init__.py b/mesonbuild/dependencies/__init__.py index 9281798..fb2bee0 100644 --- a/mesonbuild/dependencies/__init__.py +++ b/mesonbuild/dependencies/__init__.py @@ -13,3 +13,15 @@ # limitations under the License. from .base import * +from .ui import GLDependency, GnuStepDependency, Qt4Dependency, Qt5Dependency, SDL2Dependency, WxDependency + + +# From _ui: +packages.update({ + 'gl': GLDependency, + 'gnustep': GnuStepDependency, + 'qt4': Qt4Dependency, + 'qt5': Qt5Dependency, + 'sdl2': SDL2Dependency, + 'wxwidgets': WxDependency, +}) 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, diff --git a/mesonbuild/dependencies/ui.py b/mesonbuild/dependencies/ui.py new file mode 100644 index 0000000..3174176 --- /dev/null +++ b/mesonbuild/dependencies/ui.py @@ -0,0 +1,560 @@ +# Copyright 2013-2017 The Meson development team + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This file contains the detection logic for external dependencies that +# are UI-related. + +import os +import re +import shutil +import subprocess +from collections import OrderedDict + +from .. import mlog +from .. import mesonlib +from ..mesonlib import MesonException, Popen_safe, version_compare +from ..environment import for_windows + +from .base import (Dependency, DependencyException, DependencyMethods, + ExternalProgram, ExtraFrameworkDependency, PkgConfigDependency) + + +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] + + +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 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 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 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') + + +# 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 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 |