blob: 9e2c0bdcc37d55011d167f702582e7bb27954639 [file] [log] [blame]
Alexander Afanasyev2aa39622014-01-22 11:51:11 -08001#!/usr/bin/env python
2# encoding: utf-8
3#
4# partially based on boost.py written by Gernot Vormayr
5# written by Ruediger Sonderfeld <ruediger@c-plusplus.de>, 2008
6# modified by Bjoern Michaelsen, 2008
7# modified by Luca Fossati, 2008
8# rewritten for waf 1.5.1, Thomas Nagy, 2008
9# rewritten for waf 1.6.2, Sylvain Rouquette, 2011
10
11'''
12
13This is an extra tool, not bundled with the default waf binary.
14To add the boost tool to the waf file:
15$ ./waf-light --tools=compat15,boost
16 or, if you have waf >= 1.6.2
17$ ./waf update --files=boost
18
19When using this tool, the wscript will look like:
20
21 def options(opt):
22 opt.load('compiler_cxx boost')
23
24 def configure(conf):
25 conf.load('compiler_cxx boost')
26 conf.check_boost(lib='system filesystem')
27
28 def build(bld):
29 bld(source='main.cpp', target='app', use='BOOST')
30
31Options are generated, in order to specify the location of boost includes/libraries.
32The `check_boost` configuration function allows to specify the used boost libraries.
Alexander Afanasyeve6f0e912016-02-25 23:42:36 -080033It can also provide default arguments to the --boost-mt command-line arguments.
Alexander Afanasyev2aa39622014-01-22 11:51:11 -080034Everything will be packaged together in a BOOST component that you can use.
35
36When using MSVC, a lot of compilation flags need to match your BOOST build configuration:
37 - you may have to add /EHsc to your CXXFLAGS or define boost::throw_exception if BOOST_NO_EXCEPTIONS is defined.
38 Errors: C4530
39 - boost libraries will try to be smart and use the (pretty but often not useful) auto-linking feature of MSVC
Alexander Afanasyeve6f0e912016-02-25 23:42:36 -080040 So before calling `conf.check_boost` you might want to disabling by adding
41 conf.env.DEFINES_BOOST += ['BOOST_ALL_NO_LIB']
Alexander Afanasyeve1724c42014-02-26 22:00:54 -080042 Errors:
Alexander Afanasyev2aa39622014-01-22 11:51:11 -080043 - boost might also be compiled with /MT, which links the runtime statically.
Alexander Afanasyeve1724c42014-02-26 22:00:54 -080044 If you have problems with redefined symbols,
Alexander Afanasyev2aa39622014-01-22 11:51:11 -080045 self.env['DEFINES_%s' % var] += ['BOOST_ALL_NO_LIB']
46 self.env['CXXFLAGS_%s' % var] += ['/MD', '/EHsc']
47Passing `--boost-linkage_autodetect` might help ensuring having a correct linkage in some basic cases.
48
49'''
50
51import sys
52import re
53from waflib import Utils, Logs, Errors
54from waflib.Configure import conf
Alexander Afanasyeve6f0e912016-02-25 23:42:36 -080055from waflib.TaskGen import feature, after_method
Alexander Afanasyev2aa39622014-01-22 11:51:11 -080056
Alexander Afanasyeve6f0e912016-02-25 23:42:36 -080057BOOST_LIBS = ['/usr/lib/x86_64-linux-gnu', '/usr/lib/i386-linux-gnu',
58 '/usr/lib', '/usr/local/lib', '/opt/local/lib', '/sw/lib', '/lib']
59BOOST_INCLUDES = ['/usr/include', '/usr/local/include', '/opt/local/include', '/sw/include']
Alexander Afanasyev2aa39622014-01-22 11:51:11 -080060BOOST_VERSION_FILE = 'boost/version.hpp'
61BOOST_VERSION_CODE = '''
62#include <iostream>
63#include <boost/version.hpp>
Alexander Afanasyeve1724c42014-02-26 22:00:54 -080064int main() { std::cout << BOOST_LIB_VERSION << ":" << BOOST_VERSION << std::endl; }
Alexander Afanasyev2aa39622014-01-22 11:51:11 -080065'''
Alexander Afanasyeve6f0e912016-02-25 23:42:36 -080066
67BOOST_ERROR_CODE = '''
Davide Pesavento1bdef282014-04-08 20:59:50 +020068#include <boost/system/error_code.hpp>
69int main() { boost::system::error_code c; }
70'''
Alexander Afanasyeve6f0e912016-02-25 23:42:36 -080071
Davide Pesavento1bdef282014-04-08 20:59:50 +020072BOOST_THREAD_CODE = '''
73#include <boost/thread.hpp>
74int main() { boost::thread t; }
75'''
Alexander Afanasyev2aa39622014-01-22 11:51:11 -080076
Alexander Afanasyeve6f0e912016-02-25 23:42:36 -080077BOOST_LOG_CODE = '''
78#include <boost/log/trivial.hpp>
79#include <boost/log/utility/setup/console.hpp>
80#include <boost/log/utility/setup/common_attributes.hpp>
81int main() {
82 using namespace boost::log;
83 add_common_attributes();
84 add_console_log(std::clog, keywords::format = "%Message%");
85 BOOST_LOG_TRIVIAL(debug) << "log is working" << std::endl;
86}
87'''
88
Alexander Afanasyev2aa39622014-01-22 11:51:11 -080089# toolsets from {boost_dir}/tools/build/v2/tools/common.jam
90PLATFORM = Utils.unversioned_sys_platform()
91detect_intel = lambda env: (PLATFORM == 'win32') and 'iw' or 'il'
92detect_clang = lambda env: (PLATFORM == 'darwin') and 'clang-darwin' or 'clang'
93detect_mingw = lambda env: (re.search('MinGW', env.CXX[0])) and 'mgw' or 'gcc'
94BOOST_TOOLSETS = {
95 'borland': 'bcb',
96 'clang': detect_clang,
97 'como': 'como',
98 'cw': 'cw',
99 'darwin': 'xgcc',
100 'edg': 'edg',
101 'g++': detect_mingw,
102 'gcc': detect_mingw,
103 'icpc': detect_intel,
104 'intel': detect_intel,
105 'kcc': 'kcc',
106 'kylix': 'bck',
107 'mipspro': 'mp',
108 'mingw': 'mgw',
109 'msvc': 'vc',
110 'qcc': 'qcc',
111 'sun': 'sw',
112 'sunc++': 'sw',
113 'tru64cxx': 'tru',
114 'vacpp': 'xlc'
115}
116
117
118def options(opt):
119 opt = opt.add_option_group('Boost Options')
Alexander Afanasyev2aa39622014-01-22 11:51:11 -0800120 opt.add_option('--boost-includes', type='string',
121 default='', dest='boost_includes',
Alexander Afanasyeve6f0e912016-02-25 23:42:36 -0800122 help='''path to the directory where the boost includes are,
123 e.g., /path/to/boost_1_55_0/stage/include''')
Alexander Afanasyev2aa39622014-01-22 11:51:11 -0800124 opt.add_option('--boost-libs', type='string',
125 default='', dest='boost_libs',
Alexander Afanasyeve6f0e912016-02-25 23:42:36 -0800126 help='''path to the directory where the boost libs are,
127 e.g., path/to/boost_1_55_0/stage/lib''')
Alexander Afanasyev2aa39622014-01-22 11:51:11 -0800128 opt.add_option('--boost-mt', action='store_true',
129 default=False, dest='boost_mt',
130 help='select multi-threaded libraries')
131 opt.add_option('--boost-abi', type='string', default='', dest='boost_abi',
Alexander Afanasyeve6f0e912016-02-25 23:42:36 -0800132 help='''select libraries with tags (gd for debug, static is automatically added),
133 see doc Boost, Getting Started, chapter 6.1''')
Alexander Afanasyev2aa39622014-01-22 11:51:11 -0800134 opt.add_option('--boost-linkage_autodetect', action="store_true", dest='boost_linkage_autodetect',
135 help="auto-detect boost linkage options (don't get used to it / might break other stuff)")
136 opt.add_option('--boost-toolset', type='string',
137 default='', dest='boost_toolset',
Alexander Afanasyeve6f0e912016-02-25 23:42:36 -0800138 help='force a toolset e.g. msvc, vc90, \
139 gcc, mingw, mgw45 (default: auto)')
Alexander Afanasyev2aa39622014-01-22 11:51:11 -0800140 py_version = '%d%d' % (sys.version_info[0], sys.version_info[1])
141 opt.add_option('--boost-python', type='string',
142 default=py_version, dest='boost_python',
Alexander Afanasyeve6f0e912016-02-25 23:42:36 -0800143 help='select the lib python with this version \
144 (default: %s)' % py_version)
Alexander Afanasyev2aa39622014-01-22 11:51:11 -0800145
146
147@conf
148def __boost_get_version_file(self, d):
Alexander Afanasyeve6f0e912016-02-25 23:42:36 -0800149 if not d:
150 return None
Alexander Afanasyev2aa39622014-01-22 11:51:11 -0800151 dnode = self.root.find_dir(d)
152 if dnode:
153 return dnode.find_node(BOOST_VERSION_FILE)
154 return None
155
156@conf
157def boost_get_version(self, d):
158 """silently retrieve the boost version number"""
159 node = self.__boost_get_version_file(d)
160 if node:
161 try:
162 txt = node.read()
Alexander Afanasyeve6f0e912016-02-25 23:42:36 -0800163 except EnvironmentError:
Alexander Afanasyev2aa39622014-01-22 11:51:11 -0800164 Logs.error("Could not read the file %r" % node.abspath())
165 else:
Davide Pesavento1bdef282014-04-08 20:59:50 +0200166 re_but1 = re.compile('^#define\\s+BOOST_LIB_VERSION\\s+"(.+)"', re.M)
Alexander Afanasyeve1724c42014-02-26 22:00:54 -0800167 m1 = re_but1.search(txt)
Davide Pesavento1bdef282014-04-08 20:59:50 +0200168 re_but2 = re.compile('^#define\\s+BOOST_VERSION\\s+(\\d+)', re.M)
Alexander Afanasyeve1724c42014-02-26 22:00:54 -0800169 m2 = re_but2.search(txt)
Alexander Afanasyeve1724c42014-02-26 22:00:54 -0800170 if m1 and m2:
171 return (m1.group(1), m2.group(1))
Alexander Afanasyeve1724c42014-02-26 22:00:54 -0800172 return self.check_cxx(fragment=BOOST_VERSION_CODE, includes=[d], execute=True, define_ret=True).split(":")
Alexander Afanasyev2aa39622014-01-22 11:51:11 -0800173
174@conf
175def boost_get_includes(self, *k, **kw):
176 includes = k and k[0] or kw.get('includes', None)
177 if includes and self.__boost_get_version_file(includes):
178 return includes
Alexander Afanasyeve6f0e912016-02-25 23:42:36 -0800179 for d in self.environ.get('INCLUDE', '').split(';') + BOOST_INCLUDES:
Alexander Afanasyev2aa39622014-01-22 11:51:11 -0800180 if self.__boost_get_version_file(d):
181 return d
182 if includes:
183 self.end_msg('headers not found in %s' % includes)
184 self.fatal('The configuration failed')
185 else:
186 self.end_msg('headers not found, please provide a --boost-includes argument (see help)')
187 self.fatal('The configuration failed')
188
189
190@conf
191def boost_get_toolset(self, cc):
192 toolset = cc
193 if not cc:
194 build_platform = Utils.unversioned_sys_platform()
195 if build_platform in BOOST_TOOLSETS:
196 cc = build_platform
197 else:
198 cc = self.env.CXX_NAME
199 if cc in BOOST_TOOLSETS:
200 toolset = BOOST_TOOLSETS[cc]
201 return isinstance(toolset, str) and toolset or toolset(self.env)
202
203
204@conf
205def __boost_get_libs_path(self, *k, **kw):
206 ''' return the lib path and all the files in it '''
207 if 'files' in kw:
208 return self.root.find_dir('.'), Utils.to_list(kw['files'])
209 libs = k and k[0] or kw.get('libs', None)
210 if libs:
211 path = self.root.find_dir(libs)
212 files = path.ant_glob('*boost_*')
213 if not libs or not files:
Alexander Afanasyeve6f0e912016-02-25 23:42:36 -0800214 for d in self.environ.get('LIB', '').split(';') + BOOST_LIBS:
215 if not d:
216 continue
Alexander Afanasyev2aa39622014-01-22 11:51:11 -0800217 path = self.root.find_dir(d)
218 if path:
219 files = path.ant_glob('*boost_*')
220 if files:
221 break
222 path = self.root.find_dir(d + '64')
223 if path:
224 files = path.ant_glob('*boost_*')
225 if files:
226 break
227 if not path:
228 if libs:
229 self.end_msg('libs not found in %s' % libs)
230 self.fatal('The configuration failed')
231 else:
232 self.end_msg('libs not found, please provide a --boost-libs argument (see help)')
233 self.fatal('The configuration failed')
234
235 self.to_log('Found the boost path in %r with the libraries:' % path)
236 for x in files:
237 self.to_log(' %r' % x)
238 return path, files
239
240@conf
241def boost_get_libs(self, *k, **kw):
242 '''
243 return the lib path and the required libs
244 according to the parameters
245 '''
246 path, files = self.__boost_get_libs_path(**kw)
Alexander Afanasyeve6f0e912016-02-25 23:42:36 -0800247 files = sorted(files, key=lambda f: (len(f.name), f.name), reverse=True)
Alexander Afanasyev2aa39622014-01-22 11:51:11 -0800248 toolset = self.boost_get_toolset(kw.get('toolset', ''))
Alexander Afanasyeve6f0e912016-02-25 23:42:36 -0800249 toolset_pat = '(-%s[0-9]{0,3})' % toolset
250 version = '-%s' % self.env.BOOST_VERSION
Alexander Afanasyev2aa39622014-01-22 11:51:11 -0800251
252 def find_lib(re_lib, files):
253 for file in files:
254 if re_lib.search(file.name):
255 self.to_log('Found boost lib %s' % file)
256 return file
257 return None
258
259 def format_lib_name(name):
260 if name.startswith('lib') and self.env.CC_NAME != 'msvc':
261 name = name[3:]
262 return name[:name.rfind('.')]
263
Alexander Afanasyeve6f0e912016-02-25 23:42:36 -0800264 def match_libs(lib_names, is_static):
265 libs = []
266 lib_names = Utils.to_list(lib_names)
267 if not lib_names:
268 return libs
269 t = []
270 if kw.get('mt', False):
271 t.append('-mt')
272 if kw.get('abi', None):
273 t.append('%s%s' % (is_static and '-s' or '-', kw['abi']))
274 elif is_static:
275 t.append('-s')
276 tags_pat = t and ''.join(t) or ''
277 ext = is_static and self.env.cxxstlib_PATTERN or self.env.cxxshlib_PATTERN
278 ext = ext.partition('%s')[2] # remove '%s' or 'lib%s' from PATTERN
Alexander Afanasyev2aa39622014-01-22 11:51:11 -0800279
Alexander Afanasyeve6f0e912016-02-25 23:42:36 -0800280 for lib in lib_names:
281 if lib == 'python':
282 # for instance, with python='27',
283 # accepts '-py27', '-py2', '27' and '2'
284 # but will reject '-py3', '-py26', '26' and '3'
285 tags = '({0})?((-py{2})|(-py{1}(?=[^0-9]))|({2})|({1}(?=[^0-9]))|(?=[^0-9])(?!-py))'.format(tags_pat, kw['python'][0], kw['python'])
286 else:
287 tags = tags_pat
288 # Trying libraries, from most strict match to least one
289 for pattern in ['boost_%s%s%s%s%s$' % (lib, toolset_pat, tags, version, ext),
290 'boost_%s%s%s%s$' % (lib, tags, version, ext),
291 # Give up trying to find the right version
292 'boost_%s%s%s%s$' % (lib, toolset_pat, tags, ext),
293 'boost_%s%s%s$' % (lib, tags, ext),
294 'boost_%s%s$' % (lib, ext),
295 'boost_%s' % lib]:
296 self.to_log('Trying pattern %s' % pattern)
297 file = find_lib(re.compile(pattern), files)
298 if file:
299 libs.append(format_lib_name(file.name))
300 break
301 else:
302 self.end_msg('lib %s not found in %s' % (lib, path.abspath()))
303 self.fatal('The configuration failed')
304 return libs
305
306 return path.abspath(), match_libs(kw.get('lib', None), False), match_libs(kw.get('stlib', None), True)
Alexander Afanasyev2aa39622014-01-22 11:51:11 -0800307
308
309@conf
310def check_boost(self, *k, **kw):
311 """
312 Initialize boost libraries to be used.
313
314 Keywords: you can pass the same parameters as with the command line (without "--boost-").
315 Note that the command line has the priority, and should preferably be used.
316 """
317 if not self.env['CXX']:
318 self.fatal('load a c++ compiler first, conf.load("compiler_cxx")')
319
Alexander Afanasyeve6f0e912016-02-25 23:42:36 -0800320 params = {
321 'lib': k and k[0] or kw.get('lib', None),
322 'stlib': kw.get('stlib', None)
323 }
Alexander Afanasyev2aa39622014-01-22 11:51:11 -0800324 for key, value in self.options.__dict__.items():
325 if not key.startswith('boost_'):
326 continue
327 key = key[len('boost_'):]
328 params[key] = value and value or kw.get(key, '')
329
330 var = kw.get('uselib_store', 'BOOST')
331
332 self.start_msg('Checking boost includes')
333 self.env['INCLUDES_%s' % var] = inc = self.boost_get_includes(**params)
Alexander Afanasyeve1724c42014-02-26 22:00:54 -0800334 versions = self.boost_get_version(inc)
335 self.env.BOOST_VERSION = versions[0]
336 self.env.BOOST_VERSION_NUMBER = int(versions[1])
337 self.end_msg("%d.%d.%d" % (int(versions[1]) / 100000,
Alexander Afanasyeve6f0e912016-02-25 23:42:36 -0800338 int(versions[1]) / 100 % 1000,
339 int(versions[1]) % 100))
Alexander Afanasyev2aa39622014-01-22 11:51:11 -0800340 if Logs.verbose:
341 Logs.pprint('CYAN', ' path : %s' % self.env['INCLUDES_%s' % var])
342
Alexander Afanasyeve6f0e912016-02-25 23:42:36 -0800343 if not params['lib'] and not params['stlib']:
Alexander Afanasyev2aa39622014-01-22 11:51:11 -0800344 return
Alexander Afanasyeve6f0e912016-02-25 23:42:36 -0800345 if 'static' in kw or 'static' in params:
346 Logs.warn('boost: static parameter is deprecated, use stlib instead.')
Alexander Afanasyev2aa39622014-01-22 11:51:11 -0800347 self.start_msg('Checking boost libs')
Alexander Afanasyeve6f0e912016-02-25 23:42:36 -0800348 path, libs, stlibs = self.boost_get_libs(**params)
349 self.env['LIBPATH_%s' % var] = [path]
350 self.env['STLIBPATH_%s' % var] = [path]
351 self.env['LIB_%s' % var] = libs
352 self.env['STLIB_%s' % var] = stlibs
Alexander Afanasyev2aa39622014-01-22 11:51:11 -0800353 self.end_msg('ok')
354 if Logs.verbose:
355 Logs.pprint('CYAN', ' path : %s' % path)
Alexander Afanasyeve6f0e912016-02-25 23:42:36 -0800356 Logs.pprint('CYAN', ' shared libs : %s' % libs)
357 Logs.pprint('CYAN', ' static libs : %s' % stlibs)
Alexander Afanasyev2aa39622014-01-22 11:51:11 -0800358
359
360 def try_link():
Alexander Afanasyeve6f0e912016-02-25 23:42:36 -0800361 if (params['lib'] and 'system' in params['lib']) or \
362 params['stlib'] and 'system' in params['stlib']:
363 self.check_cxx(fragment=BOOST_ERROR_CODE, use=var, execute=False)
364 if (params['lib'] and 'thread' in params['lib']) or \
365 params['stlib'] and 'thread' in params['stlib']:
366 self.check_cxx(fragment=BOOST_THREAD_CODE, use=var, execute=False)
367
368 def is_log_mt():
369 '''Check if found boost_log library is multithread-safe'''
370 for lib in libs:
371 if lib.startswith('boost_log'):
372 lib_log = lib
373 break
374 return '-mt' in lib_log
375
376 if params['lib'] and 'log' in params['lib']:
377 self.env['DEFINES_%s' % var] += ['BOOST_LOG_DYN_LINK']
378 if not is_log_mt():
379 self.env['DEFINES_%s' % var] += ['BOOST_LOG_NO_THREADS']
380 self.check_cxx(fragment=BOOST_LOG_CODE, use=var, execute=False)
381 if params['stlib'] and 'log' in params['stlib']:
382 # Static linking is assumed by default
383 if not is_log_mt():
384 self.env['DEFINES_%s' % var] += ['BOOST_LOG_NO_THREADS']
385 self.check_cxx(fragment=BOOST_LOG_CODE, use=var, execute=False)
Alexander Afanasyev2aa39622014-01-22 11:51:11 -0800386
387 if params.get('linkage_autodetect', False):
388 self.start_msg("Attempting to detect boost linkage flags")
389 toolset = self.boost_get_toolset(kw.get('toolset', ''))
Alexander Afanasyeve6f0e912016-02-25 23:42:36 -0800390 if toolset in ('vc',):
Alexander Afanasyev2aa39622014-01-22 11:51:11 -0800391 # disable auto-linking feature, causing error LNK1181
392 # because the code wants to be linked against
393 self.env['DEFINES_%s' % var] += ['BOOST_ALL_NO_LIB']
394
395 # if no dlls are present, we guess the .lib files are not stubs
396 has_dlls = False
397 for x in Utils.listdir(path):
398 if x.endswith(self.env.cxxshlib_PATTERN % ''):
399 has_dlls = True
400 break
401 if not has_dlls:
402 self.env['STLIBPATH_%s' % var] = [path]
403 self.env['STLIB_%s' % var] = libs
404 del self.env['LIB_%s' % var]
405 del self.env['LIBPATH_%s' % var]
406
407 # we attempt to play with some known-to-work CXXFLAGS combinations
408 for cxxflags in (['/MD', '/EHsc'], []):
409 self.env.stash()
410 self.env["CXXFLAGS_%s" % var] += cxxflags
411 try:
412 try_link()
413 self.end_msg("ok: winning cxxflags combination: %s" % (self.env["CXXFLAGS_%s" % var]))
Alexander Afanasyeve6f0e912016-02-25 23:42:36 -0800414 exc = None
Alexander Afanasyev2aa39622014-01-22 11:51:11 -0800415 break
Alexander Afanasyeve6f0e912016-02-25 23:42:36 -0800416 except Errors.ConfigurationError as e:
Alexander Afanasyev2aa39622014-01-22 11:51:11 -0800417 self.env.revert()
Alexander Afanasyeve6f0e912016-02-25 23:42:36 -0800418 exc = e
Alexander Afanasyev2aa39622014-01-22 11:51:11 -0800419
Alexander Afanasyeve6f0e912016-02-25 23:42:36 -0800420 if exc is not None:
421 self.end_msg("Could not auto-detect boost linking flags combination, you may report it to boost.py author", ex=exc)
Alexander Afanasyev2aa39622014-01-22 11:51:11 -0800422 self.fatal('The configuration failed')
423 else:
424 self.end_msg("Boost linkage flags auto-detection not implemented (needed ?) for this toolchain")
425 self.fatal('The configuration failed')
426 else:
427 self.start_msg('Checking for boost linkage')
428 try:
429 try_link()
430 except Errors.ConfigurationError as e:
431 self.end_msg("Could not link against boost libraries using supplied options")
432 self.fatal('The configuration failed')
433 self.end_msg('ok')
Alexander Afanasyeve6f0e912016-02-25 23:42:36 -0800434
435
436@feature('cxx')
437@after_method('apply_link')
438def install_boost(self):
439 if install_boost.done or not Utils.is_win32 or not self.bld.cmd.startswith('install'):
440 return
441 install_boost.done = True
442 inst_to = getattr(self, 'install_path', '${BINDIR}')
443 for lib in self.env.LIB_BOOST:
444 try:
445 file = self.bld.find_file(self.env.cxxshlib_PATTERN % lib, self.env.LIBPATH_BOOST)
446 self.bld.install_files(inst_to, self.bld.root.find_node(file))
447 except:
448 continue
449install_boost.done = False