diff options
-rw-r--r-- | .appveyor.yml | 2 | ||||
-rw-r--r-- | docs/markdown/Dependencies.md | 15 | ||||
-rw-r--r-- | docs/markdown/Release-notes-for-0.43.0/001-boost-platform-improvement.md | 10 | ||||
-rw-r--r-- | mesonbuild/dependencies/misc.py | 413 | ||||
-rw-r--r-- | test cases/frameworks/1 boost/meson.build | 5 | ||||
-rw-r--r-- | test cases/frameworks/1 boost/nolinkexe.cc | 12 | ||||
-rwxr-xr-x | tools/boost_names.py | 180 |
7 files changed, 502 insertions, 135 deletions
diff --git a/.appveyor.yml b/.appveyor.yml index 60a6fd3..4dc3f50 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -59,8 +59,6 @@ init: install: - cmd: set "ORIG_PATH=%PATH%" - # Boost 1.56.0: https://www.appveyor.com/docs/build-environment/#boost - #- cmd: set "BOOST_ROOT=C:\Libraries\boost" # Use a Ninja with QuLogic's patch: https://github.com/ninja-build/ninja/issues/1219 - cmd: set "MESON_FIXED_NINJA=1" - ps: (new-object net.webclient).DownloadFile('http://nirbheek.in/files/binaries/ninja/win32/ninja.exe', 'C:\projects\meson\ninja.exe') diff --git a/docs/markdown/Dependencies.md b/docs/markdown/Dependencies.md index 187c4fe..c46334c 100644 --- a/docs/markdown/Dependencies.md +++ b/docs/markdown/Dependencies.md @@ -50,8 +50,16 @@ pkg-config files. Meson has autodetection support for some of these. ## Boost ## Boost is not a single dependency but rather a group of different -libraries. To use Boost with Meson, simply list which Boost modules -you would like to use. +libraries. To use Boost headers-only libraries, simply add Boost as a +dependency. + +```meson +boost_dep = dependency('boost') +exe = executable('myprog', 'file.cc', dependencies : boost_dep) +``` + +To link against boost with Meson, simply list which libraries you would like to +use. ```meson boost_dep = dependency('boost', modules : ['thread', 'utility']) @@ -65,6 +73,9 @@ If your boost headers or libraries are in non-standard locations you can set the BOOST_ROOT, BOOST_INCLUDEDIR, and/or BOOST_LIBRARYDIR environment variables. +You can set the argument `threading` to `single` to use boost libraries that +has been compiled for single-threaded use instead. + ## GTest and GMock ## GTest and GMock come as sources that must be compiled as part of your diff --git a/docs/markdown/Release-notes-for-0.43.0/001-boost-platform-improvement.md b/docs/markdown/Release-notes-for-0.43.0/001-boost-platform-improvement.md new file mode 100644 index 0000000..2cd44ce --- /dev/null +++ b/docs/markdown/Release-notes-for-0.43.0/001-boost-platform-improvement.md @@ -0,0 +1,10 @@ +## Portability improvements to Boost Dependency + +The Boost dependency has been improved to better detect the various ways to +install boost on multiple platforms. At the same time the `modules` semantics +for the dependency has been changed. Previously it was allowed to specify +header directories as `modules` but it wasn't required. Now, modules are only +used to specify libraries that require linking. + +This is a breaking change and the fix is to remove all modules that aren't +found. diff --git a/mesonbuild/dependencies/misc.py b/mesonbuild/dependencies/misc.py index 61d0d77..a0d5e47 100644 --- a/mesonbuild/dependencies/misc.py +++ b/mesonbuild/dependencies/misc.py @@ -18,7 +18,6 @@ import glob import os import re import shlex -import stat import shutil import sysconfig @@ -30,77 +29,153 @@ from ..environment import detect_cpu_family from .base import DependencyException, DependencyMethods from .base import ExternalDependency, ExternalProgram, ExtraFrameworkDependency, PkgConfigDependency +# 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 +# +# Library names supported: +# - libboost_<module>-<compiler>-mt-gd-x_x.lib (static) +# - boost_<module>-<compiler>-mt-gd-x_x.lib|.dll (shared) +# - libboost_<module>.lib (static) +# - boost_<module>.lib|.dll (shared) +# where compiler is vc141 for example. +# +# NOTE: -gb means runtime and build time debugging is on +# -mt means threading=multi +# +# The `modules` argument accept library names. This is because every module that +# has libraries to link against also has multiple options regarding how to +# link. See for example: +# * http://www.boost.org/doc/libs/1_65_1/libs/test/doc/html/boost_test/usage_variants.html +# * http://www.boost.org/doc/libs/1_65_1/doc/html/stacktrace/configuration_and_build.html +# * http://www.boost.org/doc/libs/1_65_1/libs/math/doc/html/math_toolkit/main_tr1.html class BoostDependency(ExternalDependency): - # Some boost libraries have different names for - # their sources and libraries. This dict maps - # between the two. - name2lib = {'test': 'unit_test_framework'} - def __init__(self, environment, kwargs): super().__init__('boost', environment, 'cpp', kwargs) + self.need_static_link = ['boost_exception', 'boost_test_exec_monitor'] + self.is_debug = environment.cmd_line_options.buildtype.startswith('debug') + threading = kwargs.get("threading", "multi") + self.is_multithreading = threading == "multi" + + self.requested_modules = self.get_requested(kwargs) + + self.boost_root = None + self.boost_roots = [] + self.incdir = None self.libdir = None - try: + + if 'BOOST_ROOT' in os.environ: self.boost_root = os.environ['BOOST_ROOT'] + self.boost_roots = [self.boost_root] if not os.path.isabs(self.boost_root): raise DependencyException('BOOST_ROOT must be an absolute path.') - except KeyError: - self.boost_root = None + if 'BOOST_INCLUDEDIR' in os.environ: + self.incdir = os.environ['BOOST_INCLUDEDIR'] + if 'BOOST_LIBRARYDIR' in os.environ: + self.libdir = os.environ['BOOST_LIBRARYDIR'] + + if self.want_cross and self.boost_root is None and self.incdir is None: + raise DependencyException('BOOST_ROOT or BOOST_INCLUDEDIR is needed while cross-compiling') + if self.boost_root is None: - if self.want_cross: - if 'BOOST_INCLUDEDIR' in os.environ: - self.incdir = os.environ['BOOST_INCLUDEDIR'] - else: - raise DependencyException('BOOST_ROOT or BOOST_INCLUDEDIR is needed while cross-compiling') if mesonlib.is_windows(): - self.boost_root = self.detect_win_root() - self.incdir = self.boost_root + self.boost_roots = self.detect_win_roots() else: - if 'BOOST_INCLUDEDIR' in os.environ: - self.incdir = os.environ['BOOST_INCLUDEDIR'] - else: - self.incdir = '/usr/include' + self.boost_roots = self.detect_nix_roots() + + if self.boost_root is None and not self.boost_roots: + self.log_fail() + return + + if self.incdir is None: + if mesonlib.is_windows(): + self.incdir = self.detect_win_incdir() + else: + self.incdir = self.detect_nix_incdir() + + if self.incdir is None: + self.log_fail() + return + + mlog.debug('Boost library root dir is', mlog.bold(self.boost_root)) + mlog.debug('Boost include directory is', mlog.bold(self.incdir)) - if 'BOOST_LIBRARYDIR' in os.environ: - self.libdir = os.environ['BOOST_LIBRARYDIR'] - else: - self.incdir = os.path.join(self.boost_root, 'include') - self.boost_inc_subdir = os.path.join(self.incdir, 'boost') - mlog.debug('Boost library root dir is', self.boost_root) - self.src_modules = {} self.lib_modules = {} - self.lib_modules_mt = {} self.detect_version() - self.requested_modules = self.get_requested(kwargs) - module_str = ', '.join(self.requested_modules) if self.is_found: - self.detect_src_modules() self.detect_lib_modules() + mlog.debug('Boost library directory is', mlog.bold(self.libdir)) self.validate_requested() - if self.boost_root is not None: - info = self.version + ', ' + self.boost_root - else: - info = self.version - mlog.log('Dependency Boost (%s) found:' % module_str, mlog.green('YES'), info) + self.log_success() + else: + self.log_fail() + + def log_fail(self): + module_str = ', '.join(self.requested_modules) + mlog.log("Dependency Boost (%s) found:" % module_str, mlog.red('NO')) + + def log_success(self): + module_str = ', '.join(self.requested_modules) + if self.boost_root: + info = self.version + ', ' + self.boost_root else: - mlog.log("Dependency Boost (%s) found:" % module_str, mlog.red('NO')) + info = self.version + mlog.log('Dependency Boost (%s) found:' % module_str, mlog.green('YES'), info) + + def detect_nix_roots(self): + return ['/usr/local', '/usr'] - def detect_win_root(self): - globtext = 'c:\\local\\boost_*' + def detect_win_roots(self): + res = [] + # Where boost documentation says it should be + globtext = 'C:\\Program Files\\boost\\boost_*' files = glob.glob(globtext) - if len(files) > 0: - return files[0] - return 'C:\\' + res.extend(files) + + # Where boost built from source actually installs it + if os.path.isdir('C:\\Boost'): + res.append('C:\\Boost') + + # Where boost prebuilt binaries are + globtext = 'C:\\local\\boost_*' + files = glob.glob(globtext) + res.extend(files) + return res + + def detect_nix_incdir(self): + for root in self.boost_roots: + incdir = os.path.join(root, 'include', 'boost') + if os.path.isdir(incdir): + return os.path.join(root, 'include') + return None + + # FIXME: Should pick a version that matches the requested version + # Returns the folder that contains the boost folder. + def detect_win_incdir(self): + for root in self.boost_roots: + globtext = os.path.join(root, 'include', 'boost-*') + incdirs = glob.glob(globtext) + if len(incdirs) > 0: + return incdirs[0] + incboostdir = os.path.join(root, 'include', 'boost') + if os.path.isdir(incboostdir): + return os.path.join(root, 'include') + incboostdir = os.path.join(root, 'boost') + if os.path.isdir(incboostdir): + return root + return None def get_compile_args(self): args = [] - if self.boost_root is not None: - if mesonlib.is_windows(): - include_dir = self.boost_root - else: - include_dir = os.path.join(self.boost_root, 'include') - else: - include_dir = self.incdir + include_dir = self.incdir # Use "-isystem" when including boost headers instead of "-I" # to avoid compiler warnings/failures when "-Werror" is used @@ -125,19 +200,23 @@ class BoostDependency(ExternalDependency): for c in candidates: if not isinstance(c, str): raise DependencyException('Boost module argument is not a string.') + if 'boost_' + c not in BOOST_LIBS: + raise DependencyException('Dependency {} not found. It is not a valid boost library.'.format(c)) return candidates def validate_requested(self): for m in self.requested_modules: - if m not in self.src_modules and m not in self.lib_modules and m + '-mt' not in self.lib_modules_mt: - msg = 'Requested Boost module {!r} not found' + if 'boost_' + m not in self.lib_modules: + msg = 'Requested Boost library {!r} not found' raise DependencyException(msg.format(m)) def detect_version(self): try: - ifile = open(os.path.join(self.boost_inc_subdir, 'version.hpp')) + ifile = open(os.path.join(self.incdir, 'boost', 'version.hpp')) except FileNotFoundError: return + except TypeError: + return with ifile: for line in ifile: if line.startswith("#define") and 'BOOST_LIB_VERSION' in line: @@ -147,12 +226,6 @@ class BoostDependency(ExternalDependency): self.is_found = True return - def detect_src_modules(self): - for entry in os.listdir(self.boost_inc_subdir): - entry = os.path.join(self.boost_inc_subdir, entry) - if stat.S_ISDIR(os.stat(entry).st_mode): - self.src_modules[os.path.split(entry)[-1]] = True - def detect_lib_modules(self): if mesonlib.is_windows(): return self.detect_lib_modules_win() @@ -160,32 +233,79 @@ class BoostDependency(ExternalDependency): def detect_lib_modules_win(self): arch = detect_cpu_family(self.env.coredata.compilers) - # Guess the libdir - if arch == 'x86': - gl = 'lib32*' - elif arch == 'x86_64': - gl = 'lib64*' - else: - # Does anyone do Boost cross-compiling to other archs on Windows? - gl = None - # See if the libdir is valid - if gl: - libdir = glob.glob(os.path.join(self.boost_root, gl)) - else: - libdir = [] - # Can't find libdir, bail - if not libdir: + compiler_ts = self.env.detect_cpp_compiler(self.want_cross).get_toolset_version().split('.') + compiler = 'vc{}{}'.format(compiler_ts[0], compiler_ts[1]) + if not self.libdir: + # The libdirs in the distributed binaries + if arch == 'x86': + gl = 'lib32*' + elif arch == 'x86_64': + gl = 'lib64*' + else: + # Does anyone do Boost cross-compiling to other archs on Windows? + gl = None + if self.boost_root: + roots = [self.boost_root] + else: + roots = self.boost_roots + for root in roots: + # The default libdir when building + libdir = os.path.join(root, 'lib') + if os.path.isdir(libdir): + self.libdir = libdir + break + if gl: + tmp = glob.glob(os.path.join(root, gl)) + if len(tmp) > 0: + # FIXME: Should pick the correct version + self.libdir = tmp[0] + break + + if not self.libdir: return - libdir = libdir[0] - # Don't override what was set in the environment - if self.libdir: - self.libdir = libdir - globber = 'libboost_*-gd-*.lib' if self.static else 'boost_*-gd-*.lib' # FIXME - for entry in glob.glob(os.path.join(libdir, globber)): + + for name in self.need_static_link: + libname = "lib{}".format(name) + '-' + compiler + if self.is_multithreading: + libname = libname + '-mt' + if self.is_debug: + libname = libname + '-gd' + libname = libname + "-{}.lib".format(self.version.replace('.', '_')) + if os.path.isfile(os.path.join(self.libdir, libname)): + modname = libname.split('-', 1)[0][3:] + self.lib_modules[modname] = libname + else: + libname = "lib{}.lib".format(name) + if os.path.isfile(os.path.join(self.libdir, libname)): + self.lib_modules[name[3:]] = libname + + # globber1 applies to a layout=system installation + # globber2 applies to a layout=versioned installation + globber1 = 'libboost_*' if self.static else 'boost_*' + globber2 = globber1 + '-' + compiler + if self.is_multithreading: + globber2 = globber2 + '-mt' + if self.is_debug: + globber2 = globber2 + '-gd' + globber2 = globber2 + '-{}'.format(self.version.replace('.', '_')) + globber2_matches = glob.glob(os.path.join(self.libdir, globber2 + '.lib')) + for entry in globber2_matches: (_, fname) = os.path.split(entry) - base = fname.split('_', 1)[1] - modname = base.split('-', 1)[0] - self.lib_modules_mt[modname] = fname + modname = fname.split('-', 1) + if len(modname) > 1: + modname = modname[0] + else: + modname = modname.split('.', 1)[0] + if self.static: + modname = modname[3:] + self.lib_modules[modname] = fname + if len(globber2_matches) == 0: + for entry in glob.glob(os.path.join(self.libdir, globber1 + '.lib')): + (_, fname) = os.path.split(entry) + modname = fname.split('.', 1)[0] + if self.static: + modname = modname[3:] + self.lib_modules[modname] = fname def detect_lib_modules_nix(self): if self.static: @@ -203,25 +323,32 @@ class BoostDependency(ExternalDependency): else: libdirs = [os.path.join(self.boost_root, 'lib')] for libdir in libdirs: + for name in self.need_static_link: + libname = 'lib{}.a'.format(name) + if os.path.isfile(os.path.join(libdir, libname)): + self.lib_modules[name] = libname for entry in glob.glob(os.path.join(libdir, globber)): lib = os.path.basename(entry) - name = lib.split('.')[0].split('_', 1)[-1] + name = lib.split('.')[0][3:] # I'm not 100% sure what to do here. Some distros # have modules such as thread only as -mt versions. - if entry.endswith('-mt.{}'.format(libsuffix)): - self.lib_modules_mt[name] = True - else: - self.lib_modules[name] = True + # On debian all packages are built threading=multi + # but not suffixed with -mt. + # FIXME: implement detect_lib_modules_{debian, redhat, ...} + if self.is_multithreading and mesonlib.is_debianlike(): + self.lib_modules[name] = lib + elif self.is_multithreading and entry.endswith('-mt.{}'.format(libsuffix)): + self.lib_modules[name] = lib + elif not entry.endswith('-mt.{}'.format(libsuffix)): + self.lib_modules[name] = lib def get_win_link_args(self): args = [] # TODO: should this check self.libdir? - if self.boost_root: + if self.libdir: args.append('-L' + self.libdir) - for module in self.requested_modules: - module = BoostDependency.name2lib.get(module, module) - if module in self.lib_modules_mt: - args.append(self.lib_modules_mt[module]) + for lib in self.requested_modules: + args.append(self.lib_modules['boost_' + lib]) return args def get_link_args(self): @@ -232,33 +359,14 @@ class BoostDependency(ExternalDependency): args.append('-L' + os.path.join(self.boost_root, 'lib')) elif self.libdir: args.append('-L' + self.libdir) - for module in self.requested_modules: - module = BoostDependency.name2lib.get(module, module) - libname = 'boost_' + module + for lib in self.requested_modules: # The compiler's library detector is the most reliable so use that first. - default_detect = self.compiler.find_library(libname, self.env, []) + default_detect = self.compiler.find_library('boost_' + lib, self.env, []) if default_detect is not None: - if module == 'unit_testing_framework': - emon_args = self.compiler.find_library('boost_test_exec_monitor') - else: - emon_args = None args += default_detect - if emon_args is not None: - args += emon_args - elif module in self.lib_modules or module in self.lib_modules_mt: - linkcmd = '-l' + libname - args.append(linkcmd) - # FIXME a hack, but Boost's testing framework has a lot of - # different options and it's hard to determine what to do - # without feedback from actual users. Update this - # as we get more bug reports. - if module == 'unit_testing_framework': - args.append('-lboost_test_exec_monitor') - elif module + '-mt' in self.lib_modules_mt: - linkcmd = '-lboost_' + module + '-mt' + elif lib in self.lib_modules: + linkcmd = '-l' + lib args.append(linkcmd) - if module == 'unit_testing_framework': - args.append('-lboost_test_exec_monitor-mt') return args def get_sources(self): @@ -653,3 +761,78 @@ class CupsDependency(ExternalDependency): return [DependencyMethods.PKGCONFIG, DependencyMethods.CUPSCONFIG, DependencyMethods.EXTRAFRAMEWORK] else: return [DependencyMethods.PKGCONFIG, DependencyMethods.CUPSCONFIG] + +# Generated with boost_names.py +BOOST_LIBS = [ + 'boost_atomic', + 'boost_chrono', + 'boost_chrono', + 'boost_container', + 'boost_context', + 'boost_coroutine', + 'boost_date_time', + 'boost_exception', + 'boost_fiber', + 'boost_filesystem', + 'boost_graph', + 'boost_iostreams', + 'boost_locale', + 'boost_log', + 'boost_log_setup', + 'boost_math_tr1', + 'boost_math_tr1f', + 'boost_math_tr1l', + 'boost_math_c99', + 'boost_math_c99f', + 'boost_math_c99l', + 'boost_math_tr1', + 'boost_math_tr1f', + 'boost_math_tr1l', + 'boost_math_c99', + 'boost_math_c99f', + 'boost_math_c99l', + 'boost_math_tr1', + 'boost_math_tr1f', + 'boost_math_tr1l', + 'boost_math_c99', + 'boost_math_c99f', + 'boost_math_c99l', + 'boost_math_tr1', + 'boost_math_tr1f', + 'boost_math_tr1l', + 'boost_math_c99', + 'boost_math_c99f', + 'boost_math_c99l', + 'boost_math_tr1', + 'boost_math_tr1f', + 'boost_math_tr1l', + 'boost_math_c99', + 'boost_math_c99f', + 'boost_math_c99l', + 'boost_math_tr1', + 'boost_math_tr1f', + 'boost_math_tr1l', + 'boost_math_c99', + 'boost_math_c99f', + 'boost_math_c99l', + 'boost_mpi', + 'boost_random', + 'boost_regex', + 'boost_serialization', + 'boost_wserialization', + 'boost_signals', + 'boost_stacktrace_noop', + 'boost_stacktrace_backtrace', + 'boost_stacktrace_addr2line', + 'boost_stacktrace_basic', + 'boost_stacktrace_windbg', + 'boost_stacktrace_windbg_cached', + 'boost_system', + 'boost_prg_exec_monitor', + 'boost_test_exec_monitor', + 'boost_unit_test_framework', + 'boost_thread', + 'boost_timer', + 'boost_type_erasure', + 'boost_wave' +] diff --git a/test cases/frameworks/1 boost/meson.build b/test cases/frameworks/1 boost/meson.build index 6f25f8b..b73f93a 100644 --- a/test cases/frameworks/1 boost/meson.build +++ b/test cases/frameworks/1 boost/meson.build @@ -9,21 +9,18 @@ add_project_arguments(['-DBOOST_LOG_DYN_LINK'], # within one project. The need to be independent of each other. # Use one without a library dependency and one with it. -nolinkdep = dependency('boost', modules: 'utility') linkdep = dependency('boost', modules : ['thread', 'system']) staticdep = dependency('boost', modules : ['thread', 'system'], static : true) -testdep = dependency('boost', modules : 'test') +testdep = dependency('boost', modules : ['unit_test_framework']) nomoddep = dependency('boost') extralibdep = dependency('boost', modules : ['thread', 'system', 'log_setup', 'log']) -nolinkexe = executable('nolinkedexe', 'nolinkexe.cc', dependencies : nolinkdep) linkexe = executable('linkedexe', 'linkexe.cc', dependencies : linkdep) staticexe = executable('staticlinkedexe', 'linkexe.cc', dependencies : staticdep) unitexe = executable('utf', 'unit_test.cpp', dependencies: testdep) nomodexe = executable('nomod', 'nomod.cpp', dependencies : nomoddep) extralibexe = executable('extralibexe', 'extralib.cpp', dependencies : extralibdep) -test('Boost nolinktest', nolinkexe) test('Boost linktest', linkexe) test('Boost statictest', staticexe) test('Boost UTF test', unitexe) diff --git a/test cases/frameworks/1 boost/nolinkexe.cc b/test cases/frameworks/1 boost/nolinkexe.cc deleted file mode 100644 index 7b6c6d9..0000000 --- a/test cases/frameworks/1 boost/nolinkexe.cc +++ /dev/null @@ -1,12 +0,0 @@ -#include<boost/utility.hpp> - -class MyClass : boost::noncopyable { -public: - MyClass() {}; - ~MyClass() {}; -}; - -int main(int argc, char **argv) { - MyClass obj; - return 0; -} diff --git a/tools/boost_names.py b/tools/boost_names.py new file mode 100755 index 0000000..9ac16bb --- /dev/null +++ b/tools/boost_names.py @@ -0,0 +1,180 @@ +#!/usr/bin/env python3 + +# Copyright 2017 Niklas Claesson + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""This is two implementations for how to get module names from the boost +sources. One relies on json metadata files in the sources, the other relies on +the folder names. + +Run the tool in the boost directory and append the stdout to the misc.py: + +boost/$ path/to/meson/tools/boost_names.py >> path/to/meson/dependencies/misc.py +""" + +import sys +import os +import collections +import pprint +import json +import re + +Module = collections.namedtuple('Module', ['dirname', 'name', 'libnames']) +Module.__repr__ = lambda self: str((self.dirname, self.name, self.libnames)) + +LIBS = 'libs' + +manual_map = { + 'callable_traits': 'Call Traits', + 'crc': 'CRC', + 'dll': 'DLL', + 'gil': 'GIL', + 'graph_parallel': 'GraphParallel', + 'icl': 'ICL', + 'io': 'IO State Savers', + 'msm': 'Meta State Machine', + 'mpi': 'MPI', + 'mpl': 'MPL', + 'multi_array': 'Multi-Array', + 'multi_index': 'Multi-Index', + 'numeric': 'Numeric Conversion', + 'ptr_container': 'Pointer Container', + 'poly_collection': 'PolyCollection', + 'qvm': 'QVM', + 'throw_exception': 'ThrowException', + 'tti': 'TTI', + 'vmd': 'VMD', +} + +extra = [ + Module('utility', 'Compressed Pair', []), + Module('core', 'Enable If', []), + Module('functional', 'Functional/Factory', []), + Module('functional', 'Functional/Forward', []), + Module('functional', 'Functional/Hash', []), + Module('functional', 'Functional/Overloaded Function', []), + Module('utility', 'Identity Type', []), + Module('utility', 'In Place Factory, Typed In Place Factory', []), + Module('numeric', 'Interval', []), + Module('math', 'Math Common Factor', []), + Module('math', 'Math Octonion', []), + Module('math', 'Math Quaternion', []), + Module('math', 'Math/Special Functions', []), + Module('math', 'Math/Statistical Distributions', []), + Module('bind', 'Member Function', []), + Module('algorithm', 'Min-Max', []), + Module('numeric', 'Odeint', []), + Module('utility', 'Operators', []), + Module('core', 'Ref', []), + Module('utility', 'Result Of', []), + Module('algorithm', 'String Algo', []), + Module('core', 'Swap', []), + Module('', 'Tribool', []), + Module('numeric', 'uBLAS', []), + Module('utility', 'Value Initialized', []), +] + +# Cannot find the following modules in the documentation of boost +not_modules = ['beast', 'logic', 'mp11', 'winapi'] + +def eprint(message): + print(message, file=sys.stderr) + +def get_library_names(jamfile): + libs = [] + with open(jamfile) as jamfh: + jam = jamfh.read() + res = re.finditer(r'^lib[\s]+([A-Za-z0-9_]+)([^;]*);', jam, re.MULTILINE | re.DOTALL) + for matches in res: + if ':' in matches.group(2): + libs.append(matches.group(1)) + return libs + +def exists(modules, module): + return len([x for x in modules if x.dirname == module.dirname]) != 0 + +def get_modules(init=extra): + modules = init + for directory in os.listdir(LIBS): + if not os.path.isdir(os.path.join(LIBS, directory)): + continue + if directory in not_modules: + continue + jamfile = os.path.join(LIBS, directory, 'build', 'Jamfile.v2') + if os.path.isfile(jamfile): + libs = get_library_names(jamfile) + else: + libs = [] + if directory in manual_map.keys(): + modname = manual_map[directory] + else: + modname = directory.replace('_', ' ').title() + modules.append(Module(directory, modname, libs)) + return modules + +def get_modules_2(): + modules = [] + for (root, dirs, files) in os.walk(LIBS): + for f in files: + if f == "libraries.json": + projectdir = os.path.dirname(root) + + jamfile = os.path.join(projectdir, 'build', 'Jamfile.v2') + if os.path.isfile(jamfile): + libs = get_library_names(jamfile) + else: + libs = [] + + # Get metadata for module + jsonfile = os.path.join(root, f) + with open(jsonfile) as jsonfh: + boost_modules = json.loads(jsonfh.read()) + if(isinstance(boost_modules, dict)): + boost_modules = [boost_modules] + for boost_module in boost_modules: + modules.append(Module(boost_module['key'], boost_module['name'], libs)) + + # Some subprojects do not have meta directory with json file. Find those + jsonless_modules = [x for x in get_modules([]) if not exists(modules, x)] + for module in jsonless_modules: + eprint("WARNING: {} does not have meta/libraries.json. Will guess pretty name '{}'".format(module.dirname, module.name)) + modules.extend(jsonless_modules) + + return modules + +def main(args): + if not os.path.isdir(LIBS): + eprint("ERROR: script must be run in boost source directory") + + # It will pick jsonless algorithm if 1 is given as argument + impl = 0 + if len(args) > 1: + if args[1] == '1': + impl = 1 + + if impl == 1: + modules = get_modules() + else: + modules = get_modules_2() + + sorted_modules = sorted(modules, key=lambda module: module.name.lower()) + sorted_modules = [x[2] for x in sorted_modules if x[2]] + sorted_modules = sum(sorted_modules, []) + sorted_modules = [x for x in sorted_modules if x.startswith('boost')] + + pp = pprint.PrettyPrinter() + pp.pprint(sorted_modules) + +if __name__ == '__main__': + main(sys.argv) |