Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Factor out code for windows abi tags. #3004

Merged
merged 18 commits into from Feb 15, 2018
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
206 changes: 125 additions & 81 deletions mesonbuild/dependencies/misc.py
Expand Up @@ -18,7 +18,6 @@
import os
import re
import shlex
import shutil
import sysconfig

from pathlib import Path
Expand All @@ -44,6 +43,10 @@
# - $BOOST_ROOT/boost/*.hpp
# - $BOOST_ROOT/lib<arch>-<compiler>/*.lib where arch=32/64 and compiler=msvc-14.1
#
# Note that we should also try to support:
# mingw-w64 / Windows : libboost_<module>-mt.a (location = <prefix>/mingw64/lib/)
# libboost_<module>-mt.dll.a
#
# Library names supported:
# - libboost_<module>-<compiler>-mt-gd-x_x.lib (static)
# - boost_<module>-<compiler>-mt-gd-x_x.lib|.dll (shared)
Expand Down Expand Up @@ -97,18 +100,13 @@
# 2.2. Find boost libraries with unknown suffixes using file-name globbing.

# TODO: Unix: Don't assume we know where the boost dir is, rely on -Idir and -Ldir being set.
# TODO: Determine a suffix (e.g. "-mt" or "") and use it.
# TODO: Get_win_link_args( ) and get_link_args( )
# TODO: Genericize: 'args += ['-L' + dir] => args += self.compiler.get_linker_search_args(dir)
# TODO: Allow user to specify suffix in BOOST_SUFFIX, or add specific options like BOOST_DEBUG for 'd' for debug.
# TODO: fix cross:
# is_windows() -> for_windows(self.want_cross, self.env)
# is_osx() and self.want_cross -> for_darwin(self.want_cross, self.env)

class BoostDependency(ExternalDependency):
def __init__(self, environment, kwargs):
super().__init__('boost', environment, 'cpp', kwargs)
self.need_static_link = ['boost_exception', 'boost_test_exec_monitor']
# FIXME: is this the right way to find the build type?
self.is_debug = environment.cmd_line_options.buildtype.startswith('debug')
threading = kwargs.get("threading", "multi")
self.is_multithreading = threading == "multi"
Expand All @@ -131,42 +129,34 @@ def __init__(self, environment, kwargs):
self.libdir = os.environ['BOOST_LIBRARYDIR']

if self.boost_root is None:
if mesonlib.is_windows():
if mesonlib.for_windows(self.want_cross, self.env):
self.boost_roots = self.detect_win_roots()
else:
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():
if mesonlib.for_windows(self.want_cross, self.env):
self.incdir = self.detect_win_incdir()
else:
self.incdir = self.detect_nix_incdir()

if self.incdir is None and mesonlib.is_windows():
self.log_fail()
return

if self.check_invalid_modules():
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))

self.lib_modules = {}
self.detect_version()
# 1. check if we can find BOOST headers.
self.detect_headers_and_version()

# 2. check if we can find BOOST libraries.
if self.is_found:
self.detect_lib_modules()
mlog.debug('Boost library directory is', mlog.bold(self.libdir))
for m in self.requested_modules:
if 'boost_' + m not in self.lib_modules:
mlog.debug('Requested Boost library {!r} not found'.format(m))
self.log_fail()
self.is_found = False
return

# 3. Report success or failure
if self.is_found:
self.log_success()
else:
self.log_fail()
Expand All @@ -187,7 +177,6 @@ def check_invalid_modules(self):

if invalid_modules:
mlog.log(mlog.red('ERROR:'), 'Invalid Boost modules: ' + ', '.join(invalid_modules))
self.log_fail()
return True
else:
return False
Expand Down Expand Up @@ -275,7 +264,7 @@ def get_requested(self, kwargs):
raise DependencyException('Boost module argument is not a string.')
return candidates

def detect_version(self):
def detect_headers_and_version(self):
try:
version = self.compiler.get_define('BOOST_LIB_VERSION', '#include <boost/version.hpp>', self.env, self.get_compile_args(), [])
except mesonlib.EnvironmentException:
Expand All @@ -289,9 +278,23 @@ def detect_version(self):
self.is_found = True

def detect_lib_modules(self):
if mesonlib.is_windows():
return self.detect_lib_modules_win()
return self.detect_lib_modules_nix()
self.lib_modules = {}

# 1. Try to find modules using compiler.find_library( )
if self.find_libraries_with_abi_tags(self.abi_tags()):
pass
# 2. Fall back to the old method
else:
if mesonlib.for_windows(self.want_cross, self.env):
self.detect_lib_modules_win()
else:
self.detect_lib_modules_nix()

# 3. Check if we can find the modules
for m in self.requested_modules:
if 'boost_' + m not in self.lib_modules:
mlog.debug('Requested Boost library {!r} not found'.format(m))
self.is_found = False

def modname_from_filename(self, filename):
modname = os.path.basename(filename)
Expand All @@ -301,20 +304,94 @@ def modname_from_filename(self, filename):
modname = modname[3:]
return modname

def detect_lib_modules_win(self):
arch = detect_cpu_family(self.env.coredata.compilers)
def compiler_tag(self):
tag = None
compiler = self.env.detect_cpp_compiler(self.want_cross)
if mesonlib.for_windows(self.want_cross, self.env):
if compiler.get_id() == 'msvc':
comp_ts_version = compiler.get_toolset_version()
compiler_ts = comp_ts_version.split('.')
# FIXME - what about other compilers?
tag = '-vc{}{}'.format(compiler_ts[0], compiler_ts[1])
else:
tag = ''
return tag

def threading_tag(self):
if not self.is_multithreading:
return ''

if mesonlib.for_darwin(self.want_cross, self.env):
# - Mac: requires -mt for multithreading, so should not fall back to non-mt libraries.
return '-mt'
elif mesonlib.for_windows(self.want_cross, self.env):
# - Windows: requires -mt for multithreading, so should not fall back to non-mt libraries.
return '-mt'
else:
# - Linux: leaves off -mt but libraries are multithreading-aware.
# - Cygwin: leaves off -mt but libraries are multithreading-aware.
return ''

def version_tag(self):
return '-' + self.version.replace('.', '_')

def debug_tag(self):
return '-gd' if self.is_debug else ''

def versioned_abi_tag(self):
return self.compiler_tag() + self.threading_tag() + self.debug_tag() + self.version_tag()

# FIXME - how to handle different distributions, e.g. for Mac? Currently we handle homebrew and macports, but not fink.
def abi_tags(self):
if mesonlib.for_windows(self.want_cross, self.env):
return [self.versioned_abi_tag(), self.threading_tag()]
else:
return [self.threading_tag()]

def sourceforge_dir(self):
if self.env.detect_cpp_compiler(self.want_cross).get_id() != 'msvc':
return None
comp_ts_version = self.env.detect_cpp_compiler(self.want_cross).get_toolset_version()
compiler_ts = comp_ts_version.split('.')
compiler = 'vc{}{}'.format(compiler_ts[0], compiler_ts[1])
arch = detect_cpu_family(self.env.coredata.compilers)
if arch == 'x86':
return 'lib32-msvc-{}'.format(comp_ts_version)
elif arch == 'x86_64':
return 'lib64-msvc-{}'.format(comp_ts_version)
else:
# Does anyone do Boost cross-compiling to other archs on Windows?
return None

def find_libraries_with_abi_tag(self, tag):

# All modules should have the same tag
self.lib_modules = {}

all_found = True

for module in self.requested_modules:
libname = 'boost_' + module + tag

args = self.compiler.find_library(libname, self.env, self.extra_lib_dirs())
if args is None:
mlog.debug("Couldn\'t find library '{}' for boost module '{}' (ABI tag = '{}')".format(libname, module, tag))
all_found = False
else:
mlog.debug('Link args for boost module "{}" are {}'.format(module, args))
self.lib_modules['boost_' + module] = args

return all_found

def find_libraries_with_abi_tags(self, tags):
for tag in tags:
if self.find_libraries_with_abi_tag(tag):
return True
return False

def detect_lib_modules_win(self):
if not self.libdir:
# The libdirs in the distributed binaries (from sf)
if arch == 'x86':
lib_sf = 'lib32-msvc-{}'.format(comp_ts_version)
elif arch == 'x86_64':
lib_sf = 'lib64-msvc-{}'.format(comp_ts_version)
else:
# Does anyone do Boost cross-compiling to other archs on Windows?
lib_sf = None
lib_sf = self.sourceforge_dir()

if self.boost_root:
roots = [self.boost_root]
else:
Expand All @@ -335,12 +412,8 @@ def detect_lib_modules_win(self):
return

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('.', '_'))
# FIXME - why are we only looking for *.lib? Mingw provides *.dll.a and *.a
libname = 'lib' + name + self.versioned_abi_tag() + '.lib'
if os.path.isfile(os.path.join(self.libdir, libname)):
self.lib_modules[self.modname_from_filename(libname)] = [libname]
else:
Expand All @@ -351,52 +424,23 @@ def detect_lib_modules_win(self):
# 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 = globber1 + self.versioned_abi_tag()
# FIXME - why are we only looking for *.lib? Mingw provides *.dll.a and *.a
globber2_matches = glob.glob(os.path.join(self.libdir, globber2 + '.lib'))
for entry in globber2_matches:
fname = os.path.basename(entry)
self.lib_modules[self.modname_from_filename(fname)] = [fname]
if len(globber2_matches) == 0:
# FIXME - why are we only looking for *.lib? Mingw provides *.dll.a and *.a
for entry in glob.glob(os.path.join(self.libdir, globber1 + '.lib')):
if self.static:
fname = os.path.basename(entry)
self.lib_modules[self.modname_from_filename(fname)] = [fname]

# - Linux leaves off -mt but libraries are multithreading-aware.
# - Cygwin leaves off -mt but libraries are multithreading-aware.
# - Mac requires -mt for multithreading, so should not fall back
# to non-mt libraries.
def abi_tag(self):
if mesonlib.for_windows(self.want_cross, self.env):
return None
if self.is_multithreading and mesonlib.for_darwin(self.want_cross, self.env):
return '-mt'
else:
return ''

def detect_lib_modules_nix(self):
all_found = True
for module in self.requested_modules:
libname = 'boost_' + module + self.abi_tag()

args = self.compiler.find_library(libname, self.env, self.extra_lib_dirs())
if args is None:
mlog.debug('Couldn\'t find library "{}" for boost module "{}"'.format(module, libname))
all_found = False
else:
mlog.debug('Link args for boost module "{}" are {}'.format(module, args))
self.lib_modules['boost_' + module] = args
if all_found:
return

if self.static:
libsuffix = 'a'
elif mesonlib.is_osx() and not self.want_cross:
elif mesonlib.for_darwin(self.want_cross, self.env):
libsuffix = 'dylib'
else:
libsuffix = 'so'
Expand Down Expand Up @@ -442,7 +486,7 @@ def extra_lib_dirs(self):
def get_link_args(self):
args = []
for dir in self.extra_lib_dirs():
args += self.compiler.get_linker_search_args(self.libdir)
args += self.compiler.get_linker_search_args(dir)
for lib in self.requested_modules:
args += self.lib_modules['boost_' + lib]
return args
Expand Down