The Meson Build System
http://mesonbuild.com/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1090 lines
39 KiB
1090 lines
39 KiB
# Copyright 2013-2020 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. |
|
from __future__ import annotations |
|
|
|
import re |
|
import dataclasses |
|
import functools |
|
import typing as T |
|
from pathlib import Path |
|
|
|
from .. import mlog |
|
from .. import mesonlib |
|
|
|
from .base import DependencyException, SystemDependency |
|
from .pkgconfig import PkgConfigDependency |
|
from .misc import threads_factory |
|
|
|
if T.TYPE_CHECKING: |
|
from ..environment import Environment, Properties |
|
|
|
# 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 |
|
# |
|
# 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) |
|
# Win / VS: boost_<module>-vc<ver>-mt[-gd]-<arch>-1_67.dll (location = C:/local/boost_1_67_0) |
|
# 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. |
|
# |
|
# We follow the following strategy for finding modules: |
|
# A) Detect potential boost root directories (uses also BOOST_ROOT env var) |
|
# B) Foreach candidate |
|
# 1. Look for the boost headers (boost/version.pp) |
|
# 2. Find all boost libraries |
|
# 2.1 Add all libraries in lib* |
|
# 2.2 Filter out non boost libraries |
|
# 2.3 Filter the renaining libraries based on the meson requirements (static/shared, etc.) |
|
# 2.4 Ensure that all libraries have the same boost tag (and are thus compatible) |
|
# 3. Select the libraries matching the requested modules |
|
|
|
@dataclasses.dataclass(eq=False, order=False) |
|
class UnknownFileException(Exception): |
|
path: Path |
|
|
|
@functools.total_ordering |
|
class BoostIncludeDir(): |
|
def __init__(self, path: Path, version_int: int): |
|
self.path = path |
|
self.version_int = version_int |
|
major = int(self.version_int / 100000) |
|
minor = int((self.version_int / 100) % 1000) |
|
patch = int(self.version_int % 100) |
|
self.version = f'{major}.{minor}.{patch}' |
|
self.version_lib = f'{major}_{minor}' |
|
|
|
def __repr__(self) -> str: |
|
return f'<BoostIncludeDir: {self.version} -- {self.path}>' |
|
|
|
def __lt__(self, other: object) -> bool: |
|
if isinstance(other, BoostIncludeDir): |
|
return (self.version_int, self.path) < (other.version_int, other.path) |
|
return NotImplemented |
|
|
|
@functools.total_ordering |
|
class BoostLibraryFile(): |
|
# Python libraries are special because of the included |
|
# minor version in the module name. |
|
boost_python_libs = ['boost_python', 'boost_numpy'] |
|
reg_python_mod_split = re.compile(r'(boost_[a-zA-Z]+)([0-9]*)') |
|
|
|
reg_abi_tag = re.compile(r'^s?g?y?d?p?n?$') |
|
reg_ver_tag = re.compile(r'^[0-9_]+$') |
|
|
|
def __init__(self, path: Path): |
|
self.path = path |
|
self.name = self.path.name |
|
|
|
# Initialize default properties |
|
self.static = False |
|
self.toolset = '' |
|
self.arch = '' |
|
self.version_lib = '' |
|
self.mt = True |
|
|
|
self.runtime_static = False |
|
self.runtime_debug = False |
|
self.python_debug = False |
|
self.debug = False |
|
self.stlport = False |
|
self.deprecated_iostreams = False |
|
|
|
# Post process the library name |
|
name_parts = self.name.split('.') |
|
self.basename = name_parts[0] |
|
self.suffixes = name_parts[1:] |
|
self.vers_raw = [x for x in self.suffixes if x.isdigit()] |
|
self.suffixes = [x for x in self.suffixes if not x.isdigit()] |
|
self.nvsuffix = '.'.join(self.suffixes) # Used for detecting the library type |
|
self.nametags = self.basename.split('-') |
|
self.mod_name = self.nametags[0] |
|
if self.mod_name.startswith('lib'): |
|
self.mod_name = self.mod_name[3:] |
|
|
|
# Set library version if possible |
|
if len(self.vers_raw) >= 2: |
|
self.version_lib = '{}_{}'.format(self.vers_raw[0], self.vers_raw[1]) |
|
|
|
# Detecting library type |
|
if self.nvsuffix in ['so', 'dll', 'dll.a', 'dll.lib', 'dylib']: |
|
self.static = False |
|
elif self.nvsuffix in ['a', 'lib']: |
|
self.static = True |
|
else: |
|
raise UnknownFileException(self.path) |
|
|
|
# boost_.lib is the dll import library |
|
if self.basename.startswith('boost_') and self.nvsuffix == 'lib': |
|
self.static = False |
|
|
|
# Process tags |
|
tags = self.nametags[1:] |
|
# Filter out the python version tag and fix modname |
|
if self.is_python_lib(): |
|
tags = self.fix_python_name(tags) |
|
if not tags: |
|
return |
|
|
|
# Without any tags mt is assumed, however, an absence of mt in the name |
|
# with tags present indicates that the lib was built without mt support |
|
self.mt = False |
|
for i in tags: |
|
if i == 'mt': |
|
self.mt = True |
|
elif len(i) == 3 and i[1:] in ['32', '64']: |
|
self.arch = i |
|
elif BoostLibraryFile.reg_abi_tag.match(i): |
|
self.runtime_static = 's' in i |
|
self.runtime_debug = 'g' in i |
|
self.python_debug = 'y' in i |
|
self.debug = 'd' in i |
|
self.stlport = 'p' in i |
|
self.deprecated_iostreams = 'n' in i |
|
elif BoostLibraryFile.reg_ver_tag.match(i): |
|
self.version_lib = i |
|
else: |
|
self.toolset = i |
|
|
|
def __repr__(self) -> str: |
|
return f'<LIB: {self.abitag} {self.mod_name:<32} {self.path}>' |
|
|
|
def __lt__(self, other: object) -> bool: |
|
if isinstance(other, BoostLibraryFile): |
|
return ( |
|
self.mod_name, self.static, self.version_lib, self.arch, |
|
not self.mt, not self.runtime_static, |
|
not self.debug, self.runtime_debug, self.python_debug, |
|
self.stlport, self.deprecated_iostreams, |
|
self.name, |
|
) < ( |
|
other.mod_name, other.static, other.version_lib, other.arch, |
|
not other.mt, not other.runtime_static, |
|
not other.debug, other.runtime_debug, other.python_debug, |
|
other.stlport, other.deprecated_iostreams, |
|
other.name, |
|
) |
|
return NotImplemented |
|
|
|
def __eq__(self, other: object) -> bool: |
|
if isinstance(other, BoostLibraryFile): |
|
return self.name == other.name |
|
return NotImplemented |
|
|
|
def __hash__(self) -> int: |
|
return hash(self.name) |
|
|
|
@property |
|
def abitag(self) -> str: |
|
abitag = '' |
|
abitag += 'S' if self.static else '-' |
|
abitag += 'M' if self.mt else '-' |
|
abitag += ' ' |
|
abitag += 's' if self.runtime_static else '-' |
|
abitag += 'g' if self.runtime_debug else '-' |
|
abitag += 'y' if self.python_debug else '-' |
|
abitag += 'd' if self.debug else '-' |
|
abitag += 'p' if self.stlport else '-' |
|
abitag += 'n' if self.deprecated_iostreams else '-' |
|
abitag += ' ' + (self.arch or '???') |
|
abitag += ' ' + (self.toolset or '?') |
|
abitag += ' ' + (self.version_lib or 'x_xx') |
|
return abitag |
|
|
|
def is_boost(self) -> bool: |
|
return any([self.name.startswith(x) for x in ['libboost_', 'boost_']]) |
|
|
|
def is_python_lib(self) -> bool: |
|
return any([self.mod_name.startswith(x) for x in BoostLibraryFile.boost_python_libs]) |
|
|
|
def fix_python_name(self, tags: T.List[str]) -> T.List[str]: |
|
# Handle the boost_python naming madeness. |
|
# See https://github.com/mesonbuild/meson/issues/4788 for some distro |
|
# specific naming variations. |
|
other_tags = [] # type: T.List[str] |
|
|
|
# Split the current modname into the base name and the version |
|
m_cur = BoostLibraryFile.reg_python_mod_split.match(self.mod_name) |
|
cur_name = m_cur.group(1) |
|
cur_vers = m_cur.group(2) |
|
|
|
# Update the current version string if the new version string is longer |
|
def update_vers(new_vers: str) -> None: |
|
nonlocal cur_vers |
|
new_vers = new_vers.replace('_', '') |
|
new_vers = new_vers.replace('.', '') |
|
if not new_vers.isdigit(): |
|
return |
|
if len(new_vers) > len(cur_vers): |
|
cur_vers = new_vers |
|
|
|
for i in tags: |
|
if i.startswith('py'): |
|
update_vers(i[2:]) |
|
elif i.isdigit(): |
|
update_vers(i) |
|
elif len(i) >= 3 and i[0].isdigit and i[2].isdigit() and i[1] == '.': |
|
update_vers(i) |
|
else: |
|
other_tags += [i] |
|
|
|
self.mod_name = cur_name + cur_vers |
|
return other_tags |
|
|
|
def mod_name_matches(self, mod_name: str) -> bool: |
|
if self.mod_name == mod_name: |
|
return True |
|
if not self.is_python_lib(): |
|
return False |
|
|
|
m_cur = BoostLibraryFile.reg_python_mod_split.match(self.mod_name) |
|
m_arg = BoostLibraryFile.reg_python_mod_split.match(mod_name) |
|
|
|
if not m_cur or not m_arg: |
|
return False |
|
|
|
if m_cur.group(1) != m_arg.group(1): |
|
return False |
|
|
|
cur_vers = m_cur.group(2) |
|
arg_vers = m_arg.group(2) |
|
|
|
# Always assume python 2 if nothing is specified |
|
if not arg_vers: |
|
arg_vers = '2' |
|
|
|
return cur_vers.startswith(arg_vers) |
|
|
|
def version_matches(self, version_lib: str) -> bool: |
|
# If no version tag is present, assume that it fits |
|
if not self.version_lib or not version_lib: |
|
return True |
|
return self.version_lib == version_lib |
|
|
|
def arch_matches(self, arch: str) -> bool: |
|
# If no version tag is present, assume that it fits |
|
if not self.arch or not arch: |
|
return True |
|
return self.arch == arch |
|
|
|
def vscrt_matches(self, vscrt: str) -> bool: |
|
# If no vscrt tag present, assume that it fits ['/MD', '/MDd', '/MT', '/MTd'] |
|
if not vscrt: |
|
return True |
|
if vscrt in ['/MD', '-MD']: |
|
return not self.runtime_static and not self.runtime_debug |
|
elif vscrt in ['/MDd', '-MDd']: |
|
return not self.runtime_static and self.runtime_debug |
|
elif vscrt in ['/MT', '-MT']: |
|
return (self.runtime_static or not self.static) and not self.runtime_debug |
|
elif vscrt in ['/MTd', '-MTd']: |
|
return (self.runtime_static or not self.static) and self.runtime_debug |
|
|
|
mlog.warning(f'Boost: unknown vscrt tag {vscrt}. This may cause the compilation to fail. Please consider reporting this as a bug.', once=True) |
|
return True |
|
|
|
def get_compiler_args(self) -> T.List[str]: |
|
args = [] # type: T.List[str] |
|
if self.mod_name in boost_libraries: |
|
libdef = boost_libraries[self.mod_name] # type: BoostLibrary |
|
if self.static: |
|
args += libdef.static |
|
else: |
|
args += libdef.shared |
|
if self.mt: |
|
args += libdef.multi |
|
else: |
|
args += libdef.single |
|
return args |
|
|
|
def get_link_args(self) -> T.List[str]: |
|
return [self.path.as_posix()] |
|
|
|
class BoostDependency(SystemDependency): |
|
def __init__(self, environment: Environment, kwargs: T.Dict[str, T.Any]) -> None: |
|
super().__init__('boost', environment, kwargs, language='cpp') |
|
buildtype = environment.coredata.get_option(mesonlib.OptionKey('buildtype')) |
|
assert isinstance(buildtype, str) |
|
self.debug = buildtype.startswith('debug') |
|
self.multithreading = kwargs.get('threading', 'multi') == 'multi' |
|
|
|
self.boost_root = None # type: T.Optional[Path] |
|
self.explicit_static = 'static' in kwargs |
|
|
|
# Extract and validate modules |
|
self.modules = mesonlib.extract_as_list(kwargs, 'modules') # type: T.List[str] |
|
for i in self.modules: |
|
if not isinstance(i, str): |
|
raise DependencyException('Boost module argument is not a string.') |
|
if i.startswith('boost_'): |
|
raise DependencyException('Boost modules must be passed without the boost_ prefix') |
|
|
|
self.modules_found = [] # type: T.List[str] |
|
self.modules_missing = [] # type: T.List[str] |
|
|
|
# Do we need threads? |
|
if 'thread' in self.modules: |
|
if not self._add_sub_dependency(threads_factory(environment, self.for_machine, {})): |
|
self.is_found = False |
|
return |
|
|
|
# Try figuring out the architecture tag |
|
self.arch = environment.machines[self.for_machine].cpu_family |
|
self.arch = boost_arch_map.get(self.arch, None) |
|
|
|
# First, look for paths specified in a machine file |
|
props = self.env.properties[self.for_machine] |
|
if any(x in self.env.properties[self.for_machine] for x in |
|
['boost_includedir', 'boost_librarydir', 'boost_root']): |
|
self.detect_boost_machine_file(props) |
|
return |
|
|
|
# Finally, look for paths from .pc files and from searching the filesystem |
|
self.detect_roots() |
|
|
|
def check_and_set_roots(self, roots: T.List[Path], use_system: bool) -> None: |
|
roots = list(mesonlib.OrderedSet(roots)) |
|
for j in roots: |
|
# 1. Look for the boost headers (boost/version.hpp) |
|
mlog.debug(f'Checking potential boost root {j.as_posix()}') |
|
inc_dirs = self.detect_inc_dirs(j) |
|
inc_dirs = sorted(inc_dirs, reverse=True) # Prefer the newer versions |
|
|
|
# Early abort when boost is not found |
|
if not inc_dirs: |
|
continue |
|
|
|
lib_dirs = self.detect_lib_dirs(j, use_system) |
|
self.is_found = self.run_check(inc_dirs, lib_dirs) |
|
if self.is_found: |
|
self.boost_root = j |
|
break |
|
|
|
def detect_boost_machine_file(self, props: 'Properties') -> None: |
|
"""Detect boost with values in the machine file or environment. |
|
|
|
The machine file values are defaulted to the environment values. |
|
""" |
|
# XXX: if we had a TypedDict we wouldn't need this |
|
incdir = props.get('boost_includedir') |
|
assert incdir is None or isinstance(incdir, str) |
|
libdir = props.get('boost_librarydir') |
|
assert libdir is None or isinstance(libdir, str) |
|
|
|
if incdir and libdir: |
|
inc_dir = Path(incdir) |
|
lib_dir = Path(libdir) |
|
|
|
if not inc_dir.is_absolute() or not lib_dir.is_absolute(): |
|
raise DependencyException('Paths given for boost_includedir and boost_librarydir in machine file must be absolute') |
|
|
|
mlog.debug('Trying to find boost with:') |
|
mlog.debug(f' - boost_includedir = {inc_dir}') |
|
mlog.debug(f' - boost_librarydir = {lib_dir}') |
|
|
|
return self.detect_split_root(inc_dir, lib_dir) |
|
|
|
elif incdir or libdir: |
|
raise DependencyException('Both boost_includedir *and* boost_librarydir have to be set in your machine file (one is not enough)') |
|
|
|
rootdir = props.get('boost_root') |
|
# It shouldn't be possible to get here without something in boost_root |
|
assert rootdir |
|
|
|
raw_paths = mesonlib.stringlistify(rootdir) |
|
paths = [Path(x) for x in raw_paths] |
|
if paths and any([not x.is_absolute() for x in paths]): |
|
raise DependencyException('boost_root path given in machine file must be absolute') |
|
|
|
self.check_and_set_roots(paths, use_system=False) |
|
|
|
def run_check(self, inc_dirs: T.List[BoostIncludeDir], lib_dirs: T.List[Path]) -> bool: |
|
mlog.debug(' - potential library dirs: {}'.format([x.as_posix() for x in lib_dirs])) |
|
mlog.debug(' - potential include dirs: {}'.format([x.path.as_posix() for x in inc_dirs])) |
|
|
|
# 2. Find all boost libraries |
|
libs = [] # type: T.List[BoostLibraryFile] |
|
for i in lib_dirs: |
|
libs = self.detect_libraries(i) |
|
if libs: |
|
mlog.debug(f' - found boost library dir: {i}') |
|
# mlog.debug(' - raw library list:') |
|
# for j in libs: |
|
# mlog.debug(' - {}'.format(j)) |
|
break |
|
libs = sorted(set(libs)) |
|
|
|
modules = ['boost_' + x for x in self.modules] |
|
for inc in inc_dirs: |
|
mlog.debug(f' - found boost {inc.version} include dir: {inc.path}') |
|
f_libs = self.filter_libraries(libs, inc.version_lib) |
|
|
|
mlog.debug(' - filtered library list:') |
|
for j in f_libs: |
|
mlog.debug(f' - {j}') |
|
|
|
# 3. Select the libraries matching the requested modules |
|
not_found = [] # type: T.List[str] |
|
selected_modules = [] # type: T.List[BoostLibraryFile] |
|
for mod in modules: |
|
found = False |
|
for l in f_libs: |
|
if l.mod_name_matches(mod): |
|
selected_modules += [l] |
|
found = True |
|
break |
|
if not found: |
|
not_found += [mod] |
|
|
|
# log the result |
|
mlog.debug(' - found:') |
|
comp_args = [] # type: T.List[str] |
|
link_args = [] # type: T.List[str] |
|
for j in selected_modules: |
|
c_args = j.get_compiler_args() |
|
l_args = j.get_link_args() |
|
mlog.debug(' - {:<24} link={} comp={}'.format(j.mod_name, str(l_args), str(c_args))) |
|
comp_args += c_args |
|
link_args += l_args |
|
|
|
comp_args = list(mesonlib.OrderedSet(comp_args)) |
|
link_args = list(mesonlib.OrderedSet(link_args)) |
|
|
|
self.modules_found = [x.mod_name for x in selected_modules] |
|
self.modules_found = [x[6:] for x in self.modules_found] |
|
self.modules_found = sorted(set(self.modules_found)) |
|
self.modules_missing = not_found |
|
self.modules_missing = [x[6:] for x in self.modules_missing] |
|
self.modules_missing = sorted(set(self.modules_missing)) |
|
|
|
# if we found all modules we are done |
|
if not not_found: |
|
self.version = inc.version |
|
self.compile_args = ['-I' + inc.path.as_posix()] |
|
self.compile_args += comp_args |
|
self.compile_args += self._extra_compile_args() |
|
self.compile_args = list(mesonlib.OrderedSet(self.compile_args)) |
|
self.link_args = link_args |
|
mlog.debug(f' - final compile args: {self.compile_args}') |
|
mlog.debug(f' - final link args: {self.link_args}') |
|
return True |
|
|
|
# in case we missed something log it and try again |
|
mlog.debug(' - NOT found:') |
|
for mod in not_found: |
|
mlog.debug(f' - {mod}') |
|
|
|
return False |
|
|
|
def detect_inc_dirs(self, root: Path) -> T.List[BoostIncludeDir]: |
|
candidates = [] # type: T.List[Path] |
|
inc_root = root / 'include' |
|
|
|
candidates += [root / 'boost'] |
|
candidates += [inc_root / 'boost'] |
|
if inc_root.is_dir(): |
|
for i in inc_root.iterdir(): |
|
if not i.is_dir() or not i.name.startswith('boost-'): |
|
continue |
|
candidates += [i / 'boost'] |
|
candidates = [x for x in candidates if x.is_dir()] |
|
candidates = [x / 'version.hpp' for x in candidates] |
|
candidates = [x for x in candidates if x.exists()] |
|
return [self._include_dir_from_version_header(x) for x in candidates] |
|
|
|
def detect_lib_dirs(self, root: Path, use_system: bool) -> T.List[Path]: |
|
# First check the system include paths. Only consider those within the |
|
# given root path |
|
|
|
if use_system: |
|
system_dirs_t = self.clib_compiler.get_library_dirs(self.env) |
|
system_dirs = [Path(x) for x in system_dirs_t] |
|
system_dirs = [x.resolve() for x in system_dirs if x.exists()] |
|
system_dirs = [x for x in system_dirs if mesonlib.path_is_in_root(x, root)] |
|
system_dirs = list(mesonlib.OrderedSet(system_dirs)) |
|
|
|
if system_dirs: |
|
return system_dirs |
|
|
|
# No system include paths were found --> fall back to manually looking |
|
# for library dirs in root |
|
dirs = [] # type: T.List[Path] |
|
subdirs = [] # type: T.List[Path] |
|
for i in root.iterdir(): |
|
if i.is_dir() and i.name.startswith('lib'): |
|
dirs += [i] |
|
|
|
# Some distros put libraries not directly inside /usr/lib but in /usr/lib/x86_64-linux-gnu |
|
for i in dirs: |
|
for j in i.iterdir(): |
|
if j.is_dir() and j.name.endswith('-linux-gnu'): |
|
subdirs += [j] |
|
|
|
# Filter out paths that don't match the target arch to avoid finding |
|
# the wrong libraries. See https://github.com/mesonbuild/meson/issues/7110 |
|
if not self.arch: |
|
return dirs + subdirs |
|
|
|
arch_list_32 = ['32', 'i386'] |
|
arch_list_64 = ['64'] |
|
|
|
raw_list = dirs + subdirs |
|
no_arch = [x for x in raw_list if not any([y in x.name for y in arch_list_32 + arch_list_64])] |
|
|
|
matching_arch = [] # type: T.List[Path] |
|
if '32' in self.arch: |
|
matching_arch = [x for x in raw_list if any([y in x.name for y in arch_list_32])] |
|
elif '64' in self.arch: |
|
matching_arch = [x for x in raw_list if any([y in x.name for y in arch_list_64])] |
|
|
|
return sorted(matching_arch) + sorted(no_arch) |
|
|
|
def filter_libraries(self, libs: T.List[BoostLibraryFile], lib_vers: str) -> T.List[BoostLibraryFile]: |
|
# MSVC is very picky with the library tags |
|
vscrt = '' |
|
try: |
|
crt_val = self.env.coredata.options[mesonlib.OptionKey('b_vscrt')].value |
|
buildtype = self.env.coredata.options[mesonlib.OptionKey('buildtype')].value |
|
vscrt = self.clib_compiler.get_crt_compile_args(crt_val, buildtype)[0] |
|
except (KeyError, IndexError, AttributeError): |
|
pass |
|
|
|
# mlog.debug(' - static: {}'.format(self.static)) |
|
# mlog.debug(' - not explicit static: {}'.format(not self.explicit_static)) |
|
# mlog.debug(' - mt: {}'.format(self.multithreading)) |
|
# mlog.debug(' - version: {}'.format(lib_vers)) |
|
# mlog.debug(' - arch: {}'.format(self.arch)) |
|
# mlog.debug(' - vscrt: {}'.format(vscrt)) |
|
libs = [x for x in libs if x.static == self.static or not self.explicit_static] |
|
libs = [x for x in libs if x.mt == self.multithreading] |
|
libs = [x for x in libs if x.version_matches(lib_vers)] |
|
libs = [x for x in libs if x.arch_matches(self.arch)] |
|
libs = [x for x in libs if x.vscrt_matches(vscrt)] |
|
libs = [x for x in libs if x.nvsuffix != 'dll'] # Only link to import libraries |
|
|
|
# Only filter by debug when we are building in release mode. Debug |
|
# libraries are automatically preferred through sorting otherwise. |
|
if not self.debug: |
|
libs = [x for x in libs if not x.debug] |
|
|
|
# Take the abitag from the first library and filter by it. This |
|
# ensures that we have a set of libraries that are always compatible. |
|
if not libs: |
|
return [] |
|
abitag = libs[0].abitag |
|
libs = [x for x in libs if x.abitag == abitag] |
|
|
|
return libs |
|
|
|
def detect_libraries(self, libdir: Path) -> T.List[BoostLibraryFile]: |
|
libs = set() # type: T.Set[BoostLibraryFile] |
|
for i in libdir.iterdir(): |
|
if not i.is_file(): |
|
continue |
|
if not any([i.name.startswith(x) for x in ['libboost_', 'boost_']]): |
|
continue |
|
# Windows binaries from SourceForge ship with PDB files alongside |
|
# DLLs (#8325). Ignore them. |
|
if i.name.endswith('.pdb'): |
|
continue |
|
|
|
try: |
|
libs.add(BoostLibraryFile(i.resolve())) |
|
except UnknownFileException as e: |
|
mlog.warning('Boost: ignoring unknown file {} under lib directory'.format(e.path.name)) |
|
|
|
return [x for x in libs if x.is_boost()] # Filter out no boost libraries |
|
|
|
def detect_split_root(self, inc_dir: Path, lib_dir: Path) -> None: |
|
boost_inc_dir = None |
|
for j in [inc_dir / 'version.hpp', inc_dir / 'boost' / 'version.hpp']: |
|
if j.is_file(): |
|
boost_inc_dir = self._include_dir_from_version_header(j) |
|
break |
|
if not boost_inc_dir: |
|
self.is_found = False |
|
return |
|
|
|
self.is_found = self.run_check([boost_inc_dir], [lib_dir]) |
|
|
|
def detect_roots(self) -> None: |
|
roots = [] # type: T.List[Path] |
|
|
|
# Try getting the BOOST_ROOT from a boost.pc if it exists. This primarily |
|
# allows BoostDependency to find boost from Conan. See #5438 |
|
try: |
|
boost_pc = PkgConfigDependency('boost', self.env, {'required': False}) |
|
if boost_pc.found(): |
|
boost_root = boost_pc.get_pkgconfig_variable('prefix', [], None) |
|
if boost_root: |
|
roots += [Path(boost_root)] |
|
except DependencyException: |
|
pass |
|
|
|
# Add roots from system paths |
|
inc_paths = [Path(x) for x in self.clib_compiler.get_default_include_dirs()] |
|
inc_paths = [x.parent for x in inc_paths if x.exists()] |
|
inc_paths = [x.resolve() for x in inc_paths] |
|
roots += inc_paths |
|
|
|
# Add system paths |
|
if self.env.machines[self.for_machine].is_windows(): |
|
# Where boost built from source actually installs it |
|
c_root = Path('C:/Boost') |
|
if c_root.is_dir(): |
|
roots += [c_root] |
|
|
|
# Where boost documentation says it should be |
|
prog_files = Path('C:/Program Files/boost') |
|
# Where boost prebuilt binaries are |
|
local_boost = Path('C:/local') |
|
|
|
candidates = [] # type: T.List[Path] |
|
if prog_files.is_dir(): |
|
candidates += [*prog_files.iterdir()] |
|
if local_boost.is_dir(): |
|
candidates += [*local_boost.iterdir()] |
|
|
|
roots += [x for x in candidates if x.name.lower().startswith('boost') and x.is_dir()] |
|
else: |
|
tmp = [] # type: T.List[Path] |
|
|
|
# Add some default system paths |
|
tmp += [Path('/opt/local')] |
|
tmp += [Path('/usr/local/opt/boost')] |
|
tmp += [Path('/usr/local')] |
|
tmp += [Path('/usr')] |
|
|
|
# Cleanup paths |
|
tmp = [x for x in tmp if x.is_dir()] |
|
tmp = [x.resolve() for x in tmp] |
|
roots += tmp |
|
|
|
self.check_and_set_roots(roots, use_system=True) |
|
|
|
def log_details(self) -> str: |
|
res = '' |
|
if self.modules_found: |
|
res += 'found: ' + ', '.join(self.modules_found) |
|
if self.modules_missing: |
|
if res: |
|
res += ' | ' |
|
res += 'missing: ' + ', '.join(self.modules_missing) |
|
return res |
|
|
|
def log_info(self) -> str: |
|
if self.boost_root: |
|
return self.boost_root.as_posix() |
|
return '' |
|
|
|
def _include_dir_from_version_header(self, hfile: Path) -> BoostIncludeDir: |
|
# Extract the version with a regex. Using clib_compiler.get_define would |
|
# also work, however, this is slower (since it the compiler has to be |
|
# invoked) and overkill since the layout of the header is always the same. |
|
assert hfile.exists() |
|
raw = hfile.read_text(encoding='utf-8') |
|
m = re.search(r'#define\s+BOOST_VERSION\s+([0-9]+)', raw) |
|
if not m: |
|
mlog.debug(f'Failed to extract version information from {hfile}') |
|
return BoostIncludeDir(hfile.parents[1], 0) |
|
return BoostIncludeDir(hfile.parents[1], int(m.group(1))) |
|
|
|
def _extra_compile_args(self) -> T.List[str]: |
|
# BOOST_ALL_DYN_LINK should not be required with the known defines below |
|
return ['-DBOOST_ALL_NO_LIB'] # Disable automatic linking |
|
|
|
|
|
# See https://www.boost.org/doc/libs/1_72_0/more/getting_started/unix-variants.html#library-naming |
|
# See https://mesonbuild.com/Reference-tables.html#cpu-families |
|
boost_arch_map = { |
|
'aarch64': 'a64', |
|
'arc': 'a32', |
|
'arm': 'a32', |
|
'ia64': 'i64', |
|
'mips': 'm32', |
|
'mips64': 'm64', |
|
'ppc': 'p32', |
|
'ppc64': 'p64', |
|
'sparc': 's32', |
|
'sparc64': 's64', |
|
'x86': 'x32', |
|
'x86_64': 'x64', |
|
} |
|
|
|
|
|
#### ---- BEGIN GENERATED ---- #### |
|
# # |
|
# Generated with tools/boost_names.py: |
|
# - boost version: 1.73.0 |
|
# - modules found: 159 |
|
# - libraries found: 43 |
|
# |
|
|
|
class BoostLibrary(): |
|
def __init__(self, name: str, shared: T.List[str], static: T.List[str], single: T.List[str], multi: T.List[str]): |
|
self.name = name |
|
self.shared = shared |
|
self.static = static |
|
self.single = single |
|
self.multi = multi |
|
|
|
class BoostModule(): |
|
def __init__(self, name: str, key: str, desc: str, libs: T.List[str]): |
|
self.name = name |
|
self.key = key |
|
self.desc = desc |
|
self.libs = libs |
|
|
|
|
|
# dict of all know libraries with additional compile options |
|
boost_libraries = { |
|
'boost_atomic': BoostLibrary( |
|
name='boost_atomic', |
|
shared=['-DBOOST_ATOMIC_DYN_LINK=1'], |
|
static=['-DBOOST_ATOMIC_STATIC_LINK=1'], |
|
single=[], |
|
multi=[], |
|
), |
|
'boost_chrono': BoostLibrary( |
|
name='boost_chrono', |
|
shared=['-DBOOST_CHRONO_DYN_LINK=1'], |
|
static=['-DBOOST_CHRONO_STATIC_LINK=1'], |
|
single=['-DBOOST_CHRONO_THREAD_DISABLED'], |
|
multi=[], |
|
), |
|
'boost_container': BoostLibrary( |
|
name='boost_container', |
|
shared=['-DBOOST_CONTAINER_DYN_LINK=1'], |
|
static=['-DBOOST_CONTAINER_STATIC_LINK=1'], |
|
single=[], |
|
multi=[], |
|
), |
|
'boost_context': BoostLibrary( |
|
name='boost_context', |
|
shared=['-DBOOST_CONTEXT_DYN_LINK=1'], |
|
static=[], |
|
single=[], |
|
multi=[], |
|
), |
|
'boost_contract': BoostLibrary( |
|
name='boost_contract', |
|
shared=['-DBOOST_CONTRACT_DYN_LINK'], |
|
static=['-DBOOST_CONTRACT_STATIC_LINK'], |
|
single=['-DBOOST_CONTRACT_DISABLE_THREADS'], |
|
multi=[], |
|
), |
|
'boost_coroutine': BoostLibrary( |
|
name='boost_coroutine', |
|
shared=['-DBOOST_COROUTINES_DYN_LINK=1'], |
|
static=[], |
|
single=[], |
|
multi=[], |
|
), |
|
'boost_date_time': BoostLibrary( |
|
name='boost_date_time', |
|
shared=['-DBOOST_DATE_TIME_DYN_LINK=1'], |
|
static=[], |
|
single=[], |
|
multi=[], |
|
), |
|
'boost_exception': BoostLibrary( |
|
name='boost_exception', |
|
shared=[], |
|
static=[], |
|
single=[], |
|
multi=[], |
|
), |
|
'boost_fiber': BoostLibrary( |
|
name='boost_fiber', |
|
shared=['-DBOOST_FIBERS_DYN_LINK=1'], |
|
static=[], |
|
single=[], |
|
multi=[], |
|
), |
|
'boost_fiber_numa': BoostLibrary( |
|
name='boost_fiber_numa', |
|
shared=['-DBOOST_FIBERS_DYN_LINK=1'], |
|
static=[], |
|
single=[], |
|
multi=[], |
|
), |
|
'boost_filesystem': BoostLibrary( |
|
name='boost_filesystem', |
|
shared=['-DBOOST_FILESYSTEM_DYN_LINK=1'], |
|
static=['-DBOOST_FILESYSTEM_STATIC_LINK=1'], |
|
single=[], |
|
multi=[], |
|
), |
|
'boost_graph': BoostLibrary( |
|
name='boost_graph', |
|
shared=[], |
|
static=[], |
|
single=[], |
|
multi=[], |
|
), |
|
'boost_iostreams': BoostLibrary( |
|
name='boost_iostreams', |
|
shared=['-DBOOST_IOSTREAMS_DYN_LINK=1'], |
|
static=[], |
|
single=[], |
|
multi=[], |
|
), |
|
'boost_locale': BoostLibrary( |
|
name='boost_locale', |
|
shared=[], |
|
static=[], |
|
single=[], |
|
multi=[], |
|
), |
|
'boost_log': BoostLibrary( |
|
name='boost_log', |
|
shared=['-DBOOST_LOG_DYN_LINK=1'], |
|
static=[], |
|
single=['-DBOOST_LOG_NO_THREADS'], |
|
multi=[], |
|
), |
|
'boost_log_setup': BoostLibrary( |
|
name='boost_log_setup', |
|
shared=['-DBOOST_LOG_SETUP_DYN_LINK=1'], |
|
static=[], |
|
single=['-DBOOST_LOG_NO_THREADS'], |
|
multi=[], |
|
), |
|
'boost_math_c99': BoostLibrary( |
|
name='boost_math_c99', |
|
shared=[], |
|
static=[], |
|
single=[], |
|
multi=[], |
|
), |
|
'boost_math_c99f': BoostLibrary( |
|
name='boost_math_c99f', |
|
shared=[], |
|
static=[], |
|
single=[], |
|
multi=[], |
|
), |
|
'boost_math_c99l': BoostLibrary( |
|
name='boost_math_c99l', |
|
shared=[], |
|
static=[], |
|
single=[], |
|
multi=[], |
|
), |
|
'boost_math_tr1': BoostLibrary( |
|
name='boost_math_tr1', |
|
shared=[], |
|
static=[], |
|
single=[], |
|
multi=[], |
|
), |
|
'boost_math_tr1f': BoostLibrary( |
|
name='boost_math_tr1f', |
|
shared=[], |
|
static=[], |
|
single=[], |
|
multi=[], |
|
), |
|
'boost_math_tr1l': BoostLibrary( |
|
name='boost_math_tr1l', |
|
shared=[], |
|
static=[], |
|
single=[], |
|
multi=[], |
|
), |
|
'boost_mpi': BoostLibrary( |
|
name='boost_mpi', |
|
shared=[], |
|
static=[], |
|
single=[], |
|
multi=[], |
|
), |
|
'boost_nowide': BoostLibrary( |
|
name='boost_nowide', |
|
shared=['-DBOOST_NOWIDE_DYN_LINK=1'], |
|
static=[], |
|
single=[], |
|
multi=[], |
|
), |
|
'boost_prg_exec_monitor': BoostLibrary( |
|
name='boost_prg_exec_monitor', |
|
shared=['-DBOOST_TEST_DYN_LINK=1'], |
|
static=[], |
|
single=[], |
|
multi=[], |
|
), |
|
'boost_program_options': BoostLibrary( |
|
name='boost_program_options', |
|
shared=[], |
|
static=[], |
|
single=[], |
|
multi=[], |
|
), |
|
'boost_random': BoostLibrary( |
|
name='boost_random', |
|
shared=['-DBOOST_RANDOM_DYN_LINK'], |
|
static=[], |
|
single=[], |
|
multi=[], |
|
), |
|
'boost_regex': BoostLibrary( |
|
name='boost_regex', |
|
shared=[], |
|
static=[], |
|
single=[], |
|
multi=[], |
|
), |
|
'boost_serialization': BoostLibrary( |
|
name='boost_serialization', |
|
shared=[], |
|
static=[], |
|
single=[], |
|
multi=[], |
|
), |
|
'boost_stacktrace_addr2line': BoostLibrary( |
|
name='boost_stacktrace_addr2line', |
|
shared=[], |
|
static=[], |
|
single=[], |
|
multi=[], |
|
), |
|
'boost_stacktrace_backtrace': BoostLibrary( |
|
name='boost_stacktrace_backtrace', |
|
shared=[], |
|
static=[], |
|
single=[], |
|
multi=[], |
|
), |
|
'boost_stacktrace_basic': BoostLibrary( |
|
name='boost_stacktrace_basic', |
|
shared=[], |
|
static=[], |
|
single=[], |
|
multi=[], |
|
), |
|
'boost_stacktrace_noop': BoostLibrary( |
|
name='boost_stacktrace_noop', |
|
shared=[], |
|
static=[], |
|
single=[], |
|
multi=[], |
|
), |
|
'boost_stacktrace_windbg': BoostLibrary( |
|
name='boost_stacktrace_windbg', |
|
shared=[], |
|
static=[], |
|
single=[], |
|
multi=[], |
|
), |
|
'boost_stacktrace_windbg_cached': BoostLibrary( |
|
name='boost_stacktrace_windbg_cached', |
|
shared=[], |
|
static=[], |
|
single=[], |
|
multi=[], |
|
), |
|
'boost_system': BoostLibrary( |
|
name='boost_system', |
|
shared=['-DBOOST_SYSTEM_DYN_LINK=1'], |
|
static=['-DBOOST_SYSTEM_STATIC_LINK=1'], |
|
single=[], |
|
multi=[], |
|
), |
|
'boost_test_exec_monitor': BoostLibrary( |
|
name='boost_test_exec_monitor', |
|
shared=['-DBOOST_TEST_DYN_LINK=1'], |
|
static=[], |
|
single=[], |
|
multi=[], |
|
), |
|
'boost_thread': BoostLibrary( |
|
name='boost_thread', |
|
shared=['-DBOOST_THREAD_BUILD_DLL=1', '-DBOOST_THREAD_USE_DLL=1'], |
|
static=['-DBOOST_THREAD_BUILD_LIB=1', '-DBOOST_THREAD_USE_LIB=1'], |
|
single=[], |
|
multi=[], |
|
), |
|
'boost_timer': BoostLibrary( |
|
name='boost_timer', |
|
shared=['-DBOOST_TIMER_DYN_LINK=1'], |
|
static=['-DBOOST_TIMER_STATIC_LINK=1'], |
|
single=[], |
|
multi=[], |
|
), |
|
'boost_type_erasure': BoostLibrary( |
|
name='boost_type_erasure', |
|
shared=['-DBOOST_TYPE_ERASURE_DYN_LINK'], |
|
static=[], |
|
single=[], |
|
multi=[], |
|
), |
|
'boost_unit_test_framework': BoostLibrary( |
|
name='boost_unit_test_framework', |
|
shared=['-DBOOST_TEST_DYN_LINK=1'], |
|
static=[], |
|
single=[], |
|
multi=[], |
|
), |
|
'boost_wave': BoostLibrary( |
|
name='boost_wave', |
|
shared=[], |
|
static=[], |
|
single=[], |
|
multi=[], |
|
), |
|
'boost_wserialization': BoostLibrary( |
|
name='boost_wserialization', |
|
shared=[], |
|
static=[], |
|
single=[], |
|
multi=[], |
|
), |
|
} |
|
|
|
# # |
|
#### ---- END GENERATED ---- ####
|
|
|