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
10 #def set_options(opt):
11 # opt.tool_options('boost')
15 # # ... (e.g. conf.check_tool('g++'))
16 # conf.check_tool('boost')
17 # conf.check_boost(lib='signals filesystem', static='onlystatic', score_version=(-1000, 1000), tag_minscore=1000)
20 # bld(source='main.c', target='bar', uselib="BOOST BOOST_SYSTEM")
23 # * find_includes should be called only once!
26 ######## boost update ###########
27 ## ITA: * the method get_boost_version_number does work
28 ## * the rest of the code has not really been tried
29 # * make certain a demo is provided (in demos/adv for example)
31 # TODO: bad and underdocumented code -> boost.py will be removed in waf 1.6 to be rewritten later
33 import os
.path
, glob
, types
, re
, sys
34 import Configure
, config_c
, Options
, Utils
, Logs
35 from Logs
import warn
, debug
36 from Configure
import conf
40 #include <boost/version.hpp>
41 int main() { std::cout << BOOST_VERSION << std::endl; }
44 boost_libpath
= ['/usr/lib', '/usr/local/lib', '/opt/local/lib', '/sw/lib', '/lib']
45 boost_cpppath
= ['/usr/include', '/usr/local/include', '/opt/local/include', '/sw/include']
47 STATIC_NOSTATIC
= 'nostatic'
49 STATIC_ONLYSTATIC
= 'onlystatic'
51 is_versiontag
= re
.compile('^\d+_\d+_?\d*$')
52 is_threadingtag
= re
.compile('^mt$')
53 is_abitag
= re
.compile('^[sgydpn]+$')
54 is_toolsettag
= re
.compile('^(acc|borland|como|cw|dmc|darwin|gcc|hp_cxx|intel|kylix|vc|mgw|qcc|sun|vacpp)\d*$')
55 is_pythontag
=re
.compile('^py[0-9]{2}$')
58 opt
.add_option('--boost-includes', type='string', default
='', dest
='boostincludes', help='path to the boost directory where the includes are e.g. /usr/local/include/boost-1_35')
59 opt
.add_option('--boost-libs', type='string', default
='', dest
='boostlibs', help='path to the directory where the boost libs are e.g. /usr/local/lib')
61 def string_to_version(s
):
62 version
= s
.split('.')
63 if len(version
) < 3: return 0
64 return int(version
[0])*100000 + int(version
[1])*100 + int(version
[2])
66 def version_string(version
):
67 major
= version
/ 100000
68 minor
= version
/ 100 % 1000
69 minor_minor
= version
% 100
71 return "%d_%d" % (major
, minor
)
73 return "%d_%d_%d" % (major
, minor
, minor_minor
)
75 def libfiles(lib
, pattern
, lib_paths
):
77 for lib_path
in lib_paths
:
78 libname
= pattern
% ('boost_%s[!_]*' % lib
)
79 result
+= glob
.glob(os
.path
.join(lib_path
, libname
))
83 def get_boost_version_number(self
, dir):
84 """silently retrieve the boost version number"""
86 return self
.run_c_code(compiler
='cxx', code
=boost_code
, includes
=dir, execute
=1, env
=self
.env
.copy(), type='cprogram', compile_mode
='cxx', compile_filename
='test.cpp')
87 except Configure
.ConfigurationError
, e
:
90 def set_default(kw
, var
, val
):
94 def tags_score(tags
, kw
):
98 see http://www.boost.org/doc/libs/1_35_0/more/getting_started/unix-variants.html 6.1
102 'threading': kw
['tag_threading'],
103 'abi': kw
['tag_abi'],
104 'toolset': kw
['tag_toolset'],
105 'version': kw
['tag_version'],
106 'python': kw
['tag_python']
109 if kw
['tag_toolset'] is None:
111 toolset
= v
['CXX_NAME']
113 version_no
= v
['CXX_VERSION'].split('.')
114 toolset
+= version_no
[0]
115 if len(version_no
) > 1:
116 toolset
+= version_no
[1]
117 needed_tags
['toolset'] = toolset
121 if is_versiontag
.match(tag
): found_tags
['version'] = tag
122 if is_threadingtag
.match(tag
): found_tags
['threading'] = tag
123 if is_abitag
.match(tag
): found_tags
['abi'] = tag
124 if is_toolsettag
.match(tag
): found_tags
['toolset'] = tag
125 if is_pythontag
.match(tag
): found_tags
['python'] = tag
127 for tagname
in needed_tags
.iterkeys():
128 if needed_tags
[tagname
] is not None and tagname
in found_tags
:
129 if re
.compile(needed_tags
[tagname
]).match(found_tags
[tagname
]):
130 score
+= kw
['score_' + tagname
][0]
132 score
+= kw
['score_' + tagname
][1]
136 def validate_boost(self
, kw
):
137 ver
= kw
.get('version', '')
139 for x
in 'min_version max_version version'.split():
140 set_default(kw
, x
, ver
)
142 set_default(kw
, 'lib', '')
143 kw
['lib'] = Utils
.to_list(kw
['lib'])
145 set_default(kw
, 'env', self
.env
)
147 set_default(kw
, 'libpath', boost_libpath
)
148 set_default(kw
, 'cpppath', boost_cpppath
)
150 for x
in 'tag_threading tag_version tag_toolset'.split():
151 set_default(kw
, x
, None)
152 set_default(kw
, 'tag_abi', '^[^d]*$')
154 set_default(kw
, 'python', str(sys
.version_info
[0]) + str(sys
.version_info
[1]) )
155 set_default(kw
, 'tag_python', '^py' + kw
['python'] + '$')
157 set_default(kw
, 'score_threading', (10, -10))
158 set_default(kw
, 'score_abi', (10, -10))
159 set_default(kw
, 'score_python', (10,-10))
160 set_default(kw
, 'score_toolset', (1, -1))
161 set_default(kw
, 'score_version', (100, -100))
163 set_default(kw
, 'score_min', 0)
164 set_default(kw
, 'static', STATIC_NOSTATIC
)
165 set_default(kw
, 'found_includes', False)
166 set_default(kw
, 'min_score', 0)
168 set_default(kw
, 'errmsg', 'not found')
169 set_default(kw
, 'okmsg', 'ok')
172 def find_boost_includes(self
, kw
):
174 check every path in kw['cpppath'] for subdir
175 that either starts with boost- or is named boost.
177 Then the version is checked and selected accordingly to
178 min_version/max_version. The highest possible version number is
181 If no versiontag is set the versiontag is set accordingly to the
182 selected library and CPPPATH_BOOST is set.
184 boostPath
= getattr(Options
.options
, 'boostincludes', '')
186 boostPath
= [os
.path
.normpath(os
.path
.expandvars(os
.path
.expanduser(boostPath
)))]
188 boostPath
= Utils
.to_list(kw
['cpppath'])
190 min_version
= string_to_version(kw
.get('min_version', ''))
191 max_version
= string_to_version(kw
.get('max_version', '')) or (sys
.maxint
- 1)
194 for include_path
in boostPath
:
195 boost_paths
= [p
for p
in glob
.glob(os
.path
.join(include_path
, 'boost*')) if os
.path
.isdir(p
)]
196 debug('BOOST Paths: %r' % boost_paths
)
197 for path
in boost_paths
:
198 pathname
= os
.path
.split(path
)[-1]
200 if pathname
== 'boost':
202 ret
= self
.get_boost_version_number(path
)
203 elif pathname
.startswith('boost-'):
204 ret
= self
.get_boost_version_number(path
)
207 if ret
!= -1 and ret
>= min_version
and ret
<= max_version
and ret
> version
:
211 self
.fatal('boost headers not found! (required version min: %s max: %s)'
212 % (kw
['min_version'], kw
['max_version']))
215 found_version
= version_string(version
)
216 versiontag
= '^' + found_version
+ '$'
217 if kw
['tag_version'] is None:
218 kw
['tag_version'] = versiontag
219 elif kw
['tag_version'] != versiontag
:
220 warn('boost header version %r and tag_version %r do not match!' % (versiontag
, kw
['tag_version']))
222 env
['CPPPATH_BOOST'] = boost_path
223 env
['BOOST_VERSION'] = found_version
224 self
.found_includes
= 1
225 ret
= 'Version %s (%s)' % (found_version
, boost_path
)
229 def find_boost_library(self
, lib
, kw
):
231 def find_library_from_list(lib
, files
):
232 lib_pattern
= re
.compile('.*boost_(.*?)\..*')
233 result
= (None, None)
234 resultscore
= kw
['min_score'] - 1
236 m
= lib_pattern
.search(file, 1)
239 libtags
= libname
.split('-')[1:]
240 currentscore
= tags_score(libtags
, kw
)
241 if currentscore
> resultscore
:
242 result
= (libname
, file)
243 resultscore
= currentscore
246 lib_paths
= getattr(Options
.options
, 'boostlibs', '')
248 lib_paths
= [os
.path
.normpath(os
.path
.expandvars(os
.path
.expanduser(lib_paths
)))]
250 lib_paths
= Utils
.to_list(kw
['libpath'])
252 v
= kw
.get('env', self
.env
)
254 (libname
, file) = (None, None)
255 if kw
['static'] in [STATIC_NOSTATIC
, STATIC_BOTH
]:
256 st_env_prefix
= 'LIB'
257 files
= libfiles(lib
, v
['shlib_PATTERN'], lib_paths
)
258 (libname
, file) = find_library_from_list(lib
, files
)
259 if libname
is None and kw
['static'] in [STATIC_ONLYSTATIC
, STATIC_BOTH
]:
260 st_env_prefix
= 'STATICLIB'
261 staticLibPattern
= v
['staticlib_PATTERN']
262 if self
.env
['CC_NAME'] == 'msvc':
263 staticLibPattern
= 'lib' + staticLibPattern
264 files
= libfiles(lib
, staticLibPattern
, lib_paths
)
265 (libname
, file) = find_library_from_list(lib
, files
)
266 if libname
is not None:
267 v
['LIBPATH_BOOST_' + lib
.upper()] = [os
.path
.split(file)[0]]
268 if self
.env
['CC_NAME'] == 'msvc' and os
.path
.splitext(file)[1] == '.lib':
269 v
[st_env_prefix
+ '_BOOST_' + lib
.upper()] = ['libboost_'+libname
]
271 v
[st_env_prefix
+ '_BOOST_' + lib
.upper()] = ['boost_'+libname
]
273 self
.fatal('lib boost_' + lib
+ ' not found!')
276 def check_boost(self
, *k
, **kw
):
278 This should be the main entry point
286 - toolsettag - None or a regexp
287 - threadingtag - None or a regexp
288 - abitag - None or a regexp
289 - versiontag - WARNING: you should rather use version or min_version/max_version
290 - static - look for static libs (values:
291 'nostatic' or STATIC_NOSTATIC - ignore static libs (default)
292 'both' or STATIC_BOTH - find static libs, too
293 'onlystatic' or STATIC_ONLYSTATIC - find only static libs
298 * the scores are tuples (match_score, nomatch_score)
299 match_score is the added to the score if the tag is matched
300 nomatch_score is added when a tag is found and does not match
304 if not self
.env
['CXX']:
305 self
.fatal('load a c++ compiler tool first, for example conf.check_tool("g++")')
306 self
.validate_boost(kw
)
309 if not kw
.get('found_includes', None):
310 self
.check_message_1(kw
.get('msg_includes', 'boost headers'))
311 ret
= self
.find_boost_includes(kw
)
313 except Configure
.ConfigurationError
, e
:
315 self
.check_message_2(kw
['errmsg'], 'YELLOW')
316 if 'mandatory' in kw
:
320 self
.fatal('the configuration failed (see %r)' % self
.log
.name
)
323 self
.check_message_2(kw
.get('okmsg_includes', ret
))
325 for lib
in kw
['lib']:
326 self
.check_message_1('library boost_'+lib
)
328 self
.find_boost_library(lib
, kw
)
329 except Configure
.ConfigurationError
, e
:
332 self
.check_message_2(kw
['errmsg'], 'YELLOW')
333 if 'mandatory' in kw
:
337 self
.fatal('the configuration failed (see %r)' % self
.log
.name
)
340 self
.check_message_2(kw
['okmsg'])