jack2 codebase
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

412 lines
14KB

  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-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. from waflib.TaskGen import feature, after_method
  46. BOOST_LIBS = ['/usr/lib/x86_64-linux-gnu', '/usr/lib/i386-linux-gnu',
  47. '/usr/lib', '/usr/local/lib', '/opt/local/lib', '/sw/lib', '/lib']
  48. BOOST_INCLUDES = ['/usr/include', '/usr/local/include', '/opt/local/include', '/sw/include']
  49. BOOST_VERSION_FILE = 'boost/version.hpp'
  50. BOOST_VERSION_CODE = '''
  51. #include <iostream>
  52. #include <boost/version.hpp>
  53. int main() { std::cout << BOOST_LIB_VERSION << std::endl; }
  54. '''
  55. BOOST_ERROR_CODE = '''
  56. #include <boost/system/error_code.hpp>
  57. int main() { boost::system::error_code c; }
  58. '''
  59. BOOST_THREAD_CODE = '''
  60. #include <boost/thread.hpp>
  61. int main() { boost::thread t; }
  62. '''
  63. # toolsets from {boost_dir}/tools/build/v2/tools/common.jam
  64. PLATFORM = Utils.unversioned_sys_platform()
  65. detect_intel = lambda env: (PLATFORM == 'win32') and 'iw' or 'il'
  66. detect_clang = lambda env: (PLATFORM == 'darwin') and 'clang-darwin' or 'clang'
  67. detect_mingw = lambda env: (re.search('MinGW', env.CXX[0])) and 'mgw' or 'gcc'
  68. BOOST_TOOLSETS = {
  69. 'borland': 'bcb',
  70. 'clang': detect_clang,
  71. 'como': 'como',
  72. 'cw': 'cw',
  73. 'darwin': 'xgcc',
  74. 'edg': 'edg',
  75. 'g++': detect_mingw,
  76. 'gcc': detect_mingw,
  77. 'icpc': detect_intel,
  78. 'intel': detect_intel,
  79. 'kcc': 'kcc',
  80. 'kylix': 'bck',
  81. 'mipspro': 'mp',
  82. 'mingw': 'mgw',
  83. 'msvc': 'vc',
  84. 'qcc': 'qcc',
  85. 'sun': 'sw',
  86. 'sunc++': 'sw',
  87. 'tru64cxx': 'tru',
  88. 'vacpp': 'xlc'
  89. }
  90. def options(opt):
  91. opt.add_option('--boost-includes', type='string',
  92. default='', dest='boost_includes',
  93. help='''path to the boost includes root (~boost root)
  94. e.g. /path/to/boost_1_47_0''')
  95. opt.add_option('--boost-libs', type='string',
  96. default='', dest='boost_libs',
  97. help='''path to the directory where the boost libs are
  98. e.g. /path/to/boost_1_47_0/stage/lib''')
  99. opt.add_option('--boost-mt', action='store_true',
  100. default=False, dest='boost_mt',
  101. help='select multi-threaded libraries')
  102. opt.add_option('--boost-abi', type='string', default='', dest='boost_abi',
  103. help='''select libraries with tags (gd for debug, static is automatically added),
  104. see doc Boost, Getting Started, chapter 6.1''')
  105. opt.add_option('--boost-linkage_autodetect', action="store_true", dest='boost_linkage_autodetect',
  106. help="auto-detect boost linkage options (don't get used to it / might break other stuff)")
  107. opt.add_option('--boost-toolset', type='string',
  108. default='', dest='boost_toolset',
  109. help='force a toolset e.g. msvc, vc90, \
  110. gcc, mingw, mgw45 (default: auto)')
  111. py_version = '%d%d' % (sys.version_info[0], sys.version_info[1])
  112. opt.add_option('--boost-python', type='string',
  113. default=py_version, dest='boost_python',
  114. help='select the lib python with this version \
  115. (default: %s)' % py_version)
  116. @conf
  117. def __boost_get_version_file(self, d):
  118. if not d:
  119. return None
  120. dnode = self.root.find_dir(d)
  121. if dnode:
  122. return dnode.find_node(BOOST_VERSION_FILE)
  123. return None
  124. @conf
  125. def boost_get_version(self, d):
  126. """silently retrieve the boost version number"""
  127. node = self.__boost_get_version_file(d)
  128. if node:
  129. try:
  130. txt = node.read()
  131. except EnvironmentError:
  132. Logs.error("Could not read the file %r" % node.abspath())
  133. else:
  134. re_but = re.compile('^#define\\s+BOOST_LIB_VERSION\\s+"(.*)"', re.M)
  135. m = re_but.search(txt)
  136. if m:
  137. return m.group(1)
  138. return self.check_cxx(fragment=BOOST_VERSION_CODE, includes=[d], execute=True, define_ret=True)
  139. @conf
  140. def boost_get_includes(self, *k, **kw):
  141. includes = k and k[0] or kw.get('includes', None)
  142. if includes and self.__boost_get_version_file(includes):
  143. return includes
  144. for d in self.environ.get('INCLUDE', '').split(';') + BOOST_INCLUDES:
  145. if self.__boost_get_version_file(d):
  146. return d
  147. if includes:
  148. self.end_msg('headers not found in %s' % includes)
  149. self.fatal('The configuration failed')
  150. else:
  151. self.end_msg('headers not found, please provide a --boost-includes argument (see help)')
  152. self.fatal('The configuration failed')
  153. @conf
  154. def boost_get_toolset(self, cc):
  155. toolset = cc
  156. if not cc:
  157. build_platform = Utils.unversioned_sys_platform()
  158. if build_platform in BOOST_TOOLSETS:
  159. cc = build_platform
  160. else:
  161. cc = self.env.CXX_NAME
  162. if cc in BOOST_TOOLSETS:
  163. toolset = BOOST_TOOLSETS[cc]
  164. return isinstance(toolset, str) and toolset or toolset(self.env)
  165. @conf
  166. def __boost_get_libs_path(self, *k, **kw):
  167. ''' return the lib path and all the files in it '''
  168. if 'files' in kw:
  169. return self.root.find_dir('.'), Utils.to_list(kw['files'])
  170. libs = k and k[0] or kw.get('libs', None)
  171. if libs:
  172. path = self.root.find_dir(libs)
  173. files = path.ant_glob('*boost_*')
  174. if not libs or not files:
  175. for d in self.environ.get('LIB', '').split(';') + BOOST_LIBS:
  176. if not d:
  177. continue
  178. path = self.root.find_dir(d)
  179. if path:
  180. files = path.ant_glob('*boost_*')
  181. if files:
  182. break
  183. path = self.root.find_dir(d + '64')
  184. if path:
  185. files = path.ant_glob('*boost_*')
  186. if files:
  187. break
  188. if not path:
  189. if libs:
  190. self.end_msg('libs not found in %s' % libs)
  191. self.fatal('The configuration failed')
  192. else:
  193. self.end_msg('libs not found, please provide a --boost-libs argument (see help)')
  194. self.fatal('The configuration failed')
  195. self.to_log('Found the boost path in %r with the libraries:' % path)
  196. for x in files:
  197. self.to_log(' %r' % x)
  198. return path, files
  199. @conf
  200. def boost_get_libs(self, *k, **kw):
  201. '''
  202. return the lib path and the required libs
  203. according to the parameters
  204. '''
  205. path, files = self.__boost_get_libs_path(**kw)
  206. files = sorted(files, key=lambda f: (len(f.name), f.name), reverse=True)
  207. toolset = self.boost_get_toolset(kw.get('toolset', ''))
  208. toolset_pat = '(-%s[0-9]{0,3})' % toolset
  209. version = '-%s' % self.env.BOOST_VERSION
  210. def find_lib(re_lib, files):
  211. for file in files:
  212. if re_lib.search(file.name):
  213. self.to_log('Found boost lib %s' % file)
  214. return file
  215. return None
  216. def format_lib_name(name):
  217. if name.startswith('lib') and self.env.CC_NAME != 'msvc':
  218. name = name[3:]
  219. return name[:name.rfind('.')]
  220. def match_libs(lib_names, is_static):
  221. libs = []
  222. lib_names = Utils.to_list(lib_names)
  223. if not lib_names:
  224. return libs
  225. t = []
  226. if kw.get('mt', False):
  227. t.append('-mt')
  228. if kw.get('abi', None):
  229. t.append('%s%s' % (is_static and '-s' or '-', kw['abi']))
  230. elif is_static:
  231. t.append('-s')
  232. tags_pat = t and ''.join(t) or ''
  233. ext = is_static and self.env.cxxstlib_PATTERN or self.env.cxxshlib_PATTERN
  234. ext = ext.partition('%s')[2] # remove '%s' or 'lib%s' from PATTERN
  235. for lib in lib_names:
  236. if lib == 'python':
  237. # for instance, with python='27',
  238. # accepts '-py27', '-py2', '27' and '2'
  239. # but will reject '-py3', '-py26', '26' and '3'
  240. tags = '({0})?((-py{2})|(-py{1}(?=[^0-9]))|({2})|({1}(?=[^0-9]))|(?=[^0-9])(?!-py))'.format(tags_pat, kw['python'][0], kw['python'])
  241. else:
  242. tags = tags_pat
  243. # Trying libraries, from most strict match to least one
  244. for pattern in ['boost_%s%s%s%s%s$' % (lib, toolset_pat, tags, version, ext),
  245. 'boost_%s%s%s%s$' % (lib, tags, version, ext),
  246. # Give up trying to find the right version
  247. 'boost_%s%s%s%s$' % (lib, toolset_pat, tags, ext),
  248. 'boost_%s%s%s$' % (lib, tags, ext),
  249. 'boost_%s%s$' % (lib, ext),
  250. 'boost_%s' % lib]:
  251. self.to_log('Trying pattern %s' % pattern)
  252. file = find_lib(re.compile(pattern), files)
  253. if file:
  254. libs.append(format_lib_name(file.name))
  255. break
  256. else:
  257. self.end_msg('lib %s not found in %s' % (lib, path.abspath()))
  258. self.fatal('The configuration failed')
  259. return libs
  260. return path.abspath(), match_libs(kw.get('lib', None), False), match_libs(kw.get('stlib', None), True)
  261. @conf
  262. def check_boost(self, *k, **kw):
  263. """
  264. Initialize boost libraries to be used.
  265. Keywords: you can pass the same parameters as with the command line (without "--boost-").
  266. Note that the command line has the priority, and should preferably be used.
  267. """
  268. if not self.env['CXX']:
  269. self.fatal('load a c++ compiler first, conf.load("compiler_cxx")')
  270. params = {
  271. 'lib': k and k[0] or kw.get('lib', None),
  272. 'stlib': kw.get('stlib', None)
  273. }
  274. for key, value in self.options.__dict__.items():
  275. if not key.startswith('boost_'):
  276. continue
  277. key = key[len('boost_'):]
  278. params[key] = value and value or kw.get(key, '')
  279. var = kw.get('uselib_store', 'BOOST')
  280. self.start_msg('Checking boost includes')
  281. self.env['INCLUDES_%s' % var] = inc = self.boost_get_includes(**params)
  282. self.env.BOOST_VERSION = self.boost_get_version(inc)
  283. self.end_msg(self.env.BOOST_VERSION)
  284. if Logs.verbose:
  285. Logs.pprint('CYAN', ' path : %s' % self.env['INCLUDES_%s' % var])
  286. if not params['lib'] and not params['stlib']:
  287. return
  288. if 'static' in kw or 'static' in params:
  289. Logs.warn('boost: static parameter is deprecated, use stlib instead.')
  290. self.start_msg('Checking boost libs')
  291. path, libs, stlibs = self.boost_get_libs(**params)
  292. self.env['LIBPATH_%s' % var] = [path]
  293. self.env['STLIBPATH_%s' % var] = [path]
  294. self.env['LIB_%s' % var] = libs
  295. self.env['STLIB_%s' % var] = stlibs
  296. self.end_msg('ok')
  297. if Logs.verbose:
  298. Logs.pprint('CYAN', ' path : %s' % path)
  299. Logs.pprint('CYAN', ' shared libs : %s' % libs)
  300. Logs.pprint('CYAN', ' static libs : %s' % stlibs)
  301. def try_link():
  302. if (params['lib'] and 'system' in params['lib']) or \
  303. params['stlib'] and 'system' in params['stlib']:
  304. self.check_cxx(fragment=BOOST_ERROR_CODE, use=var, execute=False)
  305. if (params['lib'] and 'thread' in params['lib']) or \
  306. params['stlib'] and 'thread' in params['stlib']:
  307. self.check_cxx(fragment=BOOST_THREAD_CODE, use=var, execute=False)
  308. if params.get('linkage_autodetect', False):
  309. self.start_msg("Attempting to detect boost linkage flags")
  310. toolset = self.boost_get_toolset(kw.get('toolset', ''))
  311. if toolset in ('vc',):
  312. # disable auto-linking feature, causing error LNK1181
  313. # because the code wants to be linked against
  314. self.env['DEFINES_%s' % var] += ['BOOST_ALL_NO_LIB']
  315. # if no dlls are present, we guess the .lib files are not stubs
  316. has_dlls = False
  317. for x in Utils.listdir(path):
  318. if x.endswith(self.env.cxxshlib_PATTERN % ''):
  319. has_dlls = True
  320. break
  321. if not has_dlls:
  322. self.env['STLIBPATH_%s' % var] = [path]
  323. self.env['STLIB_%s' % var] = libs
  324. del self.env['LIB_%s' % var]
  325. del self.env['LIBPATH_%s' % var]
  326. # we attempt to play with some known-to-work CXXFLAGS combinations
  327. for cxxflags in (['/MD', '/EHsc'], []):
  328. self.env.stash()
  329. self.env["CXXFLAGS_%s" % var] += cxxflags
  330. try:
  331. try_link()
  332. self.end_msg("ok: winning cxxflags combination: %s" % (self.env["CXXFLAGS_%s" % var]))
  333. exc = None
  334. break
  335. except Errors.ConfigurationError as e:
  336. self.env.revert()
  337. exc = e
  338. if exc is not None:
  339. self.end_msg("Could not auto-detect boost linking flags combination, you may report it to boost.py author", ex=exc)
  340. self.fatal('The configuration failed')
  341. else:
  342. self.end_msg("Boost linkage flags auto-detection not implemented (needed ?) for this toolchain")
  343. self.fatal('The configuration failed')
  344. else:
  345. self.start_msg('Checking for boost linkage')
  346. try:
  347. try_link()
  348. except Errors.ConfigurationError as e:
  349. self.end_msg("Could not link against boost libraries using supplied options")
  350. self.fatal('The configuration failed')
  351. self.end_msg('ok')
  352. @feature('cxx')
  353. @after_method('apply_link')
  354. def install_boost(self):
  355. if install_boost.done or not Utils.is_win32 or not self.bld.cmd.startswith('install'):
  356. return
  357. install_boost.done = True
  358. inst_to = getattr(self, 'install_path', '${BINDIR}')
  359. for lib in self.env.LIB_BOOST:
  360. try:
  361. file = self.bld.find_file(self.env.cxxshlib_PATTERN % lib, self.env.LIBPATH_BOOST)
  362. self.bld.install_files(inst_to, self.bld.root.find_node(file))
  363. except:
  364. continue
  365. install_boost.done = False