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