blob: 6d7978820ed466a2ece26ba8e172a499d295ef9e [file] [log] [blame]
Alexander Afanasyeva1ae0a12014-01-28 15:21:02 -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.
33It can also provide default arguments to the --boost-static and --boost-mt command-line arguments.
34Everything 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
40 So before calling `conf.check_boost` you might want to disabling by adding:
41 conf.env.DEFINES_BOOST += ['BOOST_ALL_NO_LIB']
Alexander Afanasyevdafdc372014-03-03 15:58:44 +000042 Errors:
Alexander Afanasyeva1ae0a12014-01-28 15:21:02 -080043 - boost might also be compiled with /MT, which links the runtime statically.
Alexander Afanasyevdafdc372014-03-03 15:58:44 +000044 If you have problems with redefined symbols,
Alexander Afanasyeva1ae0a12014-01-28 15:21:02 -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
55
56BOOST_LIBS = ['/usr/lib', '/usr/local/lib', '/opt/local/lib', '/sw/lib', '/lib', '/usr/lib/x86_64-linux-gnu', '/usr/lib/i386-linux-gnu', '/usr/local/ndn/lib']
57BOOST_INCLUDES = ['/usr/include', '/usr/local/include', '/opt/local/include', '/sw/include', '/usr/local/ndn/include']
58BOOST_VERSION_FILE = 'boost/version.hpp'
59BOOST_VERSION_CODE = '''
60#include <iostream>
61#include <boost/version.hpp>
Alexander Afanasyevdafdc372014-03-03 15:58:44 +000062int main() { std::cout << BOOST_LIB_VERSION << ":" << BOOST_VERSION << std::endl; }
Alexander Afanasyeva1ae0a12014-01-28 15:21:02 -080063'''
64
65# toolsets from {boost_dir}/tools/build/v2/tools/common.jam
66PLATFORM = Utils.unversioned_sys_platform()
67detect_intel = lambda env: (PLATFORM == 'win32') and 'iw' or 'il'
68detect_clang = lambda env: (PLATFORM == 'darwin') and 'clang-darwin' or 'clang'
69detect_mingw = lambda env: (re.search('MinGW', env.CXX[0])) and 'mgw' or 'gcc'
70BOOST_TOOLSETS = {
71 'borland': 'bcb',
72 'clang': detect_clang,
73 'como': 'como',
74 'cw': 'cw',
75 'darwin': 'xgcc',
76 'edg': 'edg',
77 'g++': detect_mingw,
78 'gcc': detect_mingw,
79 'icpc': detect_intel,
80 'intel': detect_intel,
81 'kcc': 'kcc',
82 'kylix': 'bck',
83 'mipspro': 'mp',
84 'mingw': 'mgw',
85 'msvc': 'vc',
86 'qcc': 'qcc',
87 'sun': 'sw',
88 'sunc++': 'sw',
89 'tru64cxx': 'tru',
90 'vacpp': 'xlc'
91}
92
93
94def options(opt):
Alexander Afanasyevdafdc372014-03-03 15:58:44 +000095 opt = opt.add_option_group('Boost Options')
96
Alexander Afanasyeva1ae0a12014-01-28 15:21:02 -080097 opt.add_option('--boost-includes', type='string',
98 default='', dest='boost_includes',
Alexander Afanasyevdafdc372014-03-03 15:58:44 +000099 help='''path to the directory where the boost includes are, e.g., /path/to/boost_1_55_0/stage/include''')
Alexander Afanasyeva1ae0a12014-01-28 15:21:02 -0800100 opt.add_option('--boost-libs', type='string',
101 default='', dest='boost_libs',
Alexander Afanasyevdafdc372014-03-03 15:58:44 +0000102 help='''path to the directory where the boost libs are, e.g., /path/to/boost_1_55_0/stage/lib''')
Alexander Afanasyeva1ae0a12014-01-28 15:21:02 -0800103 opt.add_option('--boost-static', action='store_true',
104 default=False, dest='boost_static',
105 help='link with static boost libraries (.lib/.a)')
106 opt.add_option('--boost-mt', action='store_true',
107 default=False, dest='boost_mt',
108 help='select multi-threaded libraries')
109 opt.add_option('--boost-abi', type='string', default='', dest='boost_abi',
Alexander Afanasyevdafdc372014-03-03 15:58:44 +0000110 help='''select libraries with tags (dgsyp, d for debug), see doc Boost, Getting Started, chapter 6.1''')
Alexander Afanasyeva1ae0a12014-01-28 15:21:02 -0800111 opt.add_option('--boost-linkage_autodetect', action="store_true", dest='boost_linkage_autodetect',
112 help="auto-detect boost linkage options (don't get used to it / might break other stuff)")
113 opt.add_option('--boost-toolset', type='string',
114 default='', dest='boost_toolset',
Alexander Afanasyevdafdc372014-03-03 15:58:44 +0000115 help='force a toolset e.g. msvc, vc90, gcc, mingw, mgw45 (default: auto)')
Alexander Afanasyeva1ae0a12014-01-28 15:21:02 -0800116 py_version = '%d%d' % (sys.version_info[0], sys.version_info[1])
117 opt.add_option('--boost-python', type='string',
118 default=py_version, dest='boost_python',
Alexander Afanasyevdafdc372014-03-03 15:58:44 +0000119 help='select the lib python with this version (default: %s)' % py_version)
Alexander Afanasyeva1ae0a12014-01-28 15:21:02 -0800120
121
122@conf
123def __boost_get_version_file(self, d):
124 dnode = self.root.find_dir(d)
125 if dnode:
126 return dnode.find_node(BOOST_VERSION_FILE)
127 return None
128
129@conf
130def boost_get_version(self, d):
131 """silently retrieve the boost version number"""
132 node = self.__boost_get_version_file(d)
133 if node:
134 try:
135 txt = node.read()
136 except (OSError, IOError):
137 Logs.error("Could not read the file %r" % node.abspath())
138 else:
Alexander Afanasyevdafdc372014-03-03 15:58:44 +0000139 re_but1 = re.compile('^#define\\s+BOOST_LIB_VERSION\\s+"(.*)"', re.M)
140 m1 = re_but1.search(txt)
141
142 re_but2 = re.compile('^#define\\s+BOOST_VERSION\\s+"(.*)"', re.M)
143 m2 = re_but2.search(txt)
144
145 if m1 and m2:
146 return (m1.group(1), m2.group(1))
147
148 return self.check_cxx(fragment=BOOST_VERSION_CODE, includes=[d], execute=True, define_ret=True).split(":")
Alexander Afanasyeva1ae0a12014-01-28 15:21:02 -0800149
150@conf
151def boost_get_includes(self, *k, **kw):
152 includes = k and k[0] or kw.get('includes', None)
153 if includes and self.__boost_get_version_file(includes):
154 return includes
155 for d in Utils.to_list(self.environ.get('INCLUDE', '')) + BOOST_INCLUDES:
156 if self.__boost_get_version_file(d):
157 return d
158 if includes:
159 self.end_msg('headers not found in %s' % includes)
160 self.fatal('The configuration failed')
161 else:
162 self.end_msg('headers not found, please provide a --boost-includes argument (see help)')
163 self.fatal('The configuration failed')
164
165
166@conf
167def boost_get_toolset(self, cc):
168 toolset = cc
169 if not cc:
170 build_platform = Utils.unversioned_sys_platform()
171 if build_platform in BOOST_TOOLSETS:
172 cc = build_platform
173 else:
174 cc = self.env.CXX_NAME
175 if cc in BOOST_TOOLSETS:
176 toolset = BOOST_TOOLSETS[cc]
177 return isinstance(toolset, str) and toolset or toolset(self.env)
178
179
180@conf
181def __boost_get_libs_path(self, *k, **kw):
182 ''' return the lib path and all the files in it '''
183 if 'files' in kw:
184 return self.root.find_dir('.'), Utils.to_list(kw['files'])
185 libs = k and k[0] or kw.get('libs', None)
186 if libs:
187 path = self.root.find_dir(libs)
188 files = path.ant_glob('*boost_*')
189 if not libs or not files:
190 for d in Utils.to_list(self.environ.get('LIB', [])) + BOOST_LIBS:
191 path = self.root.find_dir(d)
192 if path:
193 files = path.ant_glob('*boost_*')
194 if files:
195 break
196 path = self.root.find_dir(d + '64')
197 if path:
198 files = path.ant_glob('*boost_*')
199 if files:
200 break
201 if not path:
202 if libs:
203 self.end_msg('libs not found in %s' % libs)
204 self.fatal('The configuration failed')
205 else:
206 self.end_msg('libs not found, please provide a --boost-libs argument (see help)')
207 self.fatal('The configuration failed')
208
209 self.to_log('Found the boost path in %r with the libraries:' % path)
210 for x in files:
211 self.to_log(' %r' % x)
212 return path, files
213
214@conf
215def boost_get_libs(self, *k, **kw):
216 '''
217 return the lib path and the required libs
218 according to the parameters
219 '''
220 path, files = self.__boost_get_libs_path(**kw)
221 t = []
222 if kw.get('mt', False):
223 t.append('mt')
224 if kw.get('abi', None):
225 t.append(kw['abi'])
226 tags = t and '(-%s)+' % '-'.join(t) or ''
227 toolset = self.boost_get_toolset(kw.get('toolset', ''))
228 toolset_pat = '(-%s[0-9]{0,3})+' % toolset
229 version = '(-%s)+' % self.env.BOOST_VERSION
230
231 def find_lib(re_lib, files):
232 for file in files:
233 if re_lib.search(file.name):
234 self.to_log('Found boost lib %s' % file)
235 return file
236 return None
237
238 def format_lib_name(name):
239 if name.startswith('lib') and self.env.CC_NAME != 'msvc':
240 name = name[3:]
241 return name[:name.rfind('.')]
242
243 libs = []
244 for lib in Utils.to_list(k and k[0] or kw.get('lib', None)):
245 py = (lib == 'python') and '(-py%s)+' % kw['python'] or ''
246 # Trying libraries, from most strict match to least one
247 for pattern in ['boost_%s%s%s%s%s' % (lib, toolset_pat, tags, py, version),
248 'boost_%s%s%s%s' % (lib, tags, py, version),
249 'boost_%s%s%s' % (lib, tags, version),
250 # Give up trying to find the right version
251 'boost_%s%s%s%s' % (lib, toolset_pat, tags, py),
252 'boost_%s%s%s' % (lib, tags, py),
253 'boost_%s%s' % (lib, tags)]:
254 self.to_log('Trying pattern %s' % pattern)
255 file = find_lib(re.compile(pattern), files)
256 if file:
257 libs.append(format_lib_name(file.name))
258 break
259 else:
260 self.end_msg('lib %s not found in %s' % (lib, path.abspath()))
261 self.fatal('The configuration failed')
262
263 return path.abspath(), libs
264
265
266@conf
267def check_boost(self, *k, **kw):
268 """
269 Initialize boost libraries to be used.
270
271 Keywords: you can pass the same parameters as with the command line (without "--boost-").
272 Note that the command line has the priority, and should preferably be used.
273 """
274 if not self.env['CXX']:
275 self.fatal('load a c++ compiler first, conf.load("compiler_cxx")')
276
277 params = {'lib': k and k[0] or kw.get('lib', None)}
278 for key, value in self.options.__dict__.items():
279 if not key.startswith('boost_'):
280 continue
281 key = key[len('boost_'):]
282 params[key] = value and value or kw.get(key, '')
283
284 var = kw.get('uselib_store', 'BOOST')
285
286 self.start_msg('Checking boost includes')
287 self.env['INCLUDES_%s' % var] = inc = self.boost_get_includes(**params)
Alexander Afanasyevdafdc372014-03-03 15:58:44 +0000288 versions = self.boost_get_version(inc)
289 self.env.BOOST_VERSION = versions[0]
290 self.env.BOOST_VERSION_NUMBER = int(versions[1])
291 self.end_msg("%d.%d.%d" % (int(versions[1]) / 100000,
292 int(versions[1]) / 100 % 1000,
293 int(versions[1]) % 100))
Alexander Afanasyeva1ae0a12014-01-28 15:21:02 -0800294 if Logs.verbose:
295 Logs.pprint('CYAN', ' path : %s' % self.env['INCLUDES_%s' % var])
296
297 if not params['lib']:
298 return
299 self.start_msg('Checking boost libs')
300 suffix = params.get('static', None) and 'ST' or ''
301 path, libs = self.boost_get_libs(**params)
302 self.env['%sLIBPATH_%s' % (suffix, var)] = [path]
303 self.env['%sLIB_%s' % (suffix, var)] = libs
304 self.end_msg('ok')
305 if Logs.verbose:
306 Logs.pprint('CYAN', ' path : %s' % path)
307 Logs.pprint('CYAN', ' libs : %s' % libs)
308
309
310 def try_link():
311 if 'system' in params['lib']:
312 self.check_cxx(
313 fragment="\n".join([
314 '#include <boost/system/error_code.hpp>',
315 'int main() { boost::system::error_code c; }',
316 ]),
317 use=var,
318 execute=False,
319 )
320 if 'thread' in params['lib']:
321 self.check_cxx(
322 fragment="\n".join([
323 '#include <boost/thread.hpp>',
324 'int main() { boost::thread t; }',
325 ]),
326 use=var,
327 execute=False,
328 )
329
330 if params.get('linkage_autodetect', False):
331 self.start_msg("Attempting to detect boost linkage flags")
332 toolset = self.boost_get_toolset(kw.get('toolset', ''))
333 if toolset in ['vc']:
334 # disable auto-linking feature, causing error LNK1181
335 # because the code wants to be linked against
336 self.env['DEFINES_%s' % var] += ['BOOST_ALL_NO_LIB']
337
338 # if no dlls are present, we guess the .lib files are not stubs
339 has_dlls = False
340 for x in Utils.listdir(path):
341 if x.endswith(self.env.cxxshlib_PATTERN % ''):
342 has_dlls = True
343 break
344 if not has_dlls:
345 self.env['STLIBPATH_%s' % var] = [path]
346 self.env['STLIB_%s' % var] = libs
347 del self.env['LIB_%s' % var]
348 del self.env['LIBPATH_%s' % var]
349
350 # we attempt to play with some known-to-work CXXFLAGS combinations
351 for cxxflags in (['/MD', '/EHsc'], []):
352 self.env.stash()
353 self.env["CXXFLAGS_%s" % var] += cxxflags
354 try:
355 try_link()
356 self.end_msg("ok: winning cxxflags combination: %s" % (self.env["CXXFLAGS_%s" % var]))
357 e = None
358 break
359 except Errors.ConfigurationError as exc:
360 self.env.revert()
361 e = exc
362
363 if e is not None:
364 self.end_msg("Could not auto-detect boost linking flags combination, you may report it to boost.py author", ex=e)
365 self.fatal('The configuration failed')
366 else:
367 self.end_msg("Boost linkage flags auto-detection not implemented (needed ?) for this toolchain")
368 self.fatal('The configuration failed')
369 else:
370 self.start_msg('Checking for boost linkage')
371 try:
372 try_link()
373 except Errors.ConfigurationError as e:
374 self.end_msg("Could not link against boost libraries using supplied options")
375 self.fatal('The configuration failed')
376 self.end_msg('ok')