123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683 |
- # 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
- from .. import mlog
- from .. import mesonlib
- from ..environment import detect_cpu_family
- from .base import (DependencyException, ExternalDependency)
- # On windows 3 directory layouts are supported:
- # * The default layout (versioned) installed:
- # - $BOOST_ROOT/include/boost-x_x/boost/*.hpp
- # - $BOOST_ROOT/lib/*.lib
- # * The non-default layout (system) installed:
- # - $BOOST_ROOT/include/boost/*.hpp
- # - $BOOST_ROOT/lib/*.lib
- # * The pre-built binaries from sf.net:
- # - $BOOST_ROOT/boost/*.hpp
- # - $BOOST_ROOT/lib<arch>-<compiler>/*.lib where arch=32/64 and compiler=msvc-14.1
- #
- # Note that we should also try to support:
- # mingw-w64 / Windows : libboost_<module>-mt.a (location = <prefix>/mingw64/lib/)
- # libboost_<module>-mt.dll.a
- #
- # Library names supported:
- # - libboost_<module>-<compiler>-mt-gd-x_x.lib (static)
- # - boost_<module>-<compiler>-mt-gd-x_x.lib|.dll (shared)
- # - libboost_<module>.lib (static)
- # - boost_<module>.lib|.dll (shared)
- # where compiler is vc141 for example.
- #
- # NOTE: -gd means runtime and build time debugging is on
- # -mt means threading=multi
- #
- # The `modules` argument accept library names. This is because every module that
- # has libraries to link against also has multiple options regarding how to
- # link. See for example:
- # * http://www.boost.org/doc/libs/1_65_1/libs/test/doc/html/boost_test/usage_variants.html
- # * http://www.boost.org/doc/libs/1_65_1/doc/html/stacktrace/configuration_and_build.html
- # * http://www.boost.org/doc/libs/1_65_1/libs/math/doc/html/math_toolkit/main_tr1.html
- # **On Unix**, official packaged versions of boost libraries follow the following schemes:
- #
- # Linux / Debian: libboost_<module>.so -> libboost_<module>.so.1.66.0
- # Linux / Red Hat: libboost_<module>.so -> libboost_<module>.so.1.66.0
- # Linux / OpenSuse: libboost_<module>.so -> libboost_<module>.so.1.66.0
- # Win / Cygwin: libboost_<module>.dll.a (location = /usr/lib)
- # libboost_<module>.a
- # cygboost_<module>_1_64.dll (location = /usr/bin)
- # Mac / homebrew: libboost_<module>.dylib + libboost_<module>-mt.dylib (location = /usr/local/lib)
- # Mac / macports: libboost_<module>.dylib + libboost_<module>-mt.dylib (location = /opt/local/lib)
- #
- # Its not clear that any other abi tags (e.g. -gd) are used in official packages.
- #
- # On Linux systems, boost libs have multithreading support enabled, but without the -mt tag.
- #
- # Boost documentation recommends using complex abi tags like "-lboost_regex-gcc34-mt-d-1_36".
- # (See http://www.boost.org/doc/libs/1_66_0/more/getting_started/unix-variants.html#library-naming)
- # However, its not clear that any Unix distribution follows this scheme.
- # Furthermore, the boost documentation for unix above uses examples from windows like
- # "libboost_regex-vc71-mt-d-x86-1_34.lib", so apparently the abi tags may be more aimed at windows.
- #
- # Probably we should use the linker search path to decide which libraries to use. This will
- # make it possible to find the macports boost libraries without setting BOOST_ROOT, and will
- # also mean that it would be possible to use user-installed boost libraries when official
- # packages are installed.
- #
- # We thus follow the following strategy:
- # 1. Look for libraries using compiler.find_library( )
- # 1.1 On Linux, just look for boost_<module>
- # 1.2 On other systems (e.g. Mac) look for boost_<module>-mt if multithreading.
- # 1.3 Otherwise look for boost_<module>
- # 2. Fall back to previous approach
- # 2.1. Search particular directories.
- # 2.2. Find boost libraries with unknown suffixes using file-name globbing.
- # TODO: Unix: Don't assume we know where the boost dir is, rely on -Idir and -Ldir being set.
- # TODO: Allow user to specify suffix in BOOST_SUFFIX, or add specific options like BOOST_DEBUG for 'd' for debug.
- class BoostDependency(ExternalDependency):
- def __init__(self, environment, kwargs):
- super().__init__('boost', environment, 'cpp', kwargs)
- self.need_static_link = ['boost_exception', 'boost_test_exec_monitor']
- self.is_debug = environment.coredata.get_builtin_option('buildtype').startswith('debug')
- threading = kwargs.get("threading", "multi")
- self.is_multithreading = threading == "multi"
- self.requested_modules = self.get_requested(kwargs)
- self.boost_root = None
- self.boost_roots = []
- self.incdir = None
- self.libdir = None
- if 'BOOST_ROOT' in os.environ:
- self.boost_root = os.environ['BOOST_ROOT']
- self.boost_roots = [self.boost_root]
- if not os.path.isabs(self.boost_root):
- raise DependencyException('BOOST_ROOT must be an absolute path.')
- if 'BOOST_INCLUDEDIR' in os.environ:
- self.incdir = os.environ['BOOST_INCLUDEDIR']
- if 'BOOST_LIBRARYDIR' in os.environ:
- self.libdir = os.environ['BOOST_LIBRARYDIR']
- if self.boost_root is None:
- if mesonlib.for_windows(self.want_cross, self.env):
- self.boost_roots = self.detect_win_roots()
- else:
- self.boost_roots = self.detect_nix_roots()
- if self.incdir is None:
- if mesonlib.for_windows(self.want_cross, self.env):
- self.incdir = self.detect_win_incdir()
- else:
- self.incdir = self.detect_nix_incdir()
- if self.check_invalid_modules():
- self.log_fail()
- return
- mlog.debug('Boost library root dir is', mlog.bold(self.boost_root))
- mlog.debug('Boost include directory is', mlog.bold(self.incdir))
- # 1. check if we can find BOOST headers.
- self.detect_headers_and_version()
- # 2. check if we can find BOOST libraries.
- if self.is_found:
- self.detect_lib_modules()
- mlog.debug('Boost library directory is', mlog.bold(self.libdir))
- # 3. Report success or failure
- if self.is_found:
- self.log_success()
- else:
- self.log_fail()
- def check_invalid_modules(self):
- invalid_modules = [c for c in self.requested_modules if 'boost_' + c not in BOOST_LIBS]
- # previous versions of meson allowed include dirs as modules
- remove = []
- for m in invalid_modules:
- if m in BOOST_DIRS:
- mlog.warning('Requested boost library', mlog.bold(m), 'that doesn\'t exist. '
- 'This will be an error in the future')
- remove.append(m)
- self.requested_modules = [x for x in self.requested_modules if x not in remove]
- invalid_modules = [x for x in invalid_modules if x not in remove]
- if invalid_modules:
- mlog.error('Invalid Boost modules: ' + ', '.join(invalid_modules))
- return True
- else:
- return False
- def log_fail(self):
- module_str = ', '.join(self.requested_modules)
- mlog.log("Dependency Boost (%s) found:" % module_str, mlog.red('NO'))
- def log_success(self):
- module_str = ', '.join(self.requested_modules)
- if self.boost_root:
- info = self.version + ', ' + self.boost_root
- else:
- info = self.version
- mlog.log('Dependency Boost (%s) found:' % module_str, mlog.green('YES'), info)
- def detect_nix_roots(self):
- return [os.path.abspath(os.path.join(x, '..'))
- for x in self.clib_compiler.get_default_include_dirs()]
- def detect_win_roots(self):
- res = []
- # Where boost documentation says it should be
- globtext = 'C:\\Program Files\\boost\\boost_*'
- files = glob.glob(globtext)
- res.extend(files)
- # Where boost built from source actually installs it
- if os.path.isdir('C:\\Boost'):
- res.append('C:\\Boost')
- # Where boost prebuilt binaries are
- globtext = 'C:\\local\\boost_*'
- files = glob.glob(globtext)
- res.extend(files)
- return res
- def detect_nix_incdir(self):
- if self.boost_root:
- return os.path.join(self.boost_root, 'include')
- return None
- # FIXME: Should pick a version that matches the requested version
- # Returns the folder that contains the boost folder.
- def detect_win_incdir(self):
- for root in self.boost_roots:
- globtext = os.path.join(root, 'include', 'boost-*')
- incdirs = glob.glob(globtext)
- if len(incdirs) > 0:
- return incdirs[0]
- incboostdir = os.path.join(root, 'include', 'boost')
- if os.path.isdir(incboostdir):
- return os.path.join(root, 'include')
- incboostdir = os.path.join(root, 'boost')
- if os.path.isdir(incboostdir):
- return root
- return None
- def get_compile_args(self):
- args = []
- 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
- if include_dir and include_dir not in self.clib_compiler.get_default_include_dirs():
- args.append("".join(self.clib_compiler.get_include_args(include_dir, True)))
- return args
- def get_requested(self, kwargs):
- candidates = mesonlib.extract_as_list(kwargs, 'modules')
- for c in candidates:
- if not isinstance(c, str):
- raise DependencyException('Boost module argument is not a string.')
- return candidates
- def detect_headers_and_version(self):
- try:
- version = self.clib_compiler.get_define('BOOST_LIB_VERSION', '#include <boost/version.hpp>', self.env, self.get_compile_args(), [])
- except mesonlib.EnvironmentException:
- return
- except TypeError:
- return
- # Remove quotes
- version = version[1:-1]
- # Fix version string
- self.version = version.replace('_', '.')
- self.is_found = True
- def detect_lib_modules(self):
- self.lib_modules = {}
- # 1. Try to find modules using compiler.find_library( )
- if self.find_libraries_with_abi_tags(self.abi_tags()):
- pass
- # 2. Fall back to the old method
- else:
- if mesonlib.for_windows(self.want_cross, self.env):
- self.detect_lib_modules_win()
- else:
- self.detect_lib_modules_nix()
- # 3. Check if we can find the modules
- for m in self.requested_modules:
- if 'boost_' + m not in self.lib_modules:
- mlog.debug('Requested Boost library {!r} not found'.format(m))
- self.is_found = False
- def modname_from_filename(self, filename):
- modname = os.path.basename(filename)
- modname = modname.split('.', 1)[0]
- modname = modname.split('-', 1)[0]
- if modname.startswith('libboost'):
- modname = modname[3:]
- return modname
- def compiler_tag(self):
- tag = None
- compiler = self.env.detect_cpp_compiler(self.want_cross)
- if mesonlib.for_windows(self.want_cross, self.env):
- if compiler.get_id() == 'msvc':
- comp_ts_version = compiler.get_toolset_version()
- compiler_ts = comp_ts_version.split('.')
- # FIXME - what about other compilers?
- tag = '-vc{}{}'.format(compiler_ts[0], compiler_ts[1])
- else:
- tag = ''
- return tag
- def threading_tag(self):
- if not self.is_multithreading:
- return ''
- if mesonlib.for_darwin(self.want_cross, self.env):
- # - Mac: requires -mt for multithreading, so should not fall back to non-mt libraries.
- return '-mt'
- elif mesonlib.for_windows(self.want_cross, self.env):
- # - Windows: requires -mt for multithreading, so should not fall back to non-mt libraries.
- return '-mt'
- else:
- # - Linux: leaves off -mt but libraries are multithreading-aware.
- # - Cygwin: leaves off -mt but libraries are multithreading-aware.
- return ''
- def version_tag(self):
- return '-' + self.version.replace('.', '_')
- def debug_tag(self):
- return '-gd' if self.is_debug else ''
- def versioned_abi_tag(self):
- return self.compiler_tag() + self.threading_tag() + self.debug_tag() + self.version_tag()
- # FIXME - how to handle different distributions, e.g. for Mac? Currently we handle homebrew and macports, but not fink.
- def abi_tags(self):
- if mesonlib.for_windows(self.want_cross, self.env):
- return [self.versioned_abi_tag(), self.threading_tag()]
- else:
- return [self.threading_tag()]
- def sourceforge_dir(self):
- if self.env.detect_cpp_compiler(self.want_cross).get_id() != 'msvc':
- return None
- comp_ts_version = self.env.detect_cpp_compiler(self.want_cross).get_toolset_version()
- arch = detect_cpu_family(self.env.coredata.compilers)
- if arch == 'x86':
- return 'lib32-msvc-{}'.format(comp_ts_version)
- elif arch == 'x86_64':
- return 'lib64-msvc-{}'.format(comp_ts_version)
- else:
- # Does anyone do Boost cross-compiling to other archs on Windows?
- return None
- def find_libraries_with_abi_tag(self, tag):
- # All modules should have the same tag
- self.lib_modules = {}
- all_found = True
- for module in self.requested_modules:
- libname = 'boost_' + module + tag
- args = self.clib_compiler.find_library(libname, self.env, self.extra_lib_dirs())
- if args is None:
- mlog.debug("Couldn\'t find library '{}' for boost module '{}' (ABI tag = '{}')".format(libname, module, tag))
- all_found = False
- else:
- mlog.debug('Link args for boost module "{}" are {}'.format(module, args))
- self.lib_modules['boost_' + module] = args
- return all_found
- def find_libraries_with_abi_tags(self, tags):
- for tag in tags:
- if self.find_libraries_with_abi_tag(tag):
- return True
- return False
- def detect_lib_modules_win(self):
- if not self.libdir:
- # The libdirs in the distributed binaries (from sf)
- lib_sf = self.sourceforge_dir()
- if self.boost_root:
- roots = [self.boost_root]
- else:
- roots = self.boost_roots
- for root in roots:
- # The default libdir when building
- libdir = os.path.join(root, 'lib')
- if os.path.isdir(libdir):
- self.libdir = libdir
- break
- if lib_sf:
- full_path = os.path.join(root, lib_sf)
- if os.path.isdir(full_path):
- self.libdir = full_path
- break
- if not self.libdir:
- return
- for name in self.need_static_link:
- # FIXME - why are we only looking for *.lib? Mingw provides *.dll.a and *.a
- libname = 'lib' + name + self.versioned_abi_tag() + '.lib'
- if os.path.isfile(os.path.join(self.libdir, libname)):
- self.lib_modules[self.modname_from_filename(libname)] = [libname]
- else:
- libname = "lib{}.lib".format(name)
- if os.path.isfile(os.path.join(self.libdir, libname)):
- self.lib_modules[name[3:]] = [libname]
- # globber1 applies to a layout=system installation
- # globber2 applies to a layout=versioned installation
- globber1 = 'libboost_*' if self.static else 'boost_*'
- globber2 = globber1 + self.versioned_abi_tag()
- # FIXME - why are we only looking for *.lib? Mingw provides *.dll.a and *.a
- globber2_matches = glob.glob(os.path.join(self.libdir, globber2 + '.lib'))
- for entry in globber2_matches:
- fname = os.path.basename(entry)
- self.lib_modules[self.modname_from_filename(fname)] = [fname]
- if len(globber2_matches) == 0:
- # FIXME - why are we only looking for *.lib? Mingw provides *.dll.a and *.a
- for entry in glob.glob(os.path.join(self.libdir, globber1 + '.lib')):
- if self.static:
- fname = os.path.basename(entry)
- self.lib_modules[self.modname_from_filename(fname)] = [fname]
- def detect_lib_modules_nix(self):
- if self.static:
- libsuffix = 'a'
- elif mesonlib.for_darwin(self.want_cross, self.env):
- libsuffix = 'dylib'
- else:
- libsuffix = 'so'
- globber = 'libboost_*.{}'.format(libsuffix)
- if self.libdir:
- libdirs = [self.libdir]
- 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 name in self.need_static_link:
- libname = 'lib{}.a'.format(name)
- if os.path.isfile(os.path.join(libdir, libname)):
- self.lib_modules[name] = [libname]
- for entry in glob.glob(os.path.join(libdir, globber)):
- # I'm not 100% sure what to do here. Some distros
- # have modules such as thread only as -mt versions.
- # On debian all packages are built threading=multi
- # but not suffixed with -mt.
- # FIXME: implement detect_lib_modules_{debian, redhat, ...}
- # FIXME: this wouldn't work with -mt-gd either. -BDR
- if self.is_multithreading and mesonlib.is_debianlike():
- pass
- elif self.is_multithreading and entry.endswith('-mt.{}'.format(libsuffix)):
- pass
- elif not entry.endswith('-mt.{}'.format(libsuffix)):
- pass
- else:
- continue
- modname = self.modname_from_filename(entry)
- if modname not in self.lib_modules:
- self.lib_modules[modname] = [entry]
- def extra_lib_dirs(self):
- if self.libdir:
- return [self.libdir]
- elif self.boost_root:
- return [os.path.join(self.boost_root, 'lib')]
- return []
- def get_link_args(self):
- args = []
- for dir in self.extra_lib_dirs():
- args += self.clib_compiler.get_linker_search_args(dir)
- for lib in self.requested_modules:
- args += self.lib_modules['boost_' + lib]
- return args
- def get_sources(self):
- return []
- def need_threads(self):
- return 'thread' in self.requested_modules
- # Generated with boost_names.py
- BOOST_LIBS = [
- 'boost_atomic',
- 'boost_chrono',
- 'boost_chrono',
- 'boost_container',
- 'boost_context',
- 'boost_coroutine',
- 'boost_date_time',
- 'boost_exception',
- 'boost_fiber',
- 'boost_filesystem',
- 'boost_graph',
- 'boost_iostreams',
- 'boost_locale',
- 'boost_log',
- 'boost_log_setup',
- 'boost_math_tr1',
- 'boost_math_tr1f',
- 'boost_math_tr1l',
- 'boost_math_c99',
- 'boost_math_c99f',
- 'boost_math_c99l',
- 'boost_math_tr1',
- 'boost_math_tr1f',
- 'boost_math_tr1l',
- 'boost_math_c99',
- 'boost_math_c99f',
- 'boost_math_c99l',
- 'boost_math_tr1',
- 'boost_math_tr1f',
- 'boost_math_tr1l',
- 'boost_math_c99',
- 'boost_math_c99f',
- 'boost_math_c99l',
- 'boost_math_tr1',
- 'boost_math_tr1f',
- 'boost_math_tr1l',
- 'boost_math_c99',
- 'boost_math_c99f',
- 'boost_math_c99l',
- 'boost_math_tr1',
- 'boost_math_tr1f',
- 'boost_math_tr1l',
- 'boost_math_c99',
- 'boost_math_c99f',
- 'boost_math_c99l',
- 'boost_math_tr1',
- 'boost_math_tr1f',
- 'boost_math_tr1l',
- 'boost_math_c99',
- 'boost_math_c99f',
- 'boost_math_c99l',
- 'boost_mpi',
- 'boost_program_options',
- 'boost_python',
- 'boost_python3',
- 'boost_numpy',
- 'boost_numpy3',
- 'boost_random',
- 'boost_regex',
- 'boost_serialization',
- 'boost_wserialization',
- 'boost_signals',
- 'boost_stacktrace_noop',
- 'boost_stacktrace_backtrace',
- 'boost_stacktrace_addr2line',
- 'boost_stacktrace_basic',
- 'boost_stacktrace_windbg',
- 'boost_stacktrace_windbg_cached',
- 'boost_system',
- 'boost_prg_exec_monitor',
- 'boost_test_exec_monitor',
- 'boost_unit_test_framework',
- 'boost_thread',
- 'boost_timer',
- 'boost_type_erasure',
- 'boost_wave'
- ]
- BOOST_DIRS = [
- 'lambda',
- 'optional',
- 'convert',
- 'system',
- 'uuid',
- 'archive',
- 'align',
- 'timer',
- 'chrono',
- 'gil',
- 'logic',
- 'signals',
- 'predef',
- 'tr1',
- 'multi_index',
- 'property_map',
- 'multi_array',
- 'context',
- 'random',
- 'endian',
- 'circular_buffer',
- 'proto',
- 'assign',
- 'format',
- 'math',
- 'phoenix',
- 'graph',
- 'locale',
- 'mpl',
- 'pool',
- 'unordered',
- 'core',
- 'exception',
- 'ptr_container',
- 'flyweight',
- 'range',
- 'typeof',
- 'thread',
- 'move',
- 'spirit',
- 'dll',
- 'compute',
- 'serialization',
- 'ratio',
- 'msm',
- 'config',
- 'metaparse',
- 'coroutine2',
- 'qvm',
- 'program_options',
- 'concept',
- 'detail',
- 'hana',
- 'concept_check',
- 'compatibility',
- 'variant',
- 'type_erasure',
- 'mpi',
- 'test',
- 'fusion',
- 'log',
- 'sort',
- 'local_function',
- 'units',
- 'functional',
- 'preprocessor',
- 'integer',
- 'container',
- 'polygon',
- 'interprocess',
- 'numeric',
- 'iterator',
- 'wave',
- 'lexical_cast',
- 'multiprecision',
- 'utility',
- 'tti',
- 'asio',
- 'dynamic_bitset',
- 'algorithm',
- 'xpressive',
- 'bimap',
- 'signals2',
- 'type_traits',
- 'regex',
- 'statechart',
- 'parameter',
- 'icl',
- 'python',
- 'lockfree',
- 'intrusive',
- 'io',
- 'pending',
- 'geometry',
- 'tuple',
- 'iostreams',
- 'heap',
- 'atomic',
- 'filesystem',
- 'smart_ptr',
- 'function',
- 'fiber',
- 'type_index',
- 'accumulators',
- 'function_types',
- 'coroutine',
- 'vmd',
- 'date_time',
- 'property_tree',
- 'bind'
- ]
|