"""SCons.Tool.qt4 Tool-specific initialization for Qt4. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001-7,2010 The SCons Foundation # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be included # in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # import os.path import re import SCons.Action import SCons.Builder import SCons.Defaults import SCons.Scanner import SCons.Tool import SCons.Util class ToolQt4Warning(SCons.Warnings.Warning): pass class GeneratedMocFileNotIncluded(ToolQt4Warning): pass class QtdirNotFound(ToolQt4Warning): pass SCons.Warnings.enableWarningClass(ToolQt4Warning) try: sorted except NameError: # Pre-2.4 Python has no sorted() function. # # The pre-2.4 Python list.sort() method does not support # list.sort(key=) nor list.sort(reverse=) keyword arguments, so # we must implement the functionality of those keyword arguments # by hand instead of passing them to list.sort(). def sorted(iterable, cmp=None, key=None, reverse=0): if key is not None: result = [(key(x), x) for x in iterable] else: result = iterable[:] if cmp is None: # Pre-2.3 Python does not support list.sort(None). result.sort() else: result.sort(cmp) if key is not None: result = [t1 for t0,t1 in result] if reverse: result.reverse() return result qrcinclude_re = re.compile(r']*>([^<]*)', re.M) def transformToWinePath(path) : return os.popen('winepath -w "%s"'%path).read().strip().replace('\\','/') header_extensions = [".h", ".hxx", ".hpp", ".hh"] if SCons.Util.case_sensitive_suffixes('.h', '.H'): header_extensions.append('.H') # TODO: The following two lines will work when integrated back to SCons # TODO: Meanwhile the third line will do the work #cplusplus = __import__('c++', globals(), locals(), []) #cxx_suffixes = cplusplus.CXXSuffixes cxx_suffixes = [".c", ".cxx", ".cpp", ".cc"] def checkMocIncluded(target, source, env): moc = target[0] cpp = source[0] # looks like cpp.includes is cleared before the build stage :-( # not really sure about the path transformations (moc.cwd? cpp.cwd?) :-/ path = SCons.Defaults.CScan.path_function(env, moc.cwd) includes = SCons.Defaults.CScan(cpp, env, path) if not moc in includes: SCons.Warnings.warn( GeneratedMocFileNotIncluded, "Generated moc file '%s' is not included by '%s'" % (str(moc), str(cpp))) def find_file(filename, paths, node_factory): for dir in paths: node = node_factory(filename, dir) if node.rexists(): return node return None class _Automoc: """ Callable class, which works as an emitter for Programs, SharedLibraries and StaticLibraries. """ def __init__(self, objBuilderName): self.objBuilderName = objBuilderName # some regular expressions: # Q_OBJECT detection self.qo_search = re.compile(r'[^A-Za-z0-9]Q_OBJECT[^A-Za-z0-9]') # cxx and c comment 'eater' self.ccomment = re.compile(r'/\*(.*?)\*/',re.S) self.cxxcomment = re.compile(r'//.*$',re.M) # we also allow Q_OBJECT in a literal string self.literal_qobject = re.compile(r'"[^\n]*Q_OBJECT[^\n]*"') def create_automoc_options(self, env): """ Create a dictionary with variables related to Automocing, based on the current environment. Is executed once in the __call__ routine. """ moc_options = {'auto_scan' : True, 'auto_scan_strategy' : 0, 'gobble_comments' : 0, 'debug' : 0, 'auto_cpppath' : True, 'cpppaths' : []} try: if int(env.subst('$QT4_AUTOSCAN')) == 0: moc_options['auto_scan'] = False except ValueError: pass try: moc_options['auto_scan_strategy'] = int(env.subst('$QT4_AUTOSCAN_STRATEGY')) except ValueError: pass try: moc_options['gobble_comments'] = int(env.subst('$QT4_GOBBLECOMMENTS')) except ValueError: pass try: moc_options['debug'] = int(env.subst('$QT4_DEBUG')) except ValueError: pass try: if int(env.subst('$QT4_AUTOMOC_SCANCPPPATH')) == 0: moc_options['auto_cpppath'] = False except ValueError: pass if moc_options['auto_cpppath']: paths = env.get('QT4_AUTOMOC_CPPPATH', []) if not paths: paths = env.get('CPPPATH', []) moc_options['cpppaths'].extend(paths) return moc_options def __automoc_strategy_simple(self, env, moc_options, cpp, cpp_contents, out_sources): """ Default Automoc strategy (Q_OBJECT driven): detect a header file (alongside the current cpp/cxx) that contains a Q_OBJECT macro...and MOC it. If a Q_OBJECT macro is also found in the cpp/cxx itself, it gets MOCed too. """ h=None for h_ext in header_extensions: # try to find the header file in the corresponding source # directory hname = self.splitext(cpp.name)[0] + h_ext h = find_file(hname, [cpp.get_dir()]+moc_options['cpppaths'], env.File) if h: if moc_options['debug']: print "scons: qt4: Scanning '%s' (header of '%s')" % (str(h), str(cpp)) h_contents = h.get_contents() if moc_options['gobble_comments']: h_contents = self.ccomment.sub('', h_contents) h_contents = self.cxxcomment.sub('', h_contents) h_contents = self.literal_qobject.sub('""', h_contents) break if not h and moc_options['debug']: print "scons: qt4: no header for '%s'." % (str(cpp)) if h and self.qo_search.search(h_contents): # h file with the Q_OBJECT macro found -> add moc_cpp moc_cpp = env.Moc4(h) if moc_options['debug']: print "scons: qt4: found Q_OBJECT macro in '%s', moc'ing to '%s'" % (str(h), str(moc_cpp)) # Now, check whether the corresponding CPP file # includes the moc'ed output directly... inc_moc_cpp = r'^\s*#\s*include\s+"%s"' % str(moc_cpp[0]) if cpp and re.search(inc_moc_cpp, cpp_contents, re.M): if moc_options['debug']: print "scons: qt4: CXX file '%s' directly includes the moc'ed output '%s', no compiling required" % (str(cpp), str(moc_cpp)) env.Depends(cpp, moc_cpp) else: moc_o = self.objBuilder(moc_cpp) if moc_options['debug']: print "scons: qt4: compiling '%s' to '%s'" % (str(cpp), str(moc_o)) out_sources.extend(moc_o) if cpp and self.qo_search.search(cpp_contents): # cpp file with Q_OBJECT macro found -> add moc # (to be included in cpp) moc = env.Moc4(cpp) env.Ignore(moc, moc) if moc_options['debug']: print "scons: qt4: found Q_OBJECT macro in '%s', moc'ing to '%s'" % (str(cpp), str(moc)) def __automoc_strategy_include_driven(self, env, moc_options, cpp, cpp_contents, out_sources): """ Automoc strategy #1 (include driven): searches for "include" statements of MOCed files in the current cpp/cxx file. This strategy tries to add support for the compilation of the qtsolutions... """ if self.splitext(str(cpp))[1] in cxx_suffixes: added = False h_moc = "%s%s%s" % (env.subst('$QT4_XMOCHPREFIX'), self.splitext(cpp.name)[0], env.subst('$QT4_XMOCHSUFFIX')) cxx_moc = "%s%s%s" % (env.subst('$QT4_XMOCCXXPREFIX'), self.splitext(cpp.name)[0], env.subst('$QT4_XMOCCXXSUFFIX')) inc_h_moc = r'#include\s+"%s"' % h_moc inc_cxx_moc = r'#include\s+"%s"' % cxx_moc # Search for special includes in qtsolutions style if cpp and re.search(inc_h_moc, cpp_contents): # cpp file with #include directive for a MOCed header found -> add moc # Try to find header file h=None hname="" for h_ext in header_extensions: # Try to find the header file in the # corresponding source directory hname = self.splitext(cpp.name)[0] + h_ext h = find_file(hname, [cpp.get_dir()]+moc_options['cpppaths'], env.File) if h: if moc_options['debug']: print "scons: qt4: Scanning '%s' (header of '%s')" % (str(h), str(cpp)) h_contents = h.get_contents() if moc_options['gobble_comments']: h_contents = self.ccomment.sub('', h_contents) h_contents = self.cxxcomment.sub('', h_contents) h_contents = self.literal_qobject.sub('""', h_contents) break if not h and moc_options['debug']: print "scons: qt4: no header for '%s'." % (str(cpp)) if h and self.qo_search.search(h_contents): # h file with the Q_OBJECT macro found -> add moc_cpp moc_cpp = env.XMoc4(h) env.Ignore(moc_cpp, moc_cpp) added = True # Removing file from list of sources, because it is not to be # compiled but simply included by the cpp/cxx file. for idx, s in enumerate(out_sources): if hasattr(s, "sources") and len(s.sources) > 0: if str(s.sources[0]) == h_moc: out_sources.pop(idx) break if moc_options['debug']: print "scons: qt4: found Q_OBJECT macro in '%s', moc'ing to '%s'" % (str(h), str(h_moc)) else: if moc_options['debug']: 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) if cpp and re.search(inc_cxx_moc, cpp_contents): # cpp file with #include directive for a MOCed cxx file found -> add moc if self.qo_search.search(cpp_contents): moc = env.XMoc4(target=cxx_moc, source=cpp) env.Ignore(moc, moc) added = True if moc_options['debug']: print "scons: qt4: found Q_OBJECT macro in '%s', moc'ing to '%s'" % (str(cpp), str(moc)) else: if moc_options['debug']: 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) if not added: # Fallback to default Automoc strategy (Q_OBJECT driven) self.__automoc_strategy_simple(env, moc_options, cpp, cpp_contents, out_sources) def __call__(self, target, source, env): """ Smart autoscan function. Gets the list of objects for the Program or Lib. Adds objects and builders for the special qt4 files. """ moc_options = self.create_automoc_options(env) # some shortcuts used in the scanner self.splitext = SCons.Util.splitext self.objBuilder = getattr(env, self.objBuilderName) # The following is kind of hacky to get builders working properly (FIXME) objBuilderEnv = self.objBuilder.env self.objBuilder.env = env mocBuilderEnv = env.Moc4.env env.Moc4.env = env xMocBuilderEnv = env.XMoc4.env env.XMoc4.env = env # make a deep copy for the result; MocH objects will be appended out_sources = source[:] for obj in source: if not moc_options['auto_scan']: break if isinstance(obj,basestring): # big kludge! print "scons: qt4: '%s' MAYBE USING AN OLD SCONS VERSION AND NOT CONVERTED TO 'File'. Discarded." % str(obj) continue if not obj.has_builder(): # binary obj file provided if moc_options['debug']: print "scons: qt4: '%s' seems to be a binary. Discarded." % str(obj) continue cpp = obj.sources[0] if not self.splitext(str(cpp))[1] in cxx_suffixes: if moc_options['debug']: print "scons: qt4: '%s' is no cxx file. Discarded." % str(cpp) # c or fortran source continue try: cpp_contents = cpp.get_contents() if moc_options['gobble_comments']: cpp_contents = self.ccomment.sub('', cpp_contents) cpp_contents = self.cxxcomment.sub('', cpp_contents) cpp_contents = self.literal_qobject.sub('""', cpp_contents) except: continue # may be an still not generated source if moc_options['auto_scan_strategy'] == 0: # Default Automoc strategy (Q_OBJECT driven) self.__automoc_strategy_simple(env, moc_options, cpp, cpp_contents, out_sources) else: # Automoc strategy #1 (include driven) self.__automoc_strategy_include_driven(env, moc_options, cpp, cpp_contents, out_sources) # restore the original env attributes (FIXME) self.objBuilder.env = objBuilderEnv env.Moc4.env = mocBuilderEnv env.XMoc4.env = xMocBuilderEnv # We return the set of source entries as sorted sequence, else # the order might accidentally change from one build to another # and trigger unwanted rebuilds. For proper sorting, a key function # has to be specified...FS.Entry (and Base nodes in general) do not # provide a __cmp__, for performance reasons. return (target, sorted(set(out_sources), key=lambda entry : str(entry))) AutomocShared = _Automoc('SharedObject') AutomocStatic = _Automoc('StaticObject') def _detect(env): """Not really safe, but fast method to detect the Qt4 library""" # TODO: check output of "moc -v" for correct version >= 4.0.0 try: return env['QT4DIR'] except KeyError: pass try: return env['QTDIR'] except KeyError: pass try: return os.environ['QT4DIR'] except KeyError: pass try: return os.environ['QTDIR'] except KeyError: pass moc = env.WhereIs('moc-qt4') or env.WhereIs('moc4') or env.WhereIs('moc') if moc: QT4DIR = os.path.dirname(os.path.dirname(moc)) SCons.Warnings.warn( QtdirNotFound, "QT4DIR variable is not defined, using moc executable as a hint (QT4DIR=%s)" % QT4DIR) return QT4DIR raise SCons.Errors.StopError( QtdirNotFound, "Could not detect Qt 4 installation") return None def __scanResources(node, env, path, arg): # Helper function for scanning .qrc resource files # I've been careful on providing names relative to the qrc file # If that was not needed this code could be simplified a lot def recursiveFiles(basepath, path) : result = [] for item in os.listdir(os.path.join(basepath, path)) : itemPath = os.path.join(path, item) if os.path.isdir(os.path.join(basepath, itemPath)) : result += recursiveFiles(basepath, itemPath) else: result.append(itemPath) return result contents = node.get_contents() includes = qrcinclude_re.findall(contents) qrcpath = os.path.dirname(node.path) dirs = [included for included in includes if os.path.isdir(os.path.join(qrcpath,included))] # dirs need to include files recursively for dir in dirs : includes.remove(dir) includes+=recursiveFiles(qrcpath,dir) return includes # # Scanners # __qrcscanner = SCons.Scanner.Scanner(name = 'qrcfile', function = __scanResources, argument = None, skeys = ['.qrc']) # # Emitters # def __qrc_path(head, prefix, tail, suffix): if head: if tail: return os.path.join(head, "%s%s%s" % (prefix, tail, suffix)) else: return "%s%s%s" % (prefix, head, suffix) else: return "%s%s%s" % (prefix, tail, suffix) def __qrc_emitter(target, source, env): sourceBase, sourceExt = os.path.splitext(SCons.Util.to_String(source[0])) sHead = None sTail = sourceBase if sourceBase: sHead, sTail = os.path.split(sourceBase) t = __qrc_path(sHead, env.subst('$QT4_QRCCXXPREFIX'), sTail, env.subst('$QT4_QRCCXXSUFFIX')) return t, source # # Action generators # def __moc_generator_from_h(source, target, env, for_signature): pass_defines = False try: if int(env.subst('$QT4_CPPDEFINES_PASSTOMOC')) == 1: pass_defines = True except ValueError: pass if pass_defines: return '$QT4_MOC $QT4_MOCDEFINES $QT4_MOCFROMHFLAGS $QT4_MOCINCFLAGS -o $TARGET $SOURCE' else: return '$QT4_MOC $QT4_MOCFROMHFLAGS $QT4_MOCINCFLAGS -o $TARGET $SOURCE' def __moc_generator_from_cxx(source, target, env, for_signature): pass_defines = False try: if int(env.subst('$QT4_CPPDEFINES_PASSTOMOC')) == 1: pass_defines = True except ValueError: pass if pass_defines: return ['$QT4_MOC $QT4_MOCDEFINES $QT4_MOCFROMCXXFLAGS $QT4_MOCINCFLAGS -o $TARGET $SOURCE', SCons.Action.Action(checkMocIncluded,None)] else: return ['$QT4_MOC $QT4_MOCFROMCXXFLAGS $QT4_MOCINCFLAGS -o $TARGET $SOURCE', SCons.Action.Action(checkMocIncluded,None)] def __mocx_generator_from_h(source, target, env, for_signature): pass_defines = False try: if int(env.subst('$QT4_CPPDEFINES_PASSTOMOC')) == 1: pass_defines = True except ValueError: pass if pass_defines: return '$QT4_MOC $QT4_MOCDEFINES $QT4_MOCFROMHFLAGS $QT4_MOCINCFLAGS -o $TARGET $SOURCE' else: return '$QT4_MOC $QT4_MOCFROMHFLAGS $QT4_MOCINCFLAGS -o $TARGET $SOURCE' def __mocx_generator_from_cxx(source, target, env, for_signature): pass_defines = False try: if int(env.subst('$QT4_CPPDEFINES_PASSTOMOC')) == 1: pass_defines = True except ValueError: pass if pass_defines: return ['$QT4_MOC $QT4_MOCDEFINES $QT4_MOCFROMCXXFLAGS $QT4_MOCINCFLAGS -o $TARGET $SOURCE', SCons.Action.Action(checkMocIncluded,None)] else: return ['$QT4_MOC $QT4_MOCFROMCXXFLAGS $QT4_MOCINCFLAGS -o $TARGET $SOURCE', SCons.Action.Action(checkMocIncluded,None)] def __qrc_generator(source, target, env, for_signature): name_defined = False try: if env.subst('$QT4_QRCFLAGS').find('-name') >= 0: name_defined = True except ValueError: pass if name_defined: return '$QT4_RCC $QT4_QRCFLAGS $SOURCE -o $TARGET' else: qrc_suffix = env.subst('$QT4_QRCSUFFIX') src = str(source[0]) head, tail = os.path.split(src) if tail: src = tail qrc_suffix = env.subst('$QT4_QRCSUFFIX') if src.endswith(qrc_suffix): qrc_stem = src[:-len(qrc_suffix)] else: qrc_stem = src return '$QT4_RCC $QT4_QRCFLAGS -name %s $SOURCE -o $TARGET' % qrc_stem # # Builders # __ts_builder = SCons.Builder.Builder( action = SCons.Action.Action('$QT4_LUPDATECOM','$QT4_LUPDATECOMSTR'), suffix = '.ts', source_factory = SCons.Node.FS.Entry) __qm_builder = SCons.Builder.Builder( action = SCons.Action.Action('$QT4_LRELEASECOM','$QT4_LRELEASECOMSTR'), src_suffix = '.ts', suffix = '.qm') __qrc_builder = SCons.Builder.Builder( action = SCons.Action.CommandGeneratorAction(__qrc_generator, {"cmdstr":"$QT4_QRCCOMSTR"}), source_scanner = __qrcscanner, src_suffix = '$QT4_QRCSUFFIX', suffix = '$QT4_QRCCXXSUFFIX', prefix = '$QT4_QRCCXXPREFIX', single_source = 1) __ex_moc_builder = SCons.Builder.Builder( action = SCons.Action.CommandGeneratorAction(__moc_generator_from_h, {"cmdstr":"$QT4_MOCFROMHCOMSTR"})) __ex_uic_builder = SCons.Builder.Builder( action = SCons.Action.Action('$QT4_UICCOM', '$QT4_UICCOMSTR'), src_suffix = '.ui') # # Wrappers (pseudo-Builders) # def Ts4(env, target, source=None, *args, **kw): """ A pseudo-Builder wrapper around the LUPDATE executable of Qt4. lupdate [options] [source-file|path]... -ts ts-files """ if not SCons.Util.is_List(target): target = [target] if not source: source = target[:] if not SCons.Util.is_List(source): source = [source] # Check QT4_CLEAN_TS and use NoClean() function clean_ts = False try: if int(env.subst('$QT4_CLEAN_TS')) == 1: clean_ts = True except ValueError: pass result = [] for t in target: obj = __ts_builder.__call__(env, t, source, **kw) # Prevent deletion of the .ts file, unless explicitly specified if not clean_ts: env.NoClean(obj) # Always make our target "precious", such that it is not deleted # prior to a rebuild env.Precious(obj) # Add to resulting target list result.extend(obj) return result def Qm4(env, target, source=None, *args, **kw): """ A pseudo-Builder wrapper around the LRELEASE executable of Qt4. lrelease [options] ts-files [-qm qm-file] """ if not SCons.Util.is_List(target): target = [target] if not source: source = target[:] if not SCons.Util.is_List(source): source = [source] result = [] for t in target: result.extend(__qm_builder.__call__(env, t, source, **kw)) return result def Qrc4(env, target, source=None, *args, **kw): """ A pseudo-Builder wrapper around the RCC executable of Qt4. rcc [options] qrc-files -o out-file """ if not SCons.Util.is_List(target): target = [target] if not source: source = target[:] if not SCons.Util.is_List(source): source = [source] result = [] for t, s in zip(target, source): result.extend(__qrc_builder.__call__(env, t, s, **kw)) return result def ExplicitMoc4(env, target, source, *args, **kw): """ A pseudo-Builder wrapper around the MOC executable of Qt4. moc [options] """ if not SCons.Util.is_List(target): target = [target] if not SCons.Util.is_List(source): source = [source] result = [] for t in target: # Is it a header or a cxx file? result.extend(__ex_moc_builder.__call__(env, t, source, **kw)) return result def ExplicitUic4(env, target, source, *args, **kw): """ A pseudo-Builder wrapper around the UIC executable of Qt4. uic [options] """ if not SCons.Util.is_List(target): target = [target] if not SCons.Util.is_List(source): source = [source] result = [] for t in target: result.extend(__ex_uic_builder.__call__(env, t, source, **kw)) return result def generate(env): """Add Builders and construction variables for qt4 to an Environment.""" def locateQt4Command(env, command, qtdir) : suffixes = [ '-qt4', '-qt4.exe', '4', '4.exe', '', '.exe', ] triedPaths = [] for suffix in suffixes : fullpath = os.path.join(qtdir,'bin',command + suffix) if os.access(fullpath, os.X_OK) : return fullpath triedPaths.append(fullpath) fullpath = env.Detect([command+'-qt4', command+'4', command]) if not (fullpath is None) : return fullpath raise Exception("Qt4 command '" + command + "' not found. Tried: " + ', '.join(triedPaths)) CLVar = SCons.Util.CLVar Action = SCons.Action.Action Builder = SCons.Builder.Builder env['QT4DIR'] = _detect(env) # TODO: 'Replace' should be 'SetDefault' # env.SetDefault( env.Replace( QT4DIR = _detect(env), QT4_BINPATH = os.path.join('$QT4DIR', 'bin'), # TODO: This is not reliable to QT4DIR value changes but needed in order to support '-qt4' variants QT4_MOC = locateQt4Command(env,'moc', env['QT4DIR']), QT4_UIC = locateQt4Command(env,'uic', env['QT4DIR']), QT4_RCC = locateQt4Command(env,'rcc', env['QT4DIR']), QT4_LUPDATE = locateQt4Command(env,'lupdate', env['QT4DIR']), QT4_LRELEASE = locateQt4Command(env,'lrelease', env['QT4DIR']), QT4_AUTOSCAN = 1, # Should the qt4 tool try to figure out, which sources are to be moc'ed? QT4_AUTOSCAN_STRATEGY = 0, # While scanning for files to moc, should we search for includes in qtsolutions style? QT4_GOBBLECOMMENTS = 0, # If set to 1, comments are removed before scanning cxx/h files. QT4_CPPDEFINES_PASSTOMOC = 1, # If set to 1, all CPPDEFINES get passed to the moc executable. QT4_CLEAN_TS = 0, # If set to 1, translation files (.ts) get cleaned on 'scons -c' QT4_AUTOMOC_SCANCPPPATH = 1, # If set to 1, the CPPPATHs (or QT4_AUTOMOC_CPPPATH) get scanned for moc'able files QT4_AUTOMOC_CPPPATH = [], # Alternative paths that get scanned for moc files # Some Qt4 specific flags. I don't expect someone wants to # manipulate those ... QT4_UICFLAGS = CLVar(''), QT4_MOCFROMHFLAGS = CLVar(''), QT4_MOCFROMCXXFLAGS = CLVar('-i'), QT4_QRCFLAGS = '', QT4_LUPDATEFLAGS = '', QT4_LRELEASEFLAGS = '', # suffixes/prefixes for the headers / sources to generate QT4_UISUFFIX = '.ui', QT4_UICDECLPREFIX = 'ui_', QT4_UICDECLSUFFIX = '.h', QT4_MOCINCPREFIX = '-I', QT4_MOCHPREFIX = 'moc_', QT4_MOCHSUFFIX = '$CXXFILESUFFIX', QT4_MOCCXXPREFIX = '', QT4_MOCCXXSUFFIX = '.moc', QT4_QRCSUFFIX = '.qrc', QT4_QRCCXXSUFFIX = '$CXXFILESUFFIX', QT4_QRCCXXPREFIX = 'qrc_', QT4_MOCDEFPREFIX = '-D', QT4_MOCDEFSUFFIX = '', QT4_MOCDEFINES = '${_defines(QT4_MOCDEFPREFIX, CPPDEFINES, QT4_MOCDEFSUFFIX, __env__)}', QT4_MOCCPPPATH = [], QT4_MOCINCFLAGS = '$( ${_concat(QT4_MOCINCPREFIX, QT4_MOCCPPPATH, INCSUFFIX, __env__, RDirs)} $)', # Commands for the qt4 support ... QT4_UICCOM = '$QT4_UIC $QT4_UICFLAGS -o $TARGET $SOURCE', QT4_LUPDATECOM = '$QT4_LUPDATE $QT4_LUPDATEFLAGS $SOURCES -ts $TARGET', QT4_LRELEASECOM = '$QT4_LRELEASE $QT4_LRELEASEFLAGS -qm $TARGET $SOURCES', # Specialized variables for the Extended Automoc support # (Strategy #1 for qtsolutions) QT4_XMOCHPREFIX = 'moc_', QT4_XMOCHSUFFIX = '.cpp', QT4_XMOCCXXPREFIX = '', QT4_XMOCCXXSUFFIX = '.moc', ) try: env.AddMethod(Ts4, "Ts4") env.AddMethod(Qm4, "Qm4") env.AddMethod(Qrc4, "Qrc4") env.AddMethod(ExplicitMoc4, "ExplicitMoc4") env.AddMethod(ExplicitUic4, "ExplicitUic4") except AttributeError: # Looks like we use a pre-0.98 version of SCons... from SCons.Script.SConscript import SConsEnvironment SConsEnvironment.Ts4 = Ts4 SConsEnvironment.Qm4 = Qm4 SConsEnvironment.Qrc4 = Qrc4 SConsEnvironment.ExplicitMoc4 = ExplicitMoc4 SConsEnvironment.ExplicitUic4 = ExplicitUic4 # Interface builder uic4builder = Builder( action = SCons.Action.Action('$QT4_UICCOM', '$QT4_UICCOMSTR'), src_suffix='$QT4_UISUFFIX', suffix='$QT4_UICDECLSUFFIX', prefix='$QT4_UICDECLPREFIX', single_source = True #TODO: Consider the uiscanner on new scons version ) env['BUILDERS']['Uic4'] = uic4builder # Metaobject builder mocBld = Builder(action={}, prefix={}, suffix={}) for h in header_extensions: act = SCons.Action.CommandGeneratorAction(__moc_generator_from_h, {"cmdstr":"$QT4_MOCFROMHCOMSTR"}) mocBld.add_action(h, act) mocBld.prefix[h] = '$QT4_MOCHPREFIX' mocBld.suffix[h] = '$QT4_MOCHSUFFIX' for cxx in cxx_suffixes: act = SCons.Action.CommandGeneratorAction(__moc_generator_from_cxx, {"cmdstr":"$QT4_MOCFROMCXXCOMSTR"}) mocBld.add_action(cxx, act) mocBld.prefix[cxx] = '$QT4_MOCCXXPREFIX' mocBld.suffix[cxx] = '$QT4_MOCCXXSUFFIX' env['BUILDERS']['Moc4'] = mocBld # Metaobject builder for the extended auto scan feature # (Strategy #1 for qtsolutions) xMocBld = Builder(action={}, prefix={}, suffix={}) for h in header_extensions: act = SCons.Action.CommandGeneratorAction(__mocx_generator_from_h, {"cmdstr":"$QT4_MOCXFROMHCOMSTR"}) xMocBld.add_action(h, act) xMocBld.prefix[h] = '$QT4_XMOCHPREFIX' xMocBld.suffix[h] = '$QT4_XMOCHSUFFIX' for cxx in cxx_suffixes: act = SCons.Action.CommandGeneratorAction(__mocx_generator_from_cxx, {"cmdstr":"$QT4_MOCXFROMCXXCOMSTR"}) xMocBld.add_action(cxx, act) xMocBld.prefix[cxx] = '$QT4_XMOCCXXPREFIX' xMocBld.suffix[cxx] = '$QT4_XMOCCXXSUFFIX' env['BUILDERS']['XMoc4'] = xMocBld # Add the Qrc4 action to the CXX file builder (registers the # *.qrc extension with the Environment) cfile_builder, cxxfile_builder = SCons.Tool.createCFileBuilders(env) qrc_act = SCons.Action.CommandGeneratorAction(__qrc_generator, {"cmdstr":"$QT4_QRCCOMSTR"}) cxxfile_builder.add_action('$QT4_QRCSUFFIX', qrc_act) cxxfile_builder.add_emitter('$QT4_QRCSUFFIX', __qrc_emitter) env.Append(SCANNERS=__qrcscanner) # We use the emitters of Program / StaticLibrary / SharedLibrary # to scan for moc'able files # We can't refer to the builders directly, we have to fetch them # as Environment attributes because that sets them up to be called # correctly later by our emitter. env.AppendUnique(PROGEMITTER =[AutomocStatic], SHLIBEMITTER=[AutomocShared], LIBEMITTER =[AutomocStatic], ) # TODO: Does dbusxml2cpp need an adapter try: env.AddMethod(enable_modules, "EnableQt4Modules") except AttributeError: # Looks like we use a pre-0.98 version of SCons... from SCons.Script.SConscript import SConsEnvironment SConsEnvironment.EnableQt4Modules = enable_modules def enable_modules(self, modules, debug=False, crosscompiling=False) : import sys validModules = [ 'QtCore', 'QtGui', 'QtOpenGL', 'Qt3Support', 'QtAssistant', # deprecated 'QtAssistantClient', 'QtScript', 'QtDBus', 'QtSql', 'QtSvg', # The next modules have not been tested yet so, please # maybe they require additional work on non Linux platforms 'QtNetwork', 'QtTest', 'QtXml', 'QtXmlPatterns', 'QtUiTools', 'QtDesigner', 'QtDesignerComponents', 'QtWebKit', 'QtHelp', 'QtScript', 'QtScriptTools', 'QtMultimedia', ] pclessModules = [ # in qt <= 4.3 designer and designerComponents are pcless, on qt4.4 they are not, so removed. # 'QtDesigner', # 'QtDesignerComponents', ] staticModules = [ 'QtUiTools', ] invalidModules=[] for module in modules: if module not in validModules : invalidModules.append(module) if invalidModules : raise Exception("Modules %s are not Qt4 modules. Valid Qt4 modules are: %s"% ( str(invalidModules),str(validModules))) moduleDefines = { 'QtScript' : ['QT_SCRIPT_LIB'], 'QtSvg' : ['QT_SVG_LIB'], 'Qt3Support' : ['QT_QT3SUPPORT_LIB','QT3_SUPPORT'], 'QtSql' : ['QT_SQL_LIB'], 'QtXml' : ['QT_XML_LIB'], 'QtOpenGL' : ['QT_OPENGL_LIB'], 'QtGui' : ['QT_GUI_LIB'], 'QtNetwork' : ['QT_NETWORK_LIB'], 'QtCore' : ['QT_CORE_LIB'], } for module in modules : try : self.AppendUnique(CPPDEFINES=moduleDefines[module]) except: pass debugSuffix = '' if sys.platform in ["darwin", "linux2"] and not crosscompiling : if debug : debugSuffix = '_debug' for module in modules : if module not in pclessModules : continue self.AppendUnique(LIBS=[module+debugSuffix]) self.AppendUnique(LIBPATH=[os.path.join("$QT4DIR","lib")]) self.AppendUnique(CPPPATH=[os.path.join("$QT4DIR","include","qt4")]) self.AppendUnique(CPPPATH=[os.path.join("$QT4DIR","include","qt4",module)]) pcmodules = [module+debugSuffix for module in modules if module not in pclessModules ] if 'QtDBus' in pcmodules: self.AppendUnique(CPPPATH=[os.path.join("$QT4DIR","include","qt4","QtDBus")]) if "QtAssistant" in pcmodules: self.AppendUnique(CPPPATH=[os.path.join("$QT4DIR","include","qt4","QtAssistant")]) pcmodules.remove("QtAssistant") pcmodules.append("QtAssistantClient") self.ParseConfig('pkg-config %s --libs --cflags'% ' '.join(pcmodules)) self["QT4_MOCCPPPATH"] = self["CPPPATH"] return if sys.platform == "win32" or crosscompiling : if crosscompiling: transformedQtdir = transformToWinePath(self['QT4DIR']) self['QT4_MOC'] = "QT4DIR=%s %s"%( transformedQtdir, self['QT4_MOC']) self.AppendUnique(CPPPATH=[os.path.join("$QT4DIR","include")]) try: modules.remove("QtDBus") except: pass if debug : debugSuffix = 'd' if "QtAssistant" in modules: self.AppendUnique(CPPPATH=[os.path.join("$QT4DIR","include","QtAssistant")]) modules.remove("QtAssistant") modules.append("QtAssistantClient") self.AppendUnique(LIBS=['qtmain'+debugSuffix]) self.AppendUnique(LIBS=[lib+debugSuffix+'4' for lib in modules if lib not in staticModules]) self.PrependUnique(LIBS=[lib+debugSuffix for lib in modules if lib in staticModules]) if 'QtOpenGL' in modules: self.AppendUnique(LIBS=['opengl32']) self.AppendUnique(CPPPATH=[ '$QT4DIR/include/']) self.AppendUnique(CPPPATH=[ '$QT4DIR/include/'+module for module in modules]) if crosscompiling : self["QT4_MOCCPPPATH"] = [ path.replace('$QT4DIR', transformedQtdir) for path in self['CPPPATH'] ] else : self["QT4_MOCCPPPATH"] = self["CPPPATH"] self.AppendUnique(LIBPATH=[os.path.join('$QT4DIR','lib')]) return """ if sys.platform=="darwin" : # TODO: Test debug version on Mac self.AppendUnique(LIBPATH=[os.path.join('$QT4DIR','lib')]) self.AppendUnique(LINKFLAGS="-F$QT4DIR/lib") self.AppendUnique(LINKFLAGS="-L$QT4DIR/lib") #TODO clean! if debug : debugSuffix = 'd' for module in modules : # self.AppendUnique(CPPPATH=[os.path.join("$QT4DIR","include")]) # self.AppendUnique(CPPPATH=[os.path.join("$QT4DIR","include",module)]) # port qt4-mac: self.AppendUnique(CPPPATH=[os.path.join("$QT4DIR","include", "qt4")]) self.AppendUnique(CPPPATH=[os.path.join("$QT4DIR","include", "qt4", module)]) if module in staticModules : self.AppendUnique(LIBS=[module+debugSuffix]) # TODO: Add the debug suffix self.AppendUnique(LIBPATH=[os.path.join("$QT4DIR","lib")]) else : # self.Append(LINKFLAGS=['-framework', module]) # port qt4-mac: self.Append(LIBS=module) if 'QtOpenGL' in modules: self.AppendUnique(LINKFLAGS="-F/System/Library/Frameworks") self.Append(LINKFLAGS=['-framework', 'AGL']) #TODO ughly kludge to avoid quotes self.Append(LINKFLAGS=['-framework', 'OpenGL']) self["QT4_MOCCPPPATH"] = self["CPPPATH"] return # This should work for mac but doesn't # env.AppendUnique(FRAMEWORKPATH=[os.path.join(env['QT4DIR'],'lib')]) # env.AppendUnique(FRAMEWORKS=['QtCore','QtGui','QtOpenGL', 'AGL']) """ def exists(env): return _detect(env)