1# Copyright 2013-2020 The Meson development team 2 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6 7# http://www.apache.org/licenses/LICENSE-2.0 8 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15import re 16import functools 17import typing as T 18from pathlib import Path 19 20from .. import mlog 21from .. import mesonlib 22from ..environment import Environment 23 24from .base import DependencyException, SystemDependency 25from .pkgconfig import PkgConfigDependency 26from .misc import threads_factory 27 28if T.TYPE_CHECKING: 29 from ..environment import Properties 30 31# On windows 3 directory layouts are supported: 32# * The default layout (versioned) installed: 33# - $BOOST_ROOT/include/boost-x_x/boost/*.hpp 34# - $BOOST_ROOT/lib/*.lib 35# * The non-default layout (system) installed: 36# - $BOOST_ROOT/include/boost/*.hpp 37# - $BOOST_ROOT/lib/*.lib 38# * The pre-built binaries from sf.net: 39# - $BOOST_ROOT/boost/*.hpp 40# - $BOOST_ROOT/lib<arch>-<compiler>/*.lib where arch=32/64 and compiler=msvc-14.1 41# 42# Note that we should also try to support: 43# mingw-w64 / Windows : libboost_<module>-mt.a (location = <prefix>/mingw64/lib/) 44# libboost_<module>-mt.dll.a 45# 46# The `modules` argument accept library names. This is because every module that 47# has libraries to link against also has multiple options regarding how to 48# link. See for example: 49# * http://www.boost.org/doc/libs/1_65_1/libs/test/doc/html/boost_test/usage_variants.html 50# * http://www.boost.org/doc/libs/1_65_1/doc/html/stacktrace/configuration_and_build.html 51# * http://www.boost.org/doc/libs/1_65_1/libs/math/doc/html/math_toolkit/main_tr1.html 52 53# **On Unix**, official packaged versions of boost libraries follow the following schemes: 54# 55# Linux / Debian: libboost_<module>.so -> libboost_<module>.so.1.66.0 56# Linux / Red Hat: libboost_<module>.so -> libboost_<module>.so.1.66.0 57# Linux / OpenSuse: libboost_<module>.so -> libboost_<module>.so.1.66.0 58# Win / Cygwin: libboost_<module>.dll.a (location = /usr/lib) 59# libboost_<module>.a 60# cygboost_<module>_1_64.dll (location = /usr/bin) 61# Win / VS: boost_<module>-vc<ver>-mt[-gd]-<arch>-1_67.dll (location = C:/local/boost_1_67_0) 62# Mac / homebrew: libboost_<module>.dylib + libboost_<module>-mt.dylib (location = /usr/local/lib) 63# Mac / macports: libboost_<module>.dylib + libboost_<module>-mt.dylib (location = /opt/local/lib) 64# 65# Its not clear that any other abi tags (e.g. -gd) are used in official packages. 66# 67# On Linux systems, boost libs have multithreading support enabled, but without the -mt tag. 68# 69# Boost documentation recommends using complex abi tags like "-lboost_regex-gcc34-mt-d-1_36". 70# (See http://www.boost.org/doc/libs/1_66_0/more/getting_started/unix-variants.html#library-naming) 71# However, its not clear that any Unix distribution follows this scheme. 72# Furthermore, the boost documentation for unix above uses examples from windows like 73# "libboost_regex-vc71-mt-d-x86-1_34.lib", so apparently the abi tags may be more aimed at windows. 74# 75# We follow the following strategy for finding modules: 76# A) Detect potential boost root directories (uses also BOOST_ROOT env var) 77# B) Foreach candidate 78# 1. Look for the boost headers (boost/version.pp) 79# 2. Find all boost libraries 80# 2.1 Add all libraries in lib* 81# 2.2 Filter out non boost libraries 82# 2.3 Filter the renaining libraries based on the meson requirements (static/shared, etc.) 83# 2.4 Ensure that all libraries have the same boost tag (and are thus compatible) 84# 3. Select the libraries matching the requested modules 85 86@functools.total_ordering 87class BoostIncludeDir(): 88 def __init__(self, path: Path, version_int: int): 89 self.path = path 90 self.version_int = version_int 91 major = int(self.version_int / 100000) 92 minor = int((self.version_int / 100) % 1000) 93 patch = int(self.version_int % 100) 94 self.version = f'{major}.{minor}.{patch}' 95 self.version_lib = f'{major}_{minor}' 96 97 def __repr__(self) -> str: 98 return f'<BoostIncludeDir: {self.version} -- {self.path}>' 99 100 def __lt__(self, other: object) -> bool: 101 if isinstance(other, BoostIncludeDir): 102 return (self.version_int, self.path) < (other.version_int, other.path) 103 return NotImplemented 104 105@functools.total_ordering 106class BoostLibraryFile(): 107 # Python libraries are special because of the included 108 # minor version in the module name. 109 boost_python_libs = ['boost_python', 'boost_numpy'] 110 reg_python_mod_split = re.compile(r'(boost_[a-zA-Z]+)([0-9]*)') 111 112 reg_abi_tag = re.compile(r'^s?g?y?d?p?n?$') 113 reg_ver_tag = re.compile(r'^[0-9_]+$') 114 115 def __init__(self, path: Path): 116 self.path = path 117 self.name = self.path.name 118 119 # Initialize default properties 120 self.static = False 121 self.toolset = '' 122 self.arch = '' 123 self.version_lib = '' 124 self.mt = True 125 126 self.runtime_static = False 127 self.runtime_debug = False 128 self.python_debug = False 129 self.debug = False 130 self.stlport = False 131 self.deprecated_iostreams = False 132 133 # Post process the library name 134 name_parts = self.name.split('.') 135 self.basename = name_parts[0] 136 self.suffixes = name_parts[1:] 137 self.vers_raw = [x for x in self.suffixes if x.isdigit()] 138 self.suffixes = [x for x in self.suffixes if not x.isdigit()] 139 self.nvsuffix = '.'.join(self.suffixes) # Used for detecting the library type 140 self.nametags = self.basename.split('-') 141 self.mod_name = self.nametags[0] 142 if self.mod_name.startswith('lib'): 143 self.mod_name = self.mod_name[3:] 144 145 # Set library version if possible 146 if len(self.vers_raw) >= 2: 147 self.version_lib = '{}_{}'.format(self.vers_raw[0], self.vers_raw[1]) 148 149 # Detecting library type 150 if self.nvsuffix in ['so', 'dll', 'dll.a', 'dll.lib', 'dylib']: 151 self.static = False 152 elif self.nvsuffix in ['a', 'lib']: 153 self.static = True 154 else: 155 raise DependencyException(f'Unable to process library extension "{self.nvsuffix}" ({self.path})') 156 157 # boost_.lib is the dll import library 158 if self.basename.startswith('boost_') and self.nvsuffix == 'lib': 159 self.static = False 160 161 # Process tags 162 tags = self.nametags[1:] 163 # Filter out the python version tag and fix modname 164 if self.is_python_lib(): 165 tags = self.fix_python_name(tags) 166 if not tags: 167 return 168 169 # Without any tags mt is assumed, however, an absence of mt in the name 170 # with tags present indicates that the lib was built without mt support 171 self.mt = False 172 for i in tags: 173 if i == 'mt': 174 self.mt = True 175 elif len(i) == 3 and i[1:] in ['32', '64']: 176 self.arch = i 177 elif BoostLibraryFile.reg_abi_tag.match(i): 178 self.runtime_static = 's' in i 179 self.runtime_debug = 'g' in i 180 self.python_debug = 'y' in i 181 self.debug = 'd' in i 182 self.stlport = 'p' in i 183 self.deprecated_iostreams = 'n' in i 184 elif BoostLibraryFile.reg_ver_tag.match(i): 185 self.version_lib = i 186 else: 187 self.toolset = i 188 189 def __repr__(self) -> str: 190 return f'<LIB: {self.abitag} {self.mod_name:<32} {self.path}>' 191 192 def __lt__(self, other: object) -> bool: 193 if isinstance(other, BoostLibraryFile): 194 return ( 195 self.mod_name, self.static, self.version_lib, self.arch, 196 not self.mt, not self.runtime_static, 197 not self.debug, self.runtime_debug, self.python_debug, 198 self.stlport, self.deprecated_iostreams, 199 self.name, 200 ) < ( 201 other.mod_name, other.static, other.version_lib, other.arch, 202 not other.mt, not other.runtime_static, 203 not other.debug, other.runtime_debug, other.python_debug, 204 other.stlport, other.deprecated_iostreams, 205 other.name, 206 ) 207 return NotImplemented 208 209 def __eq__(self, other: object) -> bool: 210 if isinstance(other, BoostLibraryFile): 211 return self.name == other.name 212 return NotImplemented 213 214 def __hash__(self) -> int: 215 return hash(self.name) 216 217 @property 218 def abitag(self) -> str: 219 abitag = '' 220 abitag += 'S' if self.static else '-' 221 abitag += 'M' if self.mt else '-' 222 abitag += ' ' 223 abitag += 's' if self.runtime_static else '-' 224 abitag += 'g' if self.runtime_debug else '-' 225 abitag += 'y' if self.python_debug else '-' 226 abitag += 'd' if self.debug else '-' 227 abitag += 'p' if self.stlport else '-' 228 abitag += 'n' if self.deprecated_iostreams else '-' 229 abitag += ' ' + (self.arch or '???') 230 abitag += ' ' + (self.toolset or '?') 231 abitag += ' ' + (self.version_lib or 'x_xx') 232 return abitag 233 234 def is_boost(self) -> bool: 235 return any([self.name.startswith(x) for x in ['libboost_', 'boost_']]) 236 237 def is_python_lib(self) -> bool: 238 return any([self.mod_name.startswith(x) for x in BoostLibraryFile.boost_python_libs]) 239 240 def fix_python_name(self, tags: T.List[str]) -> T.List[str]: 241 # Handle the boost_python naming madeness. 242 # See https://github.com/mesonbuild/meson/issues/4788 for some distro 243 # specific naming variantions. 244 other_tags = [] # type: T.List[str] 245 246 # Split the current modname into the base name and the version 247 m_cur = BoostLibraryFile.reg_python_mod_split.match(self.mod_name) 248 cur_name = m_cur.group(1) 249 cur_vers = m_cur.group(2) 250 251 # Update the current version string if the new version string is longer 252 def update_vers(new_vers: str) -> None: 253 nonlocal cur_vers 254 new_vers = new_vers.replace('_', '') 255 new_vers = new_vers.replace('.', '') 256 if not new_vers.isdigit(): 257 return 258 if len(new_vers) > len(cur_vers): 259 cur_vers = new_vers 260 261 for i in tags: 262 if i.startswith('py'): 263 update_vers(i[2:]) 264 elif i.isdigit(): 265 update_vers(i) 266 elif len(i) >= 3 and i[0].isdigit and i[2].isdigit() and i[1] == '.': 267 update_vers(i) 268 else: 269 other_tags += [i] 270 271 self.mod_name = cur_name + cur_vers 272 return other_tags 273 274 def mod_name_matches(self, mod_name: str) -> bool: 275 if self.mod_name == mod_name: 276 return True 277 if not self.is_python_lib(): 278 return False 279 280 m_cur = BoostLibraryFile.reg_python_mod_split.match(self.mod_name) 281 m_arg = BoostLibraryFile.reg_python_mod_split.match(mod_name) 282 283 if not m_cur or not m_arg: 284 return False 285 286 if m_cur.group(1) != m_arg.group(1): 287 return False 288 289 cur_vers = m_cur.group(2) 290 arg_vers = m_arg.group(2) 291 292 # Always assume python 2 if nothing is specified 293 if not arg_vers: 294 arg_vers = '2' 295 296 return cur_vers.startswith(arg_vers) 297 298 def version_matches(self, version_lib: str) -> bool: 299 # If no version tag is present, assume that it fits 300 if not self.version_lib or not version_lib: 301 return True 302 return self.version_lib == version_lib 303 304 def arch_matches(self, arch: str) -> bool: 305 # If no version tag is present, assume that it fits 306 if not self.arch or not arch: 307 return True 308 return self.arch == arch 309 310 def vscrt_matches(self, vscrt: str) -> bool: 311 # If no vscrt tag present, assume that it fits ['/MD', '/MDd', '/MT', '/MTd'] 312 if not vscrt: 313 return True 314 if vscrt in ['/MD', '-MD']: 315 return not self.runtime_static and not self.runtime_debug 316 elif vscrt in ['/MDd', '-MDd']: 317 return not self.runtime_static and self.runtime_debug 318 elif vscrt in ['/MT', '-MT']: 319 return (self.runtime_static or not self.static) and not self.runtime_debug 320 elif vscrt in ['/MTd', '-MTd']: 321 return (self.runtime_static or not self.static) and self.runtime_debug 322 323 mlog.warning(f'Boost: unknow vscrt tag {vscrt}. This may cause the compilation to fail. Please consider reporting this as a bug.', once=True) 324 return True 325 326 def get_compiler_args(self) -> T.List[str]: 327 args = [] # type: T.List[str] 328 if self.mod_name in boost_libraries: 329 libdef = boost_libraries[self.mod_name] # type: BoostLibrary 330 if self.static: 331 args += libdef.static 332 else: 333 args += libdef.shared 334 if self.mt: 335 args += libdef.multi 336 else: 337 args += libdef.single 338 return args 339 340 def get_link_args(self) -> T.List[str]: 341 return [self.path.as_posix()] 342 343class BoostDependency(SystemDependency): 344 def __init__(self, environment: Environment, kwargs: T.Dict[str, T.Any]) -> None: 345 super().__init__('boost', environment, kwargs, language='cpp') 346 buildtype = environment.coredata.get_option(mesonlib.OptionKey('buildtype')) 347 assert isinstance(buildtype, str) 348 self.debug = buildtype.startswith('debug') 349 self.multithreading = kwargs.get('threading', 'multi') == 'multi' 350 351 self.boost_root = None # type: T.Optional[Path] 352 self.explicit_static = 'static' in kwargs 353 354 # Extract and validate modules 355 self.modules = mesonlib.extract_as_list(kwargs, 'modules') # type: T.List[str] 356 for i in self.modules: 357 if not isinstance(i, str): 358 raise DependencyException('Boost module argument is not a string.') 359 if i.startswith('boost_'): 360 raise DependencyException('Boost modules must be passed without the boost_ prefix') 361 362 self.modules_found = [] # type: T.List[str] 363 self.modules_missing = [] # type: T.List[str] 364 365 # Do we need threads? 366 if 'thread' in self.modules: 367 if not self._add_sub_dependency(threads_factory(environment, self.for_machine, {})): 368 self.is_found = False 369 return 370 371 # Try figuring out the architecture tag 372 self.arch = environment.machines[self.for_machine].cpu_family 373 self.arch = boost_arch_map.get(self.arch, None) 374 375 # First, look for paths specified in a machine file 376 props = self.env.properties[self.for_machine] 377 if any(x in self.env.properties[self.for_machine] for x in 378 ['boost_includedir', 'boost_librarydir', 'boost_root']): 379 self.detect_boost_machine_file(props) 380 return 381 382 # Finally, look for paths from .pc files and from searching the filesystem 383 self.detect_roots() 384 385 def check_and_set_roots(self, roots: T.List[Path], use_system: bool) -> None: 386 roots = list(mesonlib.OrderedSet(roots)) 387 for j in roots: 388 # 1. Look for the boost headers (boost/version.hpp) 389 mlog.debug(f'Checking potential boost root {j.as_posix()}') 390 inc_dirs = self.detect_inc_dirs(j) 391 inc_dirs = sorted(inc_dirs, reverse=True) # Prefer the newer versions 392 393 # Early abort when boost is not found 394 if not inc_dirs: 395 continue 396 397 lib_dirs = self.detect_lib_dirs(j, use_system) 398 self.is_found = self.run_check(inc_dirs, lib_dirs) 399 if self.is_found: 400 self.boost_root = j 401 break 402 403 def detect_boost_machine_file(self, props: 'Properties') -> None: 404 """Detect boost with values in the machine file or environment. 405 406 The machine file values are defaulted to the environment values. 407 """ 408 # XXX: if we had a TypedDict we woudn't need this 409 incdir = props.get('boost_includedir') 410 assert incdir is None or isinstance(incdir, str) 411 libdir = props.get('boost_librarydir') 412 assert libdir is None or isinstance(libdir, str) 413 414 if incdir and libdir: 415 inc_dir = Path(incdir) 416 lib_dir = Path(libdir) 417 418 if not inc_dir.is_absolute() or not lib_dir.is_absolute(): 419 raise DependencyException('Paths given for boost_includedir and boost_librarydir in machine file must be absolute') 420 421 mlog.debug('Trying to find boost with:') 422 mlog.debug(f' - boost_includedir = {inc_dir}') 423 mlog.debug(f' - boost_librarydir = {lib_dir}') 424 425 return self.detect_split_root(inc_dir, lib_dir) 426 427 elif incdir or libdir: 428 raise DependencyException('Both boost_includedir *and* boost_librarydir have to be set in your machine file (one is not enough)') 429 430 rootdir = props.get('boost_root') 431 # It shouldn't be possible to get here without something in boost_root 432 assert rootdir 433 434 raw_paths = mesonlib.stringlistify(rootdir) 435 paths = [Path(x) for x in raw_paths] 436 if paths and any([not x.is_absolute() for x in paths]): 437 raise DependencyException('boost_root path given in machine file must be absolute') 438 439 self.check_and_set_roots(paths, use_system=False) 440 441 def run_check(self, inc_dirs: T.List[BoostIncludeDir], lib_dirs: T.List[Path]) -> bool: 442 mlog.debug(' - potential library dirs: {}'.format([x.as_posix() for x in lib_dirs])) 443 mlog.debug(' - potential include dirs: {}'.format([x.path.as_posix() for x in inc_dirs])) 444 445 # 2. Find all boost libraries 446 libs = [] # type: T.List[BoostLibraryFile] 447 for i in lib_dirs: 448 libs = self.detect_libraries(i) 449 if libs: 450 mlog.debug(f' - found boost library dir: {i}') 451 # mlog.debug(' - raw library list:') 452 # for j in libs: 453 # mlog.debug(' - {}'.format(j)) 454 break 455 libs = sorted(set(libs)) 456 457 modules = ['boost_' + x for x in self.modules] 458 for inc in inc_dirs: 459 mlog.debug(f' - found boost {inc.version} include dir: {inc.path}') 460 f_libs = self.filter_libraries(libs, inc.version_lib) 461 462 mlog.debug(' - filtered library list:') 463 for j in f_libs: 464 mlog.debug(f' - {j}') 465 466 # 3. Select the libraries matching the requested modules 467 not_found = [] # type: T.List[str] 468 selected_modules = [] # type: T.List[BoostLibraryFile] 469 for mod in modules: 470 found = False 471 for l in f_libs: 472 if l.mod_name_matches(mod): 473 selected_modules += [l] 474 found = True 475 break 476 if not found: 477 not_found += [mod] 478 479 # log the result 480 mlog.debug(' - found:') 481 comp_args = [] # type: T.List[str] 482 link_args = [] # type: T.List[str] 483 for j in selected_modules: 484 c_args = j.get_compiler_args() 485 l_args = j.get_link_args() 486 mlog.debug(' - {:<24} link={} comp={}'.format(j.mod_name, str(l_args), str(c_args))) 487 comp_args += c_args 488 link_args += l_args 489 490 comp_args = list(set(comp_args)) 491 link_args = list(set(link_args)) 492 493 self.modules_found = [x.mod_name for x in selected_modules] 494 self.modules_found = [x[6:] for x in self.modules_found] 495 self.modules_found = sorted(set(self.modules_found)) 496 self.modules_missing = not_found 497 self.modules_missing = [x[6:] for x in self.modules_missing] 498 self.modules_missing = sorted(set(self.modules_missing)) 499 500 # if we found all modules we are done 501 if not not_found: 502 self.version = inc.version 503 self.compile_args = ['-I' + inc.path.as_posix()] 504 self.compile_args += comp_args 505 self.compile_args += self._extra_compile_args() 506 self.compile_args = list(mesonlib.OrderedSet(self.compile_args)) 507 self.link_args = link_args 508 mlog.debug(f' - final compile args: {self.compile_args}') 509 mlog.debug(f' - final link args: {self.link_args}') 510 return True 511 512 # in case we missed something log it and try again 513 mlog.debug(' - NOT found:') 514 for mod in not_found: 515 mlog.debug(f' - {mod}') 516 517 return False 518 519 def detect_inc_dirs(self, root: Path) -> T.List[BoostIncludeDir]: 520 candidates = [] # type: T.List[Path] 521 inc_root = root / 'include' 522 523 candidates += [root / 'boost'] 524 candidates += [inc_root / 'boost'] 525 if inc_root.is_dir(): 526 for i in inc_root.iterdir(): 527 if not i.is_dir() or not i.name.startswith('boost-'): 528 continue 529 candidates += [i / 'boost'] 530 candidates = [x for x in candidates if x.is_dir()] 531 candidates = [x / 'version.hpp' for x in candidates] 532 candidates = [x for x in candidates if x.exists()] 533 return [self._include_dir_from_version_header(x) for x in candidates] 534 535 def detect_lib_dirs(self, root: Path, use_system: bool) -> T.List[Path]: 536 # First check the system include paths. Only consider those within the 537 # given root path 538 539 if use_system: 540 system_dirs_t = self.clib_compiler.get_library_dirs(self.env) 541 system_dirs = [Path(x) for x in system_dirs_t] 542 system_dirs = [x.resolve() for x in system_dirs if x.exists()] 543 system_dirs = [x for x in system_dirs if mesonlib.path_is_in_root(x, root)] 544 system_dirs = list(mesonlib.OrderedSet(system_dirs)) 545 546 if system_dirs: 547 return system_dirs 548 549 # No system include paths were found --> fall back to manually looking 550 # for library dirs in root 551 dirs = [] # type: T.List[Path] 552 subdirs = [] # type: T.List[Path] 553 for i in root.iterdir(): 554 if i.is_dir() and i.name.startswith('lib'): 555 dirs += [i] 556 557 # Some distros put libraries not directly inside /usr/lib but in /usr/lib/x86_64-linux-gnu 558 for i in dirs: 559 for j in i.iterdir(): 560 if j.is_dir() and j.name.endswith('-linux-gnu'): 561 subdirs += [j] 562 563 # Filter out paths that don't match the target arch to avoid finding 564 # the wrong libraries. See https://github.com/mesonbuild/meson/issues/7110 565 if not self.arch: 566 return dirs + subdirs 567 568 arch_list_32 = ['32', 'i386'] 569 arch_list_64 = ['64'] 570 571 raw_list = dirs + subdirs 572 no_arch = [x for x in raw_list if not any([y in x.name for y in arch_list_32 + arch_list_64])] 573 574 matching_arch = [] # type: T.List[Path] 575 if '32' in self.arch: 576 matching_arch = [x for x in raw_list if any([y in x.name for y in arch_list_32])] 577 elif '64' in self.arch: 578 matching_arch = [x for x in raw_list if any([y in x.name for y in arch_list_64])] 579 580 return sorted(matching_arch) + sorted(no_arch) 581 582 def filter_libraries(self, libs: T.List[BoostLibraryFile], lib_vers: str) -> T.List[BoostLibraryFile]: 583 # MSVC is very picky with the library tags 584 vscrt = '' 585 try: 586 crt_val = self.env.coredata.options[mesonlib.OptionKey('b_vscrt')].value 587 buildtype = self.env.coredata.options[mesonlib.OptionKey('buildtype')].value 588 vscrt = self.clib_compiler.get_crt_compile_args(crt_val, buildtype)[0] 589 except (KeyError, IndexError, AttributeError): 590 pass 591 592 # mlog.debug(' - static: {}'.format(self.static)) 593 # mlog.debug(' - not explicit static: {}'.format(not self.explicit_static)) 594 # mlog.debug(' - mt: {}'.format(self.multithreading)) 595 # mlog.debug(' - version: {}'.format(lib_vers)) 596 # mlog.debug(' - arch: {}'.format(self.arch)) 597 # mlog.debug(' - vscrt: {}'.format(vscrt)) 598 libs = [x for x in libs if x.static == self.static or not self.explicit_static] 599 libs = [x for x in libs if x.mt == self.multithreading] 600 libs = [x for x in libs if x.version_matches(lib_vers)] 601 libs = [x for x in libs if x.arch_matches(self.arch)] 602 libs = [x for x in libs if x.vscrt_matches(vscrt)] 603 libs = [x for x in libs if x.nvsuffix != 'dll'] # Only link to import libraries 604 605 # Only filter by debug when we are building in release mode. Debug 606 # libraries are automatically preferred through sorting otherwise. 607 if not self.debug: 608 libs = [x for x in libs if not x.debug] 609 610 # Take the abitag from the first library and filter by it. This 611 # ensures that we have a set of libraries that are always compatible. 612 if not libs: 613 return [] 614 abitag = libs[0].abitag 615 libs = [x for x in libs if x.abitag == abitag] 616 617 return libs 618 619 def detect_libraries(self, libdir: Path) -> T.List[BoostLibraryFile]: 620 libs = set() # type: T.Set[BoostLibraryFile] 621 for i in libdir.iterdir(): 622 if not i.is_file(): 623 continue 624 if not any([i.name.startswith(x) for x in ['libboost_', 'boost_']]): 625 continue 626 627 libs.add(BoostLibraryFile(i.resolve())) 628 629 return [x for x in libs if x.is_boost()] # Filter out no boost libraries 630 631 def detect_split_root(self, inc_dir: Path, lib_dir: Path) -> None: 632 boost_inc_dir = None 633 for j in [inc_dir / 'version.hpp', inc_dir / 'boost' / 'version.hpp']: 634 if j.is_file(): 635 boost_inc_dir = self._include_dir_from_version_header(j) 636 break 637 if not boost_inc_dir: 638 self.is_found = False 639 return 640 641 self.is_found = self.run_check([boost_inc_dir], [lib_dir]) 642 643 def detect_roots(self) -> None: 644 roots = [] # type: T.List[Path] 645 646 # Try getting the BOOST_ROOT from a boost.pc if it exists. This primarily 647 # allows BoostDependency to find boost from Conan. See #5438 648 try: 649 boost_pc = PkgConfigDependency('boost', self.env, {'required': False}) 650 if boost_pc.found(): 651 boost_root = boost_pc.get_pkgconfig_variable('prefix', {'default': None}) 652 if boost_root: 653 roots += [Path(boost_root)] 654 except DependencyException: 655 pass 656 657 # Add roots from system paths 658 inc_paths = [Path(x) for x in self.clib_compiler.get_default_include_dirs()] 659 inc_paths = [x.parent for x in inc_paths if x.exists()] 660 inc_paths = [x.resolve() for x in inc_paths] 661 roots += inc_paths 662 663 # Add system paths 664 if self.env.machines[self.for_machine].is_windows(): 665 # Where boost built from source actually installs it 666 c_root = Path('C:/Boost') 667 if c_root.is_dir(): 668 roots += [c_root] 669 670 # Where boost documentation says it should be 671 prog_files = Path('C:/Program Files/boost') 672 # Where boost prebuilt binaries are 673 local_boost = Path('C:/local') 674 675 candidates = [] # type: T.List[Path] 676 if prog_files.is_dir(): 677 candidates += [*prog_files.iterdir()] 678 if local_boost.is_dir(): 679 candidates += [*local_boost.iterdir()] 680 681 roots += [x for x in candidates if x.name.lower().startswith('boost') and x.is_dir()] 682 else: 683 tmp = [] # type: T.List[Path] 684 685 # Add some default system paths 686 tmp += [Path('/opt/local')] 687 tmp += [Path('/usr/local/opt/boost')] 688 tmp += [Path('/usr/local')] 689 tmp += [Path('/usr')] 690 691 # Cleanup paths 692 tmp = [x for x in tmp if x.is_dir()] 693 tmp = [x.resolve() for x in tmp] 694 roots += tmp 695 696 self.check_and_set_roots(roots, use_system=True) 697 698 def log_details(self) -> str: 699 res = '' 700 if self.modules_found: 701 res += 'found: ' + ', '.join(self.modules_found) 702 if self.modules_missing: 703 if res: 704 res += ' | ' 705 res += 'missing: ' + ', '.join(self.modules_missing) 706 return res 707 708 def log_info(self) -> str: 709 if self.boost_root: 710 return self.boost_root.as_posix() 711 return '' 712 713 def _include_dir_from_version_header(self, hfile: Path) -> BoostIncludeDir: 714 # Extract the version with a regex. Using clib_compiler.get_define would 715 # also work, however, this is slower (since it the compiler has to be 716 # invoked) and overkill since the layout of the header is always the same. 717 assert hfile.exists() 718 raw = hfile.read_text(encoding='utf-8') 719 m = re.search(r'#define\s+BOOST_VERSION\s+([0-9]+)', raw) 720 if not m: 721 mlog.debug(f'Failed to extract version information from {hfile}') 722 return BoostIncludeDir(hfile.parents[1], 0) 723 return BoostIncludeDir(hfile.parents[1], int(m.group(1))) 724 725 def _extra_compile_args(self) -> T.List[str]: 726 # BOOST_ALL_DYN_LINK should not be required with the known defines below 727 return ['-DBOOST_ALL_NO_LIB'] # Disable automatic linking 728 729 730# See https://www.boost.org/doc/libs/1_72_0/more/getting_started/unix-variants.html#library-naming 731# See https://mesonbuild.com/Reference-tables.html#cpu-families 732boost_arch_map = { 733 'aarch64': 'a64', 734 'arc': 'a32', 735 'arm': 'a32', 736 'ia64': 'i64', 737 'mips': 'm32', 738 'mips64': 'm64', 739 'ppc': 'p32', 740 'ppc64': 'p64', 741 'sparc': 's32', 742 'sparc64': 's64', 743 'x86': 'x32', 744 'x86_64': 'x64', 745} 746 747 748#### ---- BEGIN GENERATED ---- #### 749# # 750# Generated with tools/boost_names.py: 751# - boost version: 1.73.0 752# - modules found: 159 753# - libraries found: 43 754# 755 756class BoostLibrary(): 757 def __init__(self, name: str, shared: T.List[str], static: T.List[str], single: T.List[str], multi: T.List[str]): 758 self.name = name 759 self.shared = shared 760 self.static = static 761 self.single = single 762 self.multi = multi 763 764class BoostModule(): 765 def __init__(self, name: str, key: str, desc: str, libs: T.List[str]): 766 self.name = name 767 self.key = key 768 self.desc = desc 769 self.libs = libs 770 771 772# dict of all know libraries with additional compile options 773boost_libraries = { 774 'boost_atomic': BoostLibrary( 775 name='boost_atomic', 776 shared=['-DBOOST_ATOMIC_DYN_LINK=1'], 777 static=['-DBOOST_ATOMIC_STATIC_LINK=1'], 778 single=[], 779 multi=[], 780 ), 781 'boost_chrono': BoostLibrary( 782 name='boost_chrono', 783 shared=['-DBOOST_CHRONO_DYN_LINK=1'], 784 static=['-DBOOST_CHRONO_STATIC_LINK=1'], 785 single=['-DBOOST_CHRONO_THREAD_DISABLED'], 786 multi=[], 787 ), 788 'boost_container': BoostLibrary( 789 name='boost_container', 790 shared=['-DBOOST_CONTAINER_DYN_LINK=1'], 791 static=['-DBOOST_CONTAINER_STATIC_LINK=1'], 792 single=[], 793 multi=[], 794 ), 795 'boost_context': BoostLibrary( 796 name='boost_context', 797 shared=['-DBOOST_CONTEXT_DYN_LINK=1'], 798 static=[], 799 single=[], 800 multi=[], 801 ), 802 'boost_contract': BoostLibrary( 803 name='boost_contract', 804 shared=['-DBOOST_CONTRACT_DYN_LINK'], 805 static=['-DBOOST_CONTRACT_STATIC_LINK'], 806 single=['-DBOOST_CONTRACT_DISABLE_THREADS'], 807 multi=[], 808 ), 809 'boost_coroutine': BoostLibrary( 810 name='boost_coroutine', 811 shared=['-DBOOST_COROUTINES_DYN_LINK=1'], 812 static=[], 813 single=[], 814 multi=[], 815 ), 816 'boost_date_time': BoostLibrary( 817 name='boost_date_time', 818 shared=['-DBOOST_DATE_TIME_DYN_LINK=1'], 819 static=[], 820 single=[], 821 multi=[], 822 ), 823 'boost_exception': BoostLibrary( 824 name='boost_exception', 825 shared=[], 826 static=[], 827 single=[], 828 multi=[], 829 ), 830 'boost_fiber': BoostLibrary( 831 name='boost_fiber', 832 shared=['-DBOOST_FIBERS_DYN_LINK=1'], 833 static=[], 834 single=[], 835 multi=[], 836 ), 837 'boost_fiber_numa': BoostLibrary( 838 name='boost_fiber_numa', 839 shared=['-DBOOST_FIBERS_DYN_LINK=1'], 840 static=[], 841 single=[], 842 multi=[], 843 ), 844 'boost_filesystem': BoostLibrary( 845 name='boost_filesystem', 846 shared=['-DBOOST_FILESYSTEM_DYN_LINK=1'], 847 static=['-DBOOST_FILESYSTEM_STATIC_LINK=1'], 848 single=[], 849 multi=[], 850 ), 851 'boost_graph': BoostLibrary( 852 name='boost_graph', 853 shared=[], 854 static=[], 855 single=[], 856 multi=[], 857 ), 858 'boost_iostreams': BoostLibrary( 859 name='boost_iostreams', 860 shared=['-DBOOST_IOSTREAMS_DYN_LINK=1'], 861 static=[], 862 single=[], 863 multi=[], 864 ), 865 'boost_locale': BoostLibrary( 866 name='boost_locale', 867 shared=[], 868 static=[], 869 single=[], 870 multi=[], 871 ), 872 'boost_log': BoostLibrary( 873 name='boost_log', 874 shared=['-DBOOST_LOG_DYN_LINK=1'], 875 static=[], 876 single=['-DBOOST_LOG_NO_THREADS'], 877 multi=[], 878 ), 879 'boost_log_setup': BoostLibrary( 880 name='boost_log_setup', 881 shared=['-DBOOST_LOG_SETUP_DYN_LINK=1'], 882 static=[], 883 single=['-DBOOST_LOG_NO_THREADS'], 884 multi=[], 885 ), 886 'boost_math_c99': BoostLibrary( 887 name='boost_math_c99', 888 shared=[], 889 static=[], 890 single=[], 891 multi=[], 892 ), 893 'boost_math_c99f': BoostLibrary( 894 name='boost_math_c99f', 895 shared=[], 896 static=[], 897 single=[], 898 multi=[], 899 ), 900 'boost_math_c99l': BoostLibrary( 901 name='boost_math_c99l', 902 shared=[], 903 static=[], 904 single=[], 905 multi=[], 906 ), 907 'boost_math_tr1': BoostLibrary( 908 name='boost_math_tr1', 909 shared=[], 910 static=[], 911 single=[], 912 multi=[], 913 ), 914 'boost_math_tr1f': BoostLibrary( 915 name='boost_math_tr1f', 916 shared=[], 917 static=[], 918 single=[], 919 multi=[], 920 ), 921 'boost_math_tr1l': BoostLibrary( 922 name='boost_math_tr1l', 923 shared=[], 924 static=[], 925 single=[], 926 multi=[], 927 ), 928 'boost_mpi': BoostLibrary( 929 name='boost_mpi', 930 shared=[], 931 static=[], 932 single=[], 933 multi=[], 934 ), 935 'boost_nowide': BoostLibrary( 936 name='boost_nowide', 937 shared=['-DBOOST_NOWIDE_DYN_LINK=1'], 938 static=[], 939 single=[], 940 multi=[], 941 ), 942 'boost_prg_exec_monitor': BoostLibrary( 943 name='boost_prg_exec_monitor', 944 shared=['-DBOOST_TEST_DYN_LINK=1'], 945 static=[], 946 single=[], 947 multi=[], 948 ), 949 'boost_program_options': BoostLibrary( 950 name='boost_program_options', 951 shared=[], 952 static=[], 953 single=[], 954 multi=[], 955 ), 956 'boost_random': BoostLibrary( 957 name='boost_random', 958 shared=['-DBOOST_RANDOM_DYN_LINK'], 959 static=[], 960 single=[], 961 multi=[], 962 ), 963 'boost_regex': BoostLibrary( 964 name='boost_regex', 965 shared=[], 966 static=[], 967 single=[], 968 multi=[], 969 ), 970 'boost_serialization': BoostLibrary( 971 name='boost_serialization', 972 shared=[], 973 static=[], 974 single=[], 975 multi=[], 976 ), 977 'boost_stacktrace_addr2line': BoostLibrary( 978 name='boost_stacktrace_addr2line', 979 shared=[], 980 static=[], 981 single=[], 982 multi=[], 983 ), 984 'boost_stacktrace_backtrace': BoostLibrary( 985 name='boost_stacktrace_backtrace', 986 shared=[], 987 static=[], 988 single=[], 989 multi=[], 990 ), 991 'boost_stacktrace_basic': BoostLibrary( 992 name='boost_stacktrace_basic', 993 shared=[], 994 static=[], 995 single=[], 996 multi=[], 997 ), 998 'boost_stacktrace_noop': BoostLibrary( 999 name='boost_stacktrace_noop', 1000 shared=[], 1001 static=[], 1002 single=[], 1003 multi=[], 1004 ), 1005 'boost_stacktrace_windbg': BoostLibrary( 1006 name='boost_stacktrace_windbg', 1007 shared=[], 1008 static=[], 1009 single=[], 1010 multi=[], 1011 ), 1012 'boost_stacktrace_windbg_cached': BoostLibrary( 1013 name='boost_stacktrace_windbg_cached', 1014 shared=[], 1015 static=[], 1016 single=[], 1017 multi=[], 1018 ), 1019 'boost_system': BoostLibrary( 1020 name='boost_system', 1021 shared=['-DBOOST_SYSTEM_DYN_LINK=1'], 1022 static=['-DBOOST_SYSTEM_STATIC_LINK=1'], 1023 single=[], 1024 multi=[], 1025 ), 1026 'boost_test_exec_monitor': BoostLibrary( 1027 name='boost_test_exec_monitor', 1028 shared=['-DBOOST_TEST_DYN_LINK=1'], 1029 static=[], 1030 single=[], 1031 multi=[], 1032 ), 1033 'boost_thread': BoostLibrary( 1034 name='boost_thread', 1035 shared=['-DBOOST_THREAD_BUILD_DLL=1', '-DBOOST_THREAD_USE_DLL=1'], 1036 static=['-DBOOST_THREAD_BUILD_LIB=1', '-DBOOST_THREAD_USE_LIB=1'], 1037 single=[], 1038 multi=[], 1039 ), 1040 'boost_timer': BoostLibrary( 1041 name='boost_timer', 1042 shared=['-DBOOST_TIMER_DYN_LINK=1'], 1043 static=['-DBOOST_TIMER_STATIC_LINK=1'], 1044 single=[], 1045 multi=[], 1046 ), 1047 'boost_type_erasure': BoostLibrary( 1048 name='boost_type_erasure', 1049 shared=['-DBOOST_TYPE_ERASURE_DYN_LINK'], 1050 static=[], 1051 single=[], 1052 multi=[], 1053 ), 1054 'boost_unit_test_framework': BoostLibrary( 1055 name='boost_unit_test_framework', 1056 shared=['-DBOOST_TEST_DYN_LINK=1'], 1057 static=[], 1058 single=[], 1059 multi=[], 1060 ), 1061 'boost_wave': BoostLibrary( 1062 name='boost_wave', 1063 shared=[], 1064 static=[], 1065 single=[], 1066 multi=[], 1067 ), 1068 'boost_wserialization': BoostLibrary( 1069 name='boost_wserialization', 1070 shared=[], 1071 static=[], 1072 single=[], 1073 multi=[], 1074 ), 1075} 1076 1077# # 1078#### ---- END GENERATED ---- #### 1079