third_party: Update waf to verison 2.0.23
[Samba.git] / third_party / waf / waflib / Tools / qt5.py
blobb3e61325e508e29cff48aa4ca0decab6385ddd4a
1 #!/usr/bin/env python
2 # encoding: utf-8
3 # Thomas Nagy, 2006-2018 (ita)
5 """
6 This tool helps with finding Qt5 tools and libraries,
7 and also provides syntactic sugar for using Qt5 tools.
9 The following snippet illustrates the tool usage::
11 def options(opt):
12 opt.load('compiler_cxx qt5')
14 def configure(conf):
15 conf.load('compiler_cxx qt5')
17 def build(bld):
18 bld(
19 features = 'qt5 cxx cxxprogram',
20 uselib = 'QT5CORE QT5GUI QT5OPENGL QT5SVG',
21 source = 'main.cpp textures.qrc aboutDialog.ui',
22 target = 'window',
25 Here, the UI description and resource files will be processed
26 to generate code.
28 Usage
29 =====
31 Load the "qt5" tool.
33 You also need to edit your sources accordingly:
35 - the normal way of doing things is to have your C++ files
36 include the .moc file.
37 This is regarded as the best practice (and provides much faster
38 compilations).
39 It also implies that the include paths have beenset properly.
41 - to have the include paths added automatically, use the following::
43 from waflib.TaskGen import feature, before_method, after_method
44 @feature('cxx')
45 @after_method('process_source')
46 @before_method('apply_incpaths')
47 def add_includes_paths(self):
48 incs = set(self.to_list(getattr(self, 'includes', '')))
49 for x in self.compiled_tasks:
50 incs.add(x.inputs[0].parent.path_from(self.path))
51 self.includes = sorted(incs)
53 Note: another tool provides Qt processing that does not require
54 .moc includes, see 'playground/slow_qt/'.
56 A few options (--qt{dir,bin,...}) and environment variables
57 (QT5_{ROOT,DIR,MOC,UIC,XCOMPILE}) allow finer tuning of the tool,
58 tool path selection, etc; please read the source for more info.
60 The detection uses pkg-config on Linux by default. The list of
61 libraries to be requested to pkg-config is formulated by scanning
62 in the QTLIBS directory (that can be passed via --qtlibs or by
63 setting the environment variable QT5_LIBDIR otherwise is derived
64 by querying qmake for QT_INSTALL_LIBS directory) for shared/static
65 libraries present.
66 Alternatively the list of libraries to be requested via pkg-config
67 can be set using the qt5_vars attribute, ie:
69 conf.qt5_vars = ['Qt5Core', 'Qt5Gui', 'Qt5Widgets', 'Qt5Test'];
71 This can speed up configuration phase if needed libraries are
72 known beforehand, can improve detection on systems with a
73 sparse QT5 libraries installation (ie. NIX) and can improve
74 detection of some header-only Qt modules (ie. Qt5UiPlugin).
76 To force static library detection use:
77 QT5_XCOMPILE=1 QT5_FORCE_STATIC=1 waf configure
78 """
80 from __future__ import with_statement
82 try:
83 from xml.sax import make_parser
84 from xml.sax.handler import ContentHandler
85 except ImportError:
86 has_xml = False
87 ContentHandler = object
88 else:
89 has_xml = True
91 import os, sys, re
92 from waflib.Tools import cxx
93 from waflib import Build, Task, Utils, Options, Errors, Context
94 from waflib.TaskGen import feature, after_method, extension, before_method
95 from waflib.Configure import conf
96 from waflib import Logs
98 MOC_H = ['.h', '.hpp', '.hxx', '.hh']
99 """
100 File extensions associated to .moc files
103 EXT_RCC = ['.qrc']
105 File extension for the resource (.qrc) files
108 EXT_UI = ['.ui']
110 File extension for the user interface (.ui) files
113 EXT_QT5 = ['.cpp', '.cc', '.cxx', '.C']
115 File extensions of C++ files that may require a .moc processing
118 class qxx(Task.classes['cxx']):
120 Each C++ file can have zero or several .moc files to create.
121 They are known only when the files are scanned (preprocessor)
122 To avoid scanning the c++ files each time (parsing C/C++), the results
123 are retrieved from the task cache (bld.node_deps/bld.raw_deps).
124 The moc tasks are also created *dynamically* during the build.
127 def __init__(self, *k, **kw):
128 Task.Task.__init__(self, *k, **kw)
129 self.moc_done = 0
131 def runnable_status(self):
133 Compute the task signature to make sure the scanner was executed. Create the
134 moc tasks by using :py:meth:`waflib.Tools.qt5.qxx.add_moc_tasks` (if necessary),
135 then postpone the task execution (there is no need to recompute the task signature).
137 if self.moc_done:
138 return Task.Task.runnable_status(self)
139 else:
140 for t in self.run_after:
141 if not t.hasrun:
142 return Task.ASK_LATER
143 self.add_moc_tasks()
144 return Task.Task.runnable_status(self)
146 def create_moc_task(self, h_node, m_node):
148 If several libraries use the same classes, it is possible that moc will run several times (Issue 1318)
149 It is not possible to change the file names, but we can assume that the moc transformation will be identical,
150 and the moc tasks can be shared in a global cache.
152 try:
153 moc_cache = self.generator.bld.moc_cache
154 except AttributeError:
155 moc_cache = self.generator.bld.moc_cache = {}
157 try:
158 return moc_cache[h_node]
159 except KeyError:
160 tsk = moc_cache[h_node] = Task.classes['moc'](env=self.env, generator=self.generator)
161 tsk.set_inputs(h_node)
162 tsk.set_outputs(m_node)
163 tsk.env.append_unique('MOC_FLAGS', '-i')
165 if self.generator:
166 self.generator.tasks.append(tsk)
168 # direct injection in the build phase (safe because called from the main thread)
169 gen = self.generator.bld.producer
170 gen.outstanding.append(tsk)
171 gen.total += 1
173 return tsk
175 else:
176 # remove the signature, it must be recomputed with the moc task
177 delattr(self, 'cache_sig')
179 def add_moc_tasks(self):
181 Creates moc tasks by looking in the list of file dependencies ``bld.raw_deps[self.uid()]``
183 node = self.inputs[0]
184 bld = self.generator.bld
186 # skip on uninstall due to generated files
187 if bld.is_install == Build.UNINSTALL:
188 return
190 try:
191 # compute the signature once to know if there is a moc file to create
192 self.signature()
193 except KeyError:
194 # the moc file may be referenced somewhere else
195 pass
196 else:
197 # remove the signature, it must be recomputed with the moc task
198 delattr(self, 'cache_sig')
200 include_nodes = [node.parent] + self.generator.includes_nodes
202 moctasks = []
203 mocfiles = set()
204 for d in bld.raw_deps.get(self.uid(), []):
205 if not d.endswith('.moc'):
206 continue
208 # process that base.moc only once
209 if d in mocfiles:
210 continue
211 mocfiles.add(d)
213 # find the source associated with the moc file
214 h_node = None
215 base2 = d[:-4]
217 # foo.moc from foo.cpp
218 prefix = node.name[:node.name.rfind('.')]
219 if base2 == prefix:
220 h_node = node
221 else:
222 # this deviates from the standard
223 # if bar.cpp includes foo.moc, then assume it is from foo.h
224 for x in include_nodes:
225 for e in MOC_H:
226 h_node = x.find_node(base2 + e)
227 if h_node:
228 break
229 else:
230 continue
231 break
232 if h_node:
233 m_node = h_node.change_ext('.moc')
234 else:
235 raise Errors.WafError('No source found for %r which is a moc file' % d)
237 # create the moc task
238 task = self.create_moc_task(h_node, m_node)
239 moctasks.append(task)
241 # simple scheduler dependency: run the moc task before others
242 self.run_after.update(set(moctasks))
243 self.moc_done = 1
245 class trans_update(Task.Task):
246 """Updates a .ts files from a list of C++ files"""
247 run_str = '${QT_LUPDATE} ${SRC} -ts ${TGT}'
248 color = 'BLUE'
250 class XMLHandler(ContentHandler):
252 Parses ``.qrc`` files
254 def __init__(self):
255 ContentHandler.__init__(self)
256 self.buf = []
257 self.files = []
258 def startElement(self, name, attrs):
259 if name == 'file':
260 self.buf = []
261 def endElement(self, name):
262 if name == 'file':
263 self.files.append(str(''.join(self.buf)))
264 def characters(self, cars):
265 self.buf.append(cars)
267 @extension(*EXT_RCC)
268 def create_rcc_task(self, node):
269 "Creates rcc and cxx tasks for ``.qrc`` files"
270 rcnode = node.change_ext('_rc.%d.cpp' % self.idx)
271 self.create_task('rcc', node, rcnode)
272 cpptask = self.create_task('cxx', rcnode, rcnode.change_ext('.o'))
273 try:
274 self.compiled_tasks.append(cpptask)
275 except AttributeError:
276 self.compiled_tasks = [cpptask]
277 return cpptask
279 @extension(*EXT_UI)
280 def create_uic_task(self, node):
281 "Create uic tasks for user interface ``.ui`` definition files"
284 If UIC file is used in more than one bld, we would have a conflict in parallel execution
285 It is not possible to change the file names (like .self.idx. as for objects) as they have
286 to be referenced by the source file, but we can assume that the transformation will be identical
287 and the tasks can be shared in a global cache.
289 try:
290 uic_cache = self.bld.uic_cache
291 except AttributeError:
292 uic_cache = self.bld.uic_cache = {}
294 if node not in uic_cache:
295 uictask = uic_cache[node] = self.create_task('ui5', node)
296 uictask.outputs = [node.parent.find_or_declare(self.env.ui_PATTERN % node.name[:-3])]
298 @extension('.ts')
299 def add_lang(self, node):
300 """Adds all the .ts file into ``self.lang``"""
301 self.lang = self.to_list(getattr(self, 'lang', [])) + [node]
303 @feature('qt5')
304 @before_method('process_source')
305 def process_mocs(self):
307 Processes MOC files included in headers::
309 def build(bld):
310 bld.program(features='qt5', source='main.cpp', target='app', use='QT5CORE', moc='foo.h')
312 The build will run moc on foo.h to create moc_foo.n.cpp. The number in the file name
313 is provided to avoid name clashes when the same headers are used by several targets.
315 lst = self.to_nodes(getattr(self, 'moc', []))
316 self.source = self.to_list(getattr(self, 'source', []))
317 for x in lst:
318 prefix = x.name[:x.name.rfind('.')] # foo.h -> foo
319 moc_target = 'moc_%s.%d.cpp' % (prefix, self.idx)
320 moc_node = x.parent.find_or_declare(moc_target)
321 self.source.append(moc_node)
323 self.create_task('moc', x, moc_node)
325 @feature('qt5')
326 @after_method('apply_link')
327 def apply_qt5(self):
329 Adds MOC_FLAGS which may be necessary for moc::
331 def build(bld):
332 bld.program(features='qt5', source='main.cpp', target='app', use='QT5CORE')
334 The additional parameters are:
336 :param lang: list of translation files (\\*.ts) to process
337 :type lang: list of :py:class:`waflib.Node.Node` or string without the .ts extension
338 :param update: whether to process the C++ files to update the \\*.ts files (use **waf --translate**)
339 :type update: bool
340 :param langname: if given, transform the \\*.ts files into a .qrc files to include in the binary file
341 :type langname: :py:class:`waflib.Node.Node` or string without the .qrc extension
343 if getattr(self, 'lang', None):
344 qmtasks = []
345 for x in self.to_list(self.lang):
346 if isinstance(x, str):
347 x = self.path.find_resource(x + '.ts')
348 qmtasks.append(self.create_task('ts2qm', x, x.change_ext('.%d.qm' % self.idx)))
350 if getattr(self, 'update', None) and Options.options.trans_qt5:
351 cxxnodes = [a.inputs[0] for a in self.compiled_tasks] + [
352 a.inputs[0] for a in self.tasks if a.inputs and a.inputs[0].name.endswith('.ui')]
353 for x in qmtasks:
354 self.create_task('trans_update', cxxnodes, x.inputs)
356 if getattr(self, 'langname', None):
357 qmnodes = [x.outputs[0] for x in qmtasks]
358 rcnode = self.langname
359 if isinstance(rcnode, str):
360 rcnode = self.path.find_or_declare(rcnode + ('.%d.qrc' % self.idx))
361 t = self.create_task('qm2rcc', qmnodes, rcnode)
362 k = create_rcc_task(self, t.outputs[0])
363 self.link_task.inputs.append(k.outputs[0])
365 lst = []
366 for flag in self.to_list(self.env.CXXFLAGS):
367 if len(flag) < 2:
368 continue
369 f = flag[0:2]
370 if f in ('-D', '-I', '/D', '/I'):
371 if (f[0] == '/'):
372 lst.append('-' + flag[1:])
373 else:
374 lst.append(flag)
375 self.env.append_value('MOC_FLAGS', lst)
377 @extension(*EXT_QT5)
378 def cxx_hook(self, node):
380 Re-maps C++ file extensions to the :py:class:`waflib.Tools.qt5.qxx` task.
382 return self.create_compiled_task('qxx', node)
384 class rcc(Task.Task):
386 Processes ``.qrc`` files
388 color = 'BLUE'
389 run_str = '${QT_RCC} -name ${tsk.rcname()} ${SRC[0].abspath()} ${RCC_ST} -o ${TGT}'
390 ext_out = ['.h']
392 def rcname(self):
393 return os.path.splitext(self.inputs[0].name)[0]
395 def scan(self):
396 """Parse the *.qrc* files"""
397 if not has_xml:
398 Logs.error('No xml.sax support was found, rcc dependencies will be incomplete!')
399 return ([], [])
401 parser = make_parser()
402 curHandler = XMLHandler()
403 parser.setContentHandler(curHandler)
404 with open(self.inputs[0].abspath(), 'r') as f:
405 parser.parse(f)
407 nodes = []
408 names = []
409 root = self.inputs[0].parent
410 for x in curHandler.files:
411 nd = root.find_resource(x)
412 if nd:
413 nodes.append(nd)
414 else:
415 names.append(x)
416 return (nodes, names)
418 def quote_flag(self, x):
420 Override Task.quote_flag. QT parses the argument files
421 differently than cl.exe and link.exe
423 :param x: flag
424 :type x: string
425 :return: quoted flag
426 :rtype: string
428 return x
431 class moc(Task.Task):
433 Creates ``.moc`` files
435 color = 'BLUE'
436 run_str = '${QT_MOC} ${MOC_FLAGS} ${MOCCPPPATH_ST:INCPATHS} ${MOCDEFINES_ST:DEFINES} ${SRC} ${MOC_ST} ${TGT}'
438 def quote_flag(self, x):
440 Override Task.quote_flag. QT parses the argument files
441 differently than cl.exe and link.exe
443 :param x: flag
444 :type x: string
445 :return: quoted flag
446 :rtype: string
448 return x
451 class ui5(Task.Task):
453 Processes ``.ui`` files
455 color = 'BLUE'
456 run_str = '${QT_UIC} ${SRC} -o ${TGT}'
457 ext_out = ['.h']
459 class ts2qm(Task.Task):
461 Generates ``.qm`` files from ``.ts`` files
463 color = 'BLUE'
464 run_str = '${QT_LRELEASE} ${QT_LRELEASE_FLAGS} ${SRC} -qm ${TGT}'
466 class qm2rcc(Task.Task):
468 Generates ``.qrc`` files from ``.qm`` files
470 color = 'BLUE'
471 after = 'ts2qm'
472 def run(self):
473 """Create a qrc file including the inputs"""
474 txt = '\n'.join(['<file>%s</file>' % k.path_from(self.outputs[0].parent) for k in self.inputs])
475 code = '<!DOCTYPE RCC><RCC version="1.0">\n<qresource>\n%s\n</qresource>\n</RCC>' % txt
476 self.outputs[0].write(code)
478 def configure(self):
480 Besides the configuration options, the environment variable QT5_ROOT may be used
481 to give the location of the qt5 libraries (absolute path).
483 The detection uses the program ``pkg-config`` through :py:func:`waflib.Tools.config_c.check_cfg`
485 if 'COMPILER_CXX' not in self.env:
486 self.fatal('No CXX compiler defined: did you forget to configure compiler_cxx first?')
488 self.find_qt5_binaries()
489 self.set_qt5_libs_dir()
490 self.set_qt5_libs_to_check()
491 self.set_qt5_defines()
492 self.find_qt5_libraries()
493 self.add_qt5_rpath()
494 self.simplify_qt5_libs()
496 # warn about this during the configuration too
497 if not has_xml:
498 Logs.error('No xml.sax support was found, rcc dependencies will be incomplete!')
500 # Qt5 may be compiled with '-reduce-relocations' which requires dependent programs to have -fPIE or -fPIC?
501 frag = '#include <QMap>\nint main(int argc, char **argv) {QMap<int,int> m;return m.keys().size();}\n'
502 uses = 'QT5CORE'
503 for flag in [[], '-fPIE', '-fPIC', '-std=c++11' , ['-std=c++11', '-fPIE'], ['-std=c++11', '-fPIC']]:
504 msg = 'See if Qt files compile '
505 if flag:
506 msg += 'with %s' % flag
507 try:
508 self.check(features='qt5 cxx', use=uses, uselib_store='qt5', cxxflags=flag, fragment=frag, msg=msg)
509 except self.errors.ConfigurationError:
510 pass
511 else:
512 break
513 else:
514 self.fatal('Could not build a simple Qt application')
516 # FreeBSD does not add /usr/local/lib and the pkg-config files do not provide it either :-/
517 if Utils.unversioned_sys_platform() == 'freebsd':
518 frag = '#include <QMap>\nint main(int argc, char **argv) {QMap<int,int> m;return m.keys().size();}\n'
519 try:
520 self.check(features='qt5 cxx cxxprogram', use=uses, fragment=frag, msg='Can we link Qt programs on FreeBSD directly?')
521 except self.errors.ConfigurationError:
522 self.check(features='qt5 cxx cxxprogram', use=uses, uselib_store='qt5', libpath='/usr/local/lib', fragment=frag, msg='Is /usr/local/lib required?')
524 @conf
525 def find_qt5_binaries(self):
527 Detects Qt programs such as qmake, moc, uic, lrelease
529 env = self.env
530 opt = Options.options
532 qtdir = getattr(opt, 'qtdir', '')
533 qtbin = getattr(opt, 'qtbin', '')
535 paths = []
537 if qtdir:
538 qtbin = os.path.join(qtdir, 'bin')
540 # the qt directory has been given from QT5_ROOT - deduce the qt binary path
541 if not qtdir:
542 qtdir = self.environ.get('QT5_ROOT', '')
543 qtbin = self.environ.get('QT5_BIN') or os.path.join(qtdir, 'bin')
545 if qtbin:
546 paths = [qtbin]
548 # no qtdir, look in the path and in /usr/local/Trolltech
549 if not qtdir:
550 paths = self.environ.get('PATH', '').split(os.pathsep)
551 paths.extend(['/usr/share/qt5/bin', '/usr/local/lib/qt5/bin'])
552 try:
553 lst = Utils.listdir('/usr/local/Trolltech/')
554 except OSError:
555 pass
556 else:
557 if lst:
558 lst.sort()
559 lst.reverse()
561 # keep the highest version
562 qtdir = '/usr/local/Trolltech/%s/' % lst[0]
563 qtbin = os.path.join(qtdir, 'bin')
564 paths.append(qtbin)
566 # at the end, try to find qmake in the paths given
567 # keep the one with the highest version
568 cand = None
569 prev_ver = ['0', '0', '0']
570 for qmk in ('qmake-qt5', 'qmake5', 'qmake'):
571 try:
572 qmake = self.find_program(qmk, path_list=paths)
573 except self.errors.ConfigurationError:
574 pass
575 else:
576 try:
577 version = self.cmd_and_log(qmake + ['-query', 'QT_VERSION']).strip()
578 except self.errors.WafError:
579 pass
580 else:
581 if version:
582 new_ver = version.split('.')
583 if new_ver[0] == '5' and new_ver > prev_ver:
584 cand = qmake
585 prev_ver = new_ver
587 # qmake could not be found easily, rely on qtchooser
588 if not cand:
589 try:
590 self.find_program('qtchooser')
591 except self.errors.ConfigurationError:
592 pass
593 else:
594 cmd = self.env.QTCHOOSER + ['-qt=5', '-run-tool=qmake']
595 try:
596 version = self.cmd_and_log(cmd + ['-query', 'QT_VERSION'])
597 except self.errors.WafError:
598 pass
599 else:
600 cand = cmd
602 if cand:
603 self.env.QMAKE = cand
604 else:
605 self.fatal('Could not find qmake for qt5')
607 self.env.QT_HOST_BINS = qtbin = self.cmd_and_log(self.env.QMAKE + ['-query', 'QT_HOST_BINS']).strip()
608 paths.insert(0, qtbin)
610 def find_bin(lst, var):
611 if var in env:
612 return
613 for f in lst:
614 try:
615 ret = self.find_program(f, path_list=paths)
616 except self.errors.ConfigurationError:
617 pass
618 else:
619 env[var]=ret
620 break
622 find_bin(['uic-qt5', 'uic'], 'QT_UIC')
623 if not env.QT_UIC:
624 self.fatal('cannot find the uic compiler for qt5')
626 self.start_msg('Checking for uic version')
627 uicver = self.cmd_and_log(env.QT_UIC + ['-version'], output=Context.BOTH)
628 uicver = ''.join(uicver).strip()
629 uicver = uicver.replace('Qt User Interface Compiler ','').replace('User Interface Compiler for Qt', '')
630 self.end_msg(uicver)
631 if uicver.find(' 3.') != -1 or uicver.find(' 4.') != -1:
632 self.fatal('this uic compiler is for qt3 or qt4, add uic for qt5 to your path')
634 find_bin(['moc-qt5', 'moc'], 'QT_MOC')
635 find_bin(['rcc-qt5', 'rcc'], 'QT_RCC')
636 find_bin(['lrelease-qt5', 'lrelease'], 'QT_LRELEASE')
637 find_bin(['lupdate-qt5', 'lupdate'], 'QT_LUPDATE')
639 env.UIC_ST = '%s -o %s'
640 env.MOC_ST = '-o'
641 env.ui_PATTERN = 'ui_%s.h'
642 env.QT_LRELEASE_FLAGS = ['-silent']
643 env.MOCCPPPATH_ST = '-I%s'
644 env.MOCDEFINES_ST = '-D%s'
646 @conf
647 def set_qt5_libs_dir(self):
648 env = self.env
649 qtlibs = getattr(Options.options, 'qtlibs', None) or self.environ.get('QT5_LIBDIR')
650 if not qtlibs:
651 try:
652 qtlibs = self.cmd_and_log(env.QMAKE + ['-query', 'QT_INSTALL_LIBS']).strip()
653 except Errors.WafError:
654 qtdir = self.cmd_and_log(env.QMAKE + ['-query', 'QT_INSTALL_PREFIX']).strip()
655 qtlibs = os.path.join(qtdir, 'lib')
656 self.msg('Found the Qt5 library path', qtlibs)
657 env.QTLIBS = qtlibs
659 @conf
660 def find_single_qt5_lib(self, name, uselib, qtlibs, qtincludes, force_static):
661 env = self.env
662 if force_static:
663 exts = ('.a', '.lib')
664 prefix = 'STLIB'
665 else:
666 exts = ('.so', '.lib')
667 prefix = 'LIB'
669 def lib_names():
670 for x in exts:
671 for k in ('', '5') if Utils.is_win32 else ['']:
672 for p in ('lib', ''):
673 yield (p, name, k, x)
675 for tup in lib_names():
676 k = ''.join(tup)
677 path = os.path.join(qtlibs, k)
678 if os.path.exists(path):
679 if env.DEST_OS == 'win32':
680 libval = ''.join(tup[:-1])
681 else:
682 libval = name
683 env.append_unique(prefix + '_' + uselib, libval)
684 env.append_unique('%sPATH_%s' % (prefix, uselib), qtlibs)
685 env.append_unique('INCLUDES_' + uselib, qtincludes)
686 env.append_unique('INCLUDES_' + uselib, os.path.join(qtincludes, name.replace('Qt5', 'Qt')))
687 return k
688 return False
690 @conf
691 def find_qt5_libraries(self):
692 env = self.env
694 qtincludes = self.environ.get('QT5_INCLUDES') or self.cmd_and_log(env.QMAKE + ['-query', 'QT_INSTALL_HEADERS']).strip()
695 force_static = self.environ.get('QT5_FORCE_STATIC')
696 try:
697 if self.environ.get('QT5_XCOMPILE'):
698 self.fatal('QT5_XCOMPILE Disables pkg-config detection')
699 self.check_cfg(atleast_pkgconfig_version='0.1')
700 except self.errors.ConfigurationError:
701 for i in self.qt5_vars:
702 uselib = i.upper()
703 if Utils.unversioned_sys_platform() == 'darwin':
704 # Since at least qt 4.7.3 each library locates in separate directory
705 fwk = i.replace('Qt5', 'Qt')
706 frameworkName = fwk + '.framework'
708 qtDynamicLib = os.path.join(env.QTLIBS, frameworkName, fwk)
709 if os.path.exists(qtDynamicLib):
710 env.append_unique('FRAMEWORK_' + uselib, fwk)
711 env.append_unique('FRAMEWORKPATH_' + uselib, env.QTLIBS)
712 self.msg('Checking for %s' % i, qtDynamicLib, 'GREEN')
713 else:
714 self.msg('Checking for %s' % i, False, 'YELLOW')
715 env.append_unique('INCLUDES_' + uselib, os.path.join(env.QTLIBS, frameworkName, 'Headers'))
716 else:
717 ret = self.find_single_qt5_lib(i, uselib, env.QTLIBS, qtincludes, force_static)
718 if not force_static and not ret:
719 ret = self.find_single_qt5_lib(i, uselib, env.QTLIBS, qtincludes, True)
720 self.msg('Checking for %s' % i, ret, 'GREEN' if ret else 'YELLOW')
721 else:
722 path = '%s:%s:%s/pkgconfig:/usr/lib/qt5/lib/pkgconfig:/opt/qt5/lib/pkgconfig:/usr/lib/qt5/lib:/opt/qt5/lib' % (
723 self.environ.get('PKG_CONFIG_PATH', ''), env.QTLIBS, env.QTLIBS)
724 for i in self.qt5_vars:
725 self.check_cfg(package=i, args='--cflags --libs', mandatory=False, force_static=force_static, pkg_config_path=path)
727 @conf
728 def simplify_qt5_libs(self):
730 Since library paths make really long command-lines,
731 and since everything depends on qtcore, remove the qtcore ones from qtgui, etc
733 env = self.env
734 def process_lib(vars_, coreval):
735 for d in vars_:
736 var = d.upper()
737 if var == 'QTCORE':
738 continue
740 value = env['LIBPATH_'+var]
741 if value:
742 core = env[coreval]
743 accu = []
744 for lib in value:
745 if lib in core:
746 continue
747 accu.append(lib)
748 env['LIBPATH_'+var] = accu
749 process_lib(self.qt5_vars, 'LIBPATH_QTCORE')
751 @conf
752 def add_qt5_rpath(self):
754 Defines rpath entries for Qt libraries
756 env = self.env
757 if getattr(Options.options, 'want_rpath', False):
758 def process_rpath(vars_, coreval):
759 for d in vars_:
760 var = d.upper()
761 value = env['LIBPATH_' + var]
762 if value:
763 core = env[coreval]
764 accu = []
765 for lib in value:
766 if var != 'QTCORE':
767 if lib in core:
768 continue
769 accu.append('-Wl,--rpath='+lib)
770 env['RPATH_' + var] = accu
771 process_rpath(self.qt5_vars, 'LIBPATH_QTCORE')
773 @conf
774 def set_qt5_libs_to_check(self):
775 self.qt5_vars = Utils.to_list(getattr(self, 'qt5_vars', []))
776 if not self.qt5_vars:
777 dirlst = Utils.listdir(self.env.QTLIBS)
779 pat = self.env.cxxshlib_PATTERN
780 if Utils.is_win32:
781 pat = pat.replace('.dll', '.lib')
782 if self.environ.get('QT5_FORCE_STATIC'):
783 pat = self.env.cxxstlib_PATTERN
784 if Utils.unversioned_sys_platform() == 'darwin':
785 pat = r"%s\.framework"
786 re_qt = re.compile(pat % 'Qt5?(?P<name>\\w+)' + '$')
787 for x in sorted(dirlst):
788 m = re_qt.match(x)
789 if m:
790 self.qt5_vars.append("Qt5%s" % m.group('name'))
791 if not self.qt5_vars:
792 self.fatal('cannot find any Qt5 library (%r)' % self.env.QTLIBS)
794 qtextralibs = getattr(Options.options, 'qtextralibs', None)
795 if qtextralibs:
796 self.qt5_vars.extend(qtextralibs.split(','))
798 @conf
799 def set_qt5_defines(self):
800 if sys.platform != 'win32':
801 return
802 for x in self.qt5_vars:
803 y=x.replace('Qt5', 'Qt')[2:].upper()
804 self.env.append_unique('DEFINES_%s' % x.upper(), 'QT_%s_LIB' % y)
806 def options(opt):
808 Command-line options
810 opt.add_option('--want-rpath', action='store_true', default=False, dest='want_rpath', help='enable the rpath for qt libraries')
811 for i in 'qtdir qtbin qtlibs'.split():
812 opt.add_option('--'+i, type='string', default='', dest=i)
814 opt.add_option('--translate', action='store_true', help='collect translation strings', dest='trans_qt5', default=False)
815 opt.add_option('--qtextralibs', type='string', default='', dest='qtextralibs', help='additional qt libraries on the system to add to default ones, comma separated')