Merge pull request #2824 from bredelings/fix-boost

Fix boost on mac and linux: correctly search for boost and set -mt suffix when needed.
pull/2755/head
Jussi Pakkanen 7 years ago committed by GitHub
commit c814f1145b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      .travis.yml
  2. 276
      mesonbuild/dependencies/misc.py
  3. 12
      mesonbuild/mesonlib.py
  4. 6
      run_project_tests.py

@ -49,4 +49,4 @@ script:
withgit \
/bin/sh -c "cd /root && mkdir -p tools; wget -c http://nirbheek.in/files/binaries/ninja/linux-amd64/ninja -O /root/tools/ninja; chmod +x /root/tools/ninja; CC=$CC CXX=$CXX OBJC=$CC OBJCXX=$CXX PATH=/root/tools:$PATH MESON_FIXED_NINJA=1 ./run_tests.py -- $MESON_ARGS && chmod -R a+rwX .coverage"
fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then SDKROOT=$(xcodebuild -version -sdk macosx Path) OBJC=$CC OBJCXX=$CXX PATH=$HOME/tools:$PATH MESON_FIXED_NINJA=1 ./run_tests.py --backend=ninja -- $MESON_ARGS ; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then SDKROOT=$(xcodebuild -version -sdk macosx Path) CPPFLAGS=-I/usr/local/include LDFLAGS=-L/usr/local/lib OBJC=$CC OBJCXX=$CXX PATH=$HOME/tools:$PATH MESON_FIXED_NINJA=1 ./run_tests.py --backend=ninja -- $MESON_ARGS ; fi

@ -25,7 +25,6 @@ from pathlib import Path
from .. import mlog
from .. import mesonlib
from ..mesonlib import Popen_safe, extract_as_list
from ..environment import detect_cpu_family
from .base import (
@ -52,7 +51,7 @@ from .base import (
# - boost_<module>.lib|.dll (shared)
# where compiler is vc141 for example.
#
# NOTE: -gb means runtime and build time debugging is on
# 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
@ -62,6 +61,47 @@ from .base import (
# * 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.1.66.0 -> libboost_<module>.so
# Linux / Red Hat: libboost_<module>.so.1.66.0 -> libboost_<module>.so
# Linux / OpenSuse: libboost_<module>.so.1.66.0 -> libboost_<module>.so
# 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: Determine a suffix (e.g. "-mt" or "") and use it.
# TODO: Get_win_link_args( ) and get_link_args( )
# TODO: Genericize: 'args += ['-L' + dir] => args += self.compiler.get_linker_search_args(dir)
# TODO: Allow user to specify suffix in BOOST_SUFFIX, or add specific options like BOOST_DEBUG for 'd' for debug.
# TODO: fix cross:
# is_windows() -> for_windows(self.want_cross, self.env)
# is_osx() and self.want_cross -> for_darwin(self.want_cross, self.env)
class BoostDependency(ExternalDependency):
def __init__(self, environment, kwargs):
super().__init__('boost', environment, 'cpp', kwargs)
@ -103,7 +143,7 @@ class BoostDependency(ExternalDependency):
else:
self.incdir = self.detect_nix_incdir()
if self.incdir is None:
if self.incdir is None and mesonlib.is_windows():
self.log_fail()
return
@ -112,7 +152,7 @@ class BoostDependency(ExternalDependency):
# previous versions of meson allowed include dirs as modules
remove = []
for m in invalid_modules:
if m in os.listdir(os.path.join(self.incdir, 'boost')):
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)
@ -121,7 +161,7 @@ class BoostDependency(ExternalDependency):
invalid_modules = [x for x in invalid_modules if x not in remove]
if invalid_modules:
mlog.warning('Invalid Boost modules: ' + ', '.join(invalid_modules))
mlog.log(mlog.red('ERROR:'), 'Invalid Boost modules: ' + ', '.join(invalid_modules))
self.log_fail()
return
@ -138,6 +178,7 @@ class BoostDependency(ExternalDependency):
else:
self.log_fail()
def log_fail(self):
module_str = ', '.join(self.requested_modules)
mlog.log("Dependency Boost (%s) found:" % module_str, mlog.red('NO'))
@ -172,10 +213,8 @@ class BoostDependency(ExternalDependency):
return res
def detect_nix_incdir(self):
for root in self.boost_roots:
incdir = os.path.join(root, 'include', 'boost')
if os.path.isdir(incdir):
return os.path.join(root, 'include')
if self.boost_root:
return os.path.join(self.boost_root, 'include')
return None
# FIXME: Should pick a version that matches the requested version
@ -217,7 +256,7 @@ class BoostDependency(ExternalDependency):
return args
def get_requested(self, kwargs):
candidates = extract_as_list(kwargs, 'modules')
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.')
@ -231,25 +270,30 @@ class BoostDependency(ExternalDependency):
def detect_version(self):
try:
ifile = open(os.path.join(self.incdir, 'boost', 'version.hpp'))
except FileNotFoundError:
version = self.compiler.get_define('BOOST_LIB_VERSION', '#include <boost/version.hpp>', self.env, self.get_compile_args(), [])
except mesonlib.EnvironmentException:
return
except TypeError:
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('_', '.')
# Remove quotes
version = version[1:-1]
# Fix version string
self.version = version.replace('_', '.')
self.is_found = True
return
def detect_lib_modules(self):
if mesonlib.is_windows():
return self.detect_lib_modules_win()
return self.detect_lib_modules_nix()
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 detect_lib_modules_win(self):
arch = detect_cpu_family(self.env.coredata.compilers)
comp_ts_version = self.env.detect_cpp_compiler(self.want_cross).get_toolset_version()
@ -291,12 +335,11 @@ class BoostDependency(ExternalDependency):
libname = libname + '-gd'
libname = libname + "-{}.lib".format(self.version.replace('.', '_'))
if os.path.isfile(os.path.join(self.libdir, libname)):
modname = libname.split('-', 1)[0][3:]
self.lib_modules[modname] = 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
self.lib_modules[name[3:]] = [libname]
# globber1 applies to a layout=system installation
# globber2 applies to a layout=versioned installation
@ -309,24 +352,33 @@ class BoostDependency(ExternalDependency):
globber2 = globber2 + '-{}'.format(self.version.replace('.', '_'))
globber2_matches = glob.glob(os.path.join(self.libdir, globber2 + '.lib'))
for entry in globber2_matches:
(_, fname) = os.path.split(entry)
modname = fname.split('-', 1)
if len(modname) > 1:
modname = modname[0]
else:
modname = modname.split('.', 1)[0]
if self.static:
modname = modname[3:]
self.lib_modules[modname] = fname
fname = os.path.basename(entry)
self.lib_modules[self.modname_from_filename(fname)] = [fname]
if len(globber2_matches) == 0:
for entry in glob.glob(os.path.join(self.libdir, globber1 + '.lib')):
(_, fname) = os.path.split(entry)
modname = fname.split('.', 1)[0]
if self.static:
modname = modname[3:]
self.lib_modules[modname] = fname
fname = os.path.basename(entry)
self.lib_modules[self.modname_from_filename(fname)] = [fname]
def detect_lib_modules_nix(self):
all_found = True
for module in self.requested_modules:
args = None
libname = 'boost_' + module
if self.is_multithreading and not mesonlib.for_linux(self.want_cross, self.env):
# - Linux leaves off -mt but libraries are multithreading-aware.
# - Mac requires -mt for multithreading, so should not fall back to non-mt libraries.
libname = libname + '-mt'
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 "{}"'.format(module, libname))
all_found = False
else:
mlog.debug('Link args for boost module "{}" are {}'.format(module, args))
self.lib_modules['boost_' + module] = args
if all_found:
return
if self.static:
libsuffix = 'a'
elif mesonlib.is_osx() and not self.want_cross:
@ -345,21 +397,25 @@ class BoostDependency(ExternalDependency):
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
self.lib_modules[name] = [libname]
for entry in glob.glob(os.path.join(libdir, globber)):
lib = os.path.basename(entry)
name = lib.split('.')[0][3:]
# 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():
self.lib_modules[name] = lib
pass
elif self.is_multithreading and entry.endswith('-mt.{}'.format(libsuffix)):
self.lib_modules[name] = lib
pass
elif not entry.endswith('-mt.{}'.format(libsuffix)):
self.lib_modules[name] = lib
pass
else:
continue
modname = self.modname_from_filename(entry)
if modname not in self.lib_modules:
self.lib_modules[modname] = [entry]
def get_win_link_args(self):
args = []
@ -367,26 +423,25 @@ class BoostDependency(ExternalDependency):
if self.libdir:
args.append('-L' + self.libdir)
for lib in self.requested_modules:
args.append(self.lib_modules['boost_' + lib])
args += self.lib_modules['boost_' + lib]
return args
def extra_lib_dirs(self):
dirs = []
if self.boost_root:
dirs = [os.path.join(self.boost_root, 'lib')]
elif self.libdir:
dirs = [self.libdir]
return dirs
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 self.libdir:
args.append('-L' + self.libdir)
for dir in self.extra_lib_dirs():
args += ['-L' + dir]
for lib in self.requested_modules:
# The compiler's library detector is the most reliable so use that first.
boost_lib = 'boost_' + lib
default_detect = self.compiler.find_library(boost_lib, self.env, [])
if default_detect is not None:
args += default_detect
elif boost_lib in self.lib_modules:
linkcmd = '-l' + boost_lib
args.append(linkcmd)
args += self.lib_modules['boost_' + lib]
return args
def get_sources(self):
@ -963,3 +1018,116 @@ BOOST_LIBS = [
'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'
]

@ -309,6 +309,18 @@ def for_cygwin(is_cross, env):
return env.cross_info.config['host_machine']['system'] == 'cygwin'
return False
def for_linux(is_cross, env):
"""
Host machine is linux?
Note: 'host' is the machine on which compiled binaries will run
"""
if not is_cross:
return is_linux()
elif env.cross_info.has_host():
return env.cross_info.config['host_machine']['system'] == 'linux'
return False
def for_darwin(is_cross, env):
"""
Host machine is Darwin (iOS/OS X)?

@ -495,7 +495,11 @@ def detect_tests_to_run():
if mesonlib.is_windows():
# TODO: Set BOOST_ROOT in .appveyor.yml
gathered_tests += [('framework', ['test cases/frameworks/1 boost'], 'BOOST_ROOT' not in os.environ)]
elif mesonlib.is_osx() or mesonlib.is_cygwin():
elif mesonlib.is_osx():
# Just do the BOOST test
gathered_tests += [('framework', ['test cases/frameworks/1 boost'], False)]
elif mesonlib.is_cygwin():
# Skip all the framework tests
gathered_tests += [('framework', gather_tests('test cases/frameworks'), True)]
else:
gathered_tests += [('framework', gather_tests('test cases/frameworks'), False)]

Loading…
Cancel
Save