9 class ToolQtWarning(SCons.Warnings.Warning):
11 class GeneratedMocFileNotIncluded(ToolQtWarning):
13 class QtdirNotFound(ToolQtWarning):
15 SCons.Warnings.enableWarningClass(ToolQtWarning)
17 qrcinclude_re = re.compile(r'<file>([^<]*)</file>', re.M)
20 header_extensions = [".h", ".hxx", ".hpp", ".hh"]
21 if SCons.Util.case_sensitive_suffixes('.h', '.H'):
22 header_extensions.append('.H')
23 #cplusplus = __import__('c++', globals(), locals(), ['Scons.Tools'])
24 #cxx_suffixes = cplusplus.CXXSuffixes
25 cxx_suffixes = [".C", ".c", ".cxx", ".cpp", ".cc"]
27 def _checkMocIncluded(target, source, env):
30 # looks like cpp.includes is cleared before the build stage :-(
31 # not really sure about the path transformations (moc.cwd? cpp.cwd?) :-/
32 path = SCons.Defaults.CScan.path_function(env, moc.cwd)
33 includes = SCons.Defaults.CScan(cpp, env, path)
34 if not moc in includes:
36 GeneratedMocFileNotIncluded,
37 "Generated moc file '%s' is not included by '%s'" %
40 def _find_file(filename, paths, node_factory):
43 node = node_factory(filename, dir)
50 Callable class, which works as an emitter for Programs, SharedLibraries and
54 def __init__(self, objBuilderName):
55 self.objBuilderName = objBuilderName
57 def __call__(self, target, source, env):
59 Smart autoscan function. Gets the list of objects for the Program
60 or Lib. Adds objects and builders for the special qt files.
63 if int(env.subst('$QT_AUTOSCAN')) == 0:
68 debug = int(env.subst('$QT_DEBUG'))
72 # some shortcuts used in the scanner
73 FS = SCons.Node.FS.default_fs
74 splitext = SCons.Util.splitext
75 objBuilder = getattr(env, self.objBuilderName)
77 # some regular expressions:
79 q_object_search = re.compile(r'[^A-Za-z0-9]Q_OBJECT[^A-Za-z0-9]')
80 # cxx and c comment 'eater'
81 #comment = re.compile(r'(//.*)|(/\*(([^*])|(\*[^/]))*\*/)')
82 # CW: something must be wrong with the regexp. See also bug #998222
83 # CURRENTLY THERE IS NO TEST CASE FOR THAT
85 # The following is kind of hacky to get builders working properly (FIXME)
86 objBuilderEnv = objBuilder.env
88 mocBuilderEnv = env.Moc4.env
91 # make a deep copy for the result; MocH objects will be appended
92 out_sources = source[:]
95 if not obj.has_builder():
96 # binary obj file provided
98 print "scons: qt: '%s' seems to be a binary. Discarded." % str(obj)
101 if not splitext(str(cpp))[1] in cxx_suffixes:
103 print "scons: qt: '%s' is no cxx file. Discarded." % str(cpp)
104 # c or fortran source
106 #cpp_contents = comment.sub('', cpp.get_contents())
107 cpp_contents = cpp.get_contents()
109 for h_ext in header_extensions:
110 # try to find the header file in the corresponding source
112 hname = splitext(cpp.name)[0] + h_ext
113 h = _find_file(hname,
118 print "scons: qt: Scanning '%s' (header of '%s')" % (str(h), str(cpp))
119 #h_contents = comment.sub('', h.get_contents())
120 h_contents = h.get_contents()
123 print "scons: qt: no header for '%s'." % (str(cpp))
124 if h and q_object_search.search(h_contents):
125 # h file with the Q_OBJECT macro found -> add moc_cpp
126 moc_cpp = env.Moc4(h)
127 moc_o = objBuilder(moc_cpp)
128 out_sources.append(moc_o)
129 #moc_cpp.target_scanner = SCons.Defaults.CScan
131 print "scons: qt: found Q_OBJECT macro in '%s', moc'ing to '%s'" % (str(h), str(moc_cpp))
132 if cpp and q_object_search.search(cpp_contents):
133 # cpp file with Q_OBJECT macro found -> add moc
134 # (to be included in cpp)
138 print "scons: qt: found Q_OBJECT macro in '%s', moc'ing to '%s'" % (str(cpp), str(moc))
139 #moc.source_scanner = SCons.Defaults.CScan
140 # restore the original env attributes (FIXME)
141 objBuilder.env = objBuilderEnv
142 env.Moc4.env = mocBuilderEnv
144 return (target, out_sources)
146 AutomocShared = _Automoc('SharedObject')
147 AutomocStatic = _Automoc('StaticObject')
150 """Not really safe, but fast method to detect the QT library"""
152 QTDIR = env.get('QTDIR',None)
153 if QTDIR!=None : return QTDIR
155 QTDIR = os.environ.get('QTDIR',None)
156 if QTDIR!=None : return QTDIR
158 moc = env.WhereIs('moc-qt4') or env.WhereIs('moc')
162 "QTDIR variable is not defined, using moc executable as a hint (QTDIR=%s)" % QTDIR)
163 return os.path.dirname(os.path.dirname(moc))
167 "Could not detect qt, using empty QTDIR")
171 """Add Builders and construction variables for qt to an Environment."""
173 print "Loading qt4 tool..."
175 def locateQt4Command(env, command, qtdir) :
176 fullpath1 = os.path.join(qtdir,'bin',command +'-qt4')
177 if os.access(fullpath1, os.X_OK) or \
178 os.access(fullpath1+".exe", os.X_OK):
180 fullpath2 = os.path.join(qtdir,'bin',command)
181 if os.access(fullpath2, os.X_OK) or \
182 os.access(fullpath2+".exe", os.X_OK):
184 fullpath = env.Detect([command+'-qt4', command])
185 if not (fullpath is None) : return fullpath
186 raise "Qt4 command '" + command + "' not found. Tried: " + fullpath1 + " and "+ fullpath2
189 CLVar = SCons.Util.CLVar
190 Action = SCons.Action.Action
191 Builder = SCons.Builder.Builder
192 splitext = SCons.Util.splitext
195 env['QTDIR'] = _detect(env)
196 env['QT4_MOC'] = locateQt4Command(env,'moc', env['QTDIR'])
197 env['QT4_UIC'] = locateQt4Command(env,'uic', env['QTDIR'])
198 env['QT4_RCC'] = locateQt4Command(env,'rcc', env['QTDIR'])
199 env['QT4_LUPDATE'] = locateQt4Command(env,'lupdate', env['QTDIR'])
200 env['QT4_LRELEASE'] = locateQt4Command(env,'lrelease', env['QTDIR'])
202 # Should the qt tool try to figure out, which sources are to be moc'ed ?
203 env['QT4_AUTOSCAN'] = 1
205 # Some QT specific flags. I don't expect someone wants to
206 # manipulate those ...
207 env['QT4_UICDECLFLAGS'] = CLVar('')
208 env['QT4_MOCFROMHFLAGS'] = CLVar('')
209 env['QT4_MOCFROMCXXFLAGS'] = CLVar('-i')
210 env['QT4_QRCFLAGS'] = ''
212 # suffixes/prefixes for the headers / sources to generate
213 env['QT4_MOCHPREFIX'] = ''
214 env['QT4_MOCHSUFFIX'] = '$CXXFILESUFFIX'
215 env['QT4_MOCCXXPREFIX'] = 'moc_'
216 env['QT4_MOCCXXSUFFIX'] = '.moc'
217 env['QT4_UISUFFIX'] = '.ui'
218 env['QT4_UICDECLPREFIX'] = 'ui_'
219 env['QT4_UICDECLSUFFIX'] = '.h'
220 env['QT4_QRCSUFFIX'] = '.qrc',
221 env['QT4_QRCCXXSUFFIX'] = '$CXXFILESUFFIX'
222 env['QT4_QRCCXXPREFIX'] = 'qrc_'
224 env['QT4_LIB'] = '' # KLUDGE to avoid linking qt3 library
226 # Translation builder
228 action ='$QT4_LUPDATE $SOURCES -ts $TARGETS',
231 env.Append( BUILDERS = { 'Ts': tsbuilder } )
234 '$QT4_LRELEASE $SOURCE',
240 env.Append( BUILDERS = { 'Qm': qmbuilder } )
243 def scanResources(node, env, path, arg):
244 contents = node.get_contents()
245 includes = qrcinclude_re.findall(contents)
247 qrcscanner = env.Scanner(name = 'qrcfile',
248 function = scanResources,
251 qrcbuilder = Builder(
252 action ='$QT4_RCC $QT4_QRCFLAGS $SOURCE -o $TARGET',
253 source_scanner = qrcscanner,
254 src_suffix = '$QT4_QRCSUFFIX',
255 suffix = '$QT4_QRCCXXSUFFIX',
256 prefix = '$QT4_QRCCXXPREFIX',
259 env.Append( BUILDERS = { 'Qrc': qrcbuilder } )
262 #env['QT4_UIC4COM'] = [
263 # CLVar('$QT4_UIC $QT4_UICDECLFLAGS -o ${TARGETS[0]} $SOURCE'),
265 env['QT4_UIC4COM'] = '$QT4_UIC -o $TARGET $SOURCE'
266 uic4builder = Builder(
267 action='$QT4_UIC4COM',
268 src_suffix='$QT4_UISUFFIX',
269 suffix='$QT4_UICDECLSUFFIX',
270 #prefix='$QT4_UICDECLPREFIX',
273 env.Append( BUILDERS = { 'Uic4': uic4builder } )
276 env['QT4_MOCFROMHCOM'] = (
277 '$QT4_MOC $QT4_MOCFROMHFLAGS -o ${TARGETS[0]} $SOURCE')
278 env['QT4_MOCFROMCXXCOM'] = [
279 CLVar('$QT4_MOC $QT4_MOCFROMCXXFLAGS -o ${TARGETS[0]} $SOURCE'),
280 Action(_checkMocIncluded,None)]
281 mocBld = Builder(action={}, prefix={}, suffix={})
282 for h in header_extensions:
283 mocBld.add_action(h, '$QT4_MOCFROMHCOM')
284 mocBld.prefix[h] = '$QT4_MOCHPREFIX'
285 mocBld.suffix[h] = '$QT4_MOCHSUFFIX'
286 for cxx in cxx_suffixes:
287 mocBld.add_action(cxx, '$QT4_MOCFROMCXXCOM')
288 mocBld.prefix[cxx] = '$QT4_MOCCXXPREFIX'
289 mocBld.suffix[cxx] = '$QT4_MOCCXXSUFFIX'
290 env.Append( BUILDERS = { 'Moc4': mocBld } )
292 # er... no idea what that was for
293 static_obj, shared_obj = SCons.Tool.createObjBuilders(env)
294 static_obj.src_builder.append('Uic4')
295 shared_obj.src_builder.append('Uic4')
297 # We use the emitters of Program / StaticLibrary / SharedLibrary
298 # to scan for moc'able files
299 # We can't refer to the builders directly, we have to fetch them
300 # as Environment attributes because that sets them up to be called
301 # correctly later by our emitter.
302 env.AppendUnique(PROGEMITTER =[AutomocStatic],
303 SHLIBEMITTER=[AutomocShared],
304 LIBEMITTER =[AutomocStatic],
305 # Of course, we need to link against the qt libraries
306 CPPPATH=[os.path.join('$QTDIR', 'include')],
307 LIBPATH=[os.path.join('$QTDIR', 'lib')],
311 method = new.instancemethod(enable_modules, env, SCons.Environment)
312 env.EnableQt4Modules=method
314 def enable_modules(self, modules, debug=False) :
322 # The next modules have not been tested yet so, please
323 # maybe they require additional work on non Linux platforms
335 # under windows, they are named QtCore4 etc
336 validModules += [x+'4' for x in validModules]
337 pclessModules += [x+'4' for x in pclessModules]
339 for module in modules:
340 if module not in validModules :
341 invalidModules.append(module)
343 raise "Modules %s are not Qt4 modules. Valid Qt4 modules are: %s"% \
344 (str(invalidModules),str(validModules))
346 # TODO: Check whether we should add QT_CORE_LIB, QT_XML_LIB, QT_NETWORK_LIB...
347 if 'QtGui' in modules:
348 self.AppendUnique(CPPFLAGS='-DQT_GUI_LIB')
350 if sys.platform == "linux2" :
351 if debug : modules = [module+"_debug" for module in modules]
352 for module in modules :
353 if module in pclessModules :
354 # self.AppendUnique(LIBS=[module])
355 self.AppendUnique(LIBPATH=[os.path.join(self["QTDIR"],"lib",module)])
356 self.AppendUnique(CPPPATH=[os.path.join(self["QTDIR"],"include","qt4",module)])
357 modules.remove(module)
358 self.ParseConfig('PKG_CONFIG_PATH=%s/lib pkg-config %s --libs --cflags'%
363 if sys.platform == "win32" :
364 if debug : debugSuffix = 'd'
365 else : debugSuffix = ''
366 self.AppendUnique(LIBS=[lib+'4'+debugSuffix for lib in modules])
367 if 'QtOpenGL' in modules:
368 self.AppendUnique(LIBS=['opengl32'])
369 self.AppendUnique(CPPPATH=[ '$QTDIR/include/'+module[:-1]
370 for module in modules])
371 self.AppendUnique(LIBPATH=['$QTDIR/lib'])