diff options
author | Jussi Pakkanen <jpakkane@gmail.com> | 2017-05-11 22:43:10 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-05-11 22:43:10 +0300 |
commit | 86ec0d9477c3c7fe313dc250c10929dabc5c6188 (patch) | |
tree | 8da675f8a5ea931aa7e25c57e818a6a1cd925874 /mesonbuild/dependencies/misc.py | |
parent | 73494c992761a528bf98c8dd48fbdd2f71d4b521 (diff) | |
parent | e922a6f6f030b9781b994f1088bac70ec02772a4 (diff) | |
download | meson-86ec0d9477c3c7fe313dc250c10929dabc5c6188.zip meson-86ec0d9477c3c7fe313dc250c10929dabc5c6188.tar.gz meson-86ec0d9477c3c7fe313dc250c10929dabc5c6188.tar.bz2 |
Merge pull request #1631 from QuLogic/split-dependencies
Split dependencies.py into separate files
Diffstat (limited to 'mesonbuild/dependencies/misc.py')
-rw-r--r-- | mesonbuild/dependencies/misc.py | 382 |
1 files changed, 382 insertions, 0 deletions
diff --git a/mesonbuild/dependencies/misc.py b/mesonbuild/dependencies/misc.py new file mode 100644 index 0000000..3374c6e --- /dev/null +++ b/mesonbuild/dependencies/misc.py @@ -0,0 +1,382 @@ +# 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 miscellaneous external dependencies. + +import glob +import os +import stat +import sysconfig + +from .. import mlog +from .. import mesonlib +from ..environment import detect_cpu_family + +from .base import Dependency, DependencyException, DependencyMethods, ExtraFrameworkDependency, PkgConfigDependency + + +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, environment, kwargs): + Dependency.__init__(self, 'boost', kwargs) + self.name = 'boost' + self.environment = environment + self.libdir = '' + if 'native' in kwargs and environment.is_cross_build(): + self.want_cross = not kwargs['native'] + else: + self.want_cross = environment.is_cross_build() + 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: + if self.want_cross: + if 'BOOST_INCLUDEDIR' in os.environ: + self.incdir = os.environ['BOOST_INCLUDEDIR'] + else: + raise DependencyException('BOOST_ROOT or BOOST_INCLUDEDIR is needed while cross-compiling') + if mesonlib.is_windows(): + self.boost_root = self.detect_win_root() + self.incdir = self.boost_root + else: + if 'BOOST_INCLUDEDIR' in os.environ: + self.incdir = os.environ['BOOST_INCLUDEDIR'] + else: + self.incdir = '/usr/include' + else: + self.incdir = os.path.join(self.boost_root, 'include') + self.boost_inc_subdir = os.path.join(self.incdir, 'boost') + mlog.debug('Boost library root dir is', self.boost_root) + 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')) + if 'cpp' not in self.environment.coredata.compilers: + raise DependencyException('Tried to use Boost but a C++ compiler is not defined.') + self.cpp_compiler = self.environment.coredata.compilers['cpp'] + + def detect_win_root(self): + globtext = 'c:\\local\\boost_*' + files = glob.glob(globtext) + if len(files) > 0: + return files[0] + return 'C:\\' + + def get_compile_args(self): + args = [] + include_dir = '' + if self.boost_root is not None: + if mesonlib.is_windows(): + include_dir = self.boost_root + else: + include_dir = os.path.join(self.boost_root, 'include') + else: + include_dir = self.incdir + + # Use "-isystem" when including boost headers instead of "-I" + # to avoid compiler warnings/failures when "-Werror" is used + + # Careful not to use "-isystem" on default include dirs as it + # breaks some of the headers for certain gcc versions + + # For example, doing g++ -isystem /usr/include on a simple + # "int main()" source results in the error: + # "/usr/include/c++/6.3.1/cstdlib:75:25: fatal error: stdlib.h: No such file or directory" + + # See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70129 + # and http://stackoverflow.com/questions/37218953/isystem-on-a-system-include-directory-causes-errors + # for more details + + # TODO: The correct solution would probably be to ask the + # compiler for it's default include paths (ie: "gcc -xc++ -E + # -v -") and avoid including those with -isystem + + # For now, use -isystem for all includes except for some + # typical defaults (which don't need to be included at all + # since they are in the default include paths) + if include_dir != '/usr/include' and include_dir != '/usr/local/include': + args.append("".join(self.cpp_compiler.get_include_args(include_dir, True))) + return args + + def get_requested(self, kwargs): + candidates = kwargs.get('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.boost_inc_subdir, 'version.hpp')) + except FileNotFoundError: + self.version = None + return + with ifile: + 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.boost_inc_subdir): + entry = os.path.join(self.boost_inc_subdir, entry) + if stat.S_ISDIR(os.stat(entry).st_mode): + self.src_modules[os.path.split(entry)[-1]] = True + + def detect_lib_modules(self): + if mesonlib.is_windows(): + return self.detect_lib_modules_win() + return self.detect_lib_modules_nix() + + def detect_lib_modules_win(self): + arch = detect_cpu_family(self.environment.coredata.compilers) + # Guess the libdir + if arch == 'x86': + gl = 'lib32*' + elif arch == 'x86_64': + gl = 'lib64*' + else: + # Does anyone do Boost cross-compiling to other archs on Windows? + gl = None + # See if the libdir is valid + if gl: + libdir = glob.glob(os.path.join(self.boost_root, gl)) + else: + libdir = [] + # Can't find libdir, bail + if not libdir: + return + libdir = libdir[0] + self.libdir = libdir + globber = 'boost_*-gd-*.lib' # FIXME + for entry in glob.glob(os.path.join(libdir, globber)): + (_, fname) = os.path.split(entry) + base = fname.split('_', 1)[1] + modname = base.split('-', 1)[0] + self.lib_modules_mt[modname] = fname + + def detect_lib_modules_nix(self): + if mesonlib.is_osx(): + libsuffix = 'dylib' + else: + libsuffix = 'so' + + globber = 'libboost_*.{}'.format(libsuffix) + if 'BOOST_LIBRARYDIR' in os.environ: + libdirs = [os.environ['BOOST_LIBRARYDIR']] + elif 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_win_link_args(self): + args = [] + if self.boost_root: + args.append('-L' + self.libdir) + for module in self.requested_modules: + module = BoostDependency.name2lib.get(module, module) + if module in self.lib_modules_mt: + args.append(self.lib_modules_mt[module]) + return args + + def get_link_args(self): + if mesonlib.is_windows(): + return self.get_win_link_args() + args = [] + if self.boost_root: + args.append('-L' + os.path.join(self.boost_root, 'lib')) + elif 'BOOST_LIBRARYDIR' in os.environ: + args.append('-L' + os.environ['BOOST_LIBRARYDIR']) + for module in self.requested_modules: + module = BoostDependency.name2lib.get(module, module) + libname = 'boost_' + module + # The compiler's library detector is the most reliable so use that first. + default_detect = self.cpp_compiler.find_library(libname, self.environment, []) + if default_detect is not None: + if module == 'unit_testing_framework': + emon_args = self.cpp_compiler.find_library('boost_test_exec_monitor') + else: + emon_args = None + args += default_detect + if emon_args is not None: + args += emon_args + elif module in self.lib_modules or module in self.lib_modules_mt: + linkcmd = '-l' + libname + 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 ThreadDependency(Dependency): + def __init__(self, environment, kwargs): + super().__init__('threads', {}) + self.name = 'threads' + self.is_found = True + mlog.log('Dependency', mlog.bold(self.name), 'found:', mlog.green('YES')) + + def need_threads(self): + return True + + def get_version(self): + return 'unknown' + + +class Python3Dependency(Dependency): + def __init__(self, environment, kwargs): + super().__init__('python3', kwargs) + self.name = 'python3' + self.is_found = False + # We can only be sure that it is Python 3 at this point + self.version = '3' + if DependencyMethods.PKGCONFIG in self.methods: + try: + pkgdep = PkgConfigDependency('python3', environment, kwargs) + if pkgdep.found(): + self.cargs = pkgdep.cargs + self.libs = pkgdep.libs + self.version = pkgdep.get_version() + self.is_found = True + return + except Exception: + pass + if not self.is_found: + if mesonlib.is_windows() and DependencyMethods.SYSCONFIG in self.methods: + self._find_libpy3_windows(environment) + elif mesonlib.is_osx() and DependencyMethods.EXTRAFRAMEWORK in self.methods: + # In OSX the Python 3 framework does not have a version + # number in its name. + fw = ExtraFrameworkDependency('python', False, None, kwargs) + if fw.found(): + self.cargs = fw.get_compile_args() + self.libs = fw.get_link_args() + self.is_found = True + if self.is_found: + mlog.log('Dependency', mlog.bold(self.name), 'found:', mlog.green('YES')) + else: + mlog.log('Dependency', mlog.bold(self.name), 'found:', mlog.red('NO')) + + def _find_libpy3_windows(self, env): + ''' + Find python3 libraries on Windows and also verify that the arch matches + what we are building for. + ''' + pyarch = sysconfig.get_platform() + arch = detect_cpu_family(env.coredata.compilers) + if arch == 'x86': + arch = '32' + elif arch == 'x86_64': + arch = '64' + else: + # We can't cross-compile Python 3 dependencies on Windows yet + mlog.log('Unknown architecture {!r} for'.format(arch), + mlog.bold(self.name)) + self.is_found = False + return + # Pyarch ends in '32' or '64' + if arch != pyarch[-2:]: + mlog.log('Need', mlog.bold(self.name), + 'for {}-bit, but found {}-bit'.format(arch, pyarch[-2:])) + self.is_found = False + return + inc = sysconfig.get_path('include') + platinc = sysconfig.get_path('platinclude') + self.cargs = ['-I' + inc] + if inc != platinc: + self.cargs.append('-I' + platinc) + # Nothing exposes this directly that I coulf find + basedir = sysconfig.get_config_var('base') + vernum = sysconfig.get_config_var('py_version_nodot') + self.libs = ['-L{}/libs'.format(basedir), + '-lpython{}'.format(vernum)] + self.version = sysconfig.get_config_var('py_version_short') + self.is_found = True + + def get_compile_args(self): + return self.cargs + + def get_link_args(self): + return self.libs + + def get_methods(self): + if mesonlib.is_windows(): + return [DependencyMethods.PKGCONFIG, DependencyMethods.SYSCONFIG] + elif mesonlib.is_osx(): + return [DependencyMethods.PKGCONFIG, DependencyMethods.EXTRAFRAMEWORK] + else: + return [DependencyMethods.PKGCONFIG] + + def get_version(self): + return self.version |