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", ".cxx", ".cpp", ".cc", ".C"]
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 fullpath = env.Detect([command+'-qt4', command])
177 if not (fullpath is None) : return fullpath
178 fullpath1 = os.path.join(qtdir,'bin',command +'-qt4')
179 if os.access(fullpath1, os.X_OK) or \
180 os.access(fullpath1+".exe", os.X_OK):
182 fullpath2 = os.path.join(qtdir,'bin',command)
183 if os.access(fullpath2, os.X_OK) or \
184 os.access(fullpath2+".exe", os.X_OK):
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'] = 'moc_'
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 uic4builder = Builder(
266 action='$QT4_UIC4COM',
267 src_suffix='$QT4_UISUFFIX',
268 suffix='$QT4_UICDECLSUFFIX',
269 prefix='$QT4_UICDECLPREFIX',
272 env.Append( BUILDERS = { 'Uic4': uic4builder } )
275 env['QT4_MOCFROMHCOM'] = (
276 '$QT4_MOC $QT4_MOCFROMHFLAGS -o ${TARGETS[0]} $SOURCE')
277 env['QT4_MOCFROMCXXCOM'] = [
278 CLVar('$QT4_MOC $QT4_MOCFROMCXXFLAGS -o ${TARGETS[0]} $SOURCE'),
279 Action(_checkMocIncluded,None)]
280 mocBld = Builder(action={}, prefix={}, suffix={})
281 for h in header_extensions:
282 mocBld.add_action(h, '$QT4_MOCFROMHCOM')
283 mocBld.prefix[h] = '$QT4_MOCHPREFIX'
284 mocBld.suffix[h] = '$QT4_MOCHSUFFIX'
285 for cxx in cxx_suffixes:
286 mocBld.add_action(cxx, '$QT4_MOCFROMCXXCOM')
287 mocBld.prefix[cxx] = '$QT4_MOCCXXPREFIX'
288 mocBld.suffix[cxx] = '$QT4_MOCCXXSUFFIX'
289 env.Append( BUILDERS = { 'Moc4': mocBld } )
291 # er... no idea what that was for
292 static_obj, shared_obj = SCons.Tool.createObjBuilders(env)
293 static_obj.src_builder.append('Uic4')
294 shared_obj.src_builder.append('Uic4')
296 # We use the emitters of Program / StaticLibrary / SharedLibrary
297 # to scan for moc'able files
298 # We can't refer to the builders directly, we have to fetch them
299 # as Environment attributes because that sets them up to be called
300 # correctly later by our emitter.
301 env.AppendUnique(PROGEMITTER =[AutomocStatic],
302 SHLIBEMITTER=[AutomocShared],
303 LIBEMITTER =[AutomocStatic],
304 # Of course, we need to link against the qt libraries
305 CPPPATH=[os.path.join('$QTDIR', 'include')],
306 LIBPATH=[os.path.join('$QTDIR', 'lib')],
310 method = new.instancemethod(enable_modules, env, SCons.Environment)
311 env.EnableQt4Modules=method
313 def enable_modules(self, modules, debug=False) :
321 # The next modules have not been tested yet so, please
322 # maybe they require additional work on non Linux platforms
335 for module in modules:
336 if module not in validModules :
337 invalidModules.append(module)
339 raise "Modules %s are not Qt4 modules. Valid Qt4 modules are: %s"% \
340 (str(invalidModules),str(validModules))
342 # TODO: Check whether we should add QT_CORE_LIB, QT_XML_LIB, QT_NETWORK_LIB...
343 if 'QtGui' in modules:
344 self.AppendUnique(CPPFLAGS='-DQT_GUI_LIB')
346 if sys.platform == "linux2" :
347 if debug : modules = [module+"_debug" for module in modules]
348 for module in modules :
349 if module in pclessModules :
350 # self.AppendUnique(LIBS=[module])
351 self.AppendUnique(LIBPATH=[os.path.join(self["QTDIR"],"lib",module)])
352 self.AppendUnique(CPPPATH=[os.path.join(self["QTDIR"],"include","qt4",module)])
353 modules.remove(module)
354 # modified by Bo Peng (/lib/pkgconfig => /lib)
355 self.ParseConfig('PKG_CONFIG_PATH=%s/lib pkg-config %s --libs --cflags'%
360 if sys.platform == "win32" :
361 if debug : debugSuffix = 'd'
362 else : debugSuffix = ''
363 self.AppendUnique(LIBS=[lib+'4'+debugSuffix for lib in modules])
364 if 'QtOpenGL' in modules:
365 self.AppendUnique(LIBS=['opengl32'])
366 self.AppendUnique(CPPPATH=[ '$QTDIR/include/'+module
367 for module in modules])
368 self.AppendUnique(LIBPATH=['$QTDIR/lib'])