1 2"""SCons.Tool.qt 3 4Tool-specific initialization for Qt. 5 6There normally shouldn't be any need to import this module directly. 7It will usually be imported through the generic SCons.Tool.Tool() 8selection method. 9 10""" 11 12# 13# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 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__ = "src/engine/SCons/Tool/qt.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" 36 37import os.path 38import re 39 40import SCons.Action 41import SCons.Builder 42import SCons.Defaults 43import SCons.Scanner 44import SCons.Tool 45import SCons.Util 46 47class ToolQtWarning(SCons.Warnings.Warning): 48 pass 49 50class GeneratedMocFileNotIncluded(ToolQtWarning): 51 pass 52 53class QtdirNotFound(ToolQtWarning): 54 pass 55 56SCons.Warnings.enableWarningClass(ToolQtWarning) 57 58header_extensions = [".h", ".hxx", ".hpp", ".hh"] 59if SCons.Util.case_sensitive_suffixes('.h', '.H'): 60 header_extensions.append('.H') 61cplusplus = __import__('c++', globals(), locals(), []) 62cxx_suffixes = cplusplus.CXXSuffixes 63 64def checkMocIncluded(target, source, env): 65 moc = target[0] 66 cpp = source[0] 67 # looks like cpp.includes is cleared before the build stage :-( 68 # not really sure about the path transformations (moc.cwd? cpp.cwd?) :-/ 69 path = SCons.Defaults.CScan.path(env, moc.cwd) 70 includes = SCons.Defaults.CScan(cpp, env, path) 71 if not moc in includes: 72 SCons.Warnings.warn( 73 GeneratedMocFileNotIncluded, 74 "Generated moc file '%s' is not included by '%s'" % 75 (str(moc), str(cpp))) 76 77def find_file(filename, paths, node_factory): 78 for dir in paths: 79 node = node_factory(filename, dir) 80 if node.rexists(): 81 return node 82 return None 83 84class _Automoc(object): 85 """ 86 Callable class, which works as an emitter for Programs, SharedLibraries and 87 StaticLibraries. 88 """ 89 90 def __init__(self, objBuilderName): 91 self.objBuilderName = objBuilderName 92 93 def __call__(self, target, source, env): 94 """ 95 Smart autoscan function. Gets the list of objects for the Program 96 or Lib. Adds objects and builders for the special qt files. 97 """ 98 try: 99 if int(env.subst('$QT_AUTOSCAN')) == 0: 100 return target, source 101 except ValueError: 102 pass 103 try: 104 debug = int(env.subst('$QT_DEBUG')) 105 except ValueError: 106 debug = 0 107 108 # some shortcuts used in the scanner 109 splitext = SCons.Util.splitext 110 objBuilder = getattr(env, self.objBuilderName) 111 112 # some regular expressions: 113 # Q_OBJECT detection 114 q_object_search = re.compile(r'[^A-Za-z0-9]Q_OBJECT[^A-Za-z0-9]') 115 # cxx and c comment 'eater' 116 #comment = re.compile(r'(//.*)|(/\*(([^*])|(\*[^/]))*\*/)') 117 # CW: something must be wrong with the regexp. See also bug #998222 118 # CURRENTLY THERE IS NO TEST CASE FOR THAT 119 120 # The following is kind of hacky to get builders working properly (FIXME) 121 objBuilderEnv = objBuilder.env 122 objBuilder.env = env 123 mocBuilderEnv = env.Moc.env 124 env.Moc.env = env 125 126 # make a deep copy for the result; MocH objects will be appended 127 out_sources = source[:] 128 129 for obj in source: 130 if not obj.has_builder(): 131 # binary obj file provided 132 if debug: 133 print "scons: qt: '%s' seems to be a binary. Discarded." % str(obj) 134 continue 135 cpp = obj.sources[0] 136 if not splitext(str(cpp))[1] in cxx_suffixes: 137 if debug: 138 print "scons: qt: '%s' is no cxx file. Discarded." % str(cpp) 139 # c or fortran source 140 continue 141 #cpp_contents = comment.sub('', cpp.get_text_contents()) 142 cpp_contents = cpp.get_text_contents() 143 h=None 144 for h_ext in header_extensions: 145 # try to find the header file in the corresponding source 146 # directory 147 hname = splitext(cpp.name)[0] + h_ext 148 h = find_file(hname, (cpp.get_dir(),), env.File) 149 if h: 150 if debug: 151 print "scons: qt: Scanning '%s' (header of '%s')" % (str(h), str(cpp)) 152 #h_contents = comment.sub('', h.get_text_contents()) 153 h_contents = h.get_text_contents() 154 break 155 if not h and debug: 156 print "scons: qt: no header for '%s'." % (str(cpp)) 157 if h and q_object_search.search(h_contents): 158 # h file with the Q_OBJECT macro found -> add moc_cpp 159 moc_cpp = env.Moc(h) 160 moc_o = objBuilder(moc_cpp) 161 out_sources.append(moc_o) 162 #moc_cpp.target_scanner = SCons.Defaults.CScan 163 if debug: 164 print "scons: qt: found Q_OBJECT macro in '%s', moc'ing to '%s'" % (str(h), str(moc_cpp)) 165 if cpp and q_object_search.search(cpp_contents): 166 # cpp file with Q_OBJECT macro found -> add moc 167 # (to be included in cpp) 168 moc = env.Moc(cpp) 169 env.Ignore(moc, moc) 170 if debug: 171 print "scons: qt: found Q_OBJECT macro in '%s', moc'ing to '%s'" % (str(cpp), str(moc)) 172 #moc.source_scanner = SCons.Defaults.CScan 173 # restore the original env attributes (FIXME) 174 objBuilder.env = objBuilderEnv 175 env.Moc.env = mocBuilderEnv 176 177 return (target, out_sources) 178 179AutomocShared = _Automoc('SharedObject') 180AutomocStatic = _Automoc('StaticObject') 181 182def _detect(env): 183 """Not really safe, but fast method to detect the QT library""" 184 QTDIR = None 185 if not QTDIR: 186 QTDIR = env.get('QTDIR',None) 187 if not QTDIR: 188 QTDIR = os.environ.get('QTDIR',None) 189 if not QTDIR: 190 moc = env.WhereIs('moc') 191 if moc: 192 QTDIR = os.path.dirname(os.path.dirname(moc)) 193 SCons.Warnings.warn( 194 QtdirNotFound, 195 "Could not detect qt, using moc executable as a hint (QTDIR=%s)" % QTDIR) 196 else: 197 QTDIR = None 198 SCons.Warnings.warn( 199 QtdirNotFound, 200 "Could not detect qt, using empty QTDIR") 201 return QTDIR 202 203def uicEmitter(target, source, env): 204 adjustixes = SCons.Util.adjustixes 205 bs = SCons.Util.splitext(str(source[0].name))[0] 206 bs = os.path.join(str(target[0].get_dir()),bs) 207 # first target (header) is automatically added by builder 208 if len(target) < 2: 209 # second target is implementation 210 target.append(adjustixes(bs, 211 env.subst('$QT_UICIMPLPREFIX'), 212 env.subst('$QT_UICIMPLSUFFIX'))) 213 if len(target) < 3: 214 # third target is moc file 215 target.append(adjustixes(bs, 216 env.subst('$QT_MOCHPREFIX'), 217 env.subst('$QT_MOCHSUFFIX'))) 218 return target, source 219 220def uicScannerFunc(node, env, path): 221 lookout = [] 222 lookout.extend(env['CPPPATH']) 223 lookout.append(str(node.rfile().dir)) 224 includes = re.findall("<include.*?>(.*?)</include>", node.get_text_contents()) 225 result = [] 226 for incFile in includes: 227 dep = env.FindFile(incFile,lookout) 228 if dep: 229 result.append(dep) 230 return result 231 232uicScanner = SCons.Scanner.Base(uicScannerFunc, 233 name = "UicScanner", 234 node_class = SCons.Node.FS.File, 235 node_factory = SCons.Node.FS.File, 236 recursive = 0) 237 238def generate(env): 239 """Add Builders and construction variables for qt to an Environment.""" 240 CLVar = SCons.Util.CLVar 241 Action = SCons.Action.Action 242 Builder = SCons.Builder.Builder 243 244 env.SetDefault(QTDIR = _detect(env), 245 QT_BINPATH = os.path.join('$QTDIR', 'bin'), 246 QT_CPPPATH = os.path.join('$QTDIR', 'include'), 247 QT_LIBPATH = os.path.join('$QTDIR', 'lib'), 248 QT_MOC = os.path.join('$QT_BINPATH','moc'), 249 QT_UIC = os.path.join('$QT_BINPATH','uic'), 250 QT_LIB = 'qt', # may be set to qt-mt 251 252 QT_AUTOSCAN = 1, # scan for moc'able sources 253 254 # Some QT specific flags. I don't expect someone wants to 255 # manipulate those ... 256 QT_UICIMPLFLAGS = CLVar(''), 257 QT_UICDECLFLAGS = CLVar(''), 258 QT_MOCFROMHFLAGS = CLVar(''), 259 QT_MOCFROMCXXFLAGS = CLVar('-i'), 260 261 # suffixes/prefixes for the headers / sources to generate 262 QT_UICDECLPREFIX = '', 263 QT_UICDECLSUFFIX = '.h', 264 QT_UICIMPLPREFIX = 'uic_', 265 QT_UICIMPLSUFFIX = '$CXXFILESUFFIX', 266 QT_MOCHPREFIX = 'moc_', 267 QT_MOCHSUFFIX = '$CXXFILESUFFIX', 268 QT_MOCCXXPREFIX = '', 269 QT_MOCCXXSUFFIX = '.moc', 270 QT_UISUFFIX = '.ui', 271 272 # Commands for the qt support ... 273 # command to generate header, implementation and moc-file 274 # from a .ui file 275 QT_UICCOM = [ 276 CLVar('$QT_UIC $QT_UICDECLFLAGS -o ${TARGETS[0]} $SOURCE'), 277 CLVar('$QT_UIC $QT_UICIMPLFLAGS -impl ${TARGETS[0].file} ' 278 '-o ${TARGETS[1]} $SOURCE'), 279 CLVar('$QT_MOC $QT_MOCFROMHFLAGS -o ${TARGETS[2]} ${TARGETS[0]}')], 280 # command to generate meta object information for a class 281 # declarated in a header 282 QT_MOCFROMHCOM = ( 283 '$QT_MOC $QT_MOCFROMHFLAGS -o ${TARGETS[0]} $SOURCE'), 284 # command to generate meta object information for a class 285 # declarated in a cpp file 286 QT_MOCFROMCXXCOM = [ 287 CLVar('$QT_MOC $QT_MOCFROMCXXFLAGS -o ${TARGETS[0]} $SOURCE'), 288 Action(checkMocIncluded,None)]) 289 290 # ... and the corresponding builders 291 uicBld = Builder(action=SCons.Action.Action('$QT_UICCOM', '$QT_UICCOMSTR'), 292 emitter=uicEmitter, 293 src_suffix='$QT_UISUFFIX', 294 suffix='$QT_UICDECLSUFFIX', 295 prefix='$QT_UICDECLPREFIX', 296 source_scanner=uicScanner) 297 mocBld = Builder(action={}, prefix={}, suffix={}) 298 for h in header_extensions: 299 act = SCons.Action.Action('$QT_MOCFROMHCOM', '$QT_MOCFROMHCOMSTR') 300 mocBld.add_action(h, act) 301 mocBld.prefix[h] = '$QT_MOCHPREFIX' 302 mocBld.suffix[h] = '$QT_MOCHSUFFIX' 303 for cxx in cxx_suffixes: 304 act = SCons.Action.Action('$QT_MOCFROMCXXCOM', '$QT_MOCFROMCXXCOMSTR') 305 mocBld.add_action(cxx, act) 306 mocBld.prefix[cxx] = '$QT_MOCCXXPREFIX' 307 mocBld.suffix[cxx] = '$QT_MOCCXXSUFFIX' 308 309 # register the builders 310 env['BUILDERS']['Uic'] = uicBld 311 env['BUILDERS']['Moc'] = mocBld 312 static_obj, shared_obj = SCons.Tool.createObjBuilders(env) 313 static_obj.add_src_builder('Uic') 314 shared_obj.add_src_builder('Uic') 315 316 # We use the emitters of Program / StaticLibrary / SharedLibrary 317 # to scan for moc'able files 318 # We can't refer to the builders directly, we have to fetch them 319 # as Environment attributes because that sets them up to be called 320 # correctly later by our emitter. 321 env.AppendUnique(PROGEMITTER =[AutomocStatic], 322 SHLIBEMITTER=[AutomocShared], 323 LIBEMITTER =[AutomocStatic], 324 # Of course, we need to link against the qt libraries 325 CPPPATH=["$QT_CPPPATH"], 326 LIBPATH=["$QT_LIBPATH"], 327 LIBS=['$QT_LIB']) 328 329def exists(env): 330 return _detect(env) 331 332# Local Variables: 333# tab-width:4 334# indent-tabs-mode:nil 335# End: 336# vim: set expandtab tabstop=4 shiftwidth=4: 337