blob: c714b5bc34aa60840445bcb01067ead127218b9f [file] [log] [blame]
akmhoquec52a46c2014-02-17 21:13:16 -06001#!/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', '/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>
62int main() { std::cout << BOOST_LIB_VERSION << std::endl; }
63'''
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):
95 opt = opt.add_option_group('Boost Options')
96
97 opt.add_option('--boost-includes', type='string',
98 default='', dest='boost_includes',
99 help='''path to the boost includes root (~boost root)
100 e.g. /path/to/boost_1_47_0''')
101 opt.add_option('--boost-libs', type='string',
102 default='', dest='boost_libs',
103 help='''path to the directory where the boost libs are
104 e.g. /path/to/boost_1_47_0/stage/lib''')
105 opt.add_option('--boost-static', action='store_true',
106 default=False, dest='boost_static',
107 help='link with static boost libraries (.lib/.a)')
108 opt.add_option('--boost-mt', action='store_true',
109 default=False, dest='boost_mt',
110 help='select multi-threaded libraries')
111 opt.add_option('--boost-abi', type='string', default='', dest='boost_abi',
112 help='''select libraries with tags (dgsyp, d for debug),
113 see doc Boost, Getting Started, chapter 6.1''')
114 opt.add_option('--boost-linkage_autodetect', action="store_true", dest='boost_linkage_autodetect',
115 help="auto-detect boost linkage options (don't get used to it / might break other stuff)")
116 opt.add_option('--boost-toolset', type='string',
117 default='', dest='boost_toolset',
118 help='force a toolset e.g. msvc, vc90, \
119 gcc, mingw, mgw45 (default: auto)')
120 py_version = '%d%d' % (sys.version_info[0], sys.version_info[1])
121 opt.add_option('--boost-python', type='string',
122 default=py_version, dest='boost_python',
123 help='select the lib python with this version \
124 (default: %s)' % py_version)
125
126
127@conf
128def __boost_get_version_file(self, d):
129 dnode = self.root.find_dir(d)
130 if dnode:
131 return dnode.find_node(BOOST_VERSION_FILE)
132 return None
133
134@conf
135def boost_get_version(self, d):
136 """silently retrieve the boost version number"""
137 node = self.__boost_get_version_file(d)
138 if node:
139 try:
140 txt = node.read()
141 except (OSError, IOError):
142 Logs.error("Could not read the file %r" % node.abspath())
143 else:
144 re_but = re.compile('^#define\\s+BOOST_LIB_VERSION\\s+"(.*)"', re.M)
145 m = re_but.search(txt)
146 if m:
147 return m.group(1)
148 return self.check_cxx(fragment=BOOST_VERSION_CODE, includes=[d], execute=True, define_ret=True)
149
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)
288 self.env.BOOST_VERSION = self.boost_get_version(inc)
289 self.end_msg(self.env.BOOST_VERSION)
290 if Logs.verbose:
291 Logs.pprint('CYAN', ' path : %s' % self.env['INCLUDES_%s' % var])
292
293 if not params['lib']:
294 return
295 self.start_msg('Checking boost libs')
296 suffix = params.get('static', None) and 'ST' or ''
297 path, libs = self.boost_get_libs(**params)
298 self.env['%sLIBPATH_%s' % (suffix, var)] = [path]
299 self.env['%sLIB_%s' % (suffix, var)] = libs
300 self.end_msg('ok')
301 if Logs.verbose:
302 Logs.pprint('CYAN', ' path : %s' % path)
303 Logs.pprint('CYAN', ' libs : %s' % libs)
304
305
306 def try_link():
307 if 'system' in params['lib']:
308 self.check_cxx(
309 fragment="\n".join([
310 '#include <boost/system/error_code.hpp>',
311 'int main() { boost::system::error_code c; }',
312 ]),
313 use=var,
314 execute=False,
315 )
316 if 'thread' in params['lib']:
317 self.check_cxx(
318 fragment="\n".join([
319 '#include <boost/thread.hpp>',
320 'int main() { boost::thread t; }',
321 ]),
322 use=var,
323 execute=False,
324 )
325
326 if params.get('linkage_autodetect', False):
327 self.start_msg("Attempting to detect boost linkage flags")
328 toolset = self.boost_get_toolset(kw.get('toolset', ''))
329 if toolset in ['vc']:
330 # disable auto-linking feature, causing error LNK1181
331 # because the code wants to be linked against
332 self.env['DEFINES_%s' % var] += ['BOOST_ALL_NO_LIB']
333
334 # if no dlls are present, we guess the .lib files are not stubs
335 has_dlls = False
336 for x in Utils.listdir(path):
337 if x.endswith(self.env.cxxshlib_PATTERN % ''):
338 has_dlls = True
339 break
340 if not has_dlls:
341 self.env['STLIBPATH_%s' % var] = [path]
342 self.env['STLIB_%s' % var] = libs
343 del self.env['LIB_%s' % var]
344 del self.env['LIBPATH_%s' % var]
345
346 # we attempt to play with some known-to-work CXXFLAGS combinations
347 for cxxflags in (['/MD', '/EHsc'], []):
348 self.env.stash()
349 self.env["CXXFLAGS_%s" % var] += cxxflags
350 try:
351 try_link()
352 self.end_msg("ok: winning cxxflags combination: %s" % (self.env["CXXFLAGS_%s" % var]))
353 e = None
354 break
355 except Errors.ConfigurationError as exc:
356 self.env.revert()
357 e = exc
358
359 if e is not None:
360 self.end_msg("Could not auto-detect boost linking flags combination, you may report it to boost.py author", ex=e)
361 self.fatal('The configuration failed')
362 else:
363 self.end_msg("Boost linkage flags auto-detection not implemented (needed ?) for this toolchain")
364 self.fatal('The configuration failed')
365 else:
366 self.start_msg('Checking for boost linkage')
367 try:
368 try_link()
369 except Errors.ConfigurationError as e:
370 self.end_msg("Could not link against boost libraries using supplied options")
371 self.fatal('The configuration failed')
372 self.end_msg('ok')
373