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.

745 lines
21KB

  1. #!/usr/bin/env python
  2. # encoding: utf-8
  3. # Thomas Nagy, 2006-2015 (ita)
  4. """
  5. Tool Description
  6. ================
  7. This tool helps with finding Qt5 tools and libraries,
  8. and also provides syntactic sugar for using Qt5 tools.
  9. The following snippet illustrates the tool usage::
  10. def options(opt):
  11. opt.load('compiler_cxx qt5')
  12. def configure(conf):
  13. conf.load('compiler_cxx qt5')
  14. def build(bld):
  15. bld(
  16. features = 'qt5 cxx cxxprogram',
  17. uselib = 'QTCORE QTGUI QTOPENGL QTSVG',
  18. source = 'main.cpp textures.qrc aboutDialog.ui',
  19. target = 'window',
  20. )
  21. Here, the UI description and resource files will be processed
  22. to generate code.
  23. Usage
  24. =====
  25. Load the "qt5" tool.
  26. You also need to edit your sources accordingly:
  27. - the normal way of doing things is to have your C++ files
  28. include the .moc file.
  29. This is regarded as the best practice (and provides much faster
  30. compilations).
  31. It also implies that the include paths have beenset properly.
  32. - to have the include paths added automatically, use the following::
  33. from waflib.TaskGen import feature, before_method, after_method
  34. @feature('cxx')
  35. @after_method('process_source')
  36. @before_method('apply_incpaths')
  37. def add_includes_paths(self):
  38. incs = set(self.to_list(getattr(self, 'includes', '')))
  39. for x in self.compiled_tasks:
  40. incs.add(x.inputs[0].parent.path_from(self.path))
  41. self.includes = list(incs)
  42. Note: another tool provides Qt processing that does not require
  43. .moc includes, see 'playground/slow_qt/'.
  44. A few options (--qt{dir,bin,...}) and environment variables
  45. (QT5_{ROOT,DIR,MOC,UIC,XCOMPILE}) allow finer tuning of the tool,
  46. tool path selection, etc; please read the source for more info.
  47. """
  48. try:
  49. from xml.sax import make_parser
  50. from xml.sax.handler import ContentHandler
  51. except ImportError:
  52. has_xml = False
  53. ContentHandler = object
  54. else:
  55. has_xml = True
  56. import os, sys
  57. from waflib.Tools import cxx
  58. from waflib import Task, Utils, Options, Errors, Context
  59. from waflib.TaskGen import feature, after_method, extension
  60. from waflib.Configure import conf
  61. from waflib import Logs
  62. MOC_H = ['.h', '.hpp', '.hxx', '.hh']
  63. """
  64. File extensions associated to the .moc files
  65. """
  66. EXT_RCC = ['.qrc']
  67. """
  68. File extension for the resource (.qrc) files
  69. """
  70. EXT_UI = ['.ui']
  71. """
  72. File extension for the user interface (.ui) files
  73. """
  74. EXT_QT5 = ['.cpp', '.cc', '.cxx', '.C']
  75. """
  76. File extensions of C++ files that may require a .moc processing
  77. """
  78. QT5_LIBS = '''
  79. qtmain
  80. Qt5Bluetooth
  81. Qt5CLucene
  82. Qt5Concurrent
  83. Qt5Core
  84. Qt5DBus
  85. Qt5Declarative
  86. Qt5DesignerComponents
  87. Qt5Designer
  88. Qt5Gui
  89. Qt5Help
  90. Qt5MultimediaQuick_p
  91. Qt5Multimedia
  92. Qt5MultimediaWidgets
  93. Qt5Network
  94. Qt5Nfc
  95. Qt5OpenGL
  96. Qt5Positioning
  97. Qt5PrintSupport
  98. Qt5Qml
  99. Qt5QuickParticles
  100. Qt5Quick
  101. Qt5QuickTest
  102. Qt5Script
  103. Qt5ScriptTools
  104. Qt5Sensors
  105. Qt5SerialPort
  106. Qt5Sql
  107. Qt5Svg
  108. Qt5Test
  109. Qt5WebKit
  110. Qt5WebKitWidgets
  111. Qt5Widgets
  112. Qt5WinExtras
  113. Qt5X11Extras
  114. Qt5XmlPatterns
  115. Qt5Xml'''
  116. class qxx(Task.classes['cxx']):
  117. """
  118. Each C++ file can have zero or several .moc files to create.
  119. They are known only when the files are scanned (preprocessor)
  120. To avoid scanning the c++ files each time (parsing C/C++), the results
  121. are retrieved from the task cache (bld.node_deps/bld.raw_deps).
  122. The moc tasks are also created *dynamically* during the build.
  123. """
  124. def __init__(self, *k, **kw):
  125. Task.Task.__init__(self, *k, **kw)
  126. self.moc_done = 0
  127. def runnable_status(self):
  128. """
  129. Compute the task signature to make sure the scanner was executed. Create the
  130. moc tasks by using :py:meth:`waflib.Tools.qt5.qxx.add_moc_tasks` (if necessary),
  131. then postpone the task execution (there is no need to recompute the task signature).
  132. """
  133. if self.moc_done:
  134. return Task.Task.runnable_status(self)
  135. else:
  136. for t in self.run_after:
  137. if not t.hasrun:
  138. return Task.ASK_LATER
  139. self.add_moc_tasks()
  140. return Task.Task.runnable_status(self)
  141. def create_moc_task(self, h_node, m_node):
  142. """
  143. If several libraries use the same classes, it is possible that moc will run several times (Issue 1318)
  144. It is not possible to change the file names, but we can assume that the moc transformation will be identical,
  145. and the moc tasks can be shared in a global cache.
  146. The defines passed to moc will then depend on task generator order. If this is not acceptable, then
  147. use the tool slow_qt5 instead (and enjoy the slow builds... :-( )
  148. """
  149. try:
  150. moc_cache = self.generator.bld.moc_cache
  151. except AttributeError:
  152. moc_cache = self.generator.bld.moc_cache = {}
  153. try:
  154. return moc_cache[h_node]
  155. except KeyError:
  156. tsk = moc_cache[h_node] = Task.classes['moc'](env=self.env, generator=self.generator)
  157. tsk.set_inputs(h_node)
  158. tsk.set_outputs(m_node)
  159. if self.generator:
  160. self.generator.tasks.append(tsk)
  161. # direct injection in the build phase (safe because called from the main thread)
  162. gen = self.generator.bld.producer
  163. gen.outstanding.insert(0, tsk)
  164. gen.total += 1
  165. return tsk
  166. else:
  167. # remove the signature, it must be recomputed with the moc task
  168. delattr(self, 'cache_sig')
  169. def moc_h_ext(self):
  170. try:
  171. ext = Options.options.qt_header_ext.split()
  172. except AttributeError:
  173. pass
  174. if not ext:
  175. ext = MOC_H
  176. return ext
  177. def add_moc_tasks(self):
  178. """
  179. Create the moc tasks by looking in ``bld.raw_deps[self.uid()]``
  180. """
  181. node = self.inputs[0]
  182. bld = self.generator.bld
  183. try:
  184. # compute the signature once to know if there is a moc file to create
  185. self.signature()
  186. except KeyError:
  187. # the moc file may be referenced somewhere else
  188. pass
  189. else:
  190. # remove the signature, it must be recomputed with the moc task
  191. delattr(self, 'cache_sig')
  192. include_nodes = [node.parent] + self.generator.includes_nodes
  193. moctasks = []
  194. mocfiles = set([])
  195. for d in bld.raw_deps.get(self.uid(), []):
  196. if not d.endswith('.moc'):
  197. continue
  198. # process that base.moc only once
  199. if d in mocfiles:
  200. continue
  201. mocfiles.add(d)
  202. # find the source associated with the moc file
  203. h_node = None
  204. base2 = d[:-4]
  205. for x in include_nodes:
  206. for e in self.moc_h_ext():
  207. h_node = x.find_node(base2 + e)
  208. if h_node:
  209. break
  210. if h_node:
  211. m_node = h_node.change_ext('.moc')
  212. break
  213. else:
  214. # foo.cpp -> foo.cpp.moc
  215. for k in EXT_QT5:
  216. if base2.endswith(k):
  217. for x in include_nodes:
  218. h_node = x.find_node(base2)
  219. if h_node:
  220. break
  221. if h_node:
  222. m_node = h_node.change_ext(k + '.moc')
  223. break
  224. if not h_node:
  225. raise Errors.WafError('No source found for %r which is a moc file' % d)
  226. # create the moc task
  227. task = self.create_moc_task(h_node, m_node)
  228. moctasks.append(task)
  229. # simple scheduler dependency: run the moc task before others
  230. self.run_after.update(set(moctasks))
  231. self.moc_done = 1
  232. class trans_update(Task.Task):
  233. """Update a .ts files from a list of C++ files"""
  234. run_str = '${QT_LUPDATE} ${SRC} -ts ${TGT}'
  235. color = 'BLUE'
  236. Task.update_outputs(trans_update)
  237. class XMLHandler(ContentHandler):
  238. """
  239. Parser for *.qrc* files
  240. """
  241. def __init__(self):
  242. self.buf = []
  243. self.files = []
  244. def startElement(self, name, attrs):
  245. if name == 'file':
  246. self.buf = []
  247. def endElement(self, name):
  248. if name == 'file':
  249. self.files.append(str(''.join(self.buf)))
  250. def characters(self, cars):
  251. self.buf.append(cars)
  252. @extension(*EXT_RCC)
  253. def create_rcc_task(self, node):
  254. "Create rcc and cxx tasks for *.qrc* files"
  255. rcnode = node.change_ext('_rc.cpp')
  256. self.create_task('rcc', node, rcnode)
  257. cpptask = self.create_task('cxx', rcnode, rcnode.change_ext('.o'))
  258. try:
  259. self.compiled_tasks.append(cpptask)
  260. except AttributeError:
  261. self.compiled_tasks = [cpptask]
  262. return cpptask
  263. @extension(*EXT_UI)
  264. def create_uic_task(self, node):
  265. "hook for uic tasks"
  266. uictask = self.create_task('ui5', node)
  267. uictask.outputs = [self.path.find_or_declare(self.env['ui_PATTERN'] % node.name[:-3])]
  268. @extension('.ts')
  269. def add_lang(self, node):
  270. """add all the .ts file into self.lang"""
  271. self.lang = self.to_list(getattr(self, 'lang', [])) + [node]
  272. @feature('qt5')
  273. @after_method('apply_link')
  274. def apply_qt5(self):
  275. """
  276. Add MOC_FLAGS which may be necessary for moc::
  277. def build(bld):
  278. bld.program(features='qt5', source='main.cpp', target='app', use='QTCORE')
  279. The additional parameters are:
  280. :param lang: list of translation files (\*.ts) to process
  281. :type lang: list of :py:class:`waflib.Node.Node` or string without the .ts extension
  282. :param update: whether to process the C++ files to update the \*.ts files (use **waf --translate**)
  283. :type update: bool
  284. :param langname: if given, transform the \*.ts files into a .qrc files to include in the binary file
  285. :type langname: :py:class:`waflib.Node.Node` or string without the .qrc extension
  286. """
  287. if getattr(self, 'lang', None):
  288. qmtasks = []
  289. for x in self.to_list(self.lang):
  290. if isinstance(x, str):
  291. x = self.path.find_resource(x + '.ts')
  292. qmtasks.append(self.create_task('ts2qm', x, x.change_ext('.qm')))
  293. if getattr(self, 'update', None) and Options.options.trans_qt5:
  294. cxxnodes = [a.inputs[0] for a in self.compiled_tasks] + [
  295. a.inputs[0] for a in self.tasks if getattr(a, 'inputs', None) and a.inputs[0].name.endswith('.ui')]
  296. for x in qmtasks:
  297. self.create_task('trans_update', cxxnodes, x.inputs)
  298. if getattr(self, 'langname', None):
  299. qmnodes = [x.outputs[0] for x in qmtasks]
  300. rcnode = self.langname
  301. if isinstance(rcnode, str):
  302. rcnode = self.path.find_or_declare(rcnode + '.qrc')
  303. t = self.create_task('qm2rcc', qmnodes, rcnode)
  304. k = create_rcc_task(self, t.outputs[0])
  305. self.link_task.inputs.append(k.outputs[0])
  306. lst = []
  307. for flag in self.to_list(self.env['CXXFLAGS']):
  308. if len(flag) < 2: continue
  309. f = flag[0:2]
  310. if f in ('-D', '-I', '/D', '/I'):
  311. if (f[0] == '/'):
  312. lst.append('-' + flag[1:])
  313. else:
  314. lst.append(flag)
  315. self.env.append_value('MOC_FLAGS', lst)
  316. @extension(*EXT_QT5)
  317. def cxx_hook(self, node):
  318. """
  319. Re-map C++ file extensions to the :py:class:`waflib.Tools.qt5.qxx` task.
  320. """
  321. return self.create_compiled_task('qxx', node)
  322. class rcc(Task.Task):
  323. """
  324. Process *.qrc* files
  325. """
  326. color = 'BLUE'
  327. run_str = '${QT_RCC} -name ${tsk.rcname()} ${SRC[0].abspath()} ${RCC_ST} -o ${TGT}'
  328. ext_out = ['.h']
  329. def rcname(self):
  330. return os.path.splitext(self.inputs[0].name)[0]
  331. def scan(self):
  332. """Parse the *.qrc* files"""
  333. if not has_xml:
  334. Logs.error('no xml support was found, the rcc dependencies will be incomplete!')
  335. return ([], [])
  336. parser = make_parser()
  337. curHandler = XMLHandler()
  338. parser.setContentHandler(curHandler)
  339. fi = open(self.inputs[0].abspath(), 'r')
  340. try:
  341. parser.parse(fi)
  342. finally:
  343. fi.close()
  344. nodes = []
  345. names = []
  346. root = self.inputs[0].parent
  347. for x in curHandler.files:
  348. nd = root.find_resource(x)
  349. if nd: nodes.append(nd)
  350. else: names.append(x)
  351. return (nodes, names)
  352. class moc(Task.Task):
  353. """
  354. Create *.moc* files
  355. """
  356. color = 'BLUE'
  357. run_str = '${QT_MOC} ${MOC_FLAGS} ${MOCCPPPATH_ST:INCPATHS} ${MOCDEFINES_ST:DEFINES} ${SRC} ${MOC_ST} ${TGT}'
  358. class ui5(Task.Task):
  359. """
  360. Process *.ui* files
  361. """
  362. color = 'BLUE'
  363. run_str = '${QT_UIC} ${SRC} -o ${TGT}'
  364. ext_out = ['.h']
  365. class ts2qm(Task.Task):
  366. """
  367. Create *.qm* files from *.ts* files
  368. """
  369. color = 'BLUE'
  370. run_str = '${QT_LRELEASE} ${QT_LRELEASE_FLAGS} ${SRC} -qm ${TGT}'
  371. class qm2rcc(Task.Task):
  372. """
  373. Transform *.qm* files into *.rc* files
  374. """
  375. color = 'BLUE'
  376. after = 'ts2qm'
  377. def run(self):
  378. """Create a qrc file including the inputs"""
  379. txt = '\n'.join(['<file>%s</file>' % k.path_from(self.outputs[0].parent) for k in self.inputs])
  380. code = '<!DOCTYPE RCC><RCC version="1.0">\n<qresource>\n%s\n</qresource>\n</RCC>' % txt
  381. self.outputs[0].write(code)
  382. def configure(self):
  383. """
  384. Besides the configuration options, the environment variable QT5_ROOT may be used
  385. to give the location of the qt5 libraries (absolute path).
  386. The detection will use the program *pkg-config* through :py:func:`waflib.Tools.config_c.check_cfg`
  387. """
  388. self.find_qt5_binaries()
  389. self.set_qt5_libs_to_check()
  390. self.set_qt5_defines()
  391. self.find_qt5_libraries()
  392. self.add_qt5_rpath()
  393. self.simplify_qt5_libs()
  394. @conf
  395. def find_qt5_binaries(self):
  396. env = self.env
  397. opt = Options.options
  398. qtdir = getattr(opt, 'qtdir', '')
  399. qtbin = getattr(opt, 'qtbin', '')
  400. paths = []
  401. if qtdir:
  402. qtbin = os.path.join(qtdir, 'bin')
  403. # the qt directory has been given from QT5_ROOT - deduce the qt binary path
  404. if not qtdir:
  405. qtdir = os.environ.get('QT5_ROOT', '')
  406. qtbin = os.environ.get('QT5_BIN', None) or os.path.join(qtdir, 'bin')
  407. if qtbin:
  408. paths = [qtbin]
  409. # no qtdir, look in the path and in /usr/local/Trolltech
  410. if not qtdir:
  411. paths = os.environ.get('PATH', '').split(os.pathsep)
  412. paths.append('/usr/share/qt5/bin/')
  413. try:
  414. lst = Utils.listdir('/usr/local/Trolltech/')
  415. except OSError:
  416. pass
  417. else:
  418. if lst:
  419. lst.sort()
  420. lst.reverse()
  421. # keep the highest version
  422. qtdir = '/usr/local/Trolltech/%s/' % lst[0]
  423. qtbin = os.path.join(qtdir, 'bin')
  424. paths.append(qtbin)
  425. # at the end, try to find qmake in the paths given
  426. # keep the one with the highest version
  427. cand = None
  428. prev_ver = ['5', '0', '0']
  429. for qmk in ('qmake-qt5', 'qmake5', 'qmake'):
  430. try:
  431. qmake = self.find_program(qmk, path_list=paths)
  432. except self.errors.ConfigurationError:
  433. pass
  434. else:
  435. try:
  436. version = self.cmd_and_log(qmake + ['-query', 'QT_VERSION']).strip()
  437. except self.errors.WafError:
  438. pass
  439. else:
  440. if version:
  441. new_ver = version.split('.')
  442. if new_ver > prev_ver:
  443. cand = qmake
  444. prev_ver = new_ver
  445. # qmake could not be found easily, rely on qtchooser
  446. if not cand:
  447. try:
  448. self.find_program('qtchooser')
  449. except self.errors.ConfigurationError:
  450. pass
  451. else:
  452. cmd = self.env.QTCHOOSER + ['-qt=5', '-run-tool=qmake']
  453. try:
  454. version = self.cmd_and_log(cmd + ['-query', 'QT_VERSION'])
  455. except self.errors.WafError:
  456. pass
  457. else:
  458. cand = cmd
  459. if cand:
  460. self.env.QMAKE = cand
  461. else:
  462. self.fatal('Could not find qmake for qt5')
  463. self.env.QT_INSTALL_BINS = qtbin = self.cmd_and_log(self.env.QMAKE + ['-query', 'QT_INSTALL_BINS']).strip() + os.sep
  464. paths.insert(0, qtbin)
  465. def find_bin(lst, var):
  466. if var in env:
  467. return
  468. for f in lst:
  469. try:
  470. ret = self.find_program(f, path_list=paths)
  471. except self.errors.ConfigurationError:
  472. pass
  473. else:
  474. env[var]=ret
  475. break
  476. find_bin(['uic-qt5', 'uic'], 'QT_UIC')
  477. if not env.QT_UIC:
  478. self.fatal('cannot find the uic compiler for qt5')
  479. self.start_msg('Checking for uic version')
  480. uicver = self.cmd_and_log(env.QT_UIC + ['-version'], output=Context.BOTH)
  481. uicver = ''.join(uicver).strip()
  482. uicver = uicver.replace('Qt User Interface Compiler ','').replace('User Interface Compiler for Qt', '')
  483. self.end_msg(uicver)
  484. if uicver.find(' 3.') != -1 or uicver.find(' 4.') != -1:
  485. self.fatal('this uic compiler is for qt3 or qt5, add uic for qt5 to your path')
  486. find_bin(['moc-qt5', 'moc'], 'QT_MOC')
  487. find_bin(['rcc-qt5', 'rcc'], 'QT_RCC')
  488. find_bin(['lrelease-qt5', 'lrelease'], 'QT_LRELEASE')
  489. find_bin(['lupdate-qt5', 'lupdate'], 'QT_LUPDATE')
  490. env['UIC_ST'] = '%s -o %s'
  491. env['MOC_ST'] = '-o'
  492. env['ui_PATTERN'] = 'ui_%s.h'
  493. env['QT_LRELEASE_FLAGS'] = ['-silent']
  494. env.MOCCPPPATH_ST = '-I%s'
  495. env.MOCDEFINES_ST = '-D%s'
  496. @conf
  497. def find_qt5_libraries(self):
  498. qtlibs = getattr(Options.options, 'qtlibs', None) or os.environ.get("QT5_LIBDIR", None)
  499. if not qtlibs:
  500. try:
  501. qtlibs = self.cmd_and_log(self.env.QMAKE + ['-query', 'QT_INSTALL_LIBS']).strip()
  502. except Errors.WafError:
  503. qtdir = self.cmd_and_log(self.env.QMAKE + ['-query', 'QT_INSTALL_PREFIX']).strip() + os.sep
  504. qtlibs = os.path.join(qtdir, 'lib')
  505. self.msg('Found the Qt5 libraries in', qtlibs)
  506. qtincludes = os.environ.get("QT5_INCLUDES", None) or self.cmd_and_log(self.env.QMAKE + ['-query', 'QT_INSTALL_HEADERS']).strip()
  507. env = self.env
  508. if not 'PKG_CONFIG_PATH' in os.environ:
  509. os.environ['PKG_CONFIG_PATH'] = '%s:%s/pkgconfig:/usr/lib/qt5/lib/pkgconfig:/opt/qt5/lib/pkgconfig:/usr/lib/qt5/lib:/opt/qt5/lib' % (qtlibs, qtlibs)
  510. try:
  511. if os.environ.get("QT5_XCOMPILE", None):
  512. raise self.errors.ConfigurationError()
  513. self.check_cfg(atleast_pkgconfig_version='0.1')
  514. except self.errors.ConfigurationError:
  515. for i in self.qt5_vars:
  516. uselib = i.upper()
  517. if Utils.unversioned_sys_platform() == "darwin":
  518. # Since at least qt 4.7.3 each library locates in separate directory
  519. frameworkName = i + ".framework"
  520. qtDynamicLib = os.path.join(qtlibs, frameworkName, i)
  521. if os.path.exists(qtDynamicLib):
  522. env.append_unique('FRAMEWORK_' + uselib, i)
  523. self.msg('Checking for %s' % i, qtDynamicLib, 'GREEN')
  524. else:
  525. self.msg('Checking for %s' % i, False, 'YELLOW')
  526. env.append_unique('INCLUDES_' + uselib, os.path.join(qtlibs, frameworkName, 'Headers'))
  527. elif env.DEST_OS != "win32":
  528. qtDynamicLib = os.path.join(qtlibs, "lib" + i + ".so")
  529. qtStaticLib = os.path.join(qtlibs, "lib" + i + ".a")
  530. if os.path.exists(qtDynamicLib):
  531. env.append_unique('LIB_' + uselib, i)
  532. self.msg('Checking for %s' % i, qtDynamicLib, 'GREEN')
  533. elif os.path.exists(qtStaticLib):
  534. env.append_unique('LIB_' + uselib, i)
  535. self.msg('Checking for %s' % i, qtStaticLib, 'GREEN')
  536. else:
  537. self.msg('Checking for %s' % i, False, 'YELLOW')
  538. env.append_unique('LIBPATH_' + uselib, qtlibs)
  539. env.append_unique('INCLUDES_' + uselib, qtincludes)
  540. env.append_unique('INCLUDES_' + uselib, os.path.join(qtincludes, i))
  541. else:
  542. # Release library names are like QtCore5
  543. for k in ("lib%s.a", "lib%s5.a", "%s.lib", "%s5.lib"):
  544. lib = os.path.join(qtlibs, k % i)
  545. if os.path.exists(lib):
  546. env.append_unique('LIB_' + uselib, i + k[k.find("%s") + 2 : k.find('.')])
  547. self.msg('Checking for %s' % i, lib, 'GREEN')
  548. break
  549. else:
  550. self.msg('Checking for %s' % i, False, 'YELLOW')
  551. env.append_unique('LIBPATH_' + uselib, qtlibs)
  552. env.append_unique('INCLUDES_' + uselib, qtincludes)
  553. env.append_unique('INCLUDES_' + uselib, os.path.join(qtincludes, i))
  554. # Debug library names are like QtCore5d
  555. uselib = i.upper() + "_debug"
  556. for k in ("lib%sd.a", "lib%sd5.a", "%sd.lib", "%sd5.lib"):
  557. lib = os.path.join(qtlibs, k % i)
  558. if os.path.exists(lib):
  559. env.append_unique('LIB_' + uselib, i + k[k.find("%s") + 2 : k.find('.')])
  560. self.msg('Checking for %s' % i, lib, 'GREEN')
  561. break
  562. else:
  563. self.msg('Checking for %s' % i, False, 'YELLOW')
  564. env.append_unique('LIBPATH_' + uselib, qtlibs)
  565. env.append_unique('INCLUDES_' + uselib, qtincludes)
  566. env.append_unique('INCLUDES_' + uselib, os.path.join(qtincludes, i))
  567. else:
  568. for i in self.qt5_vars_debug + self.qt5_vars:
  569. self.check_cfg(package=i, args='--cflags --libs', mandatory=False)
  570. @conf
  571. def simplify_qt5_libs(self):
  572. # the libpaths make really long command-lines
  573. # remove the qtcore ones from qtgui, etc
  574. env = self.env
  575. def process_lib(vars_, coreval):
  576. for d in vars_:
  577. var = d.upper()
  578. if var == 'QTCORE':
  579. continue
  580. value = env['LIBPATH_'+var]
  581. if value:
  582. core = env[coreval]
  583. accu = []
  584. for lib in value:
  585. if lib in core:
  586. continue
  587. accu.append(lib)
  588. env['LIBPATH_'+var] = accu
  589. process_lib(self.qt5_vars, 'LIBPATH_QTCORE')
  590. process_lib(self.qt5_vars_debug, 'LIBPATH_QTCORE_DEBUG')
  591. @conf
  592. def add_qt5_rpath(self):
  593. # rpath if wanted
  594. env = self.env
  595. if getattr(Options.options, 'want_rpath', False):
  596. def process_rpath(vars_, coreval):
  597. for d in vars_:
  598. var = d.upper()
  599. value = env['LIBPATH_'+var]
  600. if value:
  601. core = env[coreval]
  602. accu = []
  603. for lib in value:
  604. if var != 'QTCORE':
  605. if lib in core:
  606. continue
  607. accu.append('-Wl,--rpath='+lib)
  608. env['RPATH_'+var] = accu
  609. process_rpath(self.qt5_vars, 'LIBPATH_QTCORE')
  610. process_rpath(self.qt5_vars_debug, 'LIBPATH_QTCORE_DEBUG')
  611. @conf
  612. def set_qt5_libs_to_check(self):
  613. if not hasattr(self, 'qt5_vars'):
  614. self.qt5_vars = QT5_LIBS
  615. self.qt5_vars = Utils.to_list(self.qt5_vars)
  616. if not hasattr(self, 'qt5_vars_debug'):
  617. self.qt5_vars_debug = [a + '_debug' for a in self.qt5_vars]
  618. self.qt5_vars_debug = Utils.to_list(self.qt5_vars_debug)
  619. @conf
  620. def set_qt5_defines(self):
  621. if sys.platform != 'win32':
  622. return
  623. for x in self.qt5_vars:
  624. y = x[2:].upper()
  625. self.env.append_unique('DEFINES_%s' % x.upper(), 'QT_%s_LIB' % y)
  626. self.env.append_unique('DEFINES_%s_DEBUG' % x.upper(), 'QT_%s_LIB' % y)
  627. def options(opt):
  628. """
  629. Command-line options
  630. """
  631. opt.add_option('--want-rpath', action='store_true', default=False, dest='want_rpath', help='enable the rpath for qt libraries')
  632. opt.add_option('--header-ext',
  633. type='string',
  634. default='',
  635. help='header extension for moc files',
  636. dest='qt_header_ext')
  637. for i in 'qtdir qtbin qtlibs'.split():
  638. opt.add_option('--'+i, type='string', default='', dest=i)
  639. opt.add_option('--translate', action="store_true", help="collect translation strings", dest="trans_qt5", default=False)