Add Qt4Glob utility
[qtscons.git] / qt4.py
1 # -*- coding: utf-8 -*-
2 """SCons.Tool.qt
3
4 Tool-specific initialization for Qt.
5
6 There normally shouldn't be any need to import this module directly.
7 It will usually be imported through the generic SCons.Tool.Tool()
8 selection method.
9
10 """
11
12 #
13 # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation
14 #
15 # Permission is hereby granted, free of charge, to any person obtaining
16 # a copy of this software and associated documentation files (the
17 # "Software"), to deal in the Software without restriction, including
18 # without limitation the rights to use, copy, modify, merge, publish,
19 # distribute, sublicense, and/or sell copies of the Software, and to
20 # permit persons to whom the Software is furnished to do so, subject to
21 # the following conditions:
22 #
23 # The above copyright notice and this permission notice shall be included
24 # in all copies or substantial portions of the Software.
25 #
26 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
27 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
28 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 #
34
35 __revision__ = "/home/scons/scons/branch.0/branch.96/baseline/src/engine/SCons/Tool/qt.py 0.96.92.D001 2006/04/10 23:13:27 knight"
36
37 import os.path
38 import re
39
40 import SCons.Action
41 import SCons.Builder
42 import SCons.Defaults
43 import SCons.Scanner
44 import SCons.Tool
45 import SCons.Util
46
47
48 class ToolQtWarning(SCons.Warnings.Warning):
49     pass
50
51 class GeneratedMocFileNotIncluded(ToolQtWarning):
52     pass
53
54 class QtdirNotFound(ToolQtWarning):
55     pass
56
57 SCons.Warnings.enableWarningClass(ToolQtWarning)
58
59 qrcinclude_re = re.compile(r'<file>([^<]*)</file>', re.M)
60
61 def transformToWinePath(path) :
62     return os.popen('winepath -w "%s"'%path).read().strip().replace('\\','/')
63
64 header_extensions = [".h", ".hxx", ".hpp", ".hh"]
65 if SCons.Util.case_sensitive_suffixes('.h', '.H'):
66     header_extensions.append('.H')
67 # TODO: The following two lines will work when integrated back to SCons
68 # TODO: Meanwhile the third line will do the work
69 #cplusplus = __import__('c++', globals(), locals(), [])
70 #cxx_suffixes = cplusplus.CXXSuffixes
71 cxx_suffixes = [".c", ".cxx", ".cpp", ".cc"]
72
73 def checkMocIncluded(target, source, env):
74     moc = target[0]
75     cpp = source[0]
76     # looks like cpp.includes is cleared before the build stage :-(
77     # not really sure about the path transformations (moc.cwd? cpp.cwd?) :-/
78     path = SCons.Defaults.CScan.path_function(env, moc.cwd)
79     includes = SCons.Defaults.CScan(cpp, env, path)
80     if not moc in includes:
81         SCons.Warnings.warn(
82             GeneratedMocFileNotIncluded,
83             "Generated moc file '%s' is not included by '%s'" %
84             (str(moc), str(cpp)))
85
86 def find_file(filename, paths, node_factory):
87     for dir in paths:
88         node = node_factory(filename, dir)
89         if node.rexists():
90             return node
91     return None
92
93 class _Automoc:
94     """
95     Callable class, which works as an emitter for Programs, SharedLibraries and
96     StaticLibraries.
97     """
98
99     def __init__(self, objBuilderName):
100         self.objBuilderName = objBuilderName
101
102     def __call__(self, target, source, env):
103         """
104         Smart autoscan function. Gets the list of objects for the Program
105         or Lib. Adds objects and builders for the special qt files.
106         """
107         try:
108             if int(env.subst('$QT4_AUTOSCAN')) == 0:
109                 return target, source
110         except ValueError:
111             pass
112         try:
113             debug = int(env.subst('$QT4_DEBUG'))
114         except ValueError:
115             debug = 0
116
117         # some shortcuts used in the scanner
118         splitext = SCons.Util.splitext
119         objBuilder = getattr(env, self.objBuilderName)
120
121         # some regular expressions:
122         # Q_OBJECT detection
123         q_object_search = re.compile(r'[^A-Za-z0-9]Q_OBJECT[^A-Za-z0-9]')
124         # cxx and c comment 'eater'
125         #comment = re.compile(r'(//.*)|(/\*(([^*])|(\*[^/]))*\*/)')
126         # CW: something must be wrong with the regexp. See also bug #998222
127         #    CURRENTLY THERE IS NO TEST CASE FOR THAT
128
129         # The following is kind of hacky to get builders working properly (FIXME)
130         objBuilderEnv = objBuilder.env
131         objBuilder.env = env
132         mocBuilderEnv = env.Moc4.env
133         env.Moc4.env = env
134
135         # make a deep copy for the result; MocH objects will be appended
136         out_sources = source[:]
137
138         for obj in source:
139             if isinstance(obj,basestring):  # big kludge!
140                 print "scons: qt4: '%s' MAYBE USING AN OLD SCONS VERSION AND NOT CONVERTED TO¨ \
141                     ¨ 'File'. Discarded." % str(obj)
142                 continue
143             if not obj.has_builder():
144                 # binary obj file provided
145                 if debug:
146                     print "scons: qt: '%s' seems to be a binary. Discarded." % str(obj)
147                 continue
148             cpp = obj.sources[0]
149             if not splitext(str(cpp))[1] in cxx_suffixes:
150                 if debug:
151                     print "scons: qt: '%s' is no cxx file. Discarded." % str(cpp)
152                 # c or fortran source
153                 continue
154             #cpp_contents = comment.sub('', cpp.get_contents())
155             try:
156                 cpp_contents = cpp.get_contents()
157             except: continue # may be an still not generated source
158             h=None
159             for h_ext in header_extensions:
160                 # try to find the header file in the corresponding source
161                 # directory
162                 hname = splitext(cpp.name)[0] + h_ext
163                 h = find_file(hname, (cpp.get_dir(),), env.File)
164                 if h:
165                     if debug:
166                         print "scons: qt: Scanning '%s' (header of '%s')" % (str(h), str(cpp))
167                     #h_contents = comment.sub('', h.get_contents())
168                     h_contents = h.get_contents()
169                     break
170             if not h and debug:
171                 print "scons: qt: no header for '%s'." % (str(cpp))
172             if h and q_object_search.search(h_contents):
173                 # h file with the Q_OBJECT macro found -> add moc_cpp
174                 moc_cpp = env.Moc4(h)
175                 moc_o = objBuilder(moc_cpp)
176                 out_sources.append(moc_o)
177                 #moc_cpp.target_scanner = SCons.Defaults.CScan
178                 if debug:
179                     print "scons: qt: found Q_OBJECT macro in '%s', moc'ing to '%s'" \
180                         % (str(h), str(moc_cpp))
181             if cpp and q_object_search.search(cpp_contents):
182                 # cpp file with Q_OBJECT macro found -> add moc
183                 # (to be included in cpp)
184                 moc = env.Moc4(cpp)
185                 env.Ignore(moc, moc)
186                 if debug:
187                     print "scons: qt: found Q_OBJECT macro in '%s', moc'ing to '%s'" \
188                         % (str(cpp), str(moc))
189                 #moc.source_scanner = SCons.Defaults.CScan
190         # restore the original env attributes (FIXME)
191         objBuilder.env = objBuilderEnv
192         env.Moc4.env = mocBuilderEnv
193
194         return (target, out_sources)
195
196 AutomocShared = _Automoc('SharedObject')
197 AutomocStatic = _Automoc('StaticObject')
198
199 def _detect(env):
200     """Not really safe, but fast method to detect the QT library"""
201     try: return env['QTDIR']
202     except KeyError: pass
203
204     try: return os.environ['QTDIR']
205     except KeyError: pass
206
207     moc = env.WhereIs('moc-qt4') or env.WhereIs('moc4') or env.WhereIs('moc')
208     if moc:
209         QTDIR = os.path.dirname(os.path.dirname(moc))
210         # SCons.Warnings.warn(
211         #   QtdirNotFound,
212         #   "QTDIR variable is not defined, using moc executable as a hint (QTDIR=%s)" % QTDIR)
213         return QTDIR
214
215     raise SCons.Errors.StopError(
216         QtdirNotFound,
217         "Could not detect Qt 4 installation")
218     return None
219
220 def generate(env):
221     """Add Builders and construction variables for qt to an Environment."""
222
223     def locateQt4Command(env, command, qtdir) :
224         suffixes = [
225             '-qt4',
226             '-qt4.exe',
227             '4',
228             '4.exe',
229             '',
230             '.exe',
231         ]
232         triedPaths = []
233         for suffix in suffixes :
234             fullpath = os.path.join(qtdir,'bin',command + suffix)
235             if os.access(fullpath, os.X_OK) :
236                 return fullpath
237             triedPaths.append(fullpath)
238
239         fullpath = env.Detect([command+'-qt4', command+'4', command])
240         if not (fullpath is None) : return fullpath
241
242         raise Exception("Qt4 command '" + command + "' not found. Tried: " + ', '.join(triedPaths))
243
244
245     CLVar = SCons.Util.CLVar
246     Action = SCons.Action.Action
247     Builder = SCons.Builder.Builder
248     splitext = SCons.Util.splitext
249
250     env['QTDIR']  = _detect(env)
251     # TODO: 'Replace' should be 'SetDefault'
252     #env.SetDefault(
253     env.Replace(
254         QTDIR  = _detect(env),
255         QT4_BINPATH = os.path.join('$QTDIR', 'bin'),
256         # TODO: This is not reliable to QTDIR value changes but needed in order to support '-qt4'
257         # variants
258         QT4_MOC = locateQt4Command(env,'moc', env['QTDIR']),
259         QT4_UIC = locateQt4Command(env,'uic', env['QTDIR']),
260         QT4_RCC = locateQt4Command(env,'rcc', env['QTDIR']),
261         QT4_LUPDATE = locateQt4Command(env,'lupdate', env['QTDIR']),
262         QT4_LRELEASE = locateQt4Command(env,'lrelease', env['QTDIR']),
263
264         QT4_AUTOSCAN = 1, # Should the qt tool try to figure out, which sources are to be moc'ed?
265
266         # Some QT specific flags. I don't expect someone wants to
267         # manipulate those ...
268         QT4_UICFLAGS = CLVar(''),
269         QT4_MOCFROMHFLAGS = CLVar(''),
270         QT4_MOCFROMCXXFLAGS = CLVar('-i'),
271         QT4_QRCFLAGS = '',
272
273         # suffixes/prefixes for the headers / sources to generate
274         QT4_UISUFFIX = '.ui',
275         QT4_UICDECLPREFIX = 'ui_',
276         QT4_UICDECLSUFFIX = '.h',
277         QT4_MOCINCPREFIX = '-I',
278         QT4_MOCHPREFIX = 'moc_',
279         QT4_MOCHSUFFIX = '$CXXFILESUFFIX',
280         QT4_MOCCXXPREFIX = '',
281         QT4_MOCCXXSUFFIX = '.moc',
282         QT4_QRCSUFFIX = '.qrc',
283         QT4_QRCCXXSUFFIX = '$CXXFILESUFFIX',
284         QT4_QRCCXXPREFIX = 'qrc_',
285         QT4_MOCCPPPATH = [],
286         QT4_MOCINCFLAGS = \
287                 '$( ${_concat(QT4_MOCINCPREFIX, QT4_MOCCPPPATH, INCSUFFIX, __env__, RDirs)} $)',
288
289         # Commands for the qt support ...
290         QT4_UICCOM = '$QT4_UIC $QT4_UICFLAGS -o $TARGET $SOURCE',
291         QT4_MOCFROMHCOM = '$QT4_MOC $QT4_MOCFROMHFLAGS $QT4_MOCINCFLAGS -o $TARGET $SOURCE',
292         QT4_MOCFROMCXXCOM = [
293             '$QT4_MOC $QT4_MOCFROMCXXFLAGS $QT4_MOCINCFLAGS -o $TARGET $SOURCE',
294             Action(checkMocIncluded,None)],
295         QT4_LUPDATECOM = '$QT4_LUPDATE $SOURCE -ts $TARGET',
296         QT4_LRELEASECOM = '$QT4_LRELEASE $SOURCE',
297         QT4_RCCCOM = '$QT4_RCC $QT4_QRCFLAGS -name ${SOURCE.filebase} $SOURCE -o $TARGET',
298         )
299
300     # Translation builder
301     tsbuilder = Builder(
302         action = SCons.Action.Action('$QT4_LUPDATECOM'), #,'$QT4_LUPDATECOMSTR'),
303         multi=1
304         )
305     env.Append( BUILDERS = { 'Ts': tsbuilder } )
306     qmbuilder = Builder(
307         action = SCons.Action.Action('$QT4_LRELEASECOM'),# , '$QT4_LRELEASECOMSTR'),
308         src_suffix = '.ts',
309         suffix = '.qm',
310         single_source = True
311         )
312     env.Append( BUILDERS = { 'Qm': qmbuilder } )
313
314     # Resource builder
315     def scanResources(node, env, path, arg):
316         # I've being careful on providing names relative to the qrc file
317         # If that was not needed that code could be simplified a lot
318         def recursiveFiles(basepath, path) :
319             result = []
320             for item in os.listdir(os.path.join(basepath, path)) :
321                 itemPath = os.path.join(path, item)
322                 if os.path.isdir(os.path.join(basepath, itemPath)) :
323                     result += recursiveFiles(basepath, itemPath)
324                 else:
325                     result.append(itemPath)
326             return result
327         contents = node.get_contents()
328         includes = qrcinclude_re.findall(contents)
329         qrcpath = os.path.dirname(node.path)
330         dirs = [included for included in includes if os.path.isdir(os.path.join(qrcpath,included))]
331         # dirs need to include files recursively
332         for dir in dirs :
333             includes.remove(dir)
334             includes+=recursiveFiles(qrcpath,dir)
335         return includes
336     qrcscanner = SCons.Scanner.Scanner(name = 'qrcfile',
337         function = scanResources,
338         argument = None,
339         skeys = ['.qrc'])
340     qrcaction = SCons.Action.Action('$QT4_RCCCOM', '$QT4_RCCCOMSTR')
341     qrcbuilder = Builder(
342         action = qrcaction,
343         source_scanner = qrcscanner,
344         src_suffix = '$QT4_QRCSUFFIX',
345         suffix = '$QT4_QRCCXXSUFFIX',
346         prefix = '$QT4_QRCCXXPREFIX',
347         single_source = True
348         )
349     env.Append( BUILDERS = { 'Qrc': qrcbuilder } )
350
351     #c_file, cxx_file = SCons.Tool.createCFileBuilders(env)
352     #cxx_file.add_action('.qrc', qrcaction)
353
354     # Interface builder
355     uic4builder = Builder(
356         action = SCons.Action.Action('$QT4_UICCOM', '$QT4_UICCOMSTR'),
357         src_suffix='$QT4_UISUFFIX',
358         suffix='$QT4_UICDECLSUFFIX',
359         prefix='$QT4_UICDECLPREFIX',
360         single_source = True
361         #TODO: Consider the uiscanner on new scons version
362         )
363     env['BUILDERS']['Uic4'] = uic4builder
364
365     # Metaobject builder
366     mocBld = Builder(action={}, prefix={}, suffix={})
367     for h in header_extensions:
368         act = SCons.Action.Action('$QT4_MOCFROMHCOM', '$QT4_MOCFROMHCOMSTR')
369         mocBld.add_action(h, act)
370         mocBld.prefix[h] = '$QT4_MOCHPREFIX'
371         mocBld.suffix[h] = '$QT4_MOCHSUFFIX'
372     for cxx in cxx_suffixes:
373         act = SCons.Action.Action('$QT4_MOCFROMCXXCOM', '$QT4_MOCFROMCXXCOMSTR')
374         mocBld.add_action(cxx, act)
375         mocBld.prefix[cxx] = '$QT4_MOCCXXPREFIX'
376         mocBld.suffix[cxx] = '$QT4_MOCCXXSUFFIX'
377     env['BUILDERS']['Moc4'] = mocBld
378
379     # er... no idea what that was for
380     # Answer: This lets the Object (and therefore the Program builder) automatically
381     #         recognize and build uic and qrc files
382     static_obj, shared_obj = SCons.Tool.createObjBuilders(env)
383     static_obj.src_builder.append('Uic4')
384     shared_obj.src_builder.append('Uic4')
385     static_obj.src_builder.append('Qrc')
386     shared_obj.src_builder.append('Qrc')
387
388     # We use the emitters of Program / StaticLibrary / SharedLibrary
389     # to scan for moc'able files
390     # We can't refer to the builders directly, we have to fetch them
391     # as Environment attributes because that sets them up to be called
392     # correctly later by our emitter.
393     env.AppendUnique( PROGEMITTER =[AutomocStatic],
394               SHLIBEMITTER=[AutomocShared],
395               LIBEMITTER  =[AutomocStatic] )
396
397     # TODO: Does dbusxml2cpp need an adapter
398     env.AddMethod(enable_modules, "EnableQt4Modules")
399
400     # Utilities
401     env.AddMethod(qt4glob, "Qt4Glob")
402
403 def enable_modules(self, modules, debug=False, crosscompiling=False) :
404     import sys
405
406     validModules = [
407         'QtCore',
408         'QtGui',
409         'QtOpenGL',
410         'Qt3Support',
411         'QtAssistant', # deprecated
412         'QtAssistantClient',
413         'QtScript',
414         'QtDBus',
415         'QtSql',
416         'QtSvg',
417         # The next modules have not been tested yet so, please
418         # maybe they require additional work on non Linux platforms
419         'QtNetwork',
420         'QtTest',
421         'QtXml',
422         'QtXmlPatterns',
423         'QtUiTools',
424         'QtDesigner',
425         'QtDesignerComponents',
426         'QtWebKit',
427         'QtHelp',
428         'QtScript',
429         'QtScriptTools',
430         'QtMultimedia',
431         ]
432     pclessModules = [
433 # in qt <= 4.3 designer and designerComponents are pcless, on qt4.4 they are not, so removed.
434 #       'QtDesigner',
435 #       'QtDesignerComponents',
436     ]
437     staticModules = [
438         'QtUiTools',
439     ]
440     invalidModules=[]
441     for module in modules:
442         if module not in validModules :
443             invalidModules.append(module)
444     if invalidModules :
445         raise Exception("Modules %s are not Qt4 modules. Valid Qt4 modules are: %s"% (
446             str(invalidModules),str(validModules)))
447
448     moduleDefines = {
449         'QtScript'   : ['QT_SCRIPT_LIB'],
450         'QtSvg'      : ['QT_SVG_LIB'],
451         'Qt3Support' : ['QT_QT3SUPPORT_LIB','QT3_SUPPORT'],
452         'QtSql'      : ['QT_SQL_LIB'],
453         'QtXml'      : ['QT_XML_LIB'],
454         'QtOpenGL'   : ['QT_OPENGL_LIB'],
455         'QtGui'      : ['QT_GUI_LIB'],
456         'QtNetwork'  : ['QT_NETWORK_LIB'],
457         'QtCore'     : ['QT_CORE_LIB'],
458     }
459     for module in modules :
460         try : self.AppendUnique(CPPDEFINES=moduleDefines[module])
461         except: pass
462     debugSuffix = ''
463     if sys.platform in ["darwin", "linux2"] and not crosscompiling :
464         if debug : debugSuffix = '_debug'
465         for module in modules :
466             if module not in pclessModules : continue
467             self.AppendUnique(LIBS=[module+debugSuffix])
468             self.AppendUnique(LIBPATH=[os.path.join("$QTDIR","lib")])
469             self.AppendUnique(CPPPATH=[os.path.join("$QTDIR","include","qt4")])
470             self.AppendUnique(CPPPATH=[os.path.join("$QTDIR","include","qt4",module)])
471         pcmodules = [module+debugSuffix for module in modules if module not in pclessModules ]
472         if 'QtDBus' in pcmodules:
473             self.AppendUnique(CPPPATH=[os.path.join("$QTDIR","include","qt4","QtDBus")])
474         if "QtAssistant" in pcmodules:
475             self.AppendUnique(CPPPATH=[os.path.join("$QTDIR","include","qt4","QtAssistant")])
476             pcmodules.remove("QtAssistant")
477             pcmodules.append("QtAssistantClient")
478         self.ParseConfig('pkg-config %s --libs --cflags'% ' '.join(pcmodules))
479         self["QT4_MOCCPPPATH"] = self["CPPPATH"]
480         return
481     if sys.platform == "win32" or crosscompiling :
482         if crosscompiling:
483             transformedQtdir = transformToWinePath(self['QTDIR'])
484             self['QT4_MOC'] = "QTDIR=%s %s"%( transformedQtdir, self['QT4_MOC'])
485         self.AppendUnique(CPPPATH=[os.path.join("$QTDIR","include")])
486         try: modules.remove("QtDBus")
487         except: pass
488         if debug : debugSuffix = 'd'
489         if "QtAssistant" in modules:
490             self.AppendUnique(CPPPATH=[os.path.join("$QTDIR","include","QtAssistant")])
491             modules.remove("QtAssistant")
492             modules.append("QtAssistantClient")
493         self.AppendUnique(LIBS=[lib+'4'+debugSuffix for lib in modules if lib not in staticModules])
494         self.PrependUnique(LIBS=[lib+debugSuffix for lib in modules if lib in staticModules])
495         if 'QtOpenGL' in modules:
496             self.AppendUnique(LIBS=['opengl32'])
497         self.AppendUnique(CPPPATH=[ '$QTDIR/include/'])
498         self.AppendUnique(CPPPATH=[ '$QTDIR/include/'+module for module in modules])
499         if crosscompiling :
500             self["QT4_MOCCPPPATH"] = [
501                 path.replace('$QTDIR', transformedQtdir)
502                     for path in self['CPPPATH'] ]
503         else :
504             self["QT4_MOCCPPPATH"] = self["CPPPATH"]
505         self.AppendUnique(LIBPATH=[os.path.join('$QTDIR','lib')])
506         return
507     """
508     if sys.platform=="darwin" :
509         # TODO: Test debug version on Mac
510         self.AppendUnique(LIBPATH=[os.path.join('$QTDIR','lib')])
511         self.AppendUnique(LINKFLAGS="-F$QTDIR/lib")
512         self.AppendUnique(LINKFLAGS="-L$QTDIR/lib") #TODO clean!
513         if debug : debugSuffix = 'd'
514         for module in modules :
515 #           self.AppendUnique(CPPPATH=[os.path.join("$QTDIR","include")])
516 #           self.AppendUnique(CPPPATH=[os.path.join("$QTDIR","include",module)])
517 # port qt4-mac:
518             self.AppendUnique(CPPPATH=[os.path.join("$QTDIR","include", "qt4")])
519             self.AppendUnique(CPPPATH=[os.path.join("$QTDIR","include", "qt4", module)])
520             if module in staticModules :
521                 self.AppendUnique(LIBS=[module+debugSuffix]) # TODO: Add the debug suffix
522                 self.AppendUnique(LIBPATH=[os.path.join("$QTDIR","lib")])
523             else :
524 #               self.Append(LINKFLAGS=['-framework', module])
525 # port qt4-mac:
526                 self.Append(LIBS=module)
527         if 'QtOpenGL' in modules:
528             self.AppendUnique(LINKFLAGS="-F/System/Library/Frameworks")
529             self.Append(LINKFLAGS=['-framework', 'AGL']) #TODO ughly kludge to avoid quotes
530             self.Append(LINKFLAGS=['-framework', 'OpenGL'])
531         self["QT4_MOCCPPPATH"] = self["CPPPATH"]
532         return
533 # This should work for mac but doesn't
534 #   env.AppendUnique(FRAMEWORKPATH=[os.path.join(env['QTDIR'],'lib')])
535 #   env.AppendUnique(FRAMEWORKS=['QtCore','QtGui','QtOpenGL', 'AGL'])
536     """
537
538 def qt4glob(env):
539     resources = env.Glob("*.qrc")
540     sources = [ f for f in env.Glob("*.cc")
541                 if not f.name.startswith("qrc_") ]
542     return sources + resources
543
544 def exists(env):
545     return _detect(env)