3 # Thomas Nagy, 2006-2018 (ita)
4 # Rafaƫl Kooi, 2023 (RA-Kooi)
7 This tool helps with finding Qt5 and Qt6 tools and libraries,
8 and also provides syntactic sugar for using Qt5 and Qt6 tools.
10 The following snippet illustrates the tool usage::
13 opt.load('compiler_cxx qt5')
16 conf.load('compiler_cxx qt5')
20 features = 'qt5 cxx cxxprogram',
21 uselib = 'QT5CORE QT5GUI QT5OPENGL QT5SVG',
22 source = 'main.cpp textures.qrc aboutDialog.ui',
26 Alternatively the following snippet illustrates Qt6 tool usage::
29 opt.load('compiler_cxx qt5')
33 conf.load('compiler_cxx qt5')
37 features = 'qt6 cxx cxxprogram',
38 uselib = 'QT6CORE QT6GUI QT6OPENGL QT6SVG',
39 source = 'main.cpp textures.qrc aboutDialog.ui',
43 Here, the UI description and resource files will be processed
51 You also need to edit your sources accordingly:
53 - the normal way of doing things is to have your C++ files
54 include the .moc file.
55 This is regarded as the best practice (and provides much faster
57 It also implies that the include paths have beenset properly.
59 - to have the include paths added automatically, use the following::
61 from waflib.TaskGen import feature, before_method, after_method
63 @after_method('process_source')
64 @before_method('apply_incpaths')
65 def add_includes_paths(self):
66 incs = set(self.to_list(getattr(self, 'includes', '')))
67 for x in self.compiled_tasks:
68 incs.add(x.inputs[0].parent.path_from(self.path))
69 self.includes = sorted(incs)
71 Note: another tool provides Qt processing that does not require
72 .moc includes, see 'playground/slow_qt/'.
74 A few options (--qt{dir,bin,...}) and environment variables
75 (QT5_{ROOT,DIR,MOC,UIC,XCOMPILE}) allow finer tuning of the tool,
76 tool path selection, etc; please read the source for more info.
77 For Qt6 replace the QT5_ prefix with QT6_.
79 The detection uses pkg-config on Linux by default. The list of
80 libraries to be requested to pkg-config is formulated by scanning
81 in the QTLIBS directory (that can be passed via --qtlibs or by
82 setting the environment variable QT5_LIBDIR or QT6_LIBDIR otherwise is
83 derived by querying qmake for QT_INSTALL_LIBS directory) for
84 shared/static libraries present.
85 Alternatively the list of libraries to be requested via pkg-config
86 can be set using the qt5_vars attribute, ie:
88 conf.qt5_vars = ['Qt5Core', 'Qt5Gui', 'Qt5Widgets', 'Qt5Test'];
90 For Qt6 use the qt6_vars attribute.
92 This can speed up configuration phase if needed libraries are
93 known beforehand, can improve detection on systems with a
94 sparse QT5/Qt6 libraries installation (ie. NIX) and can improve
95 detection of some header-only Qt modules (ie. Qt5UiPlugin).
97 To force static library detection use:
98 QT5_XCOMPILE=1 QT5_FORCE_STATIC=1 waf configure
100 To use Qt6 set the want_qt6 attribute, ie:
102 conf.want_qt6 = True;
105 from __future__
import with_statement
108 from xml
.sax
import make_parser
109 from xml
.sax
.handler
import ContentHandler
112 ContentHandler
= object
117 from waflib
.Tools
import cxx
118 from waflib
import Build
, Task
, Utils
, Options
, Errors
, Context
119 from waflib
.TaskGen
import feature
, after_method
, extension
, before_method
120 from waflib
.Configure
import conf
121 from waflib
import Logs
123 MOC_H
= ['.h', '.hpp', '.hxx', '.hh']
125 File extensions associated to .moc files
130 File extension for the resource (.qrc) files
135 File extension for the user interface (.ui) files
138 EXT_QT5
= ['.cpp', '.cc', '.cxx', '.C']
140 File extensions of C++ files that may require a .moc processing
143 class qxx(Task
.classes
['cxx']):
145 Each C++ file can have zero or several .moc files to create.
146 They are known only when the files are scanned (preprocessor)
147 To avoid scanning the c++ files each time (parsing C/C++), the results
148 are retrieved from the task cache (bld.node_deps/bld.raw_deps).
149 The moc tasks are also created *dynamically* during the build.
152 def __init__(self
, *k
, **kw
):
153 Task
.Task
.__init
__(self
, *k
, **kw
)
156 def runnable_status(self
):
158 Compute the task signature to make sure the scanner was executed. Create the
159 moc tasks by using :py:meth:`waflib.Tools.qt5.qxx.add_moc_tasks` (if necessary),
160 then postpone the task execution (there is no need to recompute the task signature).
163 return Task
.Task
.runnable_status(self
)
165 for t
in self
.run_after
:
167 return Task
.ASK_LATER
169 return Task
.Task
.runnable_status(self
)
171 def create_moc_task(self
, h_node
, m_node
):
173 If several libraries use the same classes, it is possible that moc will run several times (Issue 1318)
174 It is not possible to change the file names, but we can assume that the moc transformation will be identical,
175 and the moc tasks can be shared in a global cache.
178 moc_cache
= self
.generator
.bld
.moc_cache
179 except AttributeError:
180 moc_cache
= self
.generator
.bld
.moc_cache
= {}
183 return moc_cache
[h_node
]
185 tsk
= moc_cache
[h_node
] = Task
.classes
['moc'](env
=self
.env
, generator
=self
.generator
)
186 tsk
.set_inputs(h_node
)
187 tsk
.set_outputs(m_node
)
188 tsk
.env
.append_unique('MOC_FLAGS', '-i')
191 self
.generator
.tasks
.append(tsk
)
193 # direct injection in the build phase (safe because called from the main thread)
194 gen
= self
.generator
.bld
.producer
195 gen
.outstanding
.append(tsk
)
201 # remove the signature, it must be recomputed with the moc task
202 delattr(self
, 'cache_sig')
204 def add_moc_tasks(self
):
206 Creates moc tasks by looking in the list of file dependencies ``bld.raw_deps[self.uid()]``
208 node
= self
.inputs
[0]
209 bld
= self
.generator
.bld
211 # skip on uninstall due to generated files
212 if bld
.is_install
== Build
.UNINSTALL
:
216 # compute the signature once to know if there is a moc file to create
219 # the moc file may be referenced somewhere else
222 # remove the signature, it must be recomputed with the moc task
223 delattr(self
, 'cache_sig')
225 include_nodes
= [node
.parent
] + self
.generator
.includes_nodes
229 for d
in bld
.raw_deps
.get(self
.uid(), []):
230 if not d
.endswith('.moc'):
233 # process that base.moc only once
238 # find the source associated with the moc file
242 # foo.moc from foo.cpp
243 prefix
= node
.name
[:node
.name
.rfind('.')]
247 # this deviates from the standard
248 # if bar.cpp includes foo.moc, then assume it is from foo.h
249 for x
in include_nodes
:
251 h_node
= x
.find_node(base2
+ e
)
258 m_node
= h_node
.change_ext('.moc')
260 raise Errors
.WafError('No source found for %r which is a moc file' % d
)
262 # create the moc task
263 task
= self
.create_moc_task(h_node
, m_node
)
264 moctasks
.append(task
)
266 # simple scheduler dependency: run the moc task before others
267 self
.run_after
.update(set(moctasks
))
270 class trans_update(Task
.Task
):
271 """Updates a .ts files from a list of C++ files"""
272 run_str
= '${QT_LUPDATE} ${SRC} -ts ${TGT}'
275 class XMLHandler(ContentHandler
):
277 Parses ``.qrc`` files
280 ContentHandler
.__init
__(self
)
283 def startElement(self
, name
, attrs
):
286 def endElement(self
, name
):
288 self
.files
.append(str(''.join(self
.buf
)))
289 def characters(self
, cars
):
290 self
.buf
.append(cars
)
293 def create_rcc_task(self
, node
):
294 "Creates rcc and cxx tasks for ``.qrc`` files"
295 rcnode
= node
.change_ext('_rc.%d.cpp' % self
.idx
)
296 self
.create_task('rcc', node
, rcnode
)
297 cpptask
= self
.create_task('cxx', rcnode
, rcnode
.change_ext('.o'))
299 self
.compiled_tasks
.append(cpptask
)
300 except AttributeError:
301 self
.compiled_tasks
= [cpptask
]
305 def create_uic_task(self
, node
):
306 "Create uic tasks for user interface ``.ui`` definition files"
309 If UIC file is used in more than one bld, we would have a conflict in parallel execution
310 It is not possible to change the file names (like .self.idx. as for objects) as they have
311 to be referenced by the source file, but we can assume that the transformation will be identical
312 and the tasks can be shared in a global cache.
315 uic_cache
= self
.bld
.uic_cache
316 except AttributeError:
317 uic_cache
= self
.bld
.uic_cache
= {}
319 if node
not in uic_cache
:
320 uictask
= uic_cache
[node
] = self
.create_task('ui5', node
)
321 uictask
.outputs
= [node
.parent
.find_or_declare(self
.env
.ui_PATTERN
% node
.name
[:-3])]
324 def add_lang(self
, node
):
325 """Adds all the .ts file into ``self.lang``"""
326 self
.lang
= self
.to_list(getattr(self
, 'lang', [])) + [node
]
328 @feature('qt5', 'qt6')
329 @before_method('process_source')
330 def process_mocs(self
):
332 Processes MOC files included in headers::
335 bld.program(features='qt5', source='main.cpp', target='app', use='QT5CORE', moc='foo.h')
337 The build will run moc on foo.h to create moc_foo.n.cpp. The number in the file name
338 is provided to avoid name clashes when the same headers are used by several targets.
340 lst
= self
.to_nodes(getattr(self
, 'moc', []))
341 self
.source
= self
.to_list(getattr(self
, 'source', []))
343 prefix
= x
.name
[:x
.name
.rfind('.')] # foo.h -> foo
344 moc_target
= 'moc_%s.%d.cpp' % (prefix
, self
.idx
)
345 moc_node
= x
.parent
.find_or_declare(moc_target
)
346 self
.source
.append(moc_node
)
348 self
.create_task('moc', x
, moc_node
)
350 @feature('qt5', 'qt6')
351 @after_method('apply_link')
354 Adds MOC_FLAGS which may be necessary for moc::
357 bld.program(features='qt5', source='main.cpp', target='app', use='QT5CORE')
359 The additional parameters are:
361 :param lang: list of translation files (\\*.ts) to process
362 :type lang: list of :py:class:`waflib.Node.Node` or string without the .ts extension
363 :param update: whether to process the C++ files to update the \\*.ts files (use **waf --translate**)
365 :param langname: if given, transform the \\*.ts files into a .qrc files to include in the binary file
366 :type langname: :py:class:`waflib.Node.Node` or string without the .qrc extension
368 if getattr(self
, 'lang', None):
370 for x
in self
.to_list(self
.lang
):
371 if isinstance(x
, str):
372 x
= self
.path
.find_resource(x
+ '.ts')
373 qmtasks
.append(self
.create_task('ts2qm', x
, x
.change_ext('.%d.qm' % self
.idx
)))
375 if getattr(self
, 'update', None) and Options
.options
.trans_qt5
:
376 cxxnodes
= [a
.inputs
[0] for a
in self
.compiled_tasks
] + [
377 a
.inputs
[0] for a
in self
.tasks
if a
.inputs
and a
.inputs
[0].name
.endswith('.ui')]
379 self
.create_task('trans_update', cxxnodes
, x
.inputs
)
381 if getattr(self
, 'langname', None):
382 qmnodes
= [x
.outputs
[0] for x
in qmtasks
]
383 rcnode
= self
.langname
384 if isinstance(rcnode
, str):
385 rcnode
= self
.path
.find_or_declare(rcnode
+ ('.%d.qrc' % self
.idx
))
386 t
= self
.create_task('qm2rcc', qmnodes
, rcnode
)
387 k
= create_rcc_task(self
, t
.outputs
[0])
388 self
.link_task
.inputs
.append(k
.outputs
[0])
391 for flag
in self
.to_list(self
.env
.CXXFLAGS
):
395 if f
in ('-D', '-I', '/D', '/I'):
397 lst
.append('-' + flag
[1:])
400 self
.env
.append_value('MOC_FLAGS', lst
)
403 def cxx_hook(self
, node
):
405 Re-maps C++ file extensions to the :py:class:`waflib.Tools.qt5.qxx` task.
407 return self
.create_compiled_task('qxx', node
)
409 class rcc(Task
.Task
):
411 Processes ``.qrc`` files
414 run_str
= '${QT_RCC} -name ${tsk.rcname()} ${SRC[0].abspath()} ${RCC_ST} -o ${TGT}'
418 return os
.path
.splitext(self
.inputs
[0].name
)[0]
421 """Parse the *.qrc* files"""
423 Logs
.error('No xml.sax support was found, rcc dependencies will be incomplete!')
426 parser
= make_parser()
427 curHandler
= XMLHandler()
428 parser
.setContentHandler(curHandler
)
429 with
open(self
.inputs
[0].abspath(), 'r') as f
:
434 root
= self
.inputs
[0].parent
435 for x
in curHandler
.files
:
436 nd
= root
.find_resource(x
)
441 return (nodes
, names
)
443 def quote_flag(self
, x
):
445 Override Task.quote_flag. QT parses the argument files
446 differently than cl.exe and link.exe
456 class moc(Task
.Task
):
458 Creates ``.moc`` files
461 run_str
= '${QT_MOC} ${MOC_FLAGS} ${MOCCPPPATH_ST:INCPATHS} ${MOCDEFINES_ST:DEFINES} ${SRC} ${MOC_ST} ${TGT}'
463 def quote_flag(self
, x
):
465 Override Task.quote_flag. QT parses the argument files
466 differently than cl.exe and link.exe
476 class ui5(Task
.Task
):
478 Processes ``.ui`` files
481 run_str
= '${QT_UIC} ${SRC} -o ${TGT}'
484 class ts2qm(Task
.Task
):
486 Generates ``.qm`` files from ``.ts`` files
489 run_str
= '${QT_LRELEASE} ${QT_LRELEASE_FLAGS} ${SRC} -qm ${TGT}'
491 class qm2rcc(Task
.Task
):
493 Generates ``.qrc`` files from ``.qm`` files
498 """Create a qrc file including the inputs"""
499 txt
= '\n'.join(['<file>%s</file>' % k
.path_from(self
.outputs
[0].parent
) for k
in self
.inputs
])
500 code
= '<!DOCTYPE RCC><RCC version="1.0">\n<qresource>\n%s\n</qresource>\n</RCC>' % txt
501 self
.outputs
[0].write(code
)
505 Besides the configuration options, the environment variable QT5_ROOT may be used
506 to give the location of the qt5 libraries (absolute path).
508 The detection uses the program ``pkg-config`` through :py:func:`waflib.Tools.config_c.check_cfg`
510 if 'COMPILER_CXX' not in self
.env
:
511 self
.fatal('No CXX compiler defined: did you forget to configure compiler_cxx first?')
513 self
.want_qt6
= getattr(self
, 'want_qt6', False)
516 self
.qt_vars
= Utils
.to_list(getattr(self
, 'qt6_vars', []))
518 self
.qt_vars
= Utils
.to_list(getattr(self
, 'qt5_vars', []))
520 self
.find_qt5_binaries()
521 self
.set_qt5_libs_dir()
522 self
.set_qt5_libs_to_check()
523 self
.set_qt5_defines()
524 self
.find_qt5_libraries()
526 self
.simplify_qt5_libs()
528 # warn about this during the configuration too
530 Logs
.error('No xml.sax support was found, rcc dependencies will be incomplete!')
532 feature
= 'qt6' if self
.want_qt6
else 'qt5'
533 # Qt6 requires C++17 (https://www.qt.io/blog/qt-6.0-released)
534 stdflag
= '-std=c++17' if self
.want_qt6
else '-std=c++11'
536 # Qt5 may be compiled with '-reduce-relocations' which requires dependent programs to have -fPIE or -fPIC?
537 frag
= '#include <QMap>\nint main(int argc, char **argv) {QMap<int,int> m;return m.keys().size();}\n'
538 uses
= 'QT6CORE' if self
.want_qt6
else 'QT5CORE'
539 for flag
in [[], '-fPIE', '-fPIC', stdflag
, [stdflag
, '-fPIE'], [stdflag
, '-fPIC']]:
540 msg
= 'See if Qt files compile '
542 msg
+= 'with %s' % flag
544 self
.check(features
=feature
+ ' cxx', use
=uses
, uselib_store
=feature
, cxxflags
=flag
, fragment
=frag
, msg
=msg
)
545 except self
.errors
.ConfigurationError
:
550 self
.fatal('Could not build a simple Qt application')
552 # FreeBSD does not add /usr/local/lib and the pkg-config files do not provide it either :-/
553 if Utils
.unversioned_sys_platform() == 'freebsd':
554 frag
= '#include <QMap>\nint main(int argc, char **argv) {QMap<int,int> m;return m.keys().size();}\n'
556 self
.check(features
=feature
+ ' cxx cxxprogram', use
=uses
, fragment
=frag
, msg
='Can we link Qt programs on FreeBSD directly?')
557 except self
.errors
.ConfigurationError
:
558 self
.check(features
=feature
+ ' cxx cxxprogram', use
=uses
, uselib_store
=feature
, libpath
='/usr/local/lib', fragment
=frag
, msg
='Is /usr/local/lib required?')
561 def find_qt5_binaries(self
):
563 Detects Qt programs such as qmake, moc, uic, lrelease
566 opt
= Options
.options
568 qtdir
= getattr(opt
, 'qtdir', '')
569 qtbin
= getattr(opt
, 'qtbin', '')
570 qt_ver
= '6' if self
.want_qt6
else '5'
575 qtbin
= os
.path
.join(qtdir
, 'bin')
577 # the qt directory has been given from QT5_ROOT - deduce the qt binary path
579 qtdir
= self
.environ
.get('QT' + qt_ver
+ '_ROOT', '')
580 qtbin
= self
.environ
.get('QT' + qt_ver
+ '_BIN') or os
.path
.join(qtdir
, 'bin')
585 # no qtdir, look in the path and in /usr/local/Trolltech
587 paths
= self
.environ
.get('PATH', '').split(os
.pathsep
)
589 '/usr/share/qt' + qt_ver
+ '/bin',
590 '/usr/local/lib/qt' + qt_ver
+ '/bin'])
593 lst
= Utils
.listdir('/usr/local/Trolltech/')
601 # keep the highest version
602 qtdir
= '/usr/local/Trolltech/%s/' % lst
[0]
603 qtbin
= os
.path
.join(qtdir
, 'bin')
606 # at the end, try to find qmake in the paths given
607 # keep the one with the highest version
609 prev_ver
= ['0', '0', '0']
610 qmake_vars
= ['qmake-qt' + qt_ver
, 'qmake' + qt_ver
, 'qmake']
612 for qmk
in qmake_vars
:
614 qmake
= self
.find_program(qmk
, path_list
=paths
)
615 except self
.errors
.ConfigurationError
:
619 version
= self
.cmd_and_log(qmake
+ ['-query', 'QT_VERSION']).strip()
620 except self
.errors
.WafError
:
624 new_ver
= version
.split('.')
625 if new_ver
[0] == qt_ver
and new_ver
> prev_ver
:
629 # qmake could not be found easily, rely on qtchooser
632 self
.find_program('qtchooser')
633 except self
.errors
.ConfigurationError
:
636 cmd
= self
.env
.QTCHOOSER
+ ['-qt=' + qt_ver
, '-run-tool=qmake']
638 version
= self
.cmd_and_log(cmd
+ ['-query', 'QT_VERSION'])
639 except self
.errors
.WafError
:
645 self
.env
.QMAKE
= cand
647 self
.fatal('Could not find qmake for qt' + qt_ver
)
649 # Once we have qmake, we want to query qmake for the paths where we want to look for tools instead
652 self
.env
.QT_HOST_BINS
= qtbin
= self
.cmd_and_log(self
.env
.QMAKE
+ ['-query', 'QT_HOST_BINS']).strip()
656 self
.env
.QT_HOST_LIBEXECS
= self
.cmd_and_log(self
.env
.QMAKE
+ ['-query', 'QT_HOST_LIBEXECS']).strip()
657 paths
.append(self
.env
.QT_HOST_LIBEXECS
)
659 def find_bin(lst
, var
):
664 ret
= self
.find_program(f
, path_list
=paths
)
665 except self
.errors
.ConfigurationError
:
671 find_bin(['uic-qt' + qt_ver
, 'uic'], 'QT_UIC')
673 self
.fatal('cannot find the uic compiler for qt' + qt_ver
)
675 self
.start_msg('Checking for uic version')
676 uicver
= self
.cmd_and_log(env
.QT_UIC
+ ['-version'], output
=Context
.BOTH
)
677 uicver
= ''.join(uicver
).strip()
678 uicver
= uicver
.replace('Qt User Interface Compiler ','').replace('User Interface Compiler for Qt', '')
680 if uicver
.find(' 3.') != -1 or uicver
.find(' 4.') != -1 or (self
.want_qt6
and uicver
.find(' 5.') != -1):
682 self
.fatal('this uic compiler is for qt3 or qt4 or qt5, add uic for qt6 to your path')
684 self
.fatal('this uic compiler is for qt3 or qt4, add uic for qt5 to your path')
686 find_bin(['moc-qt' + qt_ver
, 'moc'], 'QT_MOC')
687 find_bin(['rcc-qt' + qt_ver
, 'rcc'], 'QT_RCC')
688 find_bin(['lrelease-qt' + qt_ver
, 'lrelease'], 'QT_LRELEASE')
689 find_bin(['lupdate-qt' + qt_ver
, 'lupdate'], 'QT_LUPDATE')
691 env
.UIC_ST
= '%s -o %s'
693 env
.ui_PATTERN
= 'ui_%s.h'
694 env
.QT_LRELEASE_FLAGS
= ['-silent']
695 env
.MOCCPPPATH_ST
= '-I%s'
696 env
.MOCDEFINES_ST
= '-D%s'
699 def set_qt5_libs_dir(self
):
701 qt_ver
= '6' if self
.want_qt6
else '5'
703 qtlibs
= getattr(Options
.options
, 'qtlibs', None) or self
.environ
.get('QT' + qt_ver
+ '_LIBDIR')
707 qtlibs
= self
.cmd_and_log(env
.QMAKE
+ ['-query', 'QT_INSTALL_LIBS']).strip()
708 except Errors
.WafError
:
709 qtdir
= self
.cmd_and_log(env
.QMAKE
+ ['-query', 'QT_INSTALL_PREFIX']).strip()
710 qtlibs
= os
.path
.join(qtdir
, 'lib')
712 self
.msg('Found the Qt' + qt_ver
+ ' library path', qtlibs
)
717 def find_single_qt5_lib(self
, name
, uselib
, qtlibs
, qtincludes
, force_static
):
719 qt_ver
= '6' if self
.want_qt6
else '5'
722 exts
= ('.a', '.lib')
725 exts
= ('.so', '.lib')
730 for k
in ('', qt_ver
) if Utils
.is_win32
else ['']:
731 for p
in ('lib', ''):
732 yield (p
, name
, k
, x
)
734 for tup
in lib_names():
736 path
= os
.path
.join(qtlibs
, k
)
737 if os
.path
.exists(path
):
738 if env
.DEST_OS
== 'win32':
739 libval
= ''.join(tup
[:-1])
742 env
.append_unique(prefix
+ '_' + uselib
, libval
)
743 env
.append_unique('%sPATH_%s' % (prefix
, uselib
), qtlibs
)
744 env
.append_unique('INCLUDES_' + uselib
, qtincludes
)
745 env
.append_unique('INCLUDES_' + uselib
, os
.path
.join(qtincludes
, name
.replace('Qt' + qt_ver
, 'Qt')))
750 def find_qt5_libraries(self
):
752 qt_ver
= '6' if self
.want_qt6
else '5'
754 qtincludes
= self
.environ
.get('QT' + qt_ver
+ '_INCLUDES') or self
.cmd_and_log(env
.QMAKE
+ ['-query', 'QT_INSTALL_HEADERS']).strip()
755 force_static
= self
.environ
.get('QT' + qt_ver
+ '_FORCE_STATIC')
758 if self
.environ
.get('QT' + qt_ver
+ '_XCOMPILE'):
759 self
.fatal('QT' + qt_ver
+ '_XCOMPILE Disables pkg-config detection')
760 self
.check_cfg(atleast_pkgconfig_version
='0.1')
761 except self
.errors
.ConfigurationError
:
762 for i
in self
.qt_vars
:
764 if Utils
.unversioned_sys_platform() == 'darwin':
765 # Since at least qt 4.7.3 each library locates in separate directory
766 fwk
= i
.replace('Qt' + qt_ver
, 'Qt')
767 frameworkName
= fwk
+ '.framework'
769 qtDynamicLib
= os
.path
.join(env
.QTLIBS
, frameworkName
, fwk
)
770 if os
.path
.exists(qtDynamicLib
):
771 env
.append_unique('FRAMEWORK_' + uselib
, fwk
)
772 env
.append_unique('FRAMEWORKPATH_' + uselib
, env
.QTLIBS
)
773 self
.msg('Checking for %s' % i
, qtDynamicLib
, 'GREEN')
775 self
.msg('Checking for %s' % i
, False, 'YELLOW')
776 env
.append_unique('INCLUDES_' + uselib
, os
.path
.join(env
.QTLIBS
, frameworkName
, 'Headers'))
778 ret
= self
.find_single_qt5_lib(i
, uselib
, env
.QTLIBS
, qtincludes
, force_static
)
779 if not force_static
and not ret
:
780 ret
= self
.find_single_qt5_lib(i
, uselib
, env
.QTLIBS
, qtincludes
, True)
781 self
.msg('Checking for %s' % i
, ret
, 'GREEN' if ret
else 'YELLOW')
783 path
= '%s:%s:%s/pkgconfig:/usr/lib/qt%s/lib/pkgconfig:/opt/qt%s/lib/pkgconfig:/usr/lib/qt%s/lib:/opt/qt%s/lib' % (
784 self
.environ
.get('PKG_CONFIG_PATH', ''), env
.QTLIBS
, env
.QTLIBS
, qt_ver
, qt_ver
, qt_ver
, qt_ver
)
785 for i
in self
.qt_vars
:
786 self
.check_cfg(package
=i
, args
='--cflags --libs', mandatory
=False, force_static
=force_static
, pkg_config_path
=path
)
789 def simplify_qt5_libs(self
):
791 Since library paths make really long command-lines,
792 and since everything depends on qtcore, remove the qtcore ones from qtgui, etc
795 def process_lib(vars_
, coreval
):
801 value
= env
['LIBPATH_'+var
]
809 env
['LIBPATH_'+var
] = accu
810 process_lib(self
.qt_vars
, 'LIBPATH_QTCORE')
813 def add_qt5_rpath(self
):
815 Defines rpath entries for Qt libraries
818 if getattr(Options
.options
, 'want_rpath', False):
819 def process_rpath(vars_
, coreval
):
822 value
= env
['LIBPATH_' + var
]
830 accu
.append('-Wl,--rpath='+lib
)
831 env
['RPATH_' + var
] = accu
832 process_rpath(self
.qt_vars
, 'LIBPATH_QTCORE')
835 def set_qt5_libs_to_check(self
):
836 qt_ver
= '6' if self
.want_qt6
else '5'
839 dirlst
= Utils
.listdir(self
.env
.QTLIBS
)
841 pat
= self
.env
.cxxshlib_PATTERN
843 pat
= pat
.replace('.dll', '.lib')
844 if self
.environ
.get('QT' + qt_ver
+ '_FORCE_STATIC'):
845 pat
= self
.env
.cxxstlib_PATTERN
846 if Utils
.unversioned_sys_platform() == 'darwin':
847 pat
= r
"%s\.framework"
849 # We only want to match Qt5 or Qt in the case of Qt5, in the case
850 # of Qt6 we want to match Qt6 or Qt. This speeds up configuration
851 # and reduces the chattiness of the configuration. Should also prevent
852 # possible misconfiguration.
854 re_qt
= re
.compile(pat
% 'Qt6?(?!\\d)(?P<name>\\w+)' + '$')
856 re_qt
= re
.compile(pat
% 'Qt5?(?!\\d)(?P<name>\\w+)' + '$')
858 for x
in sorted(dirlst
):
861 self
.qt_vars
.append("Qt%s%s" % (qt_ver
, m
.group('name')))
863 self
.fatal('cannot find any Qt%s library (%r)' % (qt_ver
, self
.env
.QTLIBS
))
865 qtextralibs
= getattr(Options
.options
, 'qtextralibs', None)
867 self
.qt_vars
.extend(qtextralibs
.split(','))
870 def set_qt5_defines(self
):
871 qt_ver
= '6' if self
.want_qt6
else '5'
873 if sys
.platform
!= 'win32':
876 for x
in self
.qt_vars
:
877 y
=x
.replace('Qt' + qt_ver
, 'Qt')[2:].upper()
878 self
.env
.append_unique('DEFINES_%s' % x
.upper(), 'QT_%s_LIB' % y
)
884 opt
.add_option('--want-rpath', action
='store_true', default
=False, dest
='want_rpath', help='enable the rpath for qt libraries')
885 for i
in 'qtdir qtbin qtlibs'.split():
886 opt
.add_option('--'+i
, type='string', default
='', dest
=i
)
888 opt
.add_option('--translate', action
='store_true', help='collect translation strings', dest
='trans_qt5', default
=False)
889 opt
.add_option('--qtextralibs', type='string', default
='', dest
='qtextralibs', help='additional qt libraries on the system to add to default ones, comma separated')