3 # Copyright The SCons Foundation
5 # Permission is hereby granted, free of charge, to any person obtaining
6 # a copy of this software and associated documentation files (the
7 # "Software"), to deal in the Software without restriction, including
8 # without limitation the rights to use, copy, modify, merge, publish,
9 # distribute, sublicense, and/or sell copies of the Software, and to
10 # permit persons to whom the Software is furnished to do so, subject to
11 # the following conditions:
13 # The above copyright notice and this permission notice shall be included
14 # in all copies or substantial portions of the Software.
16 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
17 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
18 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 """Tool-specific initialization for Qt.
26 There normally shouldn't be any need to import this module directly.
27 It will usually be imported through the generic SCons.Tool.Tool()
42 cplusplus
= SCons
.Tool
.cxx
44 class ToolQtWarning(SCons
.Warnings
.SConsWarning
):
47 class GeneratedMocFileNotIncluded(ToolQtWarning
):
50 class QtdirNotFound(ToolQtWarning
):
53 SCons
.Warnings
.enableWarningClass(ToolQtWarning
)
55 header_extensions
= [".h", ".hxx", ".hpp", ".hh"]
56 if SCons
.Util
.case_sensitive_suffixes('.h', '.H'):
57 header_extensions
.append('.H')
59 cxx_suffixes
= cplusplus
.CXXSuffixes
62 def find_platform_specific_qt_paths():
64 find non-standard QT paths
66 If the platform does not put QT tools in standard search paths,
67 the path is expected to be set using QTDIR. SCons violates
68 the normal rule of not pulling from the user's environment
69 in this case. However, some test cases try to validate what
70 happens when QTDIR is unset, so we need to try to make a guess.
72 :return: a guess at a path
77 if os
.path
.isfile('/etc/redhat-release'):
78 with
open('/etc/redhat-release','r') as rr
:
79 lines
= rr
.readlines()
80 distro
= lines
[0].split()[0]
81 if distro
== 'CentOS':
82 # Centos installs QT under /usr/{lib,lib64}/qt{4,5,-3.3}/bin
83 # so we need to handle this differently
84 # qt_bin_dirs = glob.glob('/usr/lib64/qt*/bin')
85 # TODO: all current Fedoras do the same, need to look deeper here.
86 qt_bin_dir
= '/usr/lib64/qt-3.3/bin'
91 QT_BIN_DIR
= find_platform_specific_qt_paths()
93 def checkMocIncluded(target
, source
, env
):
96 # looks like cpp.includes is cleared before the build stage :-(
97 # not really sure about the path transformations (moc.cwd? cpp.cwd?) :-/
98 path
= SCons
.Defaults
.CScan
.path(env
, moc
.cwd
)
99 includes
= SCons
.Defaults
.CScan(cpp
, env
, path
)
100 if moc
not in includes
:
102 GeneratedMocFileNotIncluded
,
103 "Generated moc file '%s' is not included by '%s'" %
104 (str(moc
), str(cpp
)))
106 def find_file(filename
, paths
, node_factory
):
108 node
= node_factory(filename
, dir)
115 Callable class, which works as an emitter for Programs, SharedLibraries and
119 def __init__(self
, objBuilderName
):
120 self
.objBuilderName
= objBuilderName
122 def __call__(self
, target
, source
, env
):
124 Smart autoscan function. Gets the list of objects for the Program
125 or Lib. Adds objects and builders for the special qt files.
128 if int(env
.subst('$QT_AUTOSCAN')) == 0:
129 return target
, source
133 debug
= int(env
.subst('$QT_DEBUG'))
137 # some shortcuts used in the scanner
138 splitext
= SCons
.Util
.splitext
139 objBuilder
= getattr(env
, self
.objBuilderName
)
141 # some regular expressions:
143 q_object_search
= re
.compile(r
'[^A-Za-z0-9]Q_OBJECT[^A-Za-z0-9]')
144 # cxx and c comment 'eater'
145 #comment = re.compile(r'(//.*)|(/\*(([^*])|(\*[^/]))*\*/)')
146 # CW: something must be wrong with the regexp. See also bug #998222
147 # CURRENTLY THERE IS NO TEST CASE FOR THAT
149 # The following is kind of hacky to get builders working properly (FIXME)
150 objBuilderEnv
= objBuilder
.env
152 mocBuilderEnv
= env
.Moc
.env
155 # make a deep copy for the result; MocH objects will be appended
156 out_sources
= source
[:]
159 if not obj
.has_builder():
160 # binary obj file provided
162 print("scons: qt: '%s' seems to be a binary. Discarded." % str(obj
))
165 if not splitext(str(cpp
))[1] in cxx_suffixes
:
167 print("scons: qt: '%s' is no cxx file. Discarded." % str(cpp
))
168 # c or fortran source
170 #cpp_contents = comment.sub('', cpp.get_text_contents())
172 print("scons: qt: Getting contents of %s" % cpp
)
173 cpp_contents
= cpp
.get_text_contents()
175 for h_ext
in header_extensions
:
176 # try to find the header file in the corresponding source
178 hname
= splitext(cpp
.name
)[0] + h_ext
179 h
= find_file(hname
, (cpp
.get_dir(),), env
.File
)
182 print("scons: qt: Scanning '%s' (header of '%s')" % (str(h
), str(cpp
)))
183 #h_contents = comment.sub('', h.get_text_contents())
184 h_contents
= h
.get_text_contents()
187 print("scons: qt: no header for '%s'." % (str(cpp
)))
188 if h
and q_object_search
.search(h_contents
):
189 # h file with the Q_OBJECT macro found -> add moc_cpp
191 moc_o
= objBuilder(moc_cpp
)
192 out_sources
.append(moc_o
)
193 #moc_cpp.target_scanner = SCons.Defaults.CScan
195 print("scons: qt: found Q_OBJECT macro in '%s', moc'ing to '%s'" % (str(h
), str(moc_cpp
)))
196 if cpp
and q_object_search
.search(cpp_contents
):
197 # cpp file with Q_OBJECT macro found -> add moc
198 # (to be included in cpp)
202 print("scons: qt: found Q_OBJECT macro in '%s', moc'ing to '%s'" % (str(cpp
), str(moc
)))
203 #moc.source_scanner = SCons.Defaults.CScan
204 # restore the original env attributes (FIXME)
205 objBuilder
.env
= objBuilderEnv
206 env
.Moc
.env
= mocBuilderEnv
208 return (target
, out_sources
)
210 AutomocShared
= _Automoc('SharedObject')
211 AutomocStatic
= _Automoc('StaticObject')
214 """Not really safe, but fast method to detect the QT library"""
216 QTDIR
= env
.get('QTDIR',None)
218 QTDIR
= os
.environ
.get('QTDIR',None)
220 moc
= env
.WhereIs('moc') or env
.WhereIs('moc',QT_BIN_DIR
)
222 QTDIR
= os
.path
.dirname(os
.path
.dirname(moc
))
225 "Could not detect qt, using moc executable as a hint (QTDIR=%s)" % QTDIR
)
230 "Could not detect qt, using empty QTDIR")
233 def uicEmitter(target
, source
, env
):
234 adjustixes
= SCons
.Util
.adjustixes
235 bs
= SCons
.Util
.splitext(str(source
[0].name
))[0]
236 bs
= os
.path
.join(str(target
[0].get_dir()),bs
)
237 # first target (header) is automatically added by builder
239 # second target is implementation
240 target
.append(adjustixes(bs
,
241 env
.subst('$QT_UICIMPLPREFIX'),
242 env
.subst('$QT_UICIMPLSUFFIX')))
244 # third target is moc file
245 target
.append(adjustixes(bs
,
246 env
.subst('$QT_MOCHPREFIX'),
247 env
.subst('$QT_MOCHSUFFIX')))
248 return target
, source
250 def uicScannerFunc(node
, env
, path
):
252 lookout
.extend(env
['CPPPATH'])
253 lookout
.append(str(node
.rfile().dir))
254 includes
= re
.findall("<include.*?>(.*?)</include>", node
.get_text_contents())
256 for incFile
in includes
:
257 dep
= env
.FindFile(incFile
,lookout
)
262 uicScanner
= SCons
.Scanner
.ScannerBase(uicScannerFunc
,
264 node_class
= SCons
.Node
.FS
.File
,
265 node_factory
= SCons
.Node
.FS
.File
,
269 """Add Builders and construction variables for qt to an Environment."""
270 CLVar
= SCons
.Util
.CLVar
271 Action
= SCons
.Action
.Action
272 Builder
= SCons
.Builder
.Builder
275 SCons
.Warnings
.ToolQtDeprecatedWarning
, "Tool module for Qt version 3 is deprecated"
278 env
.SetDefault(QTDIR
= _detect(env
),
279 QT_BINPATH
= os
.path
.join('$QTDIR', 'bin'),
280 QT_CPPPATH
= os
.path
.join('$QTDIR', 'include'),
281 QT_LIBPATH
= os
.path
.join('$QTDIR', 'lib'),
282 QT_MOC
= os
.path
.join('$QT_BINPATH','moc'),
283 QT_UIC
= os
.path
.join('$QT_BINPATH','uic'),
284 QT_LIB
= 'qt', # may be set to qt-mt
286 QT_AUTOSCAN
= 1, # scan for moc'able sources
288 # Some QT specific flags. I don't expect someone wants to
289 # manipulate those ...
290 QT_UICIMPLFLAGS
= CLVar(''),
291 QT_UICDECLFLAGS
= CLVar(''),
292 QT_MOCFROMHFLAGS
= CLVar(''),
293 QT_MOCFROMCXXFLAGS
= CLVar('-i'),
295 # suffixes/prefixes for the headers / sources to generate
296 QT_UICDECLPREFIX
= '',
297 QT_UICDECLSUFFIX
= '.h',
298 QT_UICIMPLPREFIX
= 'uic_',
299 QT_UICIMPLSUFFIX
= '$CXXFILESUFFIX',
300 QT_MOCHPREFIX
= 'moc_',
301 QT_MOCHSUFFIX
= '$CXXFILESUFFIX',
302 QT_MOCCXXPREFIX
= '',
303 QT_MOCCXXSUFFIX
= '.moc',
306 # Commands for the qt support ...
307 # command to generate header, implementation and moc-file
310 CLVar('$QT_UIC $QT_UICDECLFLAGS -o ${TARGETS[0]} $SOURCE'),
311 CLVar('$QT_UIC $QT_UICIMPLFLAGS -impl ${TARGETS[0].file} '
312 '-o ${TARGETS[1]} $SOURCE'),
313 CLVar('$QT_MOC $QT_MOCFROMHFLAGS -o ${TARGETS[2]} ${TARGETS[0]}')],
314 # command to generate meta object information for a class
315 # declarated in a header
317 '$QT_MOC $QT_MOCFROMHFLAGS -o ${TARGETS[0]} $SOURCE'),
318 # command to generate meta object information for a class
319 # declarated in a cpp file
321 CLVar('$QT_MOC $QT_MOCFROMCXXFLAGS -o ${TARGETS[0]} $SOURCE'),
322 Action(checkMocIncluded
,None)])
324 # ... and the corresponding builders
325 uicBld
= Builder(action
=SCons
.Action
.Action('$QT_UICCOM', '$QT_UICCOMSTR'),
327 src_suffix
='$QT_UISUFFIX',
328 suffix
='$QT_UICDECLSUFFIX',
329 prefix
='$QT_UICDECLPREFIX',
330 source_scanner
=uicScanner
)
331 mocBld
= Builder(action
={}, prefix
={}, suffix
={})
332 for h
in header_extensions
:
333 act
= SCons
.Action
.Action('$QT_MOCFROMHCOM', '$QT_MOCFROMHCOMSTR')
334 mocBld
.add_action(h
, act
)
335 mocBld
.prefix
[h
] = '$QT_MOCHPREFIX'
336 mocBld
.suffix
[h
] = '$QT_MOCHSUFFIX'
337 for cxx
in cxx_suffixes
:
338 act
= SCons
.Action
.Action('$QT_MOCFROMCXXCOM', '$QT_MOCFROMCXXCOMSTR')
339 mocBld
.add_action(cxx
, act
)
340 mocBld
.prefix
[cxx
] = '$QT_MOCCXXPREFIX'
341 mocBld
.suffix
[cxx
] = '$QT_MOCCXXSUFFIX'
343 # register the builders
344 env
['BUILDERS']['Uic'] = uicBld
345 env
['BUILDERS']['Moc'] = mocBld
346 static_obj
, shared_obj
= SCons
.Tool
.createObjBuilders(env
)
347 static_obj
.add_src_builder('Uic')
348 shared_obj
.add_src_builder('Uic')
350 # We use the emitters of Program / StaticLibrary / SharedLibrary
351 # to scan for moc'able files
352 # We can't refer to the builders directly, we have to fetch them
353 # as Environment attributes because that sets them up to be called
354 # correctly later by our emitter.
355 env
.AppendUnique(PROGEMITTER
=[AutomocStatic
],
356 SHLIBEMITTER
=[AutomocShared
],
357 LDMODULEEMITTER
=[AutomocShared
],
358 LIBEMITTER
=[AutomocStatic
],
359 # Of course, we need to link against the qt libraries
360 CPPPATH
=["$QT_CPPPATH"],
361 LIBPATH
=["$QT_LIBPATH"],
369 # indent-tabs-mode:nil
371 # vim: set expandtab tabstop=4 shiftwidth=4: