qt4.py

Go to the documentation of this file.
00001 import re
00002 import os.path
00003 
00004 import SCons.Defaults
00005 import SCons.Tool
00006 import SCons.Util
00007 
00008 
00009 class ToolQtWarning(SCons.Warnings.Warning):
00010       pass
00011 class GeneratedMocFileNotIncluded(ToolQtWarning):
00012       pass
00013 class QtdirNotFound(ToolQtWarning):
00014       pass
00015 SCons.Warnings.enableWarningClass(ToolQtWarning)
00016 
00017 qrcinclude_re = re.compile(r'<file>([^<]*)</file>', re.M)
00018 
00019 
00020 header_extensions = [".h", ".hxx", ".hpp", ".hh"]
00021 if SCons.Util.case_sensitive_suffixes('.h', '.H'):
00022       header_extensions.append('.H')
00023 #cplusplus = __import__('c++', globals(), locals(), ['Scons.Tools'])
00024 #cxx_suffixes = cplusplus.CXXSuffixes
00025 cxx_suffixes = [".c", ".cxx", ".cpp", ".cc"]
00026 
00027 def _checkMocIncluded(target, source, env):
00028       moc = target[0]
00029       cpp = source[0]
00030       # looks like cpp.includes is cleared before the build stage :-(
00031       # not really sure about the path transformations (moc.cwd? cpp.cwd?) :-/
00032       path = SCons.Defaults.CScan.path_function(env, moc.cwd)
00033       includes = SCons.Defaults.CScan(cpp, env, path)
00034       if not moc in includes:
00035             SCons.Warnings.warn(
00036                   GeneratedMocFileNotIncluded,
00037                   "Generated moc file '%s' is not included by '%s'" %
00038                   (str(moc), str(cpp)))
00039 
00040 def _find_file(filename, paths, node_factory):
00041       retval = None
00042       for dir in paths:
00043             node = node_factory(filename, dir)
00044             if node.rexists():
00045                   return node
00046       return None
00047 
00048 class _Automoc:
00049       """
00050       Callable class, which works as an emitter for Programs, SharedLibraries and
00051       StaticLibraries.
00052       """
00053 
00054       def __init__(self, objBuilderName):
00055             self.objBuilderNameobjBuilderName = objBuilderName
00056             
00057       def __call__(self, target, source, env):
00058             """
00059             Smart autoscan function. Gets the list of objects for the Program
00060             or Lib. Adds objects and builders for the special qt files.
00061             """
00062             try:
00063                   if int(env.subst('$QT_AUTOSCAN')) == 0:
00064                         return target, source
00065             except ValueError:
00066                   pass
00067             try:
00068                   debug = int(env.subst('$QT_DEBUG'))
00069             except ValueError:
00070                   debug = 0
00071             
00072             # some shortcuts used in the scanner
00073             FS = SCons.Node.FS.default_fs
00074             splitext = SCons.Util.splitext
00075             objBuilder = getattr(env, self.objBuilderNameobjBuilderName)
00076 
00077             # some regular expressions:
00078             # Q_OBJECT detection
00079             q_object_search = re.compile(r'[^A-Za-z0-9]Q_OBJECT[^A-Za-z0-9]') 
00080             # cxx and c comment 'eater'
00081             #comment = re.compile(r'(//.*)|(/\*(([^*])|(\*[^/]))*\*/)')
00082             # CW: something must be wrong with the regexp. See also bug #998222
00083             #     CURRENTLY THERE IS NO TEST CASE FOR THAT
00084             
00085             # The following is kind of hacky to get builders working properly (FIXME)
00086             objBuilderEnv = objBuilder.env
00087             objBuilder.env = env
00088             mocBuilderEnv = env.Moc4.env
00089             env.Moc4.env = env
00090             
00091             # make a deep copy for the result; MocH objects will be appended
00092             out_sources = source[:]
00093 
00094             for obj in source:
00095                   if not obj.has_builder():
00096                         # binary obj file provided
00097                         if debug:
00098                               print "scons: qt: '%s' seems to be a binary. Discarded." % str(obj)
00099                         continue
00100                   cpp = obj.sources[0]
00101                   if not splitext(str(cpp))[1] in cxx_suffixes:
00102                         if debug:
00103                               print "scons: qt: '%s' is no cxx file. Discarded." % str(cpp) 
00104                         # c or fortran source
00105                         continue
00106                   #cpp_contents = comment.sub('', cpp.get_contents())
00107                   cpp_contents = cpp.get_contents()
00108                   h=None
00109                   for h_ext in header_extensions:
00110                         # try to find the header file in the corresponding source
00111                         # directory
00112                         hname = splitext(cpp.name)[0] + h_ext
00113                         h = _find_file(hname,
00114                                             (cpp.get_dir(),),
00115                                             FS.File)
00116                         if h:
00117                               if debug:
00118                                     print "scons: qt: Scanning '%s' (header of '%s')" % (str(h), str(cpp))
00119                               #h_contents = comment.sub('', h.get_contents())
00120                               h_contents = h.get_contents()
00121                               break
00122                   if not h and debug:
00123                         print "scons: qt: no header for '%s'." % (str(cpp))
00124                   if h and q_object_search.search(h_contents):
00125                         # h file with the Q_OBJECT macro found -> add moc_cpp
00126                         moc_cpp = env.Moc4(h)
00127                         moc_o = objBuilder(moc_cpp)
00128                         out_sources.append(moc_o)
00129                         #moc_cpp.target_scanner = SCons.Defaults.CScan
00130                         if debug:
00131                               print "scons: qt: found Q_OBJECT macro in '%s', moc'ing to '%s'" % (str(h), str(moc_cpp))
00132                   if cpp and q_object_search.search(cpp_contents):
00133                         # cpp file with Q_OBJECT macro found -> add moc
00134                         # (to be included in cpp)
00135                         moc = env.Moc4(cpp)
00136                         env.Ignore(moc, moc)
00137                         if debug:
00138                               print "scons: qt: found Q_OBJECT macro in '%s', moc'ing to '%s'" % (str(cpp), str(moc))
00139                         #moc.source_scanner = SCons.Defaults.CScan
00140             # restore the original env attributes (FIXME)
00141             objBuilder.env = objBuilderEnv
00142             env.Moc4.env = mocBuilderEnv
00143 
00144             return (target, out_sources)
00145 
00146 AutomocShared = _Automoc('SharedObject')
00147 AutomocStatic = _Automoc('StaticObject')
00148 
00149 def _detect(env):
00150       """Not really safe, but fast method to detect the QT library"""
00151 
00152       QTDIR = env.get('QTDIR',None)
00153       if QTDIR!=None : return QTDIR
00154 
00155       QTDIR = os.environ.get('QTDIR',None)
00156       if QTDIR!=None : return QTDIR
00157 
00158       moc = env.WhereIs('moc-qt4') or env.WhereIs('moc')
00159       if moc:
00160             SCons.Warnings.warn(
00161                   QtdirNotFound,
00162                   "QTDIR variable is not defined, using moc executable as a hint (QTDIR=%s)" % QTDIR)
00163             return os.path.dirname(os.path.dirname(moc))
00164 
00165       SCons.Warnings.warn(
00166             QtdirNotFound,
00167             "Could not detect qt, using empty QTDIR")
00168       return None
00169 
00170 def generate(env):
00171       """Add Builders and construction variables for qt to an Environment."""
00172 
00173       print "Loading qt4 tool..."
00174 
00175       def locateQt4Command(env, command, qtdir) :
00176             fullpath = env.Detect([command+'-qt4', command])
00177             if not (fullpath is None) : return fullpath
00178             fullpath1 = os.path.join(qtdir,'bin',command +'-qt4')
00179             if os.access(fullpath1, os.X_OK) or \
00180                   os.access(fullpath1+".exe", os.X_OK):
00181                   return fullpath1
00182             fullpath2 = os.path.join(qtdir,'bin',command)
00183             if os.access(fullpath2, os.X_OK) or \
00184                   os.access(fullpath2+".exe", os.X_OK):
00185                   return fullpath2
00186             raise "Qt4 command '" + command + "' not found. Tried: " + fullpath1 + " and "+ fullpath2
00187             
00188 
00189       CLVar = SCons.Util.CLVar
00190       Action = SCons.Action.Action
00191       Builder = SCons.Builder.Builder
00192       splitext = SCons.Util.splitext
00193 
00194       # the basics
00195       env['QTDIR']  = _detect(env)
00196       env['QT4_MOC'] = locateQt4Command(env,'moc', env['QTDIR'])
00197       env['QT4_UIC'] = locateQt4Command(env,'uic', env['QTDIR'])
00198       env['QT4_RCC'] = locateQt4Command(env,'rcc', env['QTDIR'])
00199       env['QT4_LUPDATE'] = locateQt4Command(env,'lupdate', env['QTDIR'])
00200       env['QT4_LRELEASE'] = locateQt4Command(env,'lrelease', env['QTDIR'])
00201 
00202       # Should the qt tool try to figure out, which sources are to be moc'ed ?
00203       env['QT4_AUTOSCAN'] = 1
00204 
00205       # Some QT specific flags. I don't expect someone wants to
00206       # manipulate those ...
00207       env['QT4_UICDECLFLAGS'] = CLVar('')
00208       env['QT4_MOCFROMHFLAGS'] = CLVar('')
00209       env['QT4_MOCFROMCXXFLAGS'] = CLVar('-i')
00210       env['QT4_QRCFLAGS'] = ''
00211 
00212       # suffixes/prefixes for the headers / sources to generate
00213       env['QT4_MOCHPREFIX'] = 'moc_'
00214       env['QT4_MOCHSUFFIX'] = '$CXXFILESUFFIX'
00215       env['QT4_MOCCXXPREFIX'] = 'moc_'
00216       env['QT4_MOCCXXSUFFIX'] = '.moc'
00217       env['QT4_UISUFFIX'] = '.ui'
00218       env['QT4_UICDECLPREFIX'] = 'ui_'
00219       env['QT4_UICDECLSUFFIX'] = '.h'
00220       env['QT4_QRCSUFFIX'] = '.qrc',
00221       env['QT4_QRCCXXSUFFIX'] = '$CXXFILESUFFIX'
00222       env['QT4_QRCCXXPREFIX'] = 'qrc_'
00223 
00224       env['QT4_LIB'] = '' # KLUDGE to avoid linking qt3 library
00225 
00226       # Translation builder
00227       tsbuilder = Builder(
00228             action ='$QT4_LUPDATE $SOURCES -ts $TARGETS',
00229             multi=1
00230             )
00231       env.Append( BUILDERS = { 'Ts': tsbuilder } )
00232       qmbuilder = Builder(
00233             action =[
00234                   '$QT4_LRELEASE $SOURCE',
00235                   ],
00236             src_suffix = '.ts',
00237             suffix = '.qm',
00238             single_source = True
00239             )
00240       env.Append( BUILDERS = { 'Qm': qmbuilder } )
00241 
00242       # Resource builder
00243       def scanResources(node, env, path, arg):
00244             contents = node.get_contents()
00245             includes = qrcinclude_re.findall(contents)
00246             return includes
00247       qrcscanner = env.Scanner(name = 'qrcfile',
00248             function = scanResources,
00249             argument = None,
00250             skeys = ['.qrc'])
00251       qrcbuilder = Builder(
00252             action ='$QT4_RCC $QT4_QRCFLAGS $SOURCE -o $TARGET',
00253             source_scanner = qrcscanner,
00254             src_suffix = '$QT4_QRCSUFFIX',
00255             suffix = '$QT4_QRCCXXSUFFIX',
00256             prefix = '$QT4_QRCCXXPREFIX',
00257             single_source = True
00258             )
00259       env.Append( BUILDERS = { 'Qrc': qrcbuilder } )
00260 
00261       # Interface builder
00262       env['QT4_UIC4COM'] = [
00263             CLVar('$QT4_UIC $QT4_UICDECLFLAGS -o ${TARGETS[0]} $SOURCE'),
00264             ]
00265       uic4builder = Builder(
00266             action='$QT4_UIC4COM',
00267             src_suffix='$QT4_UISUFFIX',
00268             suffix='$QT4_UICDECLSUFFIX',
00269             prefix='$QT4_UICDECLPREFIX',
00270             single_source = True
00271             )
00272       env.Append( BUILDERS = { 'Uic4': uic4builder } )
00273 
00274       # Metaobject builder
00275       env['QT4_MOCFROMHCOM'] = (
00276             '$QT4_MOC $QT4_MOCFROMHFLAGS -o ${TARGETS[0]} $SOURCE')
00277       env['QT4_MOCFROMCXXCOM'] = [
00278             CLVar('$QT4_MOC $QT4_MOCFROMCXXFLAGS -o ${TARGETS[0]} $SOURCE'),
00279             Action(_checkMocIncluded,None)]
00280       mocBld = Builder(action={}, prefix={}, suffix={})
00281       for h in header_extensions:
00282             mocBld.add_action(h, '$QT4_MOCFROMHCOM')
00283             mocBld.prefix[h] = '$QT4_MOCHPREFIX'
00284             mocBld.suffix[h] = '$QT4_MOCHSUFFIX'
00285       for cxx in cxx_suffixes:
00286             mocBld.add_action(cxx, '$QT4_MOCFROMCXXCOM')
00287             mocBld.prefix[cxx] = '$QT4_MOCCXXPREFIX'
00288             mocBld.suffix[cxx] = '$QT4_MOCCXXSUFFIX'
00289       env.Append( BUILDERS = { 'Moc4': mocBld } )
00290 
00291       # er... no idea what that was for
00292       static_obj, shared_obj = SCons.Tool.createObjBuilders(env)
00293       static_obj.src_builder.append('Uic4')
00294       shared_obj.src_builder.append('Uic4')
00295 
00296       # We use the emitters of Program / StaticLibrary / SharedLibrary
00297       # to scan for moc'able files
00298       # We can't refer to the builders directly, we have to fetch them
00299       # as Environment attributes because that sets them up to be called
00300       # correctly later by our emitter.
00301       env.AppendUnique(PROGEMITTER =[AutomocStatic],
00302                                SHLIBEMITTER=[AutomocShared],
00303                                LIBEMITTER  =[AutomocStatic],
00304                                # Of course, we need to link against the qt libraries
00305                                CPPPATH=[os.path.join('$QTDIR', 'include')],
00306                                LIBPATH=[os.path.join('$QTDIR', 'lib')],
00307                                LIBS=['$QT4_LIB'])
00308       
00309       import new
00310       method = new.instancemethod(enable_modules, env, SCons.Environment)
00311       env.EnableQt4Modules=method
00312 
00313 def enable_modules(self, modules, debug=False) :
00314       import sys
00315 
00316       validModules = [
00317             'QtCore',
00318             'QtGui',
00319             'QtOpenGL',
00320             'Qt3Support',
00321             # The next modules have not been tested yet so, please
00322             # maybe they require additional work on non Linux platforms
00323             'QtSql',
00324             'QtNetwork',
00325             'QtSvg',
00326             'QtTest',
00327             'QtXml',
00328             'QtUiTools',
00329             ]
00330       pclessModules = [
00331             'QtUiTools',
00332             'QtUiTools_debug',
00333       ]
00334       invalidModules=[]
00335       for module in modules:
00336             if module not in validModules :
00337                   invalidModules.append(module)
00338       if invalidModules :
00339             raise "Modules %s are not Qt4 modules. Valid Qt4 modules are: %s"% \
00340                   (str(invalidModules),str(validModules))
00341 
00342       # TODO: Check whether we should add QT_CORE_LIB, QT_XML_LIB, QT_NETWORK_LIB...
00343       if 'QtGui' in modules:
00344             self.AppendUnique(CPPFLAGS='-DQT_GUI_LIB')
00345 
00346       if sys.platform == "linux2" :
00347             if debug : modules = [module+"_debug" for module in modules]
00348             for module in modules :
00349                   if module in pclessModules :
00350                   #     self.AppendUnique(LIBS=[module])
00351                         self.AppendUnique(LIBPATH=[os.path.join(self["QTDIR"],"lib",module)])
00352                         self.AppendUnique(CPPPATH=[os.path.join(self["QTDIR"],"include","qt4",module)])
00353                         modules.remove(module)
00354             self.ParseConfig('PKG_CONFIG_PATH=%s/lib/pkgconfig pkg-config %s --libs --cflags'%
00355             (
00356                   self['QTDIR'],
00357                   ' '.join(modules)))
00358             return
00359       if sys.platform == "win32" :
00360             if debug : debugSuffix = 'd'
00361             else : debugSuffix = ''
00362             self.AppendUnique(LIBS=[lib+'4'+debugSuffix for lib in modules])
00363             if 'QtOpenGL' in modules:
00364                   self.AppendUnique(LIBS=['opengl32'])
00365             self.AppendUnique(CPPPATH=[ '$QTDIR/include/'+module
00366                   for module in modules])
00367             self.AppendUnique(LIBPATH=['$QTDIR/lib'])
00368 
00369 
00370 def exists(env):
00371       return _detect(env)
00372 

Generated on Thu Dec 21 16:14:41 2006 for The Phrasehunter by  doxygen 1.5.1