diff --git a/waflib/extras/boost.py b/waflib/extras/boost.py index e99bcac3..3b0772c4 100644 --- a/waflib/extras/boost.py +++ b/waflib/extras/boost.py @@ -9,27 +9,48 @@ # rewritten for waf 1.6.2, Sylvain Rouquette, 2011 ''' + +This is an extra tool, not bundled with the default waf binary. To add the boost tool to the waf file: $ ./waf-light --tools=compat15,boost or, if you have waf >= 1.6.2 $ ./waf update --files=boost -The wscript will look like: +When using this tool, the wscript will look like: -def options(opt): - opt.load('compiler_cxx boost') + def options(opt): + opt.load('compiler_cxx boost') -def configure(conf): - conf.load('compiler_cxx boost') - conf.check_boost(lib='system filesystem', mt=True, static=True) + def configure(conf): + conf.load('compiler_cxx boost') + conf.check_boost(lib='system filesystem') + + def build(bld): + bld(source='main.cpp', target='app', use='BOOST') + +Options are generated, in order to specify the location of boost includes/libraries. +The `check_boost` configuration function allows to specify the used boost libraries. +It can also provide default arguments to the --boost-static and --boost-mt command-line arguments. +Everything will be packaged together in a BOOST component that you can use. + +When using MSVC, a lot of compilation flags need to match your BOOST build configuration: + - you may have to add /EHsc to your CXXFLAGS or define boost::throw_exception if BOOST_NO_EXCEPTIONS is defined. + Errors: C4530 + - boost libraries will try to be smart and use the (pretty but often not useful) auto-linking feature of MSVC + So before calling `conf.check_boost` you might want to disabling by adding: + conf.env.DEFINES_BOOST += ['BOOST_ALL_NO_LIB'] + Errors: + - boost might also be compiled with /MT, which links the runtime statically. + If you have problems with redefined symbols, + self.env['DEFINES_%s' % var] += ['BOOST_ALL_NO_LIB'] + self.env['CXXFLAGS_%s' % var] += ['/MD', '/EHsc'] +Passing `--boost-linkage_autodetect` might help ensuring having a correct linkage in some basic cases. -def build(bld): - bld(source='main.cpp', target='app', use='BOOST') ''' import sys import re -from waflib import Utils, Logs +from waflib import Utils, Logs, Errors from waflib.Configure import conf BOOST_LIBS = ['/usr/lib', '/usr/local/lib', '/opt/local/lib', '/sw/lib', '/lib'] @@ -73,21 +94,23 @@ BOOST_TOOLSETS = { def options(opt): opt.add_option('--boost-includes', type='string', default='', dest='boost_includes', - help='''path to the boost directory where the includes are - e.g. /boost_1_45_0/include''') + help='''path to the boost includes root (~boost root) + e.g. /path/to/boost_1_47_0''') opt.add_option('--boost-libs', type='string', default='', dest='boost_libs', help='''path to the directory where the boost libs are - e.g. /boost_1_45_0/stage/lib''') + e.g. /path/to/boost_1_47_0/stage/lib''') opt.add_option('--boost-static', action='store_true', default=False, dest='boost_static', - help='link static libraries') + help='link with static boost libraries (.lib/.a)') opt.add_option('--boost-mt', action='store_true', default=False, dest='boost_mt', help='select multi-threaded libraries') opt.add_option('--boost-abi', type='string', default='', dest='boost_abi', help='''select libraries with tags (dgsyp, d for debug), see doc Boost, Getting Started, chapter 6.1''') + opt.add_option('--boost-linkage_autodetect', action="store_true", dest='boost_linkage_autodetect', + help="auto-detect boost linkage options (don't get used to it / might break other stuff)") opt.add_option('--boost-toolset', type='string', default='', dest='boost_toolset', help='force a toolset e.g. msvc, vc90, \ @@ -130,7 +153,7 @@ def boost_get_includes(self, *k, **kw): if includes: self.fatal('headers not found in %s' % includes) else: - self.fatal('headers not found, use --boost-includes=/path/to/boost') + self.fatal('headers not found, please provide a --boost-includes argument (see help)') @conf @@ -173,7 +196,7 @@ def __boost_get_libs_path(self, *k, **kw): if libs: self.fatal('libs not found in %s' % libs) else: - self.fatal('libs not found, use --boost-includes=/path/to/boost/lib') + self.fatal('libs not found, please provide a --boost-libs argument (see help)') self.to_log('Found the boost path in %r with the libraries:' % path) for x in files: @@ -193,7 +216,8 @@ def boost_get_libs(self, *k, **kw): if kw.get('abi', None): t.append(kw['abi']) tags = t and '(-%s)+' % '-'.join(t) or '' - toolset = '(-%s[0-9]{0,3})+' % self.boost_get_toolset(kw.get('toolset', '')) + toolset = self.boost_get_toolset(kw.get('toolset', '')) + toolset_pat = '(-%s[0-9]{0,3})+' % toolset version = '(-%s)+' % self.env.BOOST_VERSION def find_lib(re_lib, files): @@ -212,11 +236,11 @@ def boost_get_libs(self, *k, **kw): for lib in Utils.to_list(k and k[0] or kw.get('lib', None)): py = (lib == 'python') and '(-py%s)+' % kw['python'] or '' # Trying libraries, from most strict match to least one - for pattern in ['boost_%s%s%s%s%s' % (lib, toolset, tags, py, version), + for pattern in ['boost_%s%s%s%s%s' % (lib, toolset_pat, tags, py, version), 'boost_%s%s%s%s' % (lib, tags, py, version), 'boost_%s%s%s' % (lib, tags, version), # Give up trying to find the right version - 'boost_%s%s%s%s' % (lib, toolset, tags, py), + 'boost_%s%s%s%s' % (lib, toolset_pat, tags, py), 'boost_%s%s%s' % (lib, tags, py), 'boost_%s%s' % (lib, tags)]: self.to_log('Trying pattern %s' % pattern) @@ -225,7 +249,7 @@ def boost_get_libs(self, *k, **kw): libs.append(format_lib_name(file.name)) break else: - self.fatal('lib %s not found in %s' % (lib, path)) + self.fatal('lib %s not found in %s' % (lib, path.abspath())) return path.abspath(), libs @@ -233,10 +257,10 @@ def boost_get_libs(self, *k, **kw): @conf def check_boost(self, *k, **kw): """ - initialize boost + Initialize boost libraries to be used. - You can pass the same parameters as the command line (without "--boost-"), - but the command line has the priority. + Keywords: you can pass the same parameters as with the command line (without "--boost-"). + Note that the command line has the priority, and should preferably be used. """ if not self.env['CXX']: self.fatal('load a c++ compiler first, conf.load("compiler_cxx")') @@ -251,8 +275,8 @@ def check_boost(self, *k, **kw): var = kw.get('uselib_store', 'BOOST') self.start_msg('Checking boost includes') - self.env['INCLUDES_%s' % var] = self.boost_get_includes(**params) - self.env.BOOST_VERSION = self.boost_get_version(self.env['INCLUDES_%s' % var]) + self.env['INCLUDES_%s' % var] = inc = self.boost_get_includes(**params) + self.env.BOOST_VERSION = self.boost_get_version(inc) self.end_msg(self.env.BOOST_VERSION) if Logs.verbose: Logs.pprint('CYAN', ' path : %s' % self.env['INCLUDES_%s' % var]) @@ -269,3 +293,70 @@ def check_boost(self, *k, **kw): Logs.pprint('CYAN', ' path : %s' % path) Logs.pprint('CYAN', ' libs : %s' % libs) + + def try_link(): + if 'system' in params['lib']: + self.check_cxx( + fragment="\n".join([ + '#include ', + 'int main() { boost::system::error_code c; }', + ]), + use='BOOST', + execute=True, + ) + if 'thread' in params['lib']: + self.check_cxx( + fragment="\n".join([ + '#include ', + 'int main() { boost::thread t; }', + ]), + use='BOOST', + execute=True, + ) + + if params.get('linkage_autodetect', False): + self.start_msg("Attempting to detect boost linkage flags") + toolset = self.boost_get_toolset(kw.get('toolset', '')) + if toolset in ['vc']: + # disable auto-linking feature, causing error LNK1181 + # because the code wants to be linked against + self.env['DEFINES_%s' % var] += ['BOOST_ALL_NO_LIB'] + + # if no dlls are present, we guess the .lib files are not stubs + has_dlls = False + for x in Utils.listdir(path): + if x.endswith(self.env.cxxshlib_PATTERN % ''): + has_dlls = True + break + if not has_dlls: + self.env['STLIBPATH_%s' % var] = [path] + self.env['STLIB_%s' % var] = libs + del self.env['LIB_%s' % var] + del self.env['LIBPATH_%s' % var] + + # we attempt to play with some known-to-work CXXFLAGS combinations + for cxxflags in (['/MD', '/EHsc'], []): + self.env.stash() + self.env["CXXFLAGS_%s" % var] += cxxflags + try: + try_link() + self.end_msg("ok: winning cxxflags combination: %s" % (self.env["CXXFLAGS_%s" % var])) + e = None + break + except Errors.ConfigurationError as exc: + self.env.revert() + e = exc + + if e is not None: + self.fatal("Could not auto-detect boost linking flags combination, you may report it to boost.py author", ex=e) + else: + self.fatal("Boost linkage flags auto-detection not implemented (needed ?) for this toolchain") + else: + self.start_msg('Checking for boost linkage') + try: + try_link() + except Errors.ConfigurationError as e: + self.fatal("Could not link against boost libraries using supplied options") + self.end_msg('ok') + +