1 2"""SCons.Tool.qt4 3 4Tool-specific initialization for Qt4. 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-7,2010 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# Additionally in Photivo : 36# Changes done in/for Photivo are largely traceable due to the 37# use of pt-Prefixes. 38 39################################################################################ 40## 41## Photivo 42## 43## Copyright (C) 2013 Jos De Laender <jos@de-laender.be> 44## 45## This file is part of Photivo. 46## 47## Photivo is free software: you can redistribute it and/or modify 48## it under the terms of the GNU General Public License version 3 49## as published by the Free Software Foundation. 50## 51## Photivo is distributed in the hope that it will be useful, 52## but WITHOUT ANY WARRANTY; without even the implied warranty of 53## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 54## GNU General Public License for more details. 55## 56## You should have received a copy of the GNU General Public License 57## along with Photivo. If not, see <http://www.gnu.org/licenses/>. 58## 59################################################################################ 60 61import os.path 62import re 63 64import SCons.Action 65import SCons.Builder 66import SCons.Defaults 67import SCons.Scanner 68import SCons.Tool 69import SCons.Util 70 71import sys 72 73class ToolQt4Warning(SCons.Warnings.Warning): 74 pass 75 76class GeneratedMocFileNotIncluded(ToolQt4Warning): 77 pass 78 79class QtdirNotFound(ToolQt4Warning): 80 pass 81 82SCons.Warnings.enableWarningClass(ToolQt4Warning) 83 84try: 85 sorted 86except NameError: 87 # Pre-2.4 Python has no sorted() function. 88 # 89 # The pre-2.4 Python list.sort() method does not support 90 # list.sort(key=) nor list.sort(reverse=) keyword arguments, so 91 # we must implement the functionality of those keyword arguments 92 # by hand instead of passing them to list.sort(). 93 def sorted(iterable, cmp=None, key=None, reverse=0): 94 if key is not None: 95 result = [(key(x), x) for x in iterable] 96 else: 97 result = iterable[:] 98 if cmp is None: 99 # Pre-2.3 Python does not support list.sort(None). 100 result.sort() 101 else: 102 result.sort(cmp) 103 if key is not None: 104 result = [t1 for t0,t1 in result] 105 if reverse: 106 result.reverse() 107 return result 108 109qrcinclude_re = re.compile(r'<file[^>]*>([^<]*)</file>', re.M) 110 111def transformToWinePath(path) : 112 return os.popen('winepath -w "%s"'%path).read().strip().replace('\\','/') 113 114header_extensions = [".h", ".hxx", ".hpp", ".hh"] 115if SCons.Util.case_sensitive_suffixes('.h', '.H'): 116 header_extensions.append('.H') 117# TODO: The following two lines will work when integrated back to SCons 118# TODO: Meanwhile the third line will do the work 119#cplusplus = __import__('c++', globals(), locals(), []) 120#cxx_suffixes = cplusplus.CXXSuffixes 121cxx_suffixes = [".c", ".cxx", ".cpp", ".cc"] 122 123def checkMocIncluded(target, source, env): 124 moc = target[0] 125 cpp = source[0] 126 # looks like cpp.includes is cleared before the build stage :-( 127 # not really sure about the path transformations (moc.cwd? cpp.cwd?) :-/ 128 path = SCons.Defaults.CScan.path_function(env, moc.cwd) 129 includes = SCons.Defaults.CScan(cpp, env, path) 130 if not moc in includes: 131 SCons.Warnings.warn( 132 GeneratedMocFileNotIncluded, 133 "Generated moc file '%s' is not included by '%s'" % 134 (str(moc), str(cpp))) 135 136def find_file(filename, paths, node_factory): 137 for dir in paths: 138 node = node_factory(filename, dir) 139 if node.rexists(): 140 return node 141 return None 142 143class _Automoc: 144 """ 145 Callable class, which works as an emitter for Programs, SharedLibraries and 146 StaticLibraries. 147 """ 148 149 def __init__(self, objBuilderName): 150 self.objBuilderName = objBuilderName 151 # some regular expressions: 152 # Q_OBJECT detection 153 self.qo_search = re.compile(r'[^A-Za-z0-9]Q_OBJECT[^A-Za-z0-9]') 154 # cxx and c comment 'eater' 155 self.ccomment = re.compile(r'/\*(.*?)\*/',re.S) 156 self.cxxcomment = re.compile(r'//.*$',re.M) 157 # we also allow Q_OBJECT in a literal string 158 self.literal_qobject = re.compile(r'"[^\n]*Q_OBJECT[^\n]*"') 159 160 def create_automoc_options(self, env): 161 """ 162 Create a dictionary with variables related to Automocing, 163 based on the current environment. 164 Is executed once in the __call__ routine. 165 """ 166 moc_options = {'auto_scan' : True, 167 'auto_scan_strategy' : 0, 168 'gobble_comments' : 0, 169 'debug' : 0, 170 'auto_cpppath' : True, 171 'cpppaths' : []} 172 try: 173 if int(env.subst('$QT4_AUTOSCAN')) == 0: 174 moc_options['auto_scan'] = False 175 except ValueError: 176 pass 177 try: 178 moc_options['auto_scan_strategy'] = int(env.subst('$QT4_AUTOSCAN_STRATEGY')) 179 except ValueError: 180 pass 181 try: 182 moc_options['gobble_comments'] = int(env.subst('$QT4_GOBBLECOMMENTS')) 183 except ValueError: 184 pass 185 try: 186 moc_options['debug'] = int(env.subst('$QT4_DEBUG')) 187 except ValueError: 188 pass 189 try: 190 if int(env.subst('$QT4_AUTOMOC_SCANCPPPATH')) == 0: 191 moc_options['auto_cpppath'] = False 192 except ValueError: 193 pass 194 if moc_options['auto_cpppath']: 195 paths = env.get('QT4_AUTOMOC_CPPPATH', []) 196 if not paths: 197 paths = env.get('CPPPATH', []) 198 moc_options['cpppaths'].extend(paths) 199 200 return moc_options 201 202 def __automoc_strategy_simple(self, env, moc_options, 203 cpp, cpp_contents, out_sources): 204 """ 205 Default Automoc strategy (Q_OBJECT driven): detect a header file 206 (alongside the current cpp/cxx) that contains a Q_OBJECT 207 macro...and MOC it. 208 If a Q_OBJECT macro is also found in the cpp/cxx itself, 209 it gets MOCed too. 210 """ 211 212 h=None 213 for h_ext in header_extensions: 214 # try to find the header file in the corresponding source 215 # directory 216 hname = self.splitext(cpp.name)[0] + h_ext 217 h = find_file(hname, [cpp.get_dir()]+moc_options['cpppaths'], env.File) 218 if h: 219 if moc_options['debug']: 220 print "scons: qt4: Scanning '%s' (header of '%s')" % (str(h), str(cpp)) 221 h_contents = h.get_contents() 222 if moc_options['gobble_comments']: 223 h_contents = self.ccomment.sub('', h_contents) 224 h_contents = self.cxxcomment.sub('', h_contents) 225 h_contents = self.literal_qobject.sub('""', h_contents) 226 break 227 if not h and moc_options['debug']: 228 print "scons: qt4: no header for '%s'." % (str(cpp)) 229 if h and self.qo_search.search(h_contents): 230 # h file with the Q_OBJECT macro found -> add moc_cpp 231 moc_cpp = env.Moc4(h) 232 moc_o = self.objBuilder(moc_cpp) 233 out_sources.extend(moc_o) 234 if moc_options['debug']: 235 print "scons: qt4: found Q_OBJECT macro in '%s', moc'ing to '%s'" % (str(h), str(moc_cpp)) 236 if cpp and self.qo_search.search(cpp_contents): 237 # cpp file with Q_OBJECT macro found -> add moc 238 # (to be included in cpp) 239 moc = env.Moc4(cpp) 240 env.Ignore(moc, moc) 241 if moc_options['debug']: 242 print "scons: qt4: found Q_OBJECT macro in '%s', moc'ing to '%s'" % (str(cpp), str(moc)) 243 244 def __automoc_strategy_include_driven(self, env, moc_options, 245 cpp, cpp_contents, out_sources): 246 """ 247 Automoc strategy #1 (include driven): searches for "include" 248 statements of MOCed files in the current cpp/cxx file. 249 This strategy tries to add support for the compilation 250 of the qtsolutions... 251 """ 252 if self.splitext(str(cpp))[1] in cxx_suffixes: 253 added = False 254 h_moc = "%s%s%s" % (env.subst('$QT4_XMOCHPREFIX'), 255 self.splitext(cpp.name)[0], 256 env.subst('$QT4_XMOCHSUFFIX')) 257 cxx_moc = "%s%s%s" % (env.subst('$QT4_XMOCCXXPREFIX'), 258 self.splitext(cpp.name)[0], 259 env.subst('$QT4_XMOCCXXSUFFIX')) 260 inc_h_moc = r'#include\s+"%s"' % h_moc 261 inc_cxx_moc = r'#include\s+"%s"' % cxx_moc 262 263 # Search for special includes in qtsolutions style 264 if cpp and re.search(inc_h_moc, cpp_contents): 265 # cpp file with #include directive for a MOCed header found -> add moc 266 267 # Try to find header file 268 h=None 269 hname="" 270 for h_ext in header_extensions: 271 # Try to find the header file in the 272 # corresponding source directory 273 hname = self.splitext(cpp.name)[0] + h_ext 274 h = find_file(hname, [cpp.get_dir()]+moc_options['cpppaths'], env.File) 275 if h: 276 if moc_options['debug']: 277 print "scons: qt4: Scanning '%s' (header of '%s')" % (str(h), str(cpp)) 278 h_contents = h.get_contents() 279 if moc_options['gobble_comments']: 280 h_contents = self.ccomment.sub('', h_contents) 281 h_contents = self.cxxcomment.sub('', h_contents) 282 h_contents = self.literal_qobject.sub('""', h_contents) 283 break 284 if not h and moc_options['debug']: 285 print "scons: qt4: no header for '%s'." % (str(cpp)) 286 if h and self.qo_search.search(h_contents): 287 # h file with the Q_OBJECT macro found -> add moc_cpp 288 moc_cpp = env.XMoc4(h) 289 env.Ignore(moc_cpp, moc_cpp) 290 added = True 291 # Removing file from list of sources, because it is not to be 292 # compiled but simply included by the cpp/cxx file. 293 for idx, s in enumerate(out_sources): 294 if hasattr(s, "sources") and len(s.sources) > 0: 295 if str(s.sources[0]) == h_moc: 296 out_sources.pop(idx) 297 break 298 if moc_options['debug']: 299 print "scons: qt4: found Q_OBJECT macro in '%s', moc'ing to '%s'" % (str(h), str(h_moc)) 300 else: 301 if moc_options['debug']: 302 print "scons: qt4: found no Q_OBJECT macro in '%s', but a moc'ed version '%s' gets included in '%s'" % (str(h), inc_h_moc, cpp.name) 303 304 if cpp and re.search(inc_cxx_moc, cpp_contents): 305 # cpp file with #include directive for a MOCed cxx file found -> add moc 306 if self.qo_search.search(cpp_contents): 307 moc = env.XMoc4(target=cxx_moc, source=cpp) 308 env.Ignore(moc, moc) 309 added = True 310 if moc_options['debug']: 311 print "scons: qt4: found Q_OBJECT macro in '%s', moc'ing to '%s'" % (str(cpp), str(moc)) 312 else: 313 if moc_options['debug']: 314 print "scons: qt4: found no Q_OBJECT macro in '%s', although a moc'ed version '%s' of itself gets included" % (cpp.name, inc_cxx_moc) 315 316 if not added: 317 # Fallback to default Automoc strategy (Q_OBJECT driven) 318 self.__automoc_strategy_simple(env, moc_options, cpp, 319 cpp_contents, out_sources) 320 321 def __call__(self, target, source, env): 322 """ 323 Smart autoscan function. Gets the list of objects for the Program 324 or Lib. Adds objects and builders for the special qt4 files. 325 """ 326 moc_options = self.create_automoc_options(env) 327 328 # some shortcuts used in the scanner 329 self.splitext = SCons.Util.splitext 330 self.objBuilder = getattr(env, self.objBuilderName) 331 332 # The following is kind of hacky to get builders working properly (FIXME) 333 objBuilderEnv = self.objBuilder.env 334 self.objBuilder.env = env 335 mocBuilderEnv = env.Moc4.env 336 env.Moc4.env = env 337 xMocBuilderEnv = env.XMoc4.env 338 env.XMoc4.env = env 339 340 # make a deep copy for the result; MocH objects will be appended 341 out_sources = source[:] 342 343 for obj in source: 344 if not moc_options['auto_scan']: 345 break 346 if isinstance(obj,basestring): # big kludge! 347 print "scons: qt4: '%s' MAYBE USING AN OLD SCONS VERSION AND NOT CONVERTED TO 'File'. Discarded." % str(obj) 348 continue 349 if not obj.has_builder(): 350 # binary obj file provided 351 if moc_options['debug']: 352 print "scons: qt4: '%s' seems to be a binary. Discarded." % str(obj) 353 continue 354 cpp = obj.sources[0] 355 if not self.splitext(str(cpp))[1] in cxx_suffixes: 356 if moc_options['debug']: 357 print "scons: qt4: '%s' is no cxx file. Discarded." % str(cpp) 358 # c or fortran source 359 continue 360 try: 361 cpp_contents = cpp.get_contents() 362 if moc_options['gobble_comments']: 363 cpp_contents = self.ccomment.sub('', cpp_contents) 364 cpp_contents = self.cxxcomment.sub('', cpp_contents) 365 cpp_contents = self.literal_qobject.sub('""', cpp_contents) 366 except: continue # may be an still not generated source 367 368 if moc_options['auto_scan_strategy'] == 0: 369 # Default Automoc strategy (Q_OBJECT driven) 370 self.__automoc_strategy_simple(env, moc_options, 371 cpp, cpp_contents, out_sources) 372 else: 373 # Automoc strategy #1 (include driven) 374 self.__automoc_strategy_include_driven(env, moc_options, 375 cpp, cpp_contents, out_sources) 376 377 # restore the original env attributes (FIXME) 378 self.objBuilder.env = objBuilderEnv 379 env.Moc4.env = mocBuilderEnv 380 env.XMoc4.env = xMocBuilderEnv 381 382 # We return the set of source entries as sorted sequence, else 383 # the order might accidentally change from one build to another 384 # and trigger unwanted rebuilds. For proper sorting, a key function 385 # has to be specified...FS.Entry (and Base nodes in general) do not 386 # provide a __cmp__, for performance reasons. 387 return (target, sorted(set(out_sources), key=lambda entry : str(entry))) 388 389AutomocShared = _Automoc('SharedObject') 390AutomocStatic = _Automoc('StaticObject') 391 392def _detect(env): 393 """Not really safe, but fast method to detect the Qt4 library""" 394 # TODO: check output of "moc -v" for correct version >= 4.0.0 395 try: return env['QT4DIR'] 396 except KeyError: pass 397 398 try: return env['QTDIR'] 399 except KeyError: pass 400 401 try: return os.environ['QT4DIR'] 402 except KeyError: pass 403 404 try: return os.environ['QTDIR'] 405 except KeyError: pass 406 407 moc = env.WhereIs('moc-qt4') or env.WhereIs('moc4') or env.WhereIs('moc') 408 if moc: 409 QT4DIR = os.path.dirname(os.path.dirname(moc)) 410 SCons.Warnings.warn( 411 QtdirNotFound, 412 "QT4DIR variable is not defined, using moc executable as a hint (QT4DIR=%s)" % QT4DIR) 413 return QT4DIR 414 415 raise SCons.Errors.StopError( 416 QtdirNotFound, 417 "Could not detect Qt 4 installation") 418 return None 419 420 421def __scanResources(node, env, path, arg): 422 # Helper function for scanning .qrc resource files 423 # I've been careful on providing names relative to the qrc file 424 # If that was not needed this code could be simplified a lot 425 def recursiveFiles(basepath, path) : 426 result = [] 427 for item in os.listdir(os.path.join(basepath, path)) : 428 itemPath = os.path.join(path, item) 429 if os.path.isdir(os.path.join(basepath, itemPath)) : 430 result += recursiveFiles(basepath, itemPath) 431 else: 432 result.append(itemPath) 433 return result 434 contents = node.get_contents() 435 includes = qrcinclude_re.findall(contents) 436 qrcpath = os.path.dirname(node.path) 437 dirs = [included for included in includes if os.path.isdir(os.path.join(qrcpath,included))] 438 # dirs need to include files recursively 439 for dir in dirs : 440 includes.remove(dir) 441 includes+=recursiveFiles(qrcpath,dir) 442 return includes 443 444# 445# Scanners 446# 447__qrcscanner = SCons.Scanner.Scanner(name = 'qrcfile', 448 function = __scanResources, 449 argument = None, 450 skeys = ['.qrc']) 451 452# 453# Emitters 454# 455def __qrc_path(head, prefix, tail, suffix): 456 if head: 457 if tail: 458 return os.path.join(head, "%s%s%s" % (prefix, tail, suffix)) 459 else: 460 return "%s%s%s" % (prefix, head, suffix) 461 else: 462 return "%s%s%s" % (prefix, tail, suffix) 463def __qrc_emitter(target, source, env): 464 sourceBase, sourceExt = os.path.splitext(SCons.Util.to_String(source[0])) 465 sHead = None 466 sTail = sourceBase 467 if sourceBase: 468 sHead, sTail = os.path.split(sourceBase) 469 470 t = __qrc_path(sHead, env.subst('$QT4_QRCCXXPREFIX'), 471 sTail, env.subst('$QT4_QRCCXXSUFFIX')) 472 473 return t, source 474 475# 476# Action generators 477# 478def __moc_generator_from_h(source, target, env, for_signature): 479 pass_defines = False 480 try: 481 if int(env.subst('$QT4_CPPDEFINES_PASSTOMOC')) == 1: 482 pass_defines = True 483 except ValueError: 484 pass 485 486 if pass_defines: 487 return '$QT4_MOC $QT4_MOCDEFINES $QT4_MOCFROMHFLAGS $QT4_MOCINCFLAGS -o $TARGET $SOURCE' 488 else: 489 return '$QT4_MOC $QT4_MOCFROMHFLAGS $QT4_MOCINCFLAGS -o $TARGET $SOURCE' 490 491def __moc_generator_from_cxx(source, target, env, for_signature): 492 pass_defines = False 493 try: 494 if int(env.subst('$QT4_CPPDEFINES_PASSTOMOC')) == 1: 495 pass_defines = True 496 except ValueError: 497 pass 498 499 if pass_defines: 500 return ['$QT4_MOC $QT4_MOCDEFINES $QT4_MOCFROMCXXFLAGS $QT4_MOCINCFLAGS -o $TARGET $SOURCE', 501 SCons.Action.Action(checkMocIncluded,None)] 502 else: 503 return ['$QT4_MOC $QT4_MOCFROMCXXFLAGS $QT4_MOCINCFLAGS -o $TARGET $SOURCE', 504 SCons.Action.Action(checkMocIncluded,None)] 505 506def __mocx_generator_from_h(source, target, env, for_signature): 507 pass_defines = False 508 try: 509 if int(env.subst('$QT4_CPPDEFINES_PASSTOMOC')) == 1: 510 pass_defines = True 511 except ValueError: 512 pass 513 514 if pass_defines: 515 return '$QT4_MOC $QT4_MOCDEFINES $QT4_MOCFROMHFLAGS $QT4_MOCINCFLAGS -o $TARGET $SOURCE' 516 else: 517 return '$QT4_MOC $QT4_MOCFROMHFLAGS $QT4_MOCINCFLAGS -o $TARGET $SOURCE' 518 519def __mocx_generator_from_cxx(source, target, env, for_signature): 520 pass_defines = False 521 try: 522 if int(env.subst('$QT4_CPPDEFINES_PASSTOMOC')) == 1: 523 pass_defines = True 524 except ValueError: 525 pass 526 527 if pass_defines: 528 return ['$QT4_MOC $QT4_MOCDEFINES $QT4_MOCFROMCXXFLAGS $QT4_MOCINCFLAGS -o $TARGET $SOURCE', 529 SCons.Action.Action(checkMocIncluded,None)] 530 else: 531 return ['$QT4_MOC $QT4_MOCFROMCXXFLAGS $QT4_MOCINCFLAGS -o $TARGET $SOURCE', 532 SCons.Action.Action(checkMocIncluded,None)] 533 534def __qrc_generator(source, target, env, for_signature): 535 name_defined = False 536 try: 537 if env.subst('$QT4_QRCFLAGS').find('-name') >= 0: 538 name_defined = True 539 except ValueError: 540 pass 541 542 if name_defined: 543 return '$QT4_RCC $QT4_QRCFLAGS $SOURCE -o $TARGET' 544 else: 545 qrc_suffix = env.subst('$QT4_QRCSUFFIX') 546 src = str(source[0]) 547 head, tail = os.path.split(src) 548 if tail: 549 src = tail 550 qrc_suffix = env.subst('$QT4_QRCSUFFIX') 551 if src.endswith(qrc_suffix): 552 qrc_stem = src[:-len(qrc_suffix)] 553 else: 554 qrc_stem = src 555 return '$QT4_RCC $QT4_QRCFLAGS -name %s $SOURCE -o $TARGET' % qrc_stem 556 557# 558# Builders 559# 560__ts_builder = SCons.Builder.Builder( 561 action = SCons.Action.Action('$QT4_LUPDATECOM','$QT4_LUPDATECOMSTR'), 562 suffix = '.ts', 563 source_factory = SCons.Node.FS.Entry) 564__qm_builder = SCons.Builder.Builder( 565 action = SCons.Action.Action('$QT4_LRELEASECOM','$QT4_LRELEASECOMSTR'), 566 src_suffix = '.ts', 567 suffix = '.qm') 568__qrc_builder = SCons.Builder.Builder( 569 action = SCons.Action.CommandGeneratorAction(__qrc_generator, 570 {"cmdstr":"QT4_QRCCOMSTR"}), 571 source_scanner = __qrcscanner, 572 src_suffix = '$QT4_QRCSUFFIX', 573 suffix = '$QT4_QRCCXXSUFFIX', 574 prefix = '$QT4_QRCCXXPREFIX', 575 single_source = 1) 576__ex_moc_builder = SCons.Builder.Builder( 577 action = SCons.Action.CommandGeneratorAction(__moc_generator_from_h, 578 {"cmdstr":"$QT4_MOCFROMHCOMSTR"})) 579__ex_uic_builder = SCons.Builder.Builder( 580 action = SCons.Action.Action('$QT4_UICCOM', '$QT4_UICCOMSTR'), 581 src_suffix = '.ui') 582 583 584# 585# Wrappers (pseudo-Builders) 586# 587def Ts4(env, target, source=None, *args, **kw): 588 """ 589 A pseudo-Builder wrapper around the LUPDATE executable of Qt4. 590 lupdate [options] [source-file|path]... -ts ts-files 591 """ 592 if not SCons.Util.is_List(target): 593 target = [target] 594 if not source: 595 source = target[:] 596 if not SCons.Util.is_List(source): 597 source = [source] 598 599 # Check QT4_CLEAN_TS and use NoClean() function 600 clean_ts = False 601 try: 602 if int(env.subst('$QT4_CLEAN_TS')) == 1: 603 clean_ts = True 604 except ValueError: 605 pass 606 607 result = [] 608 for t in target: 609 obj = __ts_builder.__call__(env, t, source, **kw) 610 # Prevent deletion of the .ts file, unless explicitly specified 611 if not clean_ts: 612 env.NoClean(obj) 613 # Always make our target "precious", such that it is not deleted 614 # prior to a rebuild 615 env.Precious(obj) 616 # Add to resulting target list 617 result.extend(obj) 618 619 return result 620 621def Qm4(env, target, source=None, *args, **kw): 622 """ 623 A pseudo-Builder wrapper around the LRELEASE executable of Qt4. 624 lrelease [options] ts-files [-qm qm-file] 625 """ 626 if not SCons.Util.is_List(target): 627 target = [target] 628 if not source: 629 source = target[:] 630 if not SCons.Util.is_List(source): 631 source = [source] 632 633 result = [] 634 for t in target: 635 result.extend(__qm_builder.__call__(env, t, source, **kw)) 636 637 return result 638 639def Qrc4(env, target, source=None, *args, **kw): 640 """ 641 A pseudo-Builder wrapper around the RCC executable of Qt4. 642 rcc [options] qrc-files -o out-file 643 """ 644 if not SCons.Util.is_List(target): 645 target = [target] 646 if not source: 647 source = target[:] 648 if not SCons.Util.is_List(source): 649 source = [source] 650 651 result = [] 652 for t, s in zip(target, source): 653 result.extend(__qrc_builder.__call__(env, t, s, **kw)) 654 655 return result 656 657def ExplicitMoc4(env, target, source, *args, **kw): 658 """ 659 A pseudo-Builder wrapper around the MOC executable of Qt4. 660 moc [options] <header-file> 661 """ 662 if not SCons.Util.is_List(target): 663 target = [target] 664 if not SCons.Util.is_List(source): 665 source = [source] 666 667 result = [] 668 for t in target: 669 # Is it a header or a cxx file? 670 result.extend(__ex_moc_builder.__call__(env, t, source, **kw)) 671 672 return result 673 674def ExplicitUic4(env, target, source, *args, **kw): 675 """ 676 A pseudo-Builder wrapper around the UIC executable of Qt4. 677 uic [options] <uifile> 678 """ 679 if not SCons.Util.is_List(target): 680 target = [target] 681 if not SCons.Util.is_List(source): 682 source = [source] 683 684 result = [] 685 for t in target: 686 result.extend(__ex_uic_builder.__call__(env, t, source, **kw)) 687 688 return result 689 690def generate(env): 691 """Add Builders and construction variables for qt4 to an Environment.""" 692 693 def locateQt4Command(env, command, qtdir) : 694 695 # Take cross into account. 696 ptCrossCommand = env['PT_CROSS'] + command 697 698 # Decorations (linux and msys/mingw/cygwin accept. env in linux) 699 # We issue something like 'ID=DSCONS_UIC /path/to/uic' 700 # This is just for recognizing at command print time. 701 ptDecoration = '' 702 if command == 'moc': 703 ptDecoration = 'ID=DSCONS_MOC' 704 elif command == 'uic' : 705 ptDecoration = 'ID=DSCONS_UIC' 706 elif command == 'rcc' : 707 ptDecoration = 'ID=DSCONS_RCC' 708 elif command == 'lupdate' : 709 ptDecoration = 'ID=DSCONS_LUPDATE' 710 elif command == 'lrelease' : 711 ptDecoration = 'ID=DSCONS_LRELEASE' 712 if sys.platform.startswith('win') : 713 ptDecoration = '' 714 715 suffixes = [ 716 '-qt4', 717 '-qt4.exe', 718 '4', 719 '4.exe', 720 '', 721 '.exe', 722 ] 723 triedPaths = [] 724 for suffix in suffixes : 725 fullpath = os.path.join(qtdir,'bin',ptCrossCommand + suffix) 726 if os.access(fullpath, os.X_OK) : 727 return ptDecoration + ' ' + fullpath 728 triedPaths.append(fullpath) 729 730 fullpath = env.Detect([ptCrossCommand+'-qt4', 731 ptCrossCommand+'4', 732 ptCrossCommand]) 733 734 if not (fullpath is None) : return ptDecoration + ' ' + fullpath 735 736 if command in ('lupdate','lrelease'): 737 print 'Qt4 could not locate \'' + \ 738 ptCrossCommand + '\' ' + \ 739 '(This might be acceptable)' 740 return None 741 742 raise Exception("Qt4 command '" + command + "' not found. Tried: " + ', '.join(triedPaths)) 743 744 CLVar = SCons.Util.CLVar 745 Action = SCons.Action.Action 746 Builder = SCons.Builder.Builder 747 748 env['QT4DIR'] = _detect(env) 749 # TODO: 'Replace' should be 'SetDefault' 750# env.SetDefault( 751 env.Replace( 752 QT4DIR = _detect(env), 753 QT4_BINPATH = os.path.join('$QT4DIR', 'bin'), 754 QT4_LIBPATH = os.path.join('$QT4DIR', 'lib'), 755 # TODO: This is not reliable to QT4DIR value changes but needed in order to support '-qt4' variants 756 QT4_MOC = locateQt4Command(env,'moc', env['QT4DIR']), 757 QT4_UIC = locateQt4Command(env,'uic', env['QT4DIR']), 758 QT4_RCC = locateQt4Command(env,'rcc', env['QT4DIR']), 759 QT4_LUPDATE = locateQt4Command(env,'lupdate', env['QT4DIR']), 760 QT4_LRELEASE = locateQt4Command(env,'lrelease', env['QT4DIR']), 761 762 QT4_AUTOSCAN = 1, # Should the qt4 tool try to figure out, which sources are to be moc'ed? 763 QT4_AUTOSCAN_STRATEGY = 0, # While scanning for files to moc, should we search for includes in qtsolutions style? 764 QT4_GOBBLECOMMENTS = 0, # If set to 1, comments are removed before scanning cxx/h files. 765 QT4_CPPDEFINES_PASSTOMOC = 1, # If set to 1, all CPPDEFINES get passed to the moc executable. 766 QT4_CLEAN_TS = 0, # If set to 1, translation files (.ts) get cleaned on 'scons -c' 767 QT4_AUTOMOC_SCANCPPPATH = 1, # If set to 1, the CPPPATHs (or QT4_AUTOMOC_CPPPATH) get scanned for moc'able files 768 QT4_AUTOMOC_CPPPATH = [], # Alternative paths that get scanned for moc files 769 770 # Some Qt4 specific flags. I don't expect someone wants to 771 # manipulate those ... 772 QT4_UICFLAGS = CLVar(''), 773 QT4_MOCFROMHFLAGS = CLVar(''), 774 QT4_MOCFROMCXXFLAGS = CLVar('-i'), 775 QT4_QRCFLAGS = '', 776 QT4_LUPDATEFLAGS = '', 777 QT4_LRELEASEFLAGS = '', 778 779 # suffixes/prefixes for the headers / sources to generate 780 QT4_UISUFFIX = '.ui', 781 QT4_UICDECLPREFIX = 'ui_', 782 QT4_UICDECLSUFFIX = '.h', 783 QT4_MOCINCPREFIX = '-I', 784 QT4_MOCHPREFIX = 'moc_', 785 QT4_MOCHSUFFIX = '$CXXFILESUFFIX', 786 QT4_MOCCXXPREFIX = '', 787 QT4_MOCCXXSUFFIX = '.moc', 788 QT4_QRCSUFFIX = '.qrc', 789 QT4_QRCCXXSUFFIX = '$CXXFILESUFFIX', 790 QT4_QRCCXXPREFIX = 'qrc_', 791 QT4_MOCDEFPREFIX = '-D', 792 QT4_MOCDEFSUFFIX = '', 793 QT4_MOCDEFINES = '${_defines(QT4_MOCDEFPREFIX, CPPDEFINES, QT4_MOCDEFSUFFIX, __env__)}', 794 QT4_MOCCPPPATH = [], 795 QT4_MOCINCFLAGS = '$( ${_concat(QT4_MOCINCPREFIX, QT4_MOCCPPPATH, INCSUFFIX, __env__, RDirs)} $)', 796 797 # Commands for the qt4 support ... 798 QT4_UICCOM = '$QT4_UIC $QT4_UICFLAGS -o $TARGET $SOURCE', 799 QT4_LUPDATECOM = '$QT4_LUPDATE $QT4_LUPDATEFLAGS $SOURCES -ts $TARGET', 800 QT4_LRELEASECOM = '$QT4_LRELEASE $QT4_LRELEASEFLAGS -qm $TARGET $SOURCES', 801 802 # Specialized variables for the Extended Automoc support 803 # (Strategy #1 for qtsolutions) 804 QT4_XMOCHPREFIX = 'moc_', 805 QT4_XMOCHSUFFIX = '.cpp', 806 QT4_XMOCCXXPREFIX = '', 807 QT4_XMOCCXXSUFFIX = '.moc', 808 809 ) 810 811 try: 812 env.AddMethod(Ts4, "Ts4") 813 env.AddMethod(Qm4, "Qm4") 814 env.AddMethod(Qrc4, "Qrc4") 815 env.AddMethod(ExplicitMoc4, "ExplicitMoc4") 816 env.AddMethod(ExplicitUic4, "ExplicitUic4") 817 except AttributeError: 818 # Looks like we use a pre-0.98 version of SCons... 819 from SCons.Script.SConscript import SConsEnvironment 820 SConsEnvironment.Ts4 = Ts4 821 SConsEnvironment.Qm4 = Qm4 822 SConsEnvironment.Qrc4 = Qrc4 823 SConsEnvironment.ExplicitMoc4 = ExplicitMoc4 824 SConsEnvironment.ExplicitUic4 = ExplicitUic4 825 826 # Interface builder 827 uic4builder = Builder( 828 action = SCons.Action.Action('$QT4_UICCOM', '$QT4_UICCOMSTR'), 829 src_suffix='$QT4_UISUFFIX', 830 suffix='$QT4_UICDECLSUFFIX', 831 prefix='$QT4_UICDECLPREFIX', 832 single_source = True 833 #TODO: Consider the uiscanner on new scons version 834 ) 835 env['BUILDERS']['Uic4'] = uic4builder 836 837 # Metaobject builder 838 mocBld = Builder(action={}, prefix={}, suffix={}) 839 for h in header_extensions: 840 act = SCons.Action.CommandGeneratorAction(__moc_generator_from_h, 841 {"cmdstr":"$QT4_MOCFROMHCOMSTR"}) 842 mocBld.add_action(h, act) 843 mocBld.prefix[h] = '$QT4_MOCHPREFIX' 844 mocBld.suffix[h] = '$QT4_MOCHSUFFIX' 845 for cxx in cxx_suffixes: 846 act = SCons.Action.CommandGeneratorAction(__moc_generator_from_cxx, 847 {"cmdstr":"$QT4_MOCFROMCXXCOMSTR"}) 848 mocBld.add_action(cxx, act) 849 mocBld.prefix[cxx] = '$QT4_MOCCXXPREFIX' 850 mocBld.suffix[cxx] = '$QT4_MOCCXXSUFFIX' 851 env['BUILDERS']['Moc4'] = mocBld 852 853 # Metaobject builder for the extended auto scan feature 854 # (Strategy #1 for qtsolutions) 855 xMocBld = Builder(action={}, prefix={}, suffix={}) 856 for h in header_extensions: 857 act = SCons.Action.CommandGeneratorAction(__mocx_generator_from_h, 858 {"cmdstr":"$QT4_MOCFROMHCOMSTR"}) 859 xMocBld.add_action(h, act) 860 xMocBld.prefix[h] = '$QT4_XMOCHPREFIX' 861 xMocBld.suffix[h] = '$QT4_XMOCHSUFFIX' 862 for cxx in cxx_suffixes: 863 act = SCons.Action.CommandGeneratorAction(__mocx_generator_from_cxx, 864 {"cmdstr":"$QT4_MOCFROMCXXCOMSTR"}) 865 xMocBld.add_action(cxx, act) 866 xMocBld.prefix[cxx] = '$QT4_XMOCCXXPREFIX' 867 xMocBld.suffix[cxx] = '$QT4_XMOCCXXSUFFIX' 868 env['BUILDERS']['XMoc4'] = xMocBld 869 870 # Add the Qrc4 action to the CXX file builder (registers the 871 # *.qrc extension with the Environment) 872 cfile_builder, cxxfile_builder = SCons.Tool.createCFileBuilders(env) 873 qrc_act = SCons.Action.CommandGeneratorAction(__qrc_generator, 874 {"cmdstr":"$QT4_QRCCOMSTR"}) 875 cxxfile_builder.add_action('$QT4_QRCSUFFIX', qrc_act) 876 cxxfile_builder.add_emitter('$QT4_QRCSUFFIX', __qrc_emitter) 877 878 # We use the emitters of Program / StaticLibrary / SharedLibrary 879 # to scan for moc'able files 880 # We can't refer to the builders directly, we have to fetch them 881 # as Environment attributes because that sets them up to be called 882 # correctly later by our emitter. 883 env.AppendUnique(PROGEMITTER =[AutomocStatic], 884 SHLIBEMITTER=[AutomocShared], 885 LIBEMITTER =[AutomocStatic], 886 ) 887 888 # TODO: Does dbusxml2cpp need an adapter 889 try: 890 env.AddMethod(enable_modules, "EnableQt4Modules") 891 except AttributeError: 892 # Looks like we use a pre-0.98 version of SCons... 893 from SCons.Script.SConscript import SConsEnvironment 894 SConsEnvironment.EnableQt4Modules = enable_modules 895 896def enable_modules(self, modules, debug=False) : 897 import sys 898 899 validModules = [ 900 'QtCore', 901 'QtGui', 902 'QtOpenGL', 903 'Qt3Support', 904 'QtAssistant', # deprecated 905 'QtAssistantClient', 906 'QtScript', 907 'QtDBus', 908 'QtSql', 909 'QtSvg', 910 # The next modules have not been tested yet so, please 911 # maybe they require additional work on non Linux platforms 912 'QtNetwork', 913 'QtTest', 914 'QtXml', 915 'QtXmlPatterns', 916 'QtUiTools', 917 'QtDesigner', 918 'QtDesignerComponents', 919 'QtWebKit', 920 'QtHelp', 921 'QtScript', 922 'QtScriptTools', 923 'QtMultimedia', 924 ] 925 pclessModules = [ 926# in qt <= 4.3 designer and designerComponents are pcless, on qt4.4 they are not, so removed. 927# 'QtDesigner', 928# 'QtDesignerComponents', 929 ] 930 staticModules = [ 931 'QtUiTools', 932 ] 933 invalidModules=[] 934 for module in modules: 935 if module not in validModules : 936 invalidModules.append(module) 937 if invalidModules : 938 raise Exception("Modules %s are not Qt4 modules. Valid Qt4 modules are: %s"% ( 939 str(invalidModules),str(validModules))) 940 941 moduleDefines = { 942 'QtScript' : ['QT_SCRIPT_LIB'], 943 'QtSvg' : ['QT_SVG_LIB'], 944 'Qt3Support' : ['QT_QT3SUPPORT_LIB','QT3_SUPPORT'], 945 'QtSql' : ['QT_SQL_LIB'], 946 'QtXml' : ['QT_XML_LIB'], 947 'QtOpenGL' : ['QT_OPENGL_LIB'], 948 'QtGui' : ['QT_GUI_LIB'], 949 'QtNetwork' : ['QT_NETWORK_LIB'], 950 'QtCore' : ['QT_CORE_LIB'], 951 } 952 for module in modules : 953 try : self.AppendUnique(CPPDEFINES=moduleDefines[module]) 954 except: pass 955 debugSuffix = '' 956 if sys.platform in ["darwin", "linux2", "win32"] : 957 if debug : 958 if sys.platform in ["win32"] : 959 debugSuffix = 'd' 960 else : 961 debugSuffix = '_debug' 962 for module in modules : 963 if module not in pclessModules : continue 964 self.AppendUnique(LIBS=[module+debugSuffix]) 965 self.AppendUnique(LIBPATH=[os.path.join("$QT4DIR","lib")]) 966 self.AppendUnique(CPPPATH=[os.path.join("$QT4DIR","include","qt4")]) 967 self.AppendUnique(CPPPATH=[os.path.join("$QT4DIR","include","qt4",module)]) 968 pcmodules = [module+debugSuffix for module in modules if module not in pclessModules ] 969 if 'QtDBus' in pcmodules: 970 self.AppendUnique(CPPPATH=[os.path.join("$QT4DIR","include","qt4","QtDBus")]) 971 if "QtAssistant" in pcmodules: 972 self.AppendUnique(CPPPATH=[os.path.join("$QT4DIR","include","qt4","QtAssistant")]) 973 self["QT4_MOCCPPPATH"] = self["CPPPATH"] 974 return 975 976 else : 977 print "CHECK ME. SHOULDN'T" 978 Exit(1) 979 980 981def exists(env): 982 return _detect(env) 983