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]) -> 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) 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) 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) -> T.List[Path]: 536 # First check the system include paths. Only consider those within the 537 # given root path 538 system_dirs_t = self.clib_compiler.get_library_dirs(self.env) 539 system_dirs = [Path(x) for x in system_dirs_t] 540 system_dirs = [x.resolve() for x in system_dirs if x.exists()] 541 system_dirs = [x for x in system_dirs if mesonlib.path_is_in_root(x, root)] 542 system_dirs = list(mesonlib.OrderedSet(system_dirs)) 543 544 if system_dirs: 545 return system_dirs 546 547 # No system include paths were found --> fall back to manually looking 548 # for library dirs in root 549 dirs = [] # type: T.List[Path] 550 subdirs = [] # type: T.List[Path] 551 for i in root.iterdir(): 552 if i.is_dir() and i.name.startswith('lib'): 553 dirs += [i] 554 555 # Some distros put libraries not directly inside /usr/lib but in /usr/lib/x86_64-linux-gnu 556 for i in dirs: 557 for j in i.iterdir(): 558 if j.is_dir() and j.name.endswith('-linux-gnu'): 559 subdirs += [j] 560 561 # Filter out paths that don't match the target arch to avoid finding 562 # the wrong libraries. See https://github.com/mesonbuild/meson/issues/7110 563 if not self.arch: 564 return dirs + subdirs 565 566 arch_list_32 = ['32', 'i386'] 567 arch_list_64 = ['64'] 568 569 raw_list = dirs + subdirs 570 no_arch = [x for x in raw_list if not any([y in x.name for y in arch_list_32 + arch_list_64])] 571 572 matching_arch = [] # type: T.List[Path] 573 if '32' in self.arch: 574 matching_arch = [x for x in raw_list if any([y in x.name for y in arch_list_32])] 575 elif '64' in self.arch: 576 matching_arch = [x for x in raw_list if any([y in x.name for y in arch_list_64])] 577 578 return sorted(matching_arch) + sorted(no_arch) 579 580 def filter_libraries(self, libs: T.List[BoostLibraryFile], lib_vers: str) -> T.List[BoostLibraryFile]: 581 # MSVC is very picky with the library tags 582 vscrt = '' 583 try: 584 crt_val = self.env.coredata.options[mesonlib.OptionKey('b_vscrt')].value 585 buildtype = self.env.coredata.options[mesonlib.OptionKey('buildtype')].value 586 vscrt = self.clib_compiler.get_crt_compile_args(crt_val, buildtype)[0] 587 except (KeyError, IndexError, AttributeError): 588 pass 589 590 # mlog.debug(' - static: {}'.format(self.static)) 591 # mlog.debug(' - not explicit static: {}'.format(not self.explicit_static)) 592 # mlog.debug(' - mt: {}'.format(self.multithreading)) 593 # mlog.debug(' - version: {}'.format(lib_vers)) 594 # mlog.debug(' - arch: {}'.format(self.arch)) 595 # mlog.debug(' - vscrt: {}'.format(vscrt)) 596 libs = [x for x in libs if x.static == self.static or not self.explicit_static] 597 libs = [x for x in libs if x.mt == self.multithreading] 598 libs = [x for x in libs if x.version_matches(lib_vers)] 599 libs = [x for x in libs if x.arch_matches(self.arch)] 600 libs = [x for x in libs if x.vscrt_matches(vscrt)] 601 libs = [x for x in libs if x.nvsuffix != 'dll'] # Only link to import libraries 602 603 # Only filter by debug when we are building in release mode. Debug 604 # libraries are automatically preferred through sorting otherwise. 605 if not self.debug: 606 libs = [x for x in libs if not x.debug] 607 608 # Take the abitag from the first library and filter by it. This 609 # ensures that we have a set of libraries that are always compatible. 610 if not libs: 611 return [] 612 abitag = libs[0].abitag 613 libs = [x for x in libs if x.abitag == abitag] 614 615 return libs 616 617 def detect_libraries(self, libdir: Path) -> T.List[BoostLibraryFile]: 618 libs = [] # type: T.List[BoostLibraryFile] 619 for i in libdir.iterdir(): 620 if not i.is_file() or i.is_symlink(): 621 continue 622 if not any([i.name.startswith(x) for x in ['libboost_', 'boost_']]): 623 continue 624 625 libs += [BoostLibraryFile(i)] 626 return [x for x in libs if x.is_boost()] # Filter out no boost libraries 627 628 def detect_split_root(self, inc_dir: Path, lib_dir: Path) -> None: 629 boost_inc_dir = None 630 for j in [inc_dir / 'version.hpp', inc_dir / 'boost' / 'version.hpp']: 631 if j.is_file(): 632 boost_inc_dir = self._include_dir_from_version_header(j) 633 break 634 if not boost_inc_dir: 635 self.is_found = False 636 return 637 638 self.is_found = self.run_check([boost_inc_dir], [lib_dir]) 639 640 def detect_roots(self) -> None: 641 roots = [] # type: T.List[Path] 642 643 # Try getting the BOOST_ROOT from a boost.pc if it exists. This primarily 644 # allows BoostDependency to find boost from Conan. See #5438 645 try: 646 boost_pc = PkgConfigDependency('boost', self.env, {'required': False}) 647 if boost_pc.found(): 648 boost_root = boost_pc.get_pkgconfig_variable('prefix', {'default': None}) 649 if boost_root: 650 roots += [Path(boost_root)] 651 except DependencyException: 652 pass 653 654 # Add roots from system paths 655 inc_paths = [Path(x) for x in self.clib_compiler.get_default_include_dirs()] 656 inc_paths = [x.parent for x in inc_paths if x.exists()] 657 inc_paths = [x.resolve() for x in inc_paths] 658 roots += inc_paths 659 660 # Add system paths 661 if self.env.machines[self.for_machine].is_windows(): 662 # Where boost built from source actually installs it 663 c_root = Path('C:/Boost') 664 if c_root.is_dir(): 665 roots += [c_root] 666 667 # Where boost documentation says it should be 668 prog_files = Path('C:/Program Files/boost') 669 # Where boost prebuilt binaries are 670 local_boost = Path('C:/local') 671 672 candidates = [] # type: T.List[Path] 673 if prog_files.is_dir(): 674 candidates += [*prog_files.iterdir()] 675 if local_boost.is_dir(): 676 candidates += [*local_boost.iterdir()] 677 678 roots += [x for x in candidates if x.name.lower().startswith('boost') and x.is_dir()] 679 else: 680 tmp = [] # type: T.List[Path] 681 682 # Homebrew 683 brew_boost = Path('/usr/local/Cellar/boost') 684 if brew_boost.is_dir(): 685 tmp += [x for x in brew_boost.iterdir()] 686 687 # Add some default system paths 688 tmp += [Path('/opt/local')] 689 tmp += [Path('/usr/local/opt/boost')] 690 tmp += [Path('/usr/local')] 691 tmp += [Path('/usr')] 692 693 # Cleanup paths 694 tmp = [x for x in tmp if x.is_dir()] 695 tmp = [x.resolve() for x in tmp] 696 roots += tmp 697 698 self.check_and_set_roots(roots) 699 700 def log_details(self) -> str: 701 res = '' 702 if self.modules_found: 703 res += 'found: ' + ', '.join(self.modules_found) 704 if self.modules_missing: 705 if res: 706 res += ' | ' 707 res += 'missing: ' + ', '.join(self.modules_missing) 708 return res 709 710 def log_info(self) -> str: 711 if self.boost_root: 712 return self.boost_root.as_posix() 713 return '' 714 715 def _include_dir_from_version_header(self, hfile: Path) -> BoostIncludeDir: 716 # Extract the version with a regex. Using clib_compiler.get_define would 717 # also work, however, this is slower (since it the compiler has to be 718 # invoked) and overkill since the layout of the header is always the same. 719 assert hfile.exists() 720 raw = hfile.read_text(encoding='utf-8') 721 m = re.search(r'#define\s+BOOST_VERSION\s+([0-9]+)', raw) 722 if not m: 723 mlog.debug(f'Failed to extract version information from {hfile}') 724 return BoostIncludeDir(hfile.parents[1], 0) 725 return BoostIncludeDir(hfile.parents[1], int(m.group(1))) 726 727 def _extra_compile_args(self) -> T.List[str]: 728 # BOOST_ALL_DYN_LINK should not be required with the known defines below 729 return ['-DBOOST_ALL_NO_LIB'] # Disable automatic linking 730 731 732# See https://www.boost.org/doc/libs/1_72_0/more/getting_started/unix-variants.html#library-naming 733# See https://mesonbuild.com/Reference-tables.html#cpu-families 734boost_arch_map = { 735 'aarch64': 'a64', 736 'arc': 'a32', 737 'arm': 'a32', 738 'ia64': 'i64', 739 'mips': 'm32', 740 'mips64': 'm64', 741 'ppc': 'p32', 742 'ppc64': 'p64', 743 'sparc': 's32', 744 'sparc64': 's64', 745 'x86': 'x32', 746 'x86_64': 'x64', 747} 748 749 750#### ---- BEGIN GENERATED ---- #### 751# # 752# Generated with tools/boost_names.py: 753# - boost version: 1.73.0 754# - modules found: 159 755# - libraries found: 43 756# 757 758class BoostLibrary(): 759 def __init__(self, name: str, shared: T.List[str], static: T.List[str], single: T.List[str], multi: T.List[str]): 760 self.name = name 761 self.shared = shared 762 self.static = static 763 self.single = single 764 self.multi = multi 765 766class BoostModule(): 767 def __init__(self, name: str, key: str, desc: str, libs: T.List[str]): 768 self.name = name 769 self.key = key 770 self.desc = desc 771 self.libs = libs 772 773 774# dict of all know libraries with additional compile options 775boost_libraries = { 776 'boost_atomic': BoostLibrary( 777 name='boost_atomic', 778 shared=['-DBOOST_ATOMIC_DYN_LINK=1'], 779 static=['-DBOOST_ATOMIC_STATIC_LINK=1'], 780 single=[], 781 multi=[], 782 ), 783 'boost_chrono': BoostLibrary( 784 name='boost_chrono', 785 shared=['-DBOOST_CHRONO_DYN_LINK=1'], 786 static=['-DBOOST_CHRONO_STATIC_LINK=1'], 787 single=['-DBOOST_CHRONO_THREAD_DISABLED'], 788 multi=[], 789 ), 790 'boost_container': BoostLibrary( 791 name='boost_container', 792 shared=['-DBOOST_CONTAINER_DYN_LINK=1'], 793 static=['-DBOOST_CONTAINER_STATIC_LINK=1'], 794 single=[], 795 multi=[], 796 ), 797 'boost_context': BoostLibrary( 798 name='boost_context', 799 shared=['-DBOOST_CONTEXT_DYN_LINK=1'], 800 static=[], 801 single=[], 802 multi=[], 803 ), 804 'boost_contract': BoostLibrary( 805 name='boost_contract', 806 shared=['-DBOOST_CONTRACT_DYN_LINK'], 807 static=['-DBOOST_CONTRACT_STATIC_LINK'], 808 single=['-DBOOST_CONTRACT_DISABLE_THREADS'], 809 multi=[], 810 ), 811 'boost_coroutine': BoostLibrary( 812 name='boost_coroutine', 813 shared=['-DBOOST_COROUTINES_DYN_LINK=1'], 814 static=[], 815 single=[], 816 multi=[], 817 ), 818 'boost_date_time': BoostLibrary( 819 name='boost_date_time', 820 shared=['-DBOOST_DATE_TIME_DYN_LINK=1'], 821 static=[], 822 single=[], 823 multi=[], 824 ), 825 'boost_exception': BoostLibrary( 826 name='boost_exception', 827 shared=[], 828 static=[], 829 single=[], 830 multi=[], 831 ), 832 'boost_fiber': BoostLibrary( 833 name='boost_fiber', 834 shared=['-DBOOST_FIBERS_DYN_LINK=1'], 835 static=[], 836 single=[], 837 multi=[], 838 ), 839 'boost_fiber_numa': BoostLibrary( 840 name='boost_fiber_numa', 841 shared=['-DBOOST_FIBERS_DYN_LINK=1'], 842 static=[], 843 single=[], 844 multi=[], 845 ), 846 'boost_filesystem': BoostLibrary( 847 name='boost_filesystem', 848 shared=['-DBOOST_FILESYSTEM_DYN_LINK=1'], 849 static=['-DBOOST_FILESYSTEM_STATIC_LINK=1'], 850 single=[], 851 multi=[], 852 ), 853 'boost_graph': BoostLibrary( 854 name='boost_graph', 855 shared=[], 856 static=[], 857 single=[], 858 multi=[], 859 ), 860 'boost_iostreams': BoostLibrary( 861 name='boost_iostreams', 862 shared=['-DBOOST_IOSTREAMS_DYN_LINK=1'], 863 static=[], 864 single=[], 865 multi=[], 866 ), 867 'boost_locale': BoostLibrary( 868 name='boost_locale', 869 shared=[], 870 static=[], 871 single=[], 872 multi=[], 873 ), 874 'boost_log': BoostLibrary( 875 name='boost_log', 876 shared=['-DBOOST_LOG_DYN_LINK=1'], 877 static=[], 878 single=['-DBOOST_LOG_NO_THREADS'], 879 multi=[], 880 ), 881 'boost_log_setup': BoostLibrary( 882 name='boost_log_setup', 883 shared=['-DBOOST_LOG_SETUP_DYN_LINK=1'], 884 static=[], 885 single=['-DBOOST_LOG_NO_THREADS'], 886 multi=[], 887 ), 888 'boost_math_c99': BoostLibrary( 889 name='boost_math_c99', 890 shared=[], 891 static=[], 892 single=[], 893 multi=[], 894 ), 895 'boost_math_c99f': BoostLibrary( 896 name='boost_math_c99f', 897 shared=[], 898 static=[], 899 single=[], 900 multi=[], 901 ), 902 'boost_math_c99l': BoostLibrary( 903 name='boost_math_c99l', 904 shared=[], 905 static=[], 906 single=[], 907 multi=[], 908 ), 909 'boost_math_tr1': BoostLibrary( 910 name='boost_math_tr1', 911 shared=[], 912 static=[], 913 single=[], 914 multi=[], 915 ), 916 'boost_math_tr1f': BoostLibrary( 917 name='boost_math_tr1f', 918 shared=[], 919 static=[], 920 single=[], 921 multi=[], 922 ), 923 'boost_math_tr1l': BoostLibrary( 924 name='boost_math_tr1l', 925 shared=[], 926 static=[], 927 single=[], 928 multi=[], 929 ), 930 'boost_mpi': BoostLibrary( 931 name='boost_mpi', 932 shared=[], 933 static=[], 934 single=[], 935 multi=[], 936 ), 937 'boost_nowide': BoostLibrary( 938 name='boost_nowide', 939 shared=['-DBOOST_NOWIDE_DYN_LINK=1'], 940 static=[], 941 single=[], 942 multi=[], 943 ), 944 'boost_prg_exec_monitor': BoostLibrary( 945 name='boost_prg_exec_monitor', 946 shared=['-DBOOST_TEST_DYN_LINK=1'], 947 static=[], 948 single=[], 949 multi=[], 950 ), 951 'boost_program_options': BoostLibrary( 952 name='boost_program_options', 953 shared=[], 954 static=[], 955 single=[], 956 multi=[], 957 ), 958 'boost_random': BoostLibrary( 959 name='boost_random', 960 shared=['-DBOOST_RANDOM_DYN_LINK'], 961 static=[], 962 single=[], 963 multi=[], 964 ), 965 'boost_regex': BoostLibrary( 966 name='boost_regex', 967 shared=[], 968 static=[], 969 single=[], 970 multi=[], 971 ), 972 'boost_serialization': BoostLibrary( 973 name='boost_serialization', 974 shared=[], 975 static=[], 976 single=[], 977 multi=[], 978 ), 979 'boost_stacktrace_addr2line': BoostLibrary( 980 name='boost_stacktrace_addr2line', 981 shared=[], 982 static=[], 983 single=[], 984 multi=[], 985 ), 986 'boost_stacktrace_backtrace': BoostLibrary( 987 name='boost_stacktrace_backtrace', 988 shared=[], 989 static=[], 990 single=[], 991 multi=[], 992 ), 993 'boost_stacktrace_basic': BoostLibrary( 994 name='boost_stacktrace_basic', 995 shared=[], 996 static=[], 997 single=[], 998 multi=[], 999 ), 1000 'boost_stacktrace_noop': BoostLibrary( 1001 name='boost_stacktrace_noop', 1002 shared=[], 1003 static=[], 1004 single=[], 1005 multi=[], 1006 ), 1007 'boost_stacktrace_windbg': BoostLibrary( 1008 name='boost_stacktrace_windbg', 1009 shared=[], 1010 static=[], 1011 single=[], 1012 multi=[], 1013 ), 1014 'boost_stacktrace_windbg_cached': BoostLibrary( 1015 name='boost_stacktrace_windbg_cached', 1016 shared=[], 1017 static=[], 1018 single=[], 1019 multi=[], 1020 ), 1021 'boost_system': BoostLibrary( 1022 name='boost_system', 1023 shared=['-DBOOST_SYSTEM_DYN_LINK=1'], 1024 static=['-DBOOST_SYSTEM_STATIC_LINK=1'], 1025 single=[], 1026 multi=[], 1027 ), 1028 'boost_test_exec_monitor': BoostLibrary( 1029 name='boost_test_exec_monitor', 1030 shared=['-DBOOST_TEST_DYN_LINK=1'], 1031 static=[], 1032 single=[], 1033 multi=[], 1034 ), 1035 'boost_thread': BoostLibrary( 1036 name='boost_thread', 1037 shared=['-DBOOST_THREAD_BUILD_DLL=1', '-DBOOST_THREAD_USE_DLL=1'], 1038 static=['-DBOOST_THREAD_BUILD_LIB=1', '-DBOOST_THREAD_USE_LIB=1'], 1039 single=[], 1040 multi=[], 1041 ), 1042 'boost_timer': BoostLibrary( 1043 name='boost_timer', 1044 shared=['-DBOOST_TIMER_DYN_LINK=1'], 1045 static=['-DBOOST_TIMER_STATIC_LINK=1'], 1046 single=[], 1047 multi=[], 1048 ), 1049 'boost_type_erasure': BoostLibrary( 1050 name='boost_type_erasure', 1051 shared=['-DBOOST_TYPE_ERASURE_DYN_LINK'], 1052 static=[], 1053 single=[], 1054 multi=[], 1055 ), 1056 'boost_unit_test_framework': BoostLibrary( 1057 name='boost_unit_test_framework', 1058 shared=['-DBOOST_TEST_DYN_LINK=1'], 1059 static=[], 1060 single=[], 1061 multi=[], 1062 ), 1063 'boost_wave': BoostLibrary( 1064 name='boost_wave', 1065 shared=[], 1066 static=[], 1067 single=[], 1068 multi=[], 1069 ), 1070 'boost_wserialization': BoostLibrary( 1071 name='boost_wserialization', 1072 shared=[], 1073 static=[], 1074 single=[], 1075 multi=[], 1076 ), 1077} 1078 1079# # 1080#### ---- END GENERATED ---- #### 1081