Split UI-related into a separate file.

Some of these are a bit bigger than just UI libraries, but this division
seems close enough.
This commit is contained in:
Elliott Sales de Andrade 2017-04-15 16:22:29 -04:00
parent 92557e1c2a
commit 23f9b42d3b
3 changed files with 575 additions and 530 deletions

View File

@ -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,
})

View File

@ -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,

View File

@ -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