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
13 This is an extra tool, not bundled with the default waf binary.
14 To 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
19 When using this tool, the wscript will look like:
22 opt.load('compiler_cxx boost')
25 conf.load('compiler_cxx boost')
26 conf.check_boost(lib='system filesystem')
29 bld(source='main.cpp', target='app', use='BOOST')
31 Options are generated, in order to specify the location of boost includes/libraries.
32 The `check_boost` configuration function allows to specify the used boost libraries.
33 It can also provide default arguments to the --boost-mt command-line arguments.
34 Everything will be packaged together in a BOOST component that you can use.
36 When 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.
39 - boost libraries will try to be smart and use the (pretty but often not useful) auto-linking feature of MSVC
40 So before calling `conf.check_boost` you might want to disabling by adding
41 conf.env.DEFINES_BOOST += ['BOOST_ALL_NO_LIB']
43 - boost might also be compiled with /MT, which links the runtime statically.
44 If you have problems with redefined symbols,
45 self.env['DEFINES_%s' % var] += ['BOOST_ALL_NO_LIB']
46 self.env['CXXFLAGS_%s' % var] += ['/MD', '/EHsc']
47 Passing `--boost-linkage_autodetect` might help ensuring having a correct linkage in some basic cases.
53 from waflib
import Utils
, Logs
, Errors
54 from waflib
.Configure
import conf
55 from waflib
.TaskGen
import feature
, after_method
57 BOOST_LIBS
= ['/usr/lib', '/usr/local/lib', '/opt/local/lib', '/sw/lib', '/lib']
58 BOOST_INCLUDES
= ['/usr/include', '/usr/local/include', '/opt/local/include', '/sw/include']
59 BOOST_VERSION_FILE
= 'boost/version.hpp'
60 BOOST_VERSION_CODE
= '''
62 #include <boost/version.hpp>
63 int main() { std::cout << BOOST_LIB_VERSION << ":" << BOOST_VERSION << std::endl; }
66 BOOST_ERROR_CODE
= '''
67 #include <boost/system/error_code.hpp>
68 int main() { boost::system::error_code c; }
73 static void* f(void*) { return 0; }
77 pthread_attr_init(&attr);
78 pthread_create(&th, &attr, &f, 0);
80 pthread_cleanup_push(0, 0);
81 pthread_cleanup_pop(0);
82 pthread_attr_destroy(&attr);
86 BOOST_THREAD_CODE
= '''
87 #include <boost/thread.hpp>
88 int main() { boost::thread t; }
92 #include <boost/log/trivial.hpp>
93 #include <boost/log/utility/setup/console.hpp>
94 #include <boost/log/utility/setup/common_attributes.hpp>
96 using namespace boost::log;
97 add_common_attributes();
98 add_console_log(std::clog, keywords::format = "%Message%");
99 BOOST_LOG_TRIVIAL(debug) << "log is working" << std::endl;
103 # toolsets from {boost_dir}/tools/build/v2/tools/common.jam
104 PLATFORM
= Utils
.unversioned_sys_platform()
105 detect_intel
= lambda env
: (PLATFORM
== 'win32') and 'iw' or 'il'
106 detect_clang
= lambda env
: (PLATFORM
== 'darwin') and 'clang-darwin' or 'clang'
107 detect_mingw
= lambda env
: (re
.search('MinGW', env
.CXX
[0])) and 'mgw' or 'gcc'
110 'clang': detect_clang
,
117 'icpc': detect_intel
,
118 'intel': detect_intel
,
133 opt
= opt
.add_option_group('Boost Options')
134 opt
.add_option('--boost-includes', type='string',
135 default
='', dest
='boost_includes',
136 help='''path to the directory where the boost includes are,
137 e.g., /path/to/boost_1_55_0/stage/include''')
138 opt
.add_option('--boost-libs', type='string',
139 default
='', dest
='boost_libs',
140 help='''path to the directory where the boost libs are,
141 e.g., path/to/boost_1_55_0/stage/lib''')
142 opt
.add_option('--boost-mt', action
='store_true',
143 default
=False, dest
='boost_mt',
144 help='select multi-threaded libraries')
145 opt
.add_option('--boost-abi', type='string', default
='', dest
='boost_abi',
146 help='''select libraries with tags (gd for debug, static is automatically added),
147 see doc Boost, Getting Started, chapter 6.1''')
148 opt
.add_option('--boost-linkage_autodetect', action
="store_true", dest
='boost_linkage_autodetect',
149 help="auto-detect boost linkage options (don't get used to it / might break other stuff)")
150 opt
.add_option('--boost-toolset', type='string',
151 default
='', dest
='boost_toolset',
152 help='force a toolset e.g. msvc, vc90, \
153 gcc, mingw, mgw45 (default: auto)')
154 py_version
= '%d%d' % (sys
.version_info
[0], sys
.version_info
[1])
155 opt
.add_option('--boost-python', type='string',
156 default
=py_version
, dest
='boost_python',
157 help='select the lib python with this version \
158 (default: %s)' % py_version
)
162 def __boost_get_version_file(self
, d
):
165 dnode
= self
.root
.find_dir(d
)
167 return dnode
.find_node(BOOST_VERSION_FILE
)
171 def boost_get_version(self
, d
):
172 """silently retrieve the boost version number"""
173 node
= self
.__boost
_get
_version
_file
(d
)
177 except EnvironmentError:
178 Logs
.error("Could not read the file %r", node
.abspath())
180 re_but1
= re
.compile('^#define\\s+BOOST_LIB_VERSION\\s+"(.+)"', re
.M
)
181 m1
= re_but1
.search(txt
)
182 re_but2
= re
.compile('^#define\\s+BOOST_VERSION\\s+(\\d+)', re
.M
)
183 m2
= re_but2
.search(txt
)
185 return (m1
.group(1), m2
.group(1))
186 return self
.check_cxx(fragment
=BOOST_VERSION_CODE
, includes
=[d
], execute
=True, define_ret
=True).split(":")
189 def boost_get_includes(self
, *k
, **kw
):
190 includes
= k
and k
[0] or kw
.get('includes')
191 if includes
and self
.__boost
_get
_version
_file
(includes
):
193 for d
in self
.environ
.get('INCLUDE', '').split(';') + BOOST_INCLUDES
:
194 if self
.__boost
_get
_version
_file
(d
):
197 self
.end_msg('headers not found in %s' % includes
)
198 self
.fatal('The configuration failed')
200 self
.end_msg('headers not found, please provide a --boost-includes argument (see help)')
201 self
.fatal('The configuration failed')
205 def boost_get_toolset(self
, cc
):
208 build_platform
= Utils
.unversioned_sys_platform()
209 if build_platform
in BOOST_TOOLSETS
:
212 cc
= self
.env
.CXX_NAME
213 if cc
in BOOST_TOOLSETS
:
214 toolset
= BOOST_TOOLSETS
[cc
]
215 return isinstance(toolset
, str) and toolset
or toolset(self
.env
)
219 def __boost_get_libs_path(self
, *k
, **kw
):
220 ''' return the lib path and all the files in it '''
222 return self
.root
.find_dir('.'), Utils
.to_list(kw
['files'])
223 libs
= k
and k
[0] or kw
.get('libs')
225 path
= self
.root
.find_dir(libs
)
226 files
= path
.ant_glob('*boost_*')
227 if not libs
or not files
:
228 for d
in self
.environ
.get('LIB', '').split(';') + BOOST_LIBS
:
231 path
= self
.root
.find_dir(d
)
233 files
= path
.ant_glob('*boost_*')
236 path
= self
.root
.find_dir(d
+ '64')
238 files
= path
.ant_glob('*boost_*')
243 self
.end_msg('libs not found in %s' % libs
)
244 self
.fatal('The configuration failed')
246 self
.end_msg('libs not found, please provide a --boost-libs argument (see help)')
247 self
.fatal('The configuration failed')
249 self
.to_log('Found the boost path in %r with the libraries:' % path
)
251 self
.to_log(' %r' % x
)
255 def boost_get_libs(self
, *k
, **kw
):
257 return the lib path and the required libs
258 according to the parameters
260 path
, files
= self
.__boost
_get
_libs
_path
(**kw
)
261 files
= sorted(files
, key
=lambda f
: (len(f
.name
), f
.name
), reverse
=True)
262 toolset
= self
.boost_get_toolset(kw
.get('toolset', ''))
263 toolset_pat
= '(-%s[0-9]{0,3})' % toolset
264 version
= '-%s' % self
.env
.BOOST_VERSION
266 def find_lib(re_lib
, files
):
268 if re_lib
.search(file.name
):
269 self
.to_log('Found boost lib %s' % file)
273 # extensions from Tools.ccroot.lib_patterns
274 wo_ext
= re
.compile(r
"\.(a|so|lib|dll|dylib)(\.[0-9\.]+)?$")
275 def format_lib_name(name
):
276 if name
.startswith('lib') and self
.env
.CC_NAME
!= 'msvc':
278 return wo_ext
.sub("", name
)
280 def match_libs(lib_names
, is_static
):
282 lib_names
= Utils
.to_list(lib_names
)
286 if kw
.get('mt', False):
289 t
.append('%s%s' % (is_static
and '-s' or '-', kw
['abi']))
292 tags_pat
= t
and ''.join(t
) or ''
293 ext
= is_static
and self
.env
.cxxstlib_PATTERN
or self
.env
.cxxshlib_PATTERN
294 ext
= ext
.partition('%s')[2] # remove '%s' or 'lib%s' from PATTERN
296 for lib
in lib_names
:
298 # for instance, with python='27',
299 # accepts '-py27', '-py2', '27', '-2.7' and '2'
300 # but will reject '-py3', '-py26', '26' and '3'
301 tags
= '({0})?((-py{2})|(-py{1}(?=[^0-9]))|({2})|(-{1}.{3})|({1}(?=[^0-9]))|(?=[^0-9])(?!-py))'.format(tags_pat
, kw
['python'][0], kw
['python'], kw
['python'][1])
304 # Trying libraries, from most strict match to least one
305 for pattern
in ['boost_%s%s%s%s%s$' % (lib
, toolset_pat
, tags
, version
, ext
),
306 'boost_%s%s%s%s$' % (lib
, tags
, version
, ext
),
307 # Give up trying to find the right version
308 'boost_%s%s%s%s$' % (lib
, toolset_pat
, tags
, ext
),
309 'boost_%s%s%s$' % (lib
, tags
, ext
),
310 'boost_%s%s$' % (lib
, ext
),
312 self
.to_log('Trying pattern %s' % pattern
)
313 file = find_lib(re
.compile(pattern
), files
)
315 libs
.append(format_lib_name(file.name
))
318 self
.end_msg('lib %s not found in %s' % (lib
, path
.abspath()))
319 self
.fatal('The configuration failed')
322 return path
.abspath(), match_libs(kw
.get('lib'), False), match_libs(kw
.get('stlib'), True)
325 def _check_pthread_flag(self
, *k
, **kw
):
327 Computes which flags should be added to CXXFLAGS and LINKFLAGS to compile in multi-threading mode
329 Yes, we *need* to put the -pthread thing in CPPFLAGS because with GCC3,
330 boost/thread.hpp will trigger a #error if -pthread isn't used:
331 boost/config/requires_threads.hpp:47:5: #error "Compiler threading support
332 is not turned on. Please set the correct command line options for
333 threading: -pthread (Linux), -pthreads (Solaris) or -mthreads (Mingw32)"
335 Based on _BOOST_PTHREAD_FLAG(): https://github.com/tsuna/boost.m4/blob/master/build-aux/boost.m4
338 var
= kw
.get('uselib_store', 'BOOST')
340 self
.start_msg('Checking the flags needed to use pthreads')
342 # The ordering *is* (sometimes) important. Some notes on the
343 # individual items follow:
344 # (none): in case threads are in libc; should be tried before -Kthread and
345 # other compiler flags to prevent continual compiler warnings
346 # -lpthreads: AIX (must check this before -lpthread)
347 # -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
348 # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
349 # -llthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
350 # -pthread: GNU Linux/GCC (kernel threads), BSD/GCC (userland threads)
351 # -pthreads: Solaris/GCC
352 # -mthreads: MinGW32/GCC, Lynx/GCC
353 # -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
354 # doesn't hurt to check since this sometimes defines pthreads too;
355 # also defines -D_REENTRANT)
356 # ... -mt is also the pthreads flag for HP/aCC
357 # -lpthread: GNU Linux, etc.
358 # --thread-safe: KAI C++
359 if Utils
.unversioned_sys_platform() == "sunos":
360 # On Solaris (at least, for some versions), libc contains stubbed
361 # (non-functional) versions of the pthreads routines, so link-based
362 # tests will erroneously succeed. (We need to link with -pthreads/-mt/
363 # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather
364 # a function called by this macro, so we could check for that, but
365 # who knows whether they'll stub that too in a future libc.) So,
366 # we'll just look for -pthreads and -lpthread first:
367 boost_pthread_flags
= ["-pthreads", "-lpthread", "-mt", "-pthread"]
369 boost_pthread_flags
= ["", "-lpthreads", "-Kthread", "-kthread", "-llthread", "-pthread",
370 "-pthreads", "-mthreads", "-lpthread", "--thread-safe", "-mt"]
372 for boost_pthread_flag
in boost_pthread_flags
:
375 self
.env
.append_value('CXXFLAGS_%s' % var
, boost_pthread_flag
)
376 self
.env
.append_value('LINKFLAGS_%s' % var
, boost_pthread_flag
)
377 self
.check_cxx(code
=PTHREAD_CODE
, msg
=None, use
=var
, execute
=False)
379 self
.end_msg(boost_pthread_flag
)
381 except self
.errors
.ConfigurationError
:
386 def check_boost(self
, *k
, **kw
):
388 Initialize boost libraries to be used.
390 Keywords: you can pass the same parameters as with the command line (without "--boost-").
391 Note that the command line has the priority, and should preferably be used.
393 if not self
.env
['CXX']:
394 self
.fatal('load a c++ compiler first, conf.load("compiler_cxx")')
397 'lib': k
and k
[0] or kw
.get('lib'),
398 'stlib': kw
.get('stlib')
400 for key
, value
in self
.options
.__dict
__.items():
401 if not key
.startswith('boost_'):
403 key
= key
[len('boost_'):]
404 params
[key
] = value
and value
or kw
.get(key
, '')
406 var
= kw
.get('uselib_store', 'BOOST')
408 self
.find_program('dpkg-architecture', var
='DPKG_ARCHITECTURE', mandatory
=False)
409 if self
.env
.DPKG_ARCHITECTURE
:
410 deb_host_multiarch
= self
.cmd_and_log([self
.env
.DPKG_ARCHITECTURE
[0], '-qDEB_HOST_MULTIARCH'])
411 BOOST_LIBS
.insert(0, '/usr/lib/%s' % deb_host_multiarch
.strip())
413 self
.start_msg('Checking boost includes')
414 self
.env
['INCLUDES_%s' % var
] = inc
= self
.boost_get_includes(**params
)
415 versions
= self
.boost_get_version(inc
)
416 self
.env
.BOOST_VERSION
= versions
[0]
417 self
.env
.BOOST_VERSION_NUMBER
= int(versions
[1])
418 self
.end_msg("%d.%d.%d" % (int(versions
[1]) / 100000,
419 int(versions
[1]) / 100 % 1000,
420 int(versions
[1]) % 100))
422 Logs
.pprint('CYAN', ' path : %s' % self
.env
['INCLUDES_%s' % var
])
424 if not params
['lib'] and not params
['stlib']:
426 if 'static' in kw
or 'static' in params
:
427 Logs
.warn('boost: static parameter is deprecated, use stlib instead.')
428 self
.start_msg('Checking boost libs')
429 path
, libs
, stlibs
= self
.boost_get_libs(**params
)
430 self
.env
['LIBPATH_%s' % var
] = [path
]
431 self
.env
['STLIBPATH_%s' % var
] = [path
]
432 self
.env
['LIB_%s' % var
] = libs
433 self
.env
['STLIB_%s' % var
] = stlibs
436 Logs
.pprint('CYAN', ' path : %s' % path
)
437 Logs
.pprint('CYAN', ' shared libs : %s' % libs
)
438 Logs
.pprint('CYAN', ' static libs : %s' % stlibs
)
441 return params
['lib'] and lib
in params
['lib']
443 return params
['stlib'] and lib
in params
['stlib']
445 return has_shlib(lib
) or has_stlib(lib
)
446 if has_lib('thread'):
447 # not inside try_link to make check visible in the output
448 self
._check
_pthread
_flag
(k
, kw
)
451 if has_lib('system'):
452 self
.check_cxx(fragment
=BOOST_ERROR_CODE
, use
=var
, execute
=False)
453 if has_lib('thread'):
454 self
.check_cxx(fragment
=BOOST_THREAD_CODE
, use
=var
, execute
=False)
456 if not has_lib('thread'):
457 self
.env
['DEFINES_%s' % var
] += ['BOOST_LOG_NO_THREADS']
459 self
.env
['DEFINES_%s' % var
] += ['BOOST_LOG_DYN_LINK']
460 self
.check_cxx(fragment
=BOOST_LOG_CODE
, use
=var
, execute
=False)
462 if params
.get('linkage_autodetect', False):
463 self
.start_msg("Attempting to detect boost linkage flags")
464 toolset
= self
.boost_get_toolset(kw
.get('toolset', ''))
465 if toolset
in ('vc',):
466 # disable auto-linking feature, causing error LNK1181
467 # because the code wants to be linked against
468 self
.env
['DEFINES_%s' % var
] += ['BOOST_ALL_NO_LIB']
470 # if no dlls are present, we guess the .lib files are not stubs
472 for x
in Utils
.listdir(path
):
473 if x
.endswith(self
.env
.cxxshlib_PATTERN
% ''):
477 self
.env
['STLIBPATH_%s' % var
] = [path
]
478 self
.env
['STLIB_%s' % var
] = libs
479 del self
.env
['LIB_%s' % var
]
480 del self
.env
['LIBPATH_%s' % var
]
482 # we attempt to play with some known-to-work CXXFLAGS combinations
483 for cxxflags
in (['/MD', '/EHsc'], []):
485 self
.env
["CXXFLAGS_%s" % var
] += cxxflags
488 except Errors
.ConfigurationError
as e
:
492 self
.end_msg("ok: winning cxxflags combination: %s" % (self
.env
["CXXFLAGS_%s" % var
]))
498 self
.end_msg("Could not auto-detect boost linking flags combination, you may report it to boost.py author", ex
=exc
)
499 self
.fatal('The configuration failed')
501 self
.end_msg("Boost linkage flags auto-detection not implemented (needed ?) for this toolchain")
502 self
.fatal('The configuration failed')
504 self
.start_msg('Checking for boost linkage')
507 except Errors
.ConfigurationError
as e
:
508 self
.end_msg("Could not link against boost libraries using supplied options")
509 self
.fatal('The configuration failed')
514 @after_method('apply_link')
515 def install_boost(self
):
516 if install_boost
.done
or not Utils
.is_win32
or not self
.bld
.cmd
.startswith('install'):
518 install_boost
.done
= True
519 inst_to
= getattr(self
, 'install_path', '${BINDIR}')
520 for lib
in self
.env
.LIB_BOOST
:
522 file = self
.bld
.find_file(self
.env
.cxxshlib_PATTERN
% lib
, self
.env
.LIBPATH_BOOST
)
523 self
.add_install_files(install_to
=inst_to
, install_from
=self
.bld
.root
.find_node(file))
526 install_boost
.done
= False