PageRenderTime 62ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 0ms

/common/waf/boost.py

https://github.com/a4/a4
Python | 366 lines | 345 code | 1 blank | 20 comment | 2 complexity | 0bb05f54676324809e5c980c5a060a0b MD5 | raw file
  1. #!/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. This is an extra tool, not bundled with the default waf binary.
  12. To add the boost tool to the waf file:
  13. $ ./waf-light --tools=compat15,boost
  14. or, if you have waf >= 1.6.2
  15. $ ./waf update --files=boost
  16. When using this tool, the wscript will look like:
  17. def options(opt):
  18. opt.load('compiler_cxx boost')
  19. def configure(conf):
  20. conf.load('compiler_cxx boost')
  21. conf.check_boost(lib='system filesystem')
  22. def build(bld):
  23. bld(source='main.cpp', target='app', use='BOOST')
  24. Options are generated, in order to specify the location of boost includes/libraries.
  25. The `check_boost` configuration function allows to specify the used boost libraries.
  26. It can also provide default arguments to the --boost-static and --boost-mt command-line arguments.
  27. Everything will be packaged together in a BOOST component that you can use.
  28. When using MSVC, a lot of compilation flags need to match your BOOST build configuration:
  29. - you may have to add /EHsc to your CXXFLAGS or define boost::throw_exception if BOOST_NO_EXCEPTIONS is defined.
  30. Errors: C4530
  31. - boost libraries will try to be smart and use the (pretty but often not useful) auto-linking feature of MSVC
  32. So before calling `conf.check_boost` you might want to disabling by adding:
  33. conf.env.DEFINES_BOOST += ['BOOST_ALL_NO_LIB']
  34. Errors:
  35. - boost might also be compiled with /MT, which links the runtime statically.
  36. If you have problems with redefined symbols,
  37. self.env['DEFINES_%s' % var] += ['BOOST_ALL_NO_LIB']
  38. self.env['CXXFLAGS_%s' % var] += ['/MD', '/EHsc']
  39. Passing `--boost-linkage_autodetect` might help ensuring having a correct linkage in some basic cases.
  40. '''
  41. import sys
  42. import re
  43. from waflib import Utils, Logs, Errors
  44. from waflib.Configure import conf
  45. BOOST_LIBS = ['/usr/lib', '/usr/local/lib', '/opt/local/lib', '/sw/lib', '/lib']
  46. BOOST_INCLUDES = ['/usr/include', '/usr/local/include', '/opt/local/include', '/sw/include']
  47. BOOST_VERSION_FILE = 'boost/version.hpp'
  48. BOOST_VERSION_CODE = '''
  49. #include <iostream>
  50. #include <boost/version.hpp>
  51. int main() { std::cout << BOOST_LIB_VERSION << std::endl; }
  52. '''
  53. # toolsets from {boost_dir}/tools/build/v2/tools/common.jam
  54. PLATFORM = Utils.unversioned_sys_platform()
  55. detect_intel = lambda env: (PLATFORM == 'win32') and 'iw' or 'il'
  56. detect_clang = lambda env: (PLATFORM == 'darwin') and 'clang-darwin' or 'clang'
  57. detect_mingw = lambda env: (re.search('MinGW', env.CXX[0])) and 'mgw' or 'gcc'
  58. BOOST_TOOLSETS = {
  59. 'borland': 'bcb',
  60. 'clang': detect_clang,
  61. 'como': 'como',
  62. 'cw': 'cw',
  63. 'darwin': 'xgcc',
  64. 'edg': 'edg',
  65. 'g++': detect_mingw,
  66. 'gcc': detect_mingw,
  67. 'icpc': detect_intel,
  68. 'intel': detect_intel,
  69. 'kcc': 'kcc',
  70. 'kylix': 'bck',
  71. 'mipspro': 'mp',
  72. 'mingw': 'mgw',
  73. 'msvc': 'vc',
  74. 'qcc': 'qcc',
  75. 'sun': 'sw',
  76. 'sunc++': 'sw',
  77. 'tru64cxx': 'tru',
  78. 'vacpp': 'xlc'
  79. }
  80. def options(opt):
  81. opt.add_option('--boost-includes', type='string',
  82. default='', dest='boost_includes',
  83. help='''path to the boost includes root (~boost root), '''\
  84. '''e.g. /path/to/boost_1_47_0''')
  85. opt.add_option('--boost-libs', type='string',
  86. default='', dest='boost_libs',
  87. help='''path to the directory where the boost libs are, '''\
  88. '''e.g. /path/to/boost_1_47_0/stage/lib''')
  89. opt.add_option('--boost-static', action='store_true',
  90. default=False, dest='boost_static',
  91. help='link with static boost libraries (.lib/.a)')
  92. opt.add_option('--boost-mt', action='store_true',
  93. default=False, dest='boost_mt',
  94. help='select multi-threaded libraries')
  95. opt.add_option('--boost-abi', type='string', default='', dest='boost_abi',
  96. help='''select libraries with tags (dgsyp, d for debug), '''\
  97. '''see doc Boost, Getting Started, chapter 6.1''')
  98. opt.add_option('--boost-linkage_autodetect', action="store_true", dest='boost_linkage_autodetect',
  99. help="auto-detect boost linkage options (don't get used to it / might break other stuff)")
  100. opt.add_option('--boost-toolset', type='string',
  101. default='', dest='boost_toolset',
  102. help='force a toolset e.g. msvc, vc90, '\
  103. 'gcc, mingw, mgw45 (default: auto)')
  104. py_version = '%d%d' % (sys.version_info[0], sys.version_info[1])
  105. opt.add_option('--boost-python', type='string',
  106. default=py_version, dest='boost_python',
  107. help='select the lib python with this version '\
  108. '(default: %s)' % py_version)
  109. @conf
  110. def __boost_get_version_file(self, dir):
  111. try:
  112. return self.root.find_dir(dir).find_node(BOOST_VERSION_FILE)
  113. except:
  114. return None
  115. @conf
  116. def boost_get_version(self, dir):
  117. """silently retrieve the boost version number"""
  118. re_but = re.compile('^#define\\s+BOOST_LIB_VERSION\\s+"(.*)"$', re.M)
  119. try:
  120. val = re_but.search(self.__boost_get_version_file(dir).read()).group(1)
  121. except:
  122. val = self.check_cxx(fragment=BOOST_VERSION_CODE, includes=[dir],
  123. execute=True, define_ret=True)
  124. return val
  125. @conf
  126. def boost_get_includes(self, *k, **kw):
  127. includes = k and k[0] or kw.get('includes', None)
  128. if includes and self.__boost_get_version_file(includes):
  129. return includes
  130. for dir in BOOST_INCLUDES:
  131. if self.__boost_get_version_file(dir):
  132. return dir
  133. if includes:
  134. self.fatal('headers not found in %s' % includes)
  135. else:
  136. self.fatal('headers not found, please provide a --boost-includes argument (see help)')
  137. @conf
  138. def boost_get_toolset(self, cc):
  139. toolset = cc
  140. if not cc:
  141. build_platform = Utils.unversioned_sys_platform()
  142. if build_platform in BOOST_TOOLSETS:
  143. cc = build_platform
  144. else:
  145. cc = self.env.CXX_NAME
  146. if cc in BOOST_TOOLSETS:
  147. toolset = BOOST_TOOLSETS[cc]
  148. return isinstance(toolset, str) and toolset or toolset(self.env)
  149. @conf
  150. def __boost_get_libs_path(self, *k, **kw):
  151. ''' return the lib path and all the files in it '''
  152. if 'files' in kw:
  153. return self.root.find_dir('.'), Utils.to_list(kw['files'])
  154. libs = k and k[0] or kw.get('libs', None)
  155. if libs:
  156. path = self.root.find_dir(libs)
  157. files = path.ant_glob('*boost_*')
  158. if not libs or not files:
  159. for dir in BOOST_LIBS:
  160. try:
  161. path = self.root.find_dir(dir)
  162. files = path.ant_glob('*boost_*')
  163. if files:
  164. break
  165. path = self.root.find_dir(dir + '64')
  166. files = path.ant_glob('*boost_*')
  167. if files:
  168. break
  169. except:
  170. path = None
  171. if not path:
  172. if libs:
  173. self.fatal('libs not found in %s' % libs)
  174. else:
  175. self.fatal('libs not found, please provide a --boost-libs argument (see help)')
  176. self.to_log('Found the boost path in %r with the libraries:' % path)
  177. for x in files:
  178. self.to_log(' %r' % x)
  179. return path, files
  180. @conf
  181. def boost_get_libs(self, *k, **kw):
  182. '''
  183. return the lib path and the required libs
  184. according to the parameters
  185. '''
  186. path, files = self.__boost_get_libs_path(**kw)
  187. t = []
  188. if kw.get('abi', None):
  189. t.append(kw['abi'])
  190. if kw.get('mt', False):
  191. t.append('mt')
  192. tags = t and '(-%s)+' % '-'.join(t) or ''
  193. toolset = self.boost_get_toolset(kw.get('toolset', ''))
  194. toolset_pat = '(-%s[0-9]{0,3})+' % toolset
  195. version = '(-%s)+' % self.env.BOOST_VERSION
  196. def find_lib(re_lib, files):
  197. for file in files:
  198. if re_lib.search(file.name):
  199. self.to_log('Found boost lib %s' % file)
  200. return file
  201. return None
  202. def format_lib_name(name):
  203. if name.startswith('lib'):
  204. name = name[3:]
  205. return name.split('.')[0]
  206. libs = []
  207. for lib in Utils.to_list(k and k[0] or kw.get('lib', None)):
  208. py = (lib == 'python') and '(-py%s)+' % kw['python'] or ''
  209. # Trying libraries, from most strict match to least one
  210. self.to_log("lib='%s' toolset='%s' tags='%s' py='%s' version='%s'" % (lib, toolset_pat,tags,py,version))
  211. for pattern in ['boost_%s%s%s%s%s' % (lib, toolset_pat, tags, py, version),
  212. 'boost_%s%s%s%s' % (lib, tags, py, version),
  213. 'boost_%s%s%s' % (lib, tags, version),
  214. 'boost_%s%s' % (lib, version),
  215. # Give up trying to find the right version
  216. 'boost_%s%s%s%s' % (lib, toolset_pat, tags, py),
  217. 'boost_%s%s%s' % (lib, tags, py),
  218. 'boost_%s%s' % (lib, tags),
  219. 'boost_%s' % (lib,)]:
  220. self.to_log('Trying pattern %s' % pattern)
  221. file = find_lib(re.compile(pattern), files)
  222. if file:
  223. libs.append(format_lib_name(file.name))
  224. break
  225. else:
  226. self.fatal('lib %s not found in %s' % (lib, path.abspath()))
  227. return path.abspath(), libs
  228. @conf
  229. def check_boost(self, *k, **kw):
  230. """
  231. Initialize boost libraries to be used.
  232. Keywords: you can pass the same parameters as with the command line (without "--boost-").
  233. Note that the command line has the priority, and should preferably be used.
  234. """
  235. if not self.env['CXX']:
  236. self.fatal('load a c++ compiler first, conf.load("compiler_cxx")')
  237. params = {'lib': k and k[0] or kw.get('lib', None)}
  238. for key, value in self.options.__dict__.items():
  239. if not key.startswith('boost_'):
  240. continue
  241. key = key[len('boost_'):]
  242. params[key] = value and value or kw.get(key, '')
  243. var = kw.get('uselib_store', 'BOOST')
  244. self.start_msg('Checking boost includes')
  245. self.env['INCLUDES_%s' % var] = inc = self.boost_get_includes(**params)
  246. self.env.BOOST_VERSION = self.boost_get_version(inc)
  247. self.end_msg(self.env.BOOST_VERSION)
  248. if Logs.verbose:
  249. Logs.pprint('CYAN', ' path : %s' % self.env['INCLUDES_%s' % var])
  250. if not params['lib']:
  251. return
  252. self.start_msg('Checking boost libs')
  253. suffix = params.get('static', None) and 'ST' or ''
  254. path, libs = self.boost_get_libs(**params)
  255. self.env['%sLIBPATH_%s' % (suffix, var)] = [path]
  256. self.env['%sLIB_%s' % (suffix, var)] = libs
  257. self.end_msg('ok')
  258. if Logs.verbose:
  259. Logs.pprint('CYAN', ' path : %s' % path)
  260. Logs.pprint('CYAN', ' libs : %s' % libs)
  261. def try_link():
  262. if 'system' in params['lib']:
  263. self.check_cxx(
  264. fragment="\n".join([
  265. '#include <boost/system/error_code.hpp>',
  266. 'int main() { boost::system::error_code c; }',
  267. ]),
  268. use=var,
  269. execute=True,
  270. )
  271. if 'thread' in params['lib']:
  272. self.check_cxx(
  273. fragment="\n".join([
  274. '#include <boost/thread.hpp>',
  275. 'int main() { boost::thread t; }',
  276. ]),
  277. use=var,
  278. execute=True,
  279. lib="pthread",
  280. )
  281. if params.get('linkage_autodetect', False):
  282. self.start_msg("Attempting to detect boost linkage flags")
  283. toolset = self.boost_get_toolset(kw.get('toolset', ''))
  284. if toolset in ['vc']:
  285. # disable auto-linking feature, causing error LNK1181
  286. # because the code wants to be linked against
  287. self.env['DEFINES_%s' % var] += ['BOOST_ALL_NO_LIB']
  288. # if no dlls are present, we guess the .lib files are not stubs
  289. has_dlls = False
  290. for x in Utils.listdir(path):
  291. if x.endswith(self.env.cxxshlib_PATTERN % ''):
  292. has_dlls = True
  293. break
  294. if not has_dlls:
  295. self.env['STLIBPATH_%s' % var] = [path]
  296. self.env['STLIB_%s' % var] = libs
  297. del self.env['LIB_%s' % var]
  298. del self.env['LIBPATH_%s' % var]
  299. # we attempt to play with some known-to-work CXXFLAGS combinations
  300. for cxxflags in (['/MD', '/EHsc'], []):
  301. self.env.stash()
  302. self.env["CXXFLAGS_%s" % var] += cxxflags
  303. try:
  304. try_link()
  305. self.end_msg("ok: winning cxxflags combination: %s" % (self.env["CXXFLAGS_%s" % var]))
  306. e = None
  307. break
  308. except Errors.ConfigurationError:
  309. self.env.revert()
  310. import sys
  311. e = sys.exec_info()[1]
  312. if e is not None:
  313. self.fatal("Could not auto-detect boost linking flags combination, you may report it to boost.py author", ex=e)
  314. else:
  315. self.fatal("Boost linkage flags auto-detection not implemented (needed ?) for this toolchain")
  316. else:
  317. self.start_msg('Checking for boost linkage')
  318. try:
  319. try_link()
  320. except Errors.ConfigurationError:
  321. self.fatal("Could not link against boost libraries using supplied options")
  322. self.end_msg('ok')