View | Details | Raw Unified | Return to bug 1562
Collapse All | Expand All

(-)a/waf-tools/boost.py (-45 / +127 lines)
 Lines 9-42    Link Here 
9
# rewritten for waf 1.6.2, Sylvain Rouquette, 2011
9
# rewritten for waf 1.6.2, Sylvain Rouquette, 2011
10
10
11
'''
11
'''
12
13
This is an extra tool, not bundled with the default waf binary.
12
To add the boost tool to the waf file:
14
To add the boost tool to the waf file:
13
$ ./waf-light --tools=compat15,boost
15
$ ./waf-light --tools=compat15,boost
14
	or, if you have waf >= 1.6.2
16
	or, if you have waf >= 1.6.2
15
$ ./waf update --files=boost
17
$ ./waf update --files=boost
16
18
17
The wscript will look like:
19
When using this tool, the wscript will look like:
18
20
19
def options(opt):
21
	def options(opt):
20
	opt.load('compiler_cxx boost')
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')
21
30
22
def configure(conf):
31
Options are generated, in order to specify the location of boost includes/libraries.
23
	conf.load('compiler_cxx boost')
32
The `check_boost` configuration function allows to specify the used boost libraries.
24
	conf.check_boost(lib='system filesystem', mt=True, static=True)
33
It can also provide default arguments to the --boost-static and --boost-mt command-line arguments.
34
Everything will be packaged together in a BOOST component that you can use.
35
36
When 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']
47
Passing `--boost-linkage_autodetect` might help ensuring having a correct linkage in some basic cases.
25
48
26
def build(bld):
27
	bld(source='main.cpp', target='app', use='BOOST')
28
'''
49
'''
29
50
30
import sys
51
import sys
31
import re
52
import re
32
from waflib import Utils, Logs
53
from waflib import Utils, Logs, Errors
33
from waflib.Configure import conf
54
from waflib.Configure import conf
34
from waflib.Errors import WafError
35
55
36
BOOST_LIBS = ('/usr/lib', '/usr/local/lib',
56
BOOST_LIBS = ['/usr/lib', '/usr/local/lib', '/opt/local/lib', '/sw/lib', '/lib']
37
			  '/opt/local/lib', '/sw/lib', '/lib')
57
BOOST_INCLUDES = ['/usr/include', '/usr/local/include', '/opt/local/include', '/sw/include']
38
BOOST_INCLUDES = ('/usr/include', '/usr/local/include',
39
				  '/opt/local/include', '/sw/include')
40
BOOST_VERSION_FILE = 'boost/version.hpp'
58
BOOST_VERSION_FILE = 'boost/version.hpp'
41
BOOST_VERSION_CODE = '''
59
BOOST_VERSION_CODE = '''
42
#include <iostream>
60
#include <iostream>
 Lines 76-96   BOOST_TOOLSETS = { Link Here 
76
def options(opt):
94
def options(opt):
77
	opt.add_option('--boost-includes', type='string',
95
	opt.add_option('--boost-includes', type='string',
78
				   default='', dest='boost_includes',
96
				   default='', dest='boost_includes',
79
				   help='''path to the boost directory where the includes are
97
				   help='''path to the boost includes root (~boost root)
80
				   e.g. /boost_1_45_0/include''')
98
				   e.g. /path/to/boost_1_47_0''')
81
	opt.add_option('--boost-libs', type='string',
99
	opt.add_option('--boost-libs', type='string',
82
				   default='', dest='boost_libs',
100
				   default='', dest='boost_libs',
83
				   help='''path to the directory where the boost libs are
101
				   help='''path to the directory where the boost libs are
84
				   e.g. /boost_1_45_0/stage/lib''')
102
				   e.g. /path/to/boost_1_47_0/stage/lib''')
85
	opt.add_option('--boost-static', action='store_true',
103
	opt.add_option('--boost-static', action='store_true',
86
				   default=False, dest='boost_static',
104
				   default=False, dest='boost_static',
87
				   help='link static libraries')
105
				   help='link with static boost libraries (.lib/.a)')
88
	opt.add_option('--boost-mt', action='store_true',
106
	opt.add_option('--boost-mt', action='store_true',
89
				   default=False, dest='boost_mt',
107
				   default=False, dest='boost_mt',
90
				   help='select multi-threaded libraries')
108
				   help='select multi-threaded libraries')
91
	opt.add_option('--boost-abi', type='string', default='', dest='boost_abi',
109
	opt.add_option('--boost-abi', type='string', default='', dest='boost_abi',
92
				   help='''select libraries with tags (dgsyp, d for debug),
110
				   help='''select libraries with tags (dgsyp, d for debug),
93
				   see doc Boost, Getting Started, chapter 6.1''')
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)")
94
	opt.add_option('--boost-toolset', type='string',
114
	opt.add_option('--boost-toolset', type='string',
95
				   default='', dest='boost_toolset',
115
				   default='', dest='boost_toolset',
96
				   help='force a toolset e.g. msvc, vc90, \
116
				   help='force a toolset e.g. msvc, vc90, \
 Lines 117-124   def boost_get_version(self, dir): Link Here 
117
	try:
137
	try:
118
		val = re_but.search(self.__boost_get_version_file(dir).read()).group(1)
138
		val = re_but.search(self.__boost_get_version_file(dir).read()).group(1)
119
	except:
139
	except:
120
		val = self.check_cxx(fragment=BOOST_VERSION_CODE, includes=[dir],
140
		val = self.check_cxx(fragment=BOOST_VERSION_CODE, includes=[dir], execute=False, define_ret=True)
121
							 execute=True, define_ret=True)
122
	return val
141
	return val
123
142
124
143
 Lines 133-139   def boost_get_includes(self, *k, **kw): Link Here 
133
	if includes:
152
	if includes:
134
		self.fatal('headers not found in %s' % includes)
153
		self.fatal('headers not found in %s' % includes)
135
	else:
154
	else:
136
		self.fatal('headers not found, use --boost-includes=/path/to/boost')
155
		self.fatal('headers not found, please provide a --boost-includes argument (see help)')
137
156
138
157
139
@conf
158
@conf
 Lines 176-185   def __boost_get_libs_path(self, *k, **kw): Link Here 
176
		if libs:
195
		if libs:
177
			self.fatal('libs not found in %s' % libs)
196
			self.fatal('libs not found in %s' % libs)
178
		else:
197
		else:
179
			self.fatal('libs not found, \
198
			self.fatal('libs not found, please provide a --boost-libs argument (see help)')
180
					   use --boost-includes=/path/to/boost/lib')
181
	return path, files
182
199
200
	self.to_log('Found the boost path in %r with the libraries:' % path)
201
	for x in files:
202
		self.to_log('    %r' % x)
203
	return path, files
183
204
184
@conf
205
@conf
185
def boost_get_libs(self, *k, **kw):
206
def boost_get_libs(self, *k, **kw):
 Lines 194-230   def boost_get_libs(self, *k, **kw): Link Here 
194
	if kw.get('abi', None):
215
	if kw.get('abi', None):
195
		t.append(kw['abi'])
216
		t.append(kw['abi'])
196
	tags = t and '(-%s)+' % '-'.join(t) or ''
217
	tags = t and '(-%s)+' % '-'.join(t) or ''
197
	toolset = '(-%s[0-9]{0,3})+' % self.boost_get_toolset(kw.get('toolset', ''))
218
	toolset = self.boost_get_toolset(kw.get('toolset', ''))
219
	toolset_pat = '(-%s[0-9]{0,3})+' % toolset
198
	version = '(-%s)+' % self.env.BOOST_VERSION
220
	version = '(-%s)+' % self.env.BOOST_VERSION
199
221
200
	def find_lib(re_lib, files):
222
	def find_lib(re_lib, files):
201
		for file in files:
223
		for file in files:
202
			if re_lib.search(file.name):
224
			if re_lib.search(file.name):
225
				self.to_log('Found boost lib %s' % file)
203
				return file
226
				return file
204
		return None
227
		return None
205
228
206
	def format_lib_name(name):
229
	def format_lib_name(name):
207
		if name.startswith('lib'):
230
		if name.startswith('lib') and self.env.CC_NAME != 'msvc':
208
			name = name[3:]
231
			name = name[3:]
209
		return name.split('.')[0]
232
		return name[:name.rfind('.')]
210
233
211
	libs = []
234
	libs = []
212
	for lib in Utils.to_list(k and k[0] or kw.get('lib', None)):
235
	for lib in Utils.to_list(k and k[0] or kw.get('lib', None)):
213
		py = (lib == 'python') and '(-py%s)+' % kw['python'] or ''
236
		py = (lib == 'python') and '(-py%s)+' % kw['python'] or ''
214
		# Trying libraries, from most strict match to least one
237
		# Trying libraries, from most strict match to least one
215
		for pattern in ['boost_%s%s%s%s%s' % (lib, toolset, tags, py, version),
238
		for pattern in ['boost_%s%s%s%s%s' % (lib, toolset_pat, tags, py, version),
216
						'boost_%s%s%s%s' % (lib, tags, py, version),
239
						'boost_%s%s%s%s' % (lib, tags, py, version),
217
						'boost_%s%s%s' % (lib, tags, version),
240
						'boost_%s%s%s' % (lib, tags, version),
218
						# Give up trying to find the right version
241
						# Give up trying to find the right version
219
						'boost_%s%s%s%s' % (lib, toolset, tags, py),
242
						'boost_%s%s%s%s' % (lib, toolset_pat, tags, py),
220
						'boost_%s%s%s' % (lib, tags, py),
243
						'boost_%s%s%s' % (lib, tags, py),
221
						'boost_%s%s' % (lib, tags)]:
244
						'boost_%s%s' % (lib, tags)]:
245
			self.to_log('Trying pattern %s' % pattern)
222
			file = find_lib(re.compile(pattern), files)
246
			file = find_lib(re.compile(pattern), files)
223
			if file:
247
			if file:
224
				libs.append(format_lib_name(file.name))
248
				libs.append(format_lib_name(file.name))
225
				break
249
				break
226
		else:
250
		else:
227
			self.fatal('lib %s not found in %s' % (lib, path))
251
			self.fatal('lib %s not found in %s' % (lib, path.abspath()))
228
252
229
	return path.abspath(), libs
253
	return path.abspath(), libs
230
254
 Lines 232-241   def boost_get_libs(self, *k, **kw): Link Here 
232
@conf
256
@conf
233
def check_boost(self, *k, **kw):
257
def check_boost(self, *k, **kw):
234
	"""
258
	"""
235
	initialize boost
259
	Initialize boost libraries to be used.
236
260
237
	You can pass the same parameters as the command line (without "--boost-"),
261
	Keywords: you can pass the same parameters as with the command line (without "--boost-").
238
	but the command line has the priority.
262
	Note that the command line has the priority, and should preferably be used.
239
	"""
263
	"""
240
	if not self.env['CXX']:
264
	if not self.env['CXX']:
241
		self.fatal('load a c++ compiler first, conf.load("compiler_cxx")')
265
		self.fatal('load a c++ compiler first, conf.load("compiler_cxx")')
 Lines 250-261   def check_boost(self, *k, **kw): Link Here 
250
	var = kw.get('uselib_store', 'BOOST')
274
	var = kw.get('uselib_store', 'BOOST')
251
275
252
	self.start_msg('Checking boost includes')
276
	self.start_msg('Checking boost includes')
253
	try:
277
	self.env['INCLUDES_%s' % var] = inc = self.boost_get_includes(**params)
254
		self.env['INCLUDES_%s' % var] = self.boost_get_includes(**params)
278
	self.env.BOOST_VERSION = self.boost_get_version(inc)
255
		self.env.BOOST_VERSION = self.boost_get_version(self.env['INCLUDES_%s' % var])
256
	except WafError:
257
		self.end_msg("not found", 'YELLOW')
258
		raise
259
	self.end_msg(self.env.BOOST_VERSION)
279
	self.end_msg(self.env.BOOST_VERSION)
260
	if Logs.verbose:
280
	if Logs.verbose:
261
		Logs.pprint('CYAN', '	path : %s' % self.env['INCLUDES_%s' % var])
281
		Logs.pprint('CYAN', '	path : %s' % self.env['INCLUDES_%s' % var])
 Lines 263-274   def check_boost(self, *k, **kw): Link Here 
263
	if not params['lib']:
283
	if not params['lib']:
264
		return
284
		return
265
	self.start_msg('Checking boost libs')
285
	self.start_msg('Checking boost libs')
266
	try:
286
	suffix = params.get('static', None) and 'ST' or ''
267
		suffix = params.get('static', 'ST') or ''
287
	path, libs = self.boost_get_libs(**params)
268
		path, libs = self.boost_get_libs(**params)
269
	except WafError:
270
		self.end_msg("not found", 'YELLOW')
271
		raise
272
	self.env['%sLIBPATH_%s' % (suffix, var)] = [path]
288
	self.env['%sLIBPATH_%s' % (suffix, var)] = [path]
273
	self.env['%sLIB_%s' % (suffix, var)] = libs
289
	self.env['%sLIB_%s' % (suffix, var)] = libs
274
	self.end_msg('ok')
290
	self.end_msg('ok')
 Lines 276-278   def check_boost(self, *k, **kw): Link Here 
276
		Logs.pprint('CYAN', '	path : %s' % path)
292
		Logs.pprint('CYAN', '	path : %s' % path)
277
		Logs.pprint('CYAN', '	libs : %s' % libs)
293
		Logs.pprint('CYAN', '	libs : %s' % libs)
278
294
295
296
	def try_link():
297
		if 'system' in params['lib']:
298
			self.check_cxx(
299
			 fragment="\n".join([
300
			  '#include <boost/system/error_code.hpp>',
301
			  'int main() { boost::system::error_code c; }',
302
			 ]),
303
			 use=var,
304
			 execute=False,
305
			)
306
		if 'thread' in params['lib']:
307
			self.check_cxx(
308
			 fragment="\n".join([
309
			  '#include <boost/thread.hpp>',
310
			  'int main() { boost::thread t; }',
311
			 ]),
312
			 use=var,
313
			 execute=False,
314
			)
315
316
	if params.get('linkage_autodetect', False):
317
		self.start_msg("Attempting to detect boost linkage flags")
318
		toolset = self.boost_get_toolset(kw.get('toolset', ''))
319
		if toolset in ['vc']:
320
			# disable auto-linking feature, causing error LNK1181
321
			# because the code wants to be linked against
322
			self.env['DEFINES_%s' % var] += ['BOOST_ALL_NO_LIB']
323
324
			# if no dlls are present, we guess the .lib files are not stubs
325
			has_dlls = False
326
			for x in Utils.listdir(path):
327
				if x.endswith(self.env.cxxshlib_PATTERN % ''):
328
					has_dlls = True
329
					break
330
			if not has_dlls:
331
				self.env['STLIBPATH_%s' % var] = [path]
332
				self.env['STLIB_%s' % var] = libs
333
				del self.env['LIB_%s' % var]
334
				del self.env['LIBPATH_%s' % var]
335
336
			# we attempt to play with some known-to-work CXXFLAGS combinations
337
			for cxxflags in (['/MD', '/EHsc'], []):
338
				self.env.stash()
339
				self.env["CXXFLAGS_%s" % var] += cxxflags
340
				try:
341
					try_link()
342
					self.end_msg("ok: winning cxxflags combination: %s" % (self.env["CXXFLAGS_%s" % var]))
343
					e = None
344
					break
345
				except Errors.ConfigurationError as exc:
346
					self.env.revert()
347
					e = exc
348
349
			if e is not None:
350
				self.fatal("Could not auto-detect boost linking flags combination, you may report it to boost.py author", ex=e)
351
		else:
352
			self.fatal("Boost linkage flags auto-detection not implemented (needed ?) for this toolchain")
353
	else:
354
		self.start_msg('Checking for boost linkage')
355
		try:
356
			try_link()
357
		except Errors.ConfigurationError as e:
358
			self.fatal("Could not link against boost libraries using supplied options")
359
		self.end_msg('ok')
360

Return to bug 1562