#!/usr/bin/env python # encoding: utf-8 # # partially based on boost.py written by Gernot Vormayr # written by Ruediger Sonderfeld , 2008 # modified by Bjoern Michaelsen, 2008 # modified by Luca Fossati, 2008 # rewritten for waf 1.5.1, Thomas Nagy, 2008 # rewritten for waf 1.6.2, Sylvain Rouquette, 2011 ''' 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: 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 build(bld): bld(source='main.cpp', target='app', use='BOOST') ''' import sys import re from waflib import Utils, Logs from waflib.Configure import conf from waflib.Errors import WafError BOOST_LIBS = ('/usr/lib', '/usr/local/lib', '/opt/local/lib', '/sw/lib', '/lib') BOOST_INCLUDES = ('/usr/include', '/usr/local/include', '/opt/local/include', '/sw/include') BOOST_VERSION_FILE = 'boost/version.hpp' BOOST_VERSION_CODE = ''' #include #include int main() { std::cout << BOOST_LIB_VERSION << std::endl; } ''' # toolsets from {boost_dir}/tools/build/v2/tools/common.jam PLATFORM = Utils.unversioned_sys_platform() detect_intel = lambda env: (PLATFORM == 'win32') and 'iw' or 'il' detect_clang = lambda env: (PLATFORM == 'darwin') and 'clang-darwin' or 'clang' detect_mingw = lambda env: (re.search('MinGW', env.CXX[0])) and 'mgw' or 'gcc' BOOST_TOOLSETS = { 'borland': 'bcb', 'clang': detect_clang, 'como': 'como', 'cw': 'cw', 'darwin': 'xgcc', 'edg': 'edg', 'g++': detect_mingw, 'gcc': detect_mingw, 'icpc': detect_intel, 'intel': detect_intel, 'kcc': 'kcc', 'kylix': 'bck', 'mipspro': 'mp', 'mingw': 'mgw', 'msvc': 'vc', 'qcc': 'qcc', 'sun': 'sw', 'sunc++': 'sw', 'tru64cxx': 'tru', 'vacpp': 'xlc' } 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''') 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''') opt.add_option('--boost-static', action='store_true', default=False, dest='boost_static', help='link static libraries') 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-toolset', type='string', default='', dest='boost_toolset', help='force a toolset e.g. msvc, vc90, \ gcc, mingw, mgw45 (default: auto)') py_version = '%d%d' % (sys.version_info[0], sys.version_info[1]) opt.add_option('--boost-python', type='string', default=py_version, dest='boost_python', help='select the lib python with this version \ (default: %s)' % py_version) @conf def __boost_get_version_file(self, dir): try: return self.root.find_dir(dir).find_node(BOOST_VERSION_FILE) except: return None @conf def boost_get_version(self, dir): """silently retrieve the boost version number""" re_but = re.compile('^#define\\s+BOOST_LIB_VERSION\\s+"(.*)"$', re.M) try: val = re_but.search(self.__boost_get_version_file(dir).read()).group(1) except: val = self.check_cxx(fragment=BOOST_VERSION_CODE, includes=[dir], execute=True, define_ret=True) return val @conf def boost_get_includes(self, *k, **kw): includes = k and k[0] or kw.get('includes', None) if includes and self.__boost_get_version_file(includes): return includes for dir in BOOST_INCLUDES: if self.__boost_get_version_file(dir): return dir if includes: self.fatal('headers not found in %s' % includes) else: self.fatal('headers not found, use --boost-includes=/path/to/boost') @conf def boost_get_toolset(self, cc): toolset = cc if not cc: build_platform = Utils.unversioned_sys_platform() if build_platform in BOOST_TOOLSETS: cc = build_platform else: cc = self.env.CXX_NAME if cc in BOOST_TOOLSETS: toolset = BOOST_TOOLSETS[cc] return isinstance(toolset, str) and toolset or toolset(self.env) @conf def __boost_get_libs_path(self, *k, **kw): ''' return the lib path and all the files in it ''' if 'files' in kw: return self.root.find_dir('.'), Utils.to_list(kw['files']) libs = k and k[0] or kw.get('libs', None) if libs: path = self.root.find_dir(libs) files = path.ant_glob('*boost_*') if not libs or not files: for dir in BOOST_LIBS: try: path = self.root.find_dir(dir) files = path.ant_glob('*boost_*') if files: break path = self.root.find_dir(dir + '64') files = path.ant_glob('*boost_*') if files: break except: path = None if not path: if libs: self.fatal('libs not found in %s' % libs) else: self.fatal('libs not found, \ use --boost-includes=/path/to/boost/lib') return path, files @conf def boost_get_libs(self, *k, **kw): ''' return the lib path and the required libs according to the parameters ''' path, files = self.__boost_get_libs_path(**kw) t = [] if kw.get('mt', False): t.append('mt') 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', '')) version = '(-%s)+' % self.env.BOOST_VERSION def find_lib(re_lib, files): for file in files: if re_lib.search(file.name): return file return None def format_lib_name(name): if name.startswith('lib'): name = name[3:] return name.split('.')[0] libs = [] 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), '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' % (lib, tags, py), 'boost_%s%s' % (lib, tags)]: file = find_lib(re.compile(pattern), files) if file: libs.append(format_lib_name(file.name)) break else: self.fatal('lib %s not found in %s' % (lib, path)) return path.abspath(), libs @conf def check_boost(self, *k, **kw): """ initialize boost You can pass the same parameters as the command line (without "--boost-"), but the command line has the priority. """ if not self.env['CXX']: self.fatal('load a c++ compiler first, conf.load("compiler_cxx")') params = {'lib': k and k[0] or kw.get('lib', None)} for key, value in self.options.__dict__.items(): if not key.startswith('boost_'): continue key = key[len('boost_'):] params[key] = value and value or kw.get(key, '') var = kw.get('uselib_store', 'BOOST') self.start_msg('Checking boost includes') try: self.env['INCLUDES_%s' % var] = self.boost_get_includes(**params) self.env.BOOST_VERSION = self.boost_get_version(self.env['INCLUDES_%s' % var]) except WafError: self.end_msg("not found", 'YELLOW') raise self.end_msg(self.env.BOOST_VERSION) if Logs.verbose: Logs.pprint('CYAN', ' path : %s' % self.env['INCLUDES_%s' % var]) if not params['lib']: return self.start_msg('Checking boost libs') try: suffix = params.get('static', 'ST') or '' path, libs = self.boost_get_libs(**params) except WafError: self.end_msg("not found", 'YELLOW') raise self.env['%sLIBPATH_%s' % (suffix, var)] = [path] self.env['%sLIB_%s' % (suffix, var)] = libs self.end_msg('ok') if Logs.verbose: Logs.pprint('CYAN', ' path : %s' % path) Logs.pprint('CYAN', ' libs : %s' % libs)