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