|
|
|
# Copyright 2013-2014 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. Mostly just uses pkg-config but also contains
|
|
|
|
# custom logic for packages that don't provide them.
|
|
|
|
|
|
|
|
# Currently one file, should probably be split into a
|
|
|
|
# package before this gets too big.
|
|
|
|
|
|
|
|
import re
|
|
|
|
import platform
|
|
|
|
import os, stat, glob, subprocess, shutil
|
|
|
|
from coredata import MesonException
|
|
|
|
import mlog
|
|
|
|
import mesonlib
|
|
|
|
|
|
|
|
class DependencyException(MesonException):
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
MesonException.__init__(self, *args, **kwargs)
|
|
|
|
|
|
|
|
class Dependency():
|
|
|
|
def __init__(self):
|
|
|
|
self.name = "null"
|
|
|
|
self.is_found = False
|
|
|
|
|
|
|
|
def get_compile_args(self):
|
|
|
|
return []
|
|
|
|
|
|
|
|
def get_link_args(self):
|
|
|
|
return []
|
|
|
|
|
|
|
|
def found(self):
|
|
|
|
return self.is_found
|
|
|
|
|
|
|
|
def get_sources(self):
|
|
|
|
"""Source files that need to be added to the target.
|
|
|
|
As an example, gtest-all.cc when using GTest."""
|
|
|
|
return []
|
|
|
|
|
|
|
|
def get_name(self):
|
|
|
|
return self.name
|
|
|
|
|
|
|
|
def get_exe_args(self):
|
|
|
|
return []
|
|
|
|
|
|
|
|
def need_threads(self):
|
|
|
|
return False
|
|
|
|
|
|
|
|
class PkgConfigDependency(Dependency):
|
|
|
|
pkgconfig_found = None
|
|
|
|
|
|
|
|
__libtool_pat = re.compile("dlname='([A-z0-9\.\-\+]+)'\n")
|
|
|
|
|
|
|
|
|
|
|
|
def __init__(self, name, kwargs):
|
|
|
|
required = kwargs.get('required', True)
|
|
|
|
Dependency.__init__(self)
|
|
|
|
self.name = name
|
|
|
|
if PkgConfigDependency.pkgconfig_found is None:
|
|
|
|
self.check_pkgconfig()
|
|
|
|
|
|
|
|
self.is_found = False
|
|
|
|
if not PkgConfigDependency.pkgconfig_found:
|
|
|
|
if required:
|
|
|
|
raise DependencyException('Pkg-config not found.')
|
|
|
|
self.cargs = []
|
|
|
|
self.libs = []
|
|
|
|
return
|
|
|
|
p = subprocess.Popen(['pkg-config', '--modversion', name], stdout=subprocess.PIPE,
|
|
|
|
stderr=subprocess.PIPE)
|
|
|
|
out = p.communicate()[0]
|
|
|
|
if p.returncode != 0:
|
|
|
|
if required:
|
|
|
|
raise DependencyException('Required dependency %s not found.' % name)
|
|
|
|
self.modversion = 'none'
|
|
|
|
self.cargs = []
|
|
|
|
self.libs = []
|
|
|
|
else:
|
|
|
|
self.modversion = out.decode().strip()
|
|
|
|
mlog.log('Dependency', mlog.bold(name), 'found:', mlog.green('YES'), self.modversion)
|
|
|
|
version_requirement = kwargs.get('version', None)
|
|
|
|
if version_requirement is None:
|
|
|
|
self.is_found = True
|
|
|
|
else:
|
|
|
|
if not isinstance(version_requirement, str):
|
|
|
|
raise DependencyException('Version argument must be string.')
|
|
|
|
self.is_found = mesonlib.version_compare(self.modversion, version_requirement)
|
|
|
|
if not self.is_found and required:
|
|
|
|
raise DependencyException('Invalid version of a dependency, needed %s %s found %s.' % (name, version_requirement, self.modversion))
|
|
|
|
if not self.is_found:
|
|
|
|
return
|
|
|
|
p = subprocess.Popen(['pkg-config', '--cflags', name], stdout=subprocess.PIPE,
|
|
|
|
stderr=subprocess.PIPE)
|
|
|
|
out = p.communicate()[0]
|
|
|
|
if p.returncode != 0:
|
|
|
|
raise RuntimeError('Could not generate cargs for %s.' % name)
|
|
|
|
self.cargs = out.decode().split()
|
|
|
|
|
|
|
|
p = subprocess.Popen(['pkg-config', '--libs', name], stdout=subprocess.PIPE,
|
|
|
|
stderr=subprocess.PIPE)
|
|
|
|
out = p.communicate()[0]
|
|
|
|
if p.returncode != 0:
|
|
|
|
raise RuntimeError('Could not generate libs for %s.' % name)
|
|
|
|
self.libs = []
|
|
|
|
for lib in out.decode().split():
|
|
|
|
if lib.endswith(".la"):
|
|
|
|
shared_libname = self.__extract_libtool_shlib(lib)
|
|
|
|
shared_lib = os.path.join(os.path.dirname(lib), shared_libname)
|
|
|
|
if not os.path.exists(shared_lib):
|
|
|
|
shared_lib = os.path.join(os.path.dirname(lib), ".libs", shared_libname)
|
|
|
|
|
|
|
|
if not os.path.exists(shared_lib):
|
|
|
|
raise RuntimeError('Got a libtools specific "%s" dependencies'
|
|
|
|
'but we could not compute the actual shared'
|
|
|
|
'library path' % lib)
|
|
|
|
lib = shared_lib
|
|
|
|
|
|
|
|
self.libs.append(lib)
|
|
|
|
|
|
|
|
def get_modversion(self):
|
|
|
|
return self.modversion
|
|
|
|
|
|
|
|
def get_compile_args(self):
|
|
|
|
return self.cargs
|
|
|
|
|
|
|
|
def get_link_args(self):
|
|
|
|
return self.libs
|
|
|
|
|
|
|
|
def check_pkgconfig(self):
|
|
|
|
try:
|
|
|
|
p = subprocess.Popen(['pkg-config', '--version'], stdout=subprocess.PIPE,
|
|
|
|
stderr=subprocess.PIPE)
|
|
|
|
out = p.communicate()[0]
|
|
|
|
if p.returncode == 0:
|
|
|
|
mlog.log('Found pkg-config:', mlog.bold(shutil.which('pkg-config')),
|
|
|
|
'(%s)' % out.decode().strip())
|
|
|
|
PkgConfigDependency.pkgconfig_found = True
|
|
|
|
return
|
|
|
|
except Exception:
|
|
|
|
pass
|
|
|
|
PkgConfigDependency.pkgconfig_found = False
|
|
|
|
mlog.log('Found Pkg-config:', mlog.red('NO'))
|
|
|
|
|
|
|
|
def found(self):
|
|
|
|
return self.is_found
|
|
|
|
|
|
|
|
def __extract_dlname_field(self, la_file):
|
|
|
|
f = open(la_file)
|
|
|
|
data = f.read()
|
|
|
|
f.close()
|
|
|
|
m = self.__libtool_pat.search(data)
|
|
|
|
if m:
|
|
|
|
return m.groups()[0]
|
|
|
|
else:
|
|
|
|
return None
|
|
|
|
|
|
|
|
def __extract_libtool_shlib(self, la_file):
|
|
|
|
'''
|
|
|
|
Returns the path to the shared library
|
|
|
|
corresponding to this .la file
|
|
|
|
'''
|
|
|
|
dlname = self.__extract_dlname_field(la_file)
|
|
|
|
if dlname is None:
|
|
|
|
return None
|
|
|
|
|
|
|
|
# Darwin uses absolute paths where possible; since the libtool files never
|
|
|
|
# contain absolute paths, use the libdir field
|
|
|
|
if platform.system() == 'Darwin':
|
|
|
|
dlbasename = os.path.basename(dlname)
|
|
|
|
libdir = self._extract_libdir_field(la_file)
|
|
|
|
if libdir is None:
|
|
|
|
return dlbasename
|
|
|
|
return libdir + '/' + dlbasename
|
|
|
|
# From the comments in extract_libtool(), older libtools had
|
|
|
|
# a path rather than the raw dlname
|
|
|
|
return os.path.basename(dlname)
|
|
|
|
|
|
|
|
class WxDependency(Dependency):
|
|
|
|
wx_found = None
|
|
|
|
|
|
|
|
def __init__(self, kwargs):
|
|
|
|
Dependency.__init__(self)
|
|
|
|
if WxDependency.wx_found is None:
|
|
|
|
self.check_wxconfig()
|
|
|
|
|
|
|
|
if not WxDependency.wx_found:
|
|
|
|
raise DependencyException('Wx-config not found.')
|
|
|
|
self.is_found = False
|
|
|
|
p = subprocess.Popen([self.wxc, '--version'], stdout=subprocess.PIPE,
|
|
|
|
stderr=subprocess.PIPE)
|
|
|
|
out = p.communicate()[0]
|
|
|
|
if p.returncode != 0:
|
|
|
|
mlog.log('Dependency wxwidgets found:', mlog.red('NO'))
|
|
|
|
self.cargs = []
|
|
|
|
self.libs = []
|
|
|
|
else:
|
|
|
|
self.modversion = out.decode().strip()
|
|
|
|
version_req = kwargs.get('version', None)
|
|
|
|
if version_req is not None:
|
|
|
|
if not mesonlib.version_compare(self.modversion, version_req):
|
|
|
|
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 = subprocess.Popen([self.wxc, '--cxxflags'], stdout=subprocess.PIPE,
|
|
|
|
stderr=subprocess.PIPE)
|
|
|
|
out = p.communicate()[0]
|
|
|
|
if p.returncode != 0:
|
|
|
|
raise RuntimeError('Could not generate cargs for wxwidgets.')
|
|
|
|
self.cargs = out.decode().split()
|
|
|
|
|
|
|
|
p = subprocess.Popen([self.wxc, '--libs'] + self.requested_modules,
|
|
|
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
|
|
out = p.communicate()[0]
|
|
|
|
if p.returncode != 0:
|
|
|
|
raise RuntimeError('Could not generate libs for wxwidgets.')
|
|
|
|
self.libs = out.decode().split()
|
|
|
|
|
|
|
|
def get_requested(self, kwargs):
|
|
|
|
modules = 'modules'
|
|
|
|
if not modules 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_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 = subprocess.Popen([wxc, '--version'], stdout=subprocess.PIPE,
|
|
|
|
stderr=subprocess.PIPE)
|
|
|
|
out = p.communicate()[0]
|
|
|
|
if p.returncode == 0:
|
|
|
|
mlog.log('Found wx-config:', mlog.bold(shutil.which(wxc)),
|
|
|
|
'(%s)' % out.decode().strip())
|
|
|
|
self.wxc = wxc
|
|
|
|
WxDependency.wx_found = True
|
|
|
|
return
|
|
|
|
except Exception:
|
|
|
|
pass
|
|
|
|
WxDependency.wxconfig_found = False
|
|
|
|
mlog.log('Found wx-config:', mlog.red('NO'))
|
|
|
|
|
|
|
|
def found(self):
|
|
|
|
return self.is_found
|
|
|
|
|
|
|
|
class ExternalProgram():
|
|
|
|
def __init__(self, name, fullpath=None, silent=False, search_dir=None):
|
|
|
|
self.name = name
|
|
|
|
self.fullpath = None
|
|
|
|
if fullpath is not None:
|
|
|
|
if not isinstance(fullpath, list):
|
|
|
|
self.fullpath = [fullpath]
|
|
|
|
else:
|
|
|
|
self.fullpath = fullpath
|
|
|
|
else:
|
|
|
|
self.fullpath = [shutil.which(name)]
|
|
|
|
if self.fullpath[0] is None and search_dir is not None:
|
|
|
|
trial = os.path.join(search_dir, name)
|
|
|
|
suffix = os.path.splitext(trial)[-1].lower()[1:]
|
|
|
|
if mesonlib.is_windows() and (suffix == 'exe' or suffix == 'com'\
|
|
|
|
or suffix == 'bat'):
|
|
|
|
self.fullpath = [trial]
|
|
|
|
elif not mesonlib.is_windows() and os.access(trial, os.X_OK):
|
|
|
|
self.fullpath = [trial]
|
|
|
|
else:
|
|
|
|
# Now getting desperate. Maybe it is a script file that is a) not chmodded
|
|
|
|
# executable or b) we are on windows so they can't be directly executed.
|
|
|
|
try:
|
|
|
|
first_line = open(trial).readline().strip()
|
|
|
|
if first_line.startswith('#!'):
|
|
|
|
commands = first_line[2:].split('#')[0].strip().split()
|
|
|
|
if mesonlib.is_windows():
|
|
|
|
commands[0] = commands[0].split('/')[-1] # Windows does not have /usr/bin.
|
|
|
|
if commands[0] == 'env':
|
|
|
|
commands = commands[1:]
|
|
|
|
self.fullpath = commands + [trial]
|
|
|
|
except Exception:
|
|
|
|
pass
|
|
|
|
if not silent:
|
|
|
|
if self.found():
|
|
|
|
mlog.log('Program', mlog.bold(name), 'found:', mlog.green('YES'), '(%s)' % ' '.join(self.fullpath))
|
|
|
|
else:
|
|
|
|
mlog.log('Program', mlog.bold(name), 'found:', mlog.red('NO'))
|
|
|
|
|
|
|
|
def found(self):
|
|
|
|
return self.fullpath[0] is not None
|
|
|
|
|
|
|
|
def get_command(self):
|
|
|
|
return self.fullpath
|
|
|
|
|
|
|
|
def get_name(self):
|
|
|
|
return self.name
|
|
|
|
|
|
|
|
class ExternalLibrary(Dependency):
|
|
|
|
def __init__(self, name, fullpath=None, silent=False):
|
|
|
|
super().__init__()
|
|
|
|
self.name = name
|
|
|
|
self.fullpath = fullpath
|
|
|
|
if not silent:
|
|
|
|
if self.found():
|
|
|
|
mlog.log('Library', mlog.bold(name), 'found:', mlog.green('YES'), '(%s)' % self.fullpath)
|
|
|
|
else:
|
|
|
|
mlog.log('Library', mlog.bold(name), 'found:', mlog.red('NO'))
|
|
|
|
|
|
|
|
def found(self):
|
|
|
|
return self.fullpath is not None
|
|
|
|
|
|
|
|
def get_link_args(self):
|
|
|
|
if self.found():
|
|
|
|
return [self.fullpath]
|
|
|
|
return []
|
|
|
|
|
|
|
|
class BoostDependency(Dependency):
|
|
|
|
# Some boost libraries have different names for
|
|
|
|
# their sources and libraries. This dict maps
|
|
|
|
# between the two.
|
|
|
|
name2lib = {'test' : 'unit_test_framework'}
|
|
|
|
|
|
|
|
def __init__(self, kwargs):
|
|
|
|
Dependency.__init__(self)
|
|
|
|
self.name = 'boost'
|
|
|
|
try:
|
|
|
|
self.boost_root = os.environ['BOOST_ROOT']
|
|
|
|
if not os.path.isabs(self.boost_root):
|
|
|
|
raise DependencyException('BOOST_ROOT must be an absolute path.')
|
|
|
|
except KeyError:
|
|
|
|
self.boost_root = None
|
|
|
|
if self.boost_root is None:
|
|
|
|
self.incdir = '/usr/include/boost'
|
|
|
|
else:
|
|
|
|
self.incdir = os.path.join(self.boost_root, 'include/boost')
|
|
|
|
self.src_modules = {}
|
|
|
|
self.lib_modules = {}
|
|
|
|
self.lib_modules_mt = {}
|
|
|
|
self.detect_version()
|
|
|
|
self.requested_modules = self.get_requested(kwargs)
|
|
|
|
module_str = ', '.join(self.requested_modules)
|
|
|
|
if self.version is not None:
|
|
|
|
self.detect_src_modules()
|
|
|
|
self.detect_lib_modules()
|
|
|
|
self.validate_requested()
|
|
|
|
if self.boost_root is not None:
|
|
|
|
info = self.version + ', ' + self.boost_root
|
|
|
|
else:
|
|
|
|
info = self.version
|
|
|
|
mlog.log('Dependency Boost (%s) found:' % module_str, mlog.green('YES'),
|
|
|
|
'(' + info + ')')
|
|
|
|
else:
|
|
|
|
mlog.log("Dependency Boost (%s) found:" % module_str, mlog.red('NO'))
|
|
|
|
|
|
|
|
def get_compile_args(self):
|
|
|
|
args = []
|
|
|
|
if self.boost_root is not None:
|
|
|
|
args.append('-I' + os.path.join(self.boost_root, 'include'))
|
|
|
|
return args
|
|
|
|
|
|
|
|
def get_requested(self, kwargs):
|
|
|
|
modules = 'modules'
|
|
|
|
if not modules in kwargs:
|
|
|
|
raise DependencyException('Boost dependency must specify "%s" keyword.' % modules)
|
|
|
|
candidates = kwargs[modules]
|
|
|
|
if isinstance(candidates, str):
|
|
|
|
return [candidates]
|
|
|
|
for c in candidates:
|
|
|
|
if not isinstance(c, str):
|
|
|
|
raise DependencyException('Boost module argument is not a string.')
|
|
|
|
return candidates
|
|
|
|
|
|
|
|
def validate_requested(self):
|
|
|
|
for m in self.requested_modules:
|
|
|
|
if m not in self.src_modules:
|
|
|
|
raise DependencyException('Requested Boost module "%s" not found.' % m)
|
|
|
|
|
|
|
|
def found(self):
|
|
|
|
return self.version is not None
|
|
|
|
|
|
|
|
def get_version(self):
|
|
|
|
return self.version
|
|
|
|
|
|
|
|
def detect_version(self):
|
|
|
|
try:
|
|
|
|
ifile = open(os.path.join(self.incdir, 'version.hpp'))
|
|
|
|
except FileNotFoundError:
|
|
|
|
self.version = None
|
|
|
|
return
|
|
|
|
for line in ifile:
|
|
|
|
if line.startswith("#define") and 'BOOST_LIB_VERSION' in line:
|
|
|
|
ver = line.split()[-1]
|
|
|
|
ver = ver[1:-1]
|
|
|
|
self.version = ver.replace('_', '.')
|
|
|
|
return
|
|
|
|
self.version = None
|
|
|
|
|
|
|
|
def detect_src_modules(self):
|
|
|
|
for entry in os.listdir(self.incdir):
|
|
|
|
entry = os.path.join(self.incdir, entry)
|
|
|
|
if stat.S_ISDIR(os.stat(entry).st_mode):
|
|
|
|
self.src_modules[os.path.split(entry)[-1]] = True
|
|
|
|
|
|
|
|
def detect_lib_modules(self):
|
|
|
|
globber = 'libboost_*.so' # FIXME, make platform independent.
|
|
|
|
if self.boost_root is None:
|
|
|
|
libdirs = mesonlib.get_library_dirs()
|
|
|
|
else:
|
|
|
|
libdirs = [os.path.join(self.boost_root, 'lib')]
|
|
|
|
for libdir in libdirs:
|
|
|
|
for entry in glob.glob(os.path.join(libdir, globber)):
|
|
|
|
lib = os.path.basename(entry)
|
|
|
|
name = lib.split('.')[0].split('_', 1)[-1]
|
|
|
|
# I'm not 100% sure what to do here. Some distros
|
|
|
|
# have modules such as thread only as -mt versions.
|
|
|
|
if entry.endswith('-mt.so'):
|
|
|
|
self.lib_modules_mt[name] = True
|
|
|
|
else:
|
|
|
|
self.lib_modules[name] = True
|
|
|
|
|
|
|
|
def get_link_args(self):
|
|
|
|
args = []
|
|
|
|
if self.boost_root:
|
|
|
|
# FIXME, these are in gcc format, not msvc.
|
|
|
|
# On the other hand, so are the args that
|
|
|
|
# pkg-config returns.
|
|
|
|
args.append('-L' + os.path.join(self.boost_root, 'lib'))
|
|
|
|
for module in self.requested_modules:
|
|
|
|
module = BoostDependency.name2lib.get(module, module)
|
|
|
|
if module in self.lib_modules or module in self.lib_modules_mt:
|
|
|
|
linkcmd = '-lboost_' + module
|
|
|
|
args.append(linkcmd)
|
|
|
|
# FIXME a hack, but Boost's testing framework has a lot of
|
|
|
|
# different options and it's hard to determine what to do
|
|
|
|
# without feedback from actual users. Update this
|
|
|
|
# as we get more bug reports.
|
|
|
|
if module == 'unit_testing_framework':
|
|
|
|
args.append('-lboost_test_exec_monitor')
|
|
|
|
elif module + '-mt' in self.lib_modules_mt:
|
|
|
|
linkcmd = '-lboost_' + module + '-mt'
|
|
|
|
args.append(linkcmd)
|
|
|
|
if module == 'unit_testing_framework':
|
|
|
|
args.append('-lboost_test_exec_monitor-mt')
|
|
|
|
return args
|
|
|
|
|
|
|
|
def get_sources(self):
|
|
|
|
return []
|
|
|
|
|
|
|
|
def need_threads(self):
|
|
|
|
return 'thread' in self.requested_modules
|
|
|
|
|
|
|
|
class GTestDependency(Dependency):
|
|
|
|
def __init__(self, kwargs):
|
|
|
|
Dependency.__init__(self)
|
|
|
|
self.main = kwargs.get('main', False)
|
|
|
|
self.name = 'gtest'
|
|
|
|
self.libname = 'libgtest.so'
|
|
|
|
self.libmain_name = 'libgtest_main.so'
|
|
|
|
self.include_dir = '/usr/include'
|
|
|
|
self.src_include_dir = '/usr/src/gtest'
|
|
|
|
self.src_dir = '/usr/src/gtest/src'
|
|
|
|
self.all_src = mesonlib.File.from_absolute_file(os.path.join(self.src_dir, 'gtest-all.cc'))
|
|
|
|
self.main_src = mesonlib.File.from_absolute_file(os.path.join(self.src_dir, 'gtest_main.cc'))
|
|
|
|
self.detect()
|
|
|
|
|
|
|
|
def found(self):
|
|
|
|
return self.is_found
|
|
|
|
|
|
|
|
def detect(self):
|
|
|
|
trial_dirs = mesonlib.get_library_dirs()
|
|
|
|
glib_found = False
|
|
|
|
gmain_found = False
|
|
|
|
for d in trial_dirs:
|
|
|
|
if os.path.isfile(os.path.join(d, self.libname)):
|
|
|
|
glib_found = True
|
|
|
|
if os.path.isfile(os.path.join(d, self.libmain_name)):
|
|
|
|
gmain_found = True
|
|
|
|
if glib_found and gmain_found:
|
|
|
|
self.is_found = True
|
|
|
|
self.compile_args = []
|
|
|
|
self.link_args = ['-lgtest']
|
|
|
|
if self.main:
|
|
|
|
self.link_args.append('-lgtest_main')
|
|
|
|
self.sources = []
|
|
|
|
mlog.log('Dependency GTest found:', mlog.green('YES'), '(prebuilt)')
|
|
|
|
elif os.path.exists(self.src_dir):
|
|
|
|
self.is_found = True
|
|
|
|
self.compile_args = ['-I' + self.src_include_dir]
|
|
|
|
self.link_args = []
|
|
|
|
if self.main:
|
|
|
|
self.sources = [self.all_src, self.main_src]
|
|
|
|
else:
|
|
|
|
self.sources = [self.all_src]
|
|
|
|
mlog.log('Dependency GTest found:', mlog.green('YES'), '(building self)')
|
|
|
|
else:
|
|
|
|
mlog.log('Dependency GTest found:', mlog.red('NO'))
|
|
|
|
self.is_found = False
|
|
|
|
return self.is_found
|
|
|
|
|
|
|
|
def get_compile_args(self):
|
|
|
|
arr = []
|
|
|
|
if self.include_dir != '/usr/include':
|
|
|
|
arr.append('-I' + self.include_dir)
|
|
|
|
arr.append('-I' + self.src_include_dir)
|
|
|
|
return arr
|
|
|
|
|
|
|
|
def get_link_args(self):
|
|
|
|
return self.link_args
|
|
|
|
def get_version(self):
|
|
|
|
return '1.something_maybe'
|
|
|
|
def get_sources(self):
|
|
|
|
return self.sources
|
|
|
|
|
|
|
|
def need_threads(self):
|
|
|
|
return True
|
|
|
|
|
|
|
|
class GMockDependency(Dependency):
|
|
|
|
def __init__(self, kwargs):
|
|
|
|
Dependency.__init__(self)
|
|
|
|
# GMock may be a library or just source.
|
|
|
|
# Work with both.
|
|
|
|
self.name = 'gmock'
|
|
|
|
self.libname = 'libgmock.so'
|
|
|
|
trial_dirs = mesonlib.get_library_dirs()
|
|
|
|
gmock_found = False
|
|
|
|
for d in trial_dirs:
|
|
|
|
if os.path.isfile(os.path.join(d, self.libname)):
|
|
|
|
gmock_found = True
|
|
|
|
if gmock_found:
|
|
|
|
self.is_found = True
|
|
|
|
self.compile_args = []
|
|
|
|
self.link_args = ['-lgmock']
|
|
|
|
self.sources = []
|
|
|
|
mlog.log('Dependency GMock found:', mlog.green('YES'), '(prebuilt)')
|
|
|
|
return
|
|
|
|
|
|
|
|
for d in ['/usr/src/gmock/src', '/usr/src/gmock']:
|
|
|
|
if os.path.exists(d):
|
|
|
|
self.is_found = True
|
|
|
|
# Yes, we need both because there are multiple
|
|
|
|
# versions of gmock that do different things.
|
|
|
|
self.compile_args = ['-I/usr/src/gmock', '-I/usr/src/gmock/src']
|
|
|
|
self.link_args = []
|
|
|
|
all_src = mesonlib.File.from_absolute_file(os.path.join(d, 'gmock-all.cc'))
|
|
|
|
main_src = mesonlib.File.from_absolute_file(os.path.join(d, 'gmock_main.cc'))
|
|
|
|
if kwargs.get('main', False):
|
|
|
|
self.sources = [all_src, main_src]
|
|
|
|
else:
|
|
|
|
self.sources = [all_src]
|
|
|
|
mlog.log('Dependency GMock found:', mlog.green('YES'), '(building self)')
|
|
|
|
return
|
|
|
|
|
|
|
|
mlog.log('Dependency GMock found:', mlog.red('NO'))
|
|
|
|
self.is_found = False
|
|
|
|
|
|
|
|
def get_version(self):
|
|
|
|
return '1.something_maybe'
|
|
|
|
|
|
|
|
def get_compile_args(self):
|
|
|
|
return self.compile_args
|
|
|
|
|
|
|
|
def get_sources(self):
|
|
|
|
return self.sources
|
|
|
|
|
|
|
|
def get_link_args(self):
|
|
|
|
return self.link_args
|
|
|
|
|
|
|
|
def found(self):
|
|
|
|
return self.is_found
|
|
|
|
|
|
|
|
class Qt5Dependency(Dependency):
|
|
|
|
def __init__(self, kwargs):
|
|
|
|
Dependency.__init__(self)
|
|
|
|
self.name = 'qt5'
|
|
|
|
self.root = '/usr'
|
|
|
|
mods = kwargs.get('modules', [])
|
|
|
|
self.cargs = []
|
|
|
|
self.largs= []
|
|
|
|
self.is_found = False
|
|
|
|
if isinstance(mods, str):
|
|
|
|
mods = [mods]
|
|
|
|
if len(mods) == 0:
|
|
|
|
raise DependencyException('No Qt5 modules specified.')
|
|
|
|
if shutil.which('pkg-config') is not None:
|
|
|
|
self.pkgconfig_detect(mods, kwargs)
|
|
|
|
elif shutil.which('qmake') is not None:
|
|
|
|
self.qmake_detect(mods, kwargs)
|
|
|
|
if not self.is_found:
|
|
|
|
mlog.log('Qt5 dependency found: ', mlog.red('NO'))
|
|
|
|
else:
|
|
|
|
mlog.log('Qt5 dependency found: ', mlog.green('YES'))
|
|
|
|
|
|
|
|
def pkgconfig_detect(self, mods, kwargs):
|
|
|
|
modules = []
|
|
|
|
for module in mods:
|
|
|
|
modules.append(PkgConfigDependency('Qt5' + module, kwargs))
|
|
|
|
for m in modules:
|
|
|
|
self.cargs += m.get_compile_args()
|
|
|
|
self.largs += m.get_link_args()
|
|
|
|
self.is_found = True
|
|
|
|
|
|
|
|
def qmake_detect(self, mods, kwargs):
|
|
|
|
pc = subprocess.Popen(['qmake', '-v'], stdout=subprocess.PIPE,
|
|
|
|
stderr=subprocess.PIPE)
|
|
|
|
(stdo, _) = pc.communicate()
|
|
|
|
if pc.returncode != 0:
|
|
|
|
return
|
|
|
|
stdo = stdo.decode()
|
|
|
|
if not 'version 5' in stdo:
|
|
|
|
mlog.log('QMake is not for Qt5.')
|
|
|
|
return
|
|
|
|
(stdo, _) = subprocess.Popen(['qmake', '-query'], stdout=subprocess.PIPE).communicate()
|
|
|
|
qvars = {}
|
|
|
|
for line in stdo.decode().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']
|
|
|
|
bindir = qvars['QT_INSTALL_BINS']
|
|
|
|
#self.largs.append('-L' + libdir)
|
|
|
|
for module in mods:
|
|
|
|
mincdir = os.path.join(incdir, 'Qt' + module)
|
|
|
|
self.cargs.append('-I' + mincdir)
|
|
|
|
libfile = os.path.join(libdir, 'Qt5' + module + '.lib')
|
|
|
|
if not os.path.isfile(libfile):
|
|
|
|
# MinGW links directly to .dll, not to .lib.
|
|
|
|
libfile = os.path.join(bindir, 'Qt5' + module + '.dll')
|
|
|
|
self.largs.append(libfile)
|
|
|
|
self.is_found = True
|
|
|
|
|
|
|
|
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)
|
|
|
|
self.cargs.append('-F' + libdir)
|
|
|
|
if fwdep.found():
|
|
|
|
self.is_found = True
|
|
|
|
self.cargs += fwdep.get_compile_args()
|
|
|
|
self.largs += fwdep.get_link_args()
|
|
|
|
|
|
|
|
|
|
|
|
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 found(self):
|
|
|
|
return self.is_found
|
|
|
|
|
|
|
|
def get_exe_args(self):
|
|
|
|
# Qt5 seems to require this always.
|
|
|
|
# Fix this to be more portable, especially to MSVC.
|
|
|
|
return ['-fPIE']
|
|
|
|
|
|
|
|
class Qt4Dependency(Dependency):
|
|
|
|
def __init__(self, kwargs):
|
|
|
|
Dependency.__init__(self)
|
|
|
|
self.name = 'qt4'
|
|
|
|
self.root = '/usr'
|
|
|
|
self.modules = []
|
|
|
|
mods = kwargs.get('modules', [])
|
|
|
|
if isinstance(mods, str):
|
|
|
|
mods = [mods]
|
|
|
|
for module in mods:
|
|
|
|
self.modules.append(PkgConfigDependency('Qt' + module, kwargs))
|
|
|
|
if len(self.modules) == 0:
|
|
|
|
raise DependencyException('No Qt4 modules specified.')
|
|
|
|
|
|
|
|
def get_version(self):
|
|
|
|
return self.modules[0].get_version()
|
|
|
|
|
|
|
|
def get_compile_args(self):
|
|
|
|
args = []
|
|
|
|
for m in self.modules:
|
|
|
|
args += m.get_compile_args()
|
|
|
|
return args
|
|
|
|
|
|
|
|
def get_sources(self):
|
|
|
|
return []
|
|
|
|
|
|
|
|
def get_link_args(self):
|
|
|
|
args = []
|
|
|
|
for module in self.modules:
|
|
|
|
args += module.get_link_args()
|
|
|
|
return args
|
|
|
|
|
|
|
|
def found(self):
|
|
|
|
for i in self.modules:
|
|
|
|
if not i.found():
|
|
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
|
|
class GnuStepDependency(Dependency):
|
|
|
|
def __init__(self, kwargs):
|
|
|
|
Dependency.__init__(self)
|
|
|
|
self.modules = kwargs.get('modules', [])
|
|
|
|
self.detect()
|
|
|
|
|
|
|
|
def detect(self):
|
|
|
|
confprog = 'gnustep-config'
|
|
|
|
try:
|
|
|
|
gp = subprocess.Popen([confprog, '--help'],
|
|
|
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
|
|
gp.communicate()
|
|
|
|
except FileNotFoundError:
|
|
|
|
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 = subprocess.Popen([confprog, '--objc-flags'],
|
|
|
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
|
|
(flagtxt, flagerr) = fp.communicate()
|
|
|
|
flagtxt = flagtxt.decode()
|
|
|
|
flagerr = flagerr.decode()
|
|
|
|
if fp.returncode != 0:
|
|
|
|
raise DependencyException('Error getting objc-args: %s %s' % (flagtxt, flagerr))
|
|
|
|
args = flagtxt.split()
|
|
|
|
self.args = self.filter_arsg(args)
|
|
|
|
fp = subprocess.Popen([confprog, arg],
|
|
|
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
|
|
(libtxt, liberr) = fp.communicate()
|
|
|
|
libtxt = libtxt.decode()
|
|
|
|
liberr = liberr.decode()
|
|
|
|
if fp.returncode != 0:
|
|
|
|
raise DependencyException('Error getting objc-lib args: %s %s' % (libtxt, liberr))
|
|
|
|
self.libs = self.weird_filter(libtxt.split())
|
|
|
|
mlog.log('Dependency GnuStep found:', mlog.green('YES'))
|
|
|
|
|
|
|
|
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 found(self):
|
|
|
|
return self.args is not None
|
|
|
|
|
|
|
|
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, kwargs):
|
|
|
|
Dependency.__init__(self)
|
|
|
|
modules = kwargs.get('modules', [])
|
|
|
|
if isinstance(modules, str):
|
|
|
|
modules = [modules]
|
|
|
|
if len(modules) == 0:
|
|
|
|
raise DependencyException("AppleFrameworks dependency requires at least one module.")
|
|
|
|
self.frameworks = modules
|
|
|
|
|
|
|
|
def get_link_args(self):
|
|
|
|
args = []
|
|
|
|
for f in self.frameworks:
|
|
|
|
args.append('-framework')
|
|
|
|
args.append(f)
|
|
|
|
return args
|
|
|
|
|
|
|
|
def found(self):
|
|
|
|
return mesonlib.is_osx()
|
|
|
|
|
|
|
|
class GLDependency(Dependency):
|
|
|
|
def __init__(self, kwargs):
|
|
|
|
Dependency.__init__(self)
|
|
|
|
self.is_found = False
|
|
|
|
self.cargs = []
|
|
|
|
self.linkargs = []
|
|
|
|
try:
|
|
|
|
pcdep = PkgConfigDependency('gl', kwargs)
|
|
|
|
if pcdep.found():
|
|
|
|
self.is_found = True
|
|
|
|
self.cargs = pcdep.get_compile_args()
|
|
|
|
self.linkargs = pcdep.get_link_args()
|
|
|
|
except Exception:
|
|
|
|
pass
|
|
|
|
if mesonlib.is_osx():
|
|
|
|
self.is_found = True
|
|
|
|
self.linkargs = ['-framework', 'OpenGL']
|
|
|
|
return
|
|
|
|
if mesonlib.is_windows():
|
|
|
|
self.is_found = True
|
|
|
|
return
|
|
|
|
|
|
|
|
def get_link_args(self):
|
|
|
|
return self.linkargs
|
|
|
|
|
|
|
|
# There are three different ways of depending on SDL2:
|
|
|
|
# sdl2-config, pkg-config and OSX framework
|
|
|
|
class SDL2Dependency(Dependency):
|
|
|
|
def __init__(self, kwargs):
|
|
|
|
Dependency.__init__(self)
|
|
|
|
self.is_found = False
|
|
|
|
self.cargs = []
|
|
|
|
self.linkargs = []
|
|
|
|
sdlconf = shutil.which('sdl2-config')
|
|
|
|
if sdlconf:
|
|
|
|
pc = subprocess.Popen(['sdl2-config', '--cflags'], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL)
|
|
|
|
(stdo, _) = pc.communicate()
|
|
|
|
self.cargs = stdo.decode().strip().split()
|
|
|
|
pc = subprocess.Popen(['sdl2-config', '--libs'], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL)
|
|
|
|
(stdo, _) = pc.communicate()
|
|
|
|
self.linkargs = stdo.decode().strip().split()
|
|
|
|
self.is_found = True
|
|
|
|
mlog.log('Dependency', mlog.bold('sdl2'), 'found:', mlog.green('YES'), '(%s)' % sdlconf)
|
|
|
|
return
|
|
|
|
try:
|
|
|
|
pcdep = PkgConfigDependency('sdl2', kwargs)
|
|
|
|
if pcdep.found():
|
|
|
|
self.is_found = True
|
|
|
|
self.cargs = pcdep.get_compile_args()
|
|
|
|
self.linkargs = pcdep.get_link_args()
|
|
|
|
return
|
|
|
|
except Exception:
|
|
|
|
pass
|
|
|
|
if mesonlib.is_osx():
|
|
|
|
fwdep = ExtraFrameworkDependency('sdl2', kwargs.get('required', True))
|
|
|
|
if fwdep.found():
|
|
|
|
self.is_found = True
|
|
|
|
self.cargs = fwdep.get_compile_args()
|
|
|
|
self.linkargs = fwdep.get_link_args()
|
|
|
|
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
|
|
|
|
|
|
|
|
class ExtraFrameworkDependency(Dependency):
|
|
|
|
def __init__(self, name, required, path=None):
|
|
|
|
Dependency.__init__(self)
|
|
|
|
self.name = None
|
|
|
|
self.detect(name, path)
|
|
|
|
if self.found():
|
|
|
|
mlog.log('Dependency', mlog.bold(name), 'found:', mlog.green('YES'), os.path.join(self.path, self.name))
|
|
|
|
else:
|
|
|
|
mlog.log('Dependency', name, 'found:', mlog.red('NO'))
|
|
|
|
|
|
|
|
def detect(self, name, path):
|
|
|
|
lname = name.lower()
|
|
|
|
if path is None:
|
|
|
|
paths = ['/Library/Frameworks']
|
|
|
|
else:
|
|
|
|
paths = [path]
|
|
|
|
for p in paths:
|
|
|
|
for d in os.listdir(p):
|
|
|
|
fullpath = os.path.join(p, d)
|
|
|
|
if lname != d.split('.')[0].lower():
|
|
|
|
continue
|
|
|
|
if not stat.S_ISDIR(os.stat(fullpath).st_mode):
|
|
|
|
continue
|
|
|
|
self.path = p
|
|
|
|
self.name = d
|
|
|
|
return
|
|
|
|
|
|
|
|
def get_compile_args(self):
|
|
|
|
if self.found():
|
|
|
|
return ['-I' + os.path.join(self.path, self.name, 'Headers')]
|
|
|
|
return []
|
|
|
|
|
|
|
|
def get_link_args(self):
|
|
|
|
if self.found():
|
|
|
|
return ['-F' + self.path, '-framework', self.name.split('.')[0]]
|
|
|
|
return []
|
|
|
|
|
|
|
|
def found(self):
|
|
|
|
return self.name is not None
|
|
|
|
|
|
|
|
def get_dep_identifier(name, kwargs):
|
|
|
|
elements = [name]
|
|
|
|
modlist = kwargs.get('modules', [])
|
|
|
|
if isinstance(modlist, str):
|
|
|
|
modlist = [modlist]
|
|
|
|
for module in modlist:
|
|
|
|
elements.append(module)
|
|
|
|
return '/'.join(elements)
|
|
|
|
|
|
|
|
def find_external_dependency(name, kwargs):
|
|
|
|
required = kwargs.get('required', True)
|
|
|
|
if not isinstance(required, bool):
|
|
|
|
raise DependencyException('Keyword "required" must be a boolean.')
|
|
|
|
lname = name.lower()
|
|
|
|
if lname in packages:
|
|
|
|
dep = packages[lname](kwargs)
|
|
|
|
if required and not dep.found():
|
|
|
|
raise DependencyException('Dependency "%s" not found' % name)
|
|
|
|
return dep
|
|
|
|
pkg_exc = None
|
|
|
|
pkgdep = None
|
|
|
|
try:
|
|
|
|
pkgdep = PkgConfigDependency(name, kwargs)
|
|
|
|
if pkgdep.found():
|
|
|
|
return pkgdep
|
|
|
|
except Exception as e:
|
|
|
|
pkg_exc = e
|
|
|
|
if mesonlib.is_osx():
|
|
|
|
fwdep = ExtraFrameworkDependency(name, required)
|
|
|
|
if required and not fwdep.found():
|
|
|
|
raise DependencyException('Dependency "%s" not found' % name)
|
|
|
|
return fwdep
|
|
|
|
if pkg_exc is not None:
|
|
|
|
raise pkg_exc
|
|
|
|
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,
|
|
|
|
}
|