From ffa52939012c0c8e40727543aad7573f82d7b6f7 Mon Sep 17 00:00:00 2001 From: Aleksey Filippov Date: Mon, 19 Feb 2018 18:31:57 +0000 Subject: Split boost dependency out into separate file Boost dependency is very complex and it is easier to manage it in a separate file. --- mesonbuild/dependencies/__init__.py | 3 +- mesonbuild/dependencies/boost.py | 683 ++++++++++++++++++++++++++++++++++++ mesonbuild/dependencies/misc.py | 660 ---------------------------------- 3 files changed, 685 insertions(+), 661 deletions(-) create mode 100644 mesonbuild/dependencies/boost.py diff --git a/mesonbuild/dependencies/__init__.py b/mesonbuild/dependencies/__init__.py index 69235da..4796980 100644 --- a/mesonbuild/dependencies/__init__.py +++ b/mesonbuild/dependencies/__init__.py @@ -12,12 +12,13 @@ # See the License for the specific language governing permissions and # limitations under the License. +from .boost import BoostDependency from .base import ( # noqa: F401 Dependency, DependencyException, DependencyMethods, ExternalProgram, NonExistingExternalProgram, ExternalDependency, ExternalLibrary, ExtraFrameworkDependency, InternalDependency, PkgConfigDependency, find_external_dependency, get_dep_identifier, packages, _packages_accept_language) from .dev import GMockDependency, GTestDependency, LLVMDependency, ValgrindDependency -from .misc import (BoostDependency, MPIDependency, Python3Dependency, ThreadDependency, PcapDependency, CupsDependency, LibWmfDependency) +from .misc import (MPIDependency, Python3Dependency, ThreadDependency, PcapDependency, CupsDependency, LibWmfDependency) from .platform import AppleFrameworks from .ui import GLDependency, GnuStepDependency, Qt4Dependency, Qt5Dependency, SDL2Dependency, WxDependency, VulkanDependency diff --git a/mesonbuild/dependencies/boost.py b/mesonbuild/dependencies/boost.py new file mode 100644 index 0000000..03cc7b8 --- /dev/null +++ b/mesonbuild/dependencies/boost.py @@ -0,0 +1,683 @@ +# 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-/*.lib where arch=32/64 and compiler=msvc-14.1 +# +# Note that we should also try to support: +# mingw-w64 / Windows : libboost_-mt.a (location = /mingw64/lib/) +# libboost_-mt.dll.a +# +# Library names supported: +# - libboost_--mt-gd-x_x.lib (static) +# - boost_--mt-gd-x_x.lib|.dll (shared) +# - libboost_.lib (static) +# - boost_.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_.so -> libboost_.so.1.66.0 +# Linux / Red Hat: libboost_.so -> libboost_.so.1.66.0 +# Linux / OpenSuse: libboost_.so -> libboost_.so.1.66.0 +# Win / Cygwin: libboost_.dll.a (location = /usr/lib) +# libboost_.a +# cygboost__1_64.dll (location = /usr/bin) +# Mac / homebrew: libboost_.dylib + libboost_-mt.dylib (location = /usr/local/lib) +# Mac / macports: libboost_.dylib + libboost_-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_ +# 1.2 On other systems (e.g. Mac) look for boost_-mt if multithreading. +# 1.3 Otherwise look for boost_ +# 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'] + # FIXME: is this the right way to find the build type? + self.is_debug = environment.cmd_line_options.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.log(mlog.red('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.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.compiler.get_default_include_dirs(): + args.append("".join(self.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.compiler.get_define('BOOST_LIB_VERSION', '#include ', 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.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.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' +] diff --git a/mesonbuild/dependencies/misc.py b/mesonbuild/dependencies/misc.py index 9e9441f..2a218be 100644 --- a/mesonbuild/dependencies/misc.py +++ b/mesonbuild/dependencies/misc.py @@ -14,7 +14,6 @@ # This file contains the detection logic for miscellaneous external dependencies. -import glob import os import re import shlex @@ -32,471 +31,6 @@ from .base import ( ConfigToolDependency, ) -# 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-/*.lib where arch=32/64 and compiler=msvc-14.1 -# -# Note that we should also try to support: -# mingw-w64 / Windows : libboost_-mt.a (location = /mingw64/lib/) -# libboost_-mt.dll.a -# -# Library names supported: -# - libboost_--mt-gd-x_x.lib (static) -# - boost_--mt-gd-x_x.lib|.dll (shared) -# - libboost_.lib (static) -# - boost_.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_.so -> libboost_.so.1.66.0 -# Linux / Red Hat: libboost_.so -> libboost_.so.1.66.0 -# Linux / OpenSuse: libboost_.so -> libboost_.so.1.66.0 -# Win / Cygwin: libboost_.dll.a (location = /usr/lib) -# libboost_.a -# cygboost__1_64.dll (location = /usr/bin) -# Mac / homebrew: libboost_.dylib + libboost_-mt.dylib (location = /usr/local/lib) -# Mac / macports: libboost_.dylib + libboost_-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_ -# 1.2 On other systems (e.g. Mac) look for boost_-mt if multithreading. -# 1.3 Otherwise look for boost_ -# 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'] - # FIXME: is this the right way to find the build type? - self.is_debug = environment.cmd_line_options.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.log(mlog.red('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.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.compiler.get_default_include_dirs(): - args.append("".join(self.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.compiler.get_define('BOOST_LIB_VERSION', '#include ', 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.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.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 - class MPIDependency(ExternalDependency): def __init__(self, environment, kwargs): @@ -972,197 +506,3 @@ class LibWmfDependency(ExternalDependency): return [DependencyMethods.PKGCONFIG, DependencyMethods.CONFIG_TOOL, DependencyMethods.EXTRAFRAMEWORK] else: return [DependencyMethods.PKGCONFIG, DependencyMethods.CONFIG_TOOL] - - -# 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' -] -- cgit v1.1