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