QT fixes and enhancements: Moc() and Uic() builders, a lot of new variables. (Chris...
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Mon, 12 Jul 2004 20:33:44 +0000 (20:33 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Mon, 12 Jul 2004 20:33:44 +0000 (20:33 +0000)
git-svn-id: http://scons.tigris.org/svn/scons/trunk@1001 fdb21ef1-2011-0410-befe-b5e4ea1792b1

13 files changed:
doc/man/scons.1
src/CHANGES.txt
src/engine/SCons/Builder.py
src/engine/SCons/BuilderTests.py
src/engine/SCons/Node/FS.py
src/engine/SCons/Script/__init__.py
src/engine/SCons/Tool/__init__.py
src/engine/SCons/Tool/link.py
src/engine/SCons/Tool/qt.py
test/Object.py
test/QT.py
test/QTFLAGS.py
test/import.py

index 947804f6c8f7793c8fb1ead929689f3efa21f11f..35a1619f0a64dd3a444878c68c771078099ec2ab 100644 (file)
@@ -1220,8 +1220,8 @@ env.CFile(target = 'bar', source = 'bar.y')
 '\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
 .IP CXXFile()
 .IP env.CXXFile()
-Builds a C++ source file given a lex (.ll), yacc (.yy)
-or uic (.ui) input file.
+Builds a C++ source file given a lex (.ll) or yacc (.yy)
+input file.
 The suffix specified by the $CXXFILESUFFIX construction variable
 (.cc by default)
 is automatically added to the target
@@ -1404,6 +1404,19 @@ Example:
 env.M4(target = 'foo.c', source = 'foo.c.m4')
 .EE
 
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.IP Moc()
+.IP env.Moc()
+Builds an output file from a moc input file. Moc input files are either 
+header files or cxx files. This builder is only available after using the 
+tool 'qt'. See the QTDIR variable for more information.
+Example:
+
+.ES
+env.Moc('foo.h') # generates moc_foo.cc
+env.Moc('foo.cpp') # generates foo.moc
+.EE
+
 '\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
 .IP MSVSProject()
 .IP env.MSVSProject()
@@ -1834,6 +1847,25 @@ env.TypeLibrary(source="foo.idl")
 .IP
 Will create foo.tlb, foo.h, foo_i.c, foo_p.c, and foo_data.c.
 
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.IP Uic()
+.IP env.Uic()
+Builds a header file, an implementation file and a moc file from an ui file.
+and returns the corresponding nodes in the above order.
+This builder is only available after using the tool 'qt'. Note: you can 
+specify .ui files directly as inputs for Program, Library and SharedLibrary
+without using this builder. Using the builder lets you override the standard
+naming conventions (be careful: prefixes are always prepended to names of
+built files; if you don't want prefixes, you may set them to ``).
+See the QTDIR variable for more information.
+Example:
+
+.ES
+env.Uic('foo.ui') # -> ['foo.h', 'uic_foo.cc', 'moc_foo.cc']
+env.Uic(target = Split('include/foo.h gen/uicfoo.cc gen/mocfoo.cc'),
+        source = 'foo.ui') # -> ['include/foo.h', 'gen/uicfoo.cc', 'gen/mocfoo.cc']
+.EE
+
 '\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
 .IP Zip()
 .IP env.Zip()
@@ -5076,96 +5108,111 @@ are modified. Because the build-performance is affected when using this tool,
 you have to explicitly specify it at Environment creation:
 
 .ES
-Environment(tools=['default','qt']).
+Environment(tools=['default','qt'])
 .EE
-.IP
-You may want to use
-.B Configure
-to verify that the qt support really works.
+
 The qt tool supports the following operations:
 
 .B Automatic moc file generation from header files.
 You do not have to specify moc files explicitly, the tool does it for you.
 However, there are a few preconditions to do so: Your header file must have
 the same filebase as your implementation file and must stay in the same
-directory. It must have one of the suffixes .h, .hpp, .H, .hxx, .hh.
+directory. It must have one of the suffixes .h, .hpp, .H, .hxx, .hh. You 
+can turn off automatic moc file generation by setting QT_AUTOSCAN to 0.
+See also the corresponding builder method
+.B Moc()
 
 .B Automatic moc file generation from cxx files.
-As stated in the qt documentation,
-include the moc file at the end of the cxx file.
-Note that you have to include the file, which is generated by the
-QT_MOCNAMEGENERATOR function. If you are using BuildDir, you may need to
-specify duplicate=1.
+As stated in the qt documentation, include the moc file at the end of 
+the cxx file. Note that you have to include the file, which is generated
+by the transformation ${QT_MOCCXXPREFIX}<basename>${QT_MOCCXXSUFFIX}, by default
+<basename>.moc. A warning is generated after building the moc file, if you 
+do not include the correct file. If you are using BuildDir, you may 
+need to specify duplicate=1. You can turn off automatic moc file generation 
+by setting QT_AUTOSCAN to 0. See also the corresponding builder method
+.B Moc()
 
 .B Automatic handling of .ui files.
 The implementation files generated from .ui files are handled much the same
-as yacc or lex files. Because there are also generated headers, you may
-need to specify duplicate=1 in calls to BuildDir.
+as yacc or lex files. Each .ui file given as a source of Program, Library or
+SharedLibrary will generate three files, the declaration file, the 
+implementation file and a moc file. Because there are also generated headers, 
+you may need to specify duplicate=1 in calls to BuildDir. See also the corresponding builder method
+.B Uic()
+
+.IP QT_AUTOSCAN
+Turn off scanning for mocable files. Use the Moc Builder to explicitely 
+specify files to run moc on.
 
-.IP QT_AUTOBUILD_MOC_SOURCES
-If true, moc-generated sources are automatically compiled into the
-program or library that uses them.  Defaults to 1.
+.IP QT_DEBUG 
+Prints lots of debugging information while scanning for moc files.
 
 .IP QT_LIB
-Default value is 'qt'. You may want to set this to 'qt-mt'
+Default value is 'qt'. You may want to set this to 'qt-mt'.
 
 .IP QT_MOC
 Default value is '$QTDIR/bin/moc'.
 
-.IP QT_UIC
-Default value is '$QTDIR/bin/uic'.
+.IP QT_MOCCXXPREFIX
+Default value is ''. Prefix for moc output files, when source is a cxx file.
 
-.IP QT_UICIMPLFLAGS
-Default value is ''. These flags are passed to uic, when creating a cxx
-file from a .ui file.
+.IP QT_MOCCXXSUFFIX
+Default value is '.moc'. Suffix for moc output files, when source is a cxx 
+file.
 
-.IP QT_UICDECLFLAGS
-Default value is ''. These flags are passed to uic, when creating a a h
-file from a .ui file.
+.IP QT_MOCFROMCPPFLAGS
+Default value is '-i'. These flags are passed to moc, when moccing a
+cpp file.
+
+.IP QT_MOCFROMCXXCOM
+Command to generate a moc file from a cpp file.
+
+.IP QT_MOCFROMHCOM
+Command to generate a moc file from a header.
 
 .IP QT_MOCFROMHFLAGS
 Default value is ''. These flags are passed to moc, when moccing a header
 file.
 
-.IP QT_MOCFROMCPPFLAGS
-Default value is '-i'. These flags are passed to moc, when moccing a
-cpp file.
+.IP QT_MOCHPREFIX
+Default value is 'moc_'. Prefix for moc output files, when source is a header.
 
-.IP QT_HSUFFIX
-Default value is '.h'. Suffix of headers generated with uic.
+.IP QT_MOCHSUFFIX
+Default value is '$CXXFILESUFFIX'. Suffix for moc output files, when source is
+a header.
 
-.IP QT_UISUFFIX
-Default value is '.ui'. Suffix of designer files.
-
-.IP QT_UIHSUFFIX
-Default value is '.ui.h'.
-
-.IP QT_MOCNAMEGENERATOR
-Three-argument function, which generates names of moc output files.
-This is the most flexible way to support the huge number of conventions
-for this type of files. The arguments are the
-.I filebase
-, which is the file to be moc'd without path and extension, the
-.I src_suffix
-, which is the extension of the file to be moc'd and the environment
-.I env
-The default value maps 'myfile.myext' to 'moc_myfile.$CXXFILESUFFIX':
+.IP QT_UIC
+Default value is '$QTDIR/bin/uic'.
 
-.ES
-lambda filebase, src_suffix, env: 'moc_' + filebase + env['CXXFILESUFFIX']
-.EE
+.IP QT_UICDECLCOM
+Command to generate header files from .ui files.
+
+.IP QT_UICDECLFLAGS
+Default value is ''. These flags are passed to uic, when creating a a h
+file from a .ui file.
+
+.IP QT_UICDECLPREFIX
+Default value is ''. Prefix for uic generated header files.
+
+.IP QT_UICDECLSUFFIX
+Default value is '.h'. Suffix for uic generated header files.
 
 .IP QT_UICIMPLCOM
 Command to generate cxx files from .ui files.
 
-.IP QT_UICDECLCOM
-Command to generate header files from .ui files.
+.IP QT_UICIMPLFLAGS
+Default value is ''. These flags are passed to uic, when creating a cxx
+file from a .ui file.
 
-.IP QT_MOCFROMHCOM
-Command to generate a moc file from a header.
+.IP QT_UICIMPLPREFIX
+Default value is 'uic_'. Prefix for uic generated implementation files.
 
-.IP QT_MOCFROMCXXCOM
-Command to generate a moc file from a cpp file.
+.IP QT_UICIMPLSUFFIX
+Default value is '$CXXFILESUFFIX'. Suffix for uic generated implementation 
+files.
+
+.IP QT_UISUFFIX
+Default value is '.ui'. Suffix of designer input files.
 
 .IP RANLIB
 The archive indexer.
@@ -6576,6 +6623,12 @@ Specifies a builder to use when a source file name suffix does not match
 any of the suffixes of the builder. Using this argument produces a
 multi-stage builder.
 
+.IP single_source
+Specifies that this builder expects exactly one source file per call. Giving
+more than one source files without target files results in implicitely calling
+the builder multiple times (once for each source given). Giving multiple 
+source files together with target files results in a UserError exception.
+
 .RE
 .IP
 The 
index 0ecb5269583ed4732bd9a3a3746ff18001a9e943..72748d9e3a7f18684a2c7226bdf39dc21b30b875 100644 (file)
@@ -16,9 +16,6 @@ RELEASE 0.96 - XXX
 
   - Allow construction variable substitutions in $LIBS specifications.
 
-  - Add a $QT_AUTOBUILD_MOC_SOURCES construction variable that controls
-    whether moc-generated .cpp files get compiled.
-
   - Allow the emitter argument to a Builder() to be or expand to a list
     of emitter functions, which will be called in sequence.
 
@@ -420,6 +417,12 @@ RELEASE 0.95 - Mon, 08 Mar 2004 06:43:20 -0600
     the flags from the environment used to specify the target, not
     the environment that first has the Qt Builders attached.
 
+  - Add new Moc() and Uic() Builders for Qt, and a slew of $QT_*
+    construction variables to control them.
+
+  - Add a new single_source keyword argument for Builders that enforces
+    a single source file on calls to the Builder.
+
 
 
 RELEASE 0.94 - Fri, 07 Nov 2003 05:29:48 -0600
index 1dcf84c26fe848e88b52b34b0542467e8d0c9b9b..57f50c9bc9fc4184b3154bd552f528f65e0c6c12 100644 (file)
@@ -311,6 +311,10 @@ def _init_nodes(builder, env, overrides, tlist, slist):
             elif t.sources != slist:
                 raise UserError, "Multiple ways to build the same target were specified for: %s" % str(t)
 
+    if builder.single_source:
+        if len(slist) > 1:
+            raise UserError, "More than one source given for single-source builder: targets=%s sources=%s" % (map(str,tlist), map(str,slist))
+
     # The targets are fine, so find or make the appropriate Executor to
     # build this particular list of targets from this particular list of
     # sources.
@@ -400,6 +404,7 @@ class BuilderBase:
                         emitter = None,
                         multi = 0,
                         env = None,
+                        single_source = 0,
                         **overrides):
         if __debug__: logInstanceCreation(self, 'BuilderBase')
         self.action = SCons.Action.Action(action)
@@ -411,6 +416,7 @@ class BuilderBase:
             suffix = CallableSelector(suffix)
         self.suffix = suffix
         self.env = env
+        self.single_source = single_source
         if overrides.has_key('overrides'):
             SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning,
                 "The \"overrides\" keyword to Builder() creation has been deprecated;\n" +\
@@ -522,6 +528,21 @@ class BuilderBase:
         if source is _null:
             source = target
             target = None
+
+        if(self.single_source and
+           SCons.Util.is_List(source) and
+           len(source) > 1 and
+           target is None):
+            result = []
+            if target is None: target = [None]*len(source)
+            for k in range(len(source)):
+                t = self._execute(env, target[k], source[k], overwarn)
+                if SCons.Util.is_List(t):
+                    result.extend(t)
+                else:
+                    result.append(t)
+            return result
+        
         tlist, slist = self._create_nodes(env, overwarn, target, source)
 
         if len(tlist) == 1:
@@ -605,6 +626,7 @@ class ListBuilder(SCons.Util.Proxy):
         self.env = env
         self.tlist = tlist
         self.multi = builder.multi
+        self.single_source = builder.single_source
 
     def targets(self, node):
         """Return the list of targets for this builder instance.
@@ -639,11 +661,13 @@ class MultiStepBuilder(BuilderBase):
                         source_factory = SCons.Node.FS.default_fs.File,
                         target_scanner = None,
                         source_scanner = None,
-                        emitter=None):
+                        emitter=None,
+                        single_source=0):
         if __debug__: logInstanceCreation(self)
         BuilderBase.__init__(self, action, prefix, suffix, src_suffix,
                              target_factory, source_factory,
-                             target_scanner, source_scanner, emitter)
+                             target_scanner, source_scanner, emitter,
+                             single_source = single_source)
         if not SCons.Util.is_List(src_builder):
             src_builder = [ src_builder ]
         self.src_builder = src_builder
index e592250b4cf1187d23a03e544c5c09b67e0629d9..57e9f2809b47145b7f38a413e7303bb9e35c2c5c 100644 (file)
@@ -524,6 +524,51 @@ class BuilderTestCase(unittest.TestCase):
         tgt = builder(my_env, source = 'f6.zzz')
         assert tgt.path == 'f6.emit', tgt.path
 
+    def test_single_source(self):
+        """Test Builder with single_source flag set"""
+        def func(target, source, env):
+            open(str(target[0]), "w")
+            if (len(source) == 1 and len(target) == 1):
+                env['CNT'][0] = env['CNT'][0] + 1
+                
+        env = Environment()
+        infiles = []
+        outfiles = []
+        for i in range(10):
+            infiles.append(test.workpath('%d.in' % i))
+            outfiles.append(test.workpath('%d.out' % i))
+            test.write(infiles[-1], "\n")
+        builder = SCons.Builder.Builder(action=SCons.Action.Action(func,None),
+                                        single_source = 1, suffix='.out')
+        env['CNT'] = [0]
+        tgt = builder(env, target=outfiles[0], source=infiles[0])
+        tgt.prepare()
+        tgt.build()
+        assert env['CNT'][0] == 1, env['CNT'][0]
+        tgt = builder(env, outfiles[1], infiles[1])
+        tgt.prepare()
+        tgt.build()
+        assert env['CNT'][0] == 2
+        tgts = builder(env, infiles[2:4])
+        for t in tgts: t.prepare()
+        tgts[0].build()
+        tgts[1].build()
+        assert env['CNT'][0] == 4
+        try:
+            tgt = builder(env, outfiles[4], infiles[4:6])
+        except SCons.Errors.UserError:
+            pass
+        else:
+            assert 0
+        try:
+            # The builder may output more than one target per input file.
+            tgt = builder(env, outfiles[4:6], infiles[4:6])
+        except SCons.Errors.UserError:
+            pass
+        else:
+            assert 0
+        
+        
     def test_ListBuilder(self):
         """Testing ListBuilder class."""
         def function2(target, source, env, tlist = [outfile, outfile2], **kw):
index e61878baa63f3184aca037b2f38bcc6e3a9bfe5c..72ed154dbe3394e7c1c3ca373d32ec5e2b48dc6a 100644 (file)
@@ -169,15 +169,16 @@ LocalCopy = SCons.Action.Action(LinkFunc, LocalString)
 
 def UnlinkFunc(target, source, env):
     t = target[0]
-    t.fs.unlink(t.path)
+    t.fs.unlink(t.abspath)
     return 0
 
 Unlink = SCons.Action.Action(UnlinkFunc, None)
 
 def MkdirFunc(target, source, env):
     t = target[0]
-    if not t.fs.exists(t.path):
-        t.fs.mkdir(t.path)
+    p = t.abspath
+    if not t.fs.exists(p):
+        t.fs.mkdir(p)
     return 0
 
 Mkdir = SCons.Action.Action(MkdirFunc, None, presub=None)
index 835409ebdb026a845fd0a1ee86b825c3d4831d32..853a94458b37ca3a10d0044d269839439b46740b 100644 (file)
@@ -96,7 +96,7 @@ class BuildTask(SCons.Taskmaster.Task):
         target = self.targets[0]
         if target.get_state() == SCons.Node.up_to_date:
             if self.top and target.has_builder():
-                display("scons: `%s' is up to date." % str(target))
+                display("scons: `%s' is up to date." % str(self.node))
         elif target.has_builder() and not hasattr(target.builder, 'status'):
             if print_time:
                 start_time = time.time()
index 4364cf3e0ba47380a303324cc835a5b875004681..5753bb92b7b07044aefc4426414fde59c5c7e276 100644 (file)
@@ -174,7 +174,7 @@ def createObjBuilders(env):
                                            prefix = '$OBJPREFIX',
                                            suffix = '$OBJSUFFIX',
                                            src_builder = ['CFile', 'CXXFile'],
-                                           source_scanner = SCons.Defaults.ObjSourceScan)
+                                           source_scanner = SCons.Defaults.ObjSourceScan, single_source=1)
         env['BUILDERS']['StaticObject'] = static_obj
         env['BUILDERS']['Object'] = static_obj
         env['OBJEMITTER'] = SCons.Defaults.StaticObjectEmitter
@@ -187,7 +187,7 @@ def createObjBuilders(env):
                                            prefix = '$SHOBJPREFIX',
                                            suffix = '$SHOBJSUFFIX',
                                            src_builder = ['CFile', 'CXXFile'],
-                                           source_scanner = SCons.Defaults.ObjSourceScan)
+                                           source_scanner = SCons.Defaults.ObjSourceScan, single_source=1)
         env['BUILDERS']['SharedObject'] = shared_obj
         env['SHOBJEMITTER'] = SCons.Defaults.SharedObjectEmitter
 
index cc0d4f4a3ff0f838aa3c95092c17621128bbcb97..8248be9e2dcd6838237387a07f15af9d2d67efe5 100644 (file)
@@ -52,7 +52,9 @@ def generate(env):
     env['SHLINK']      = '$LINK'
     env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -shared')
     env['SHLINKCOM']   = '$SHLINK $SHLINKFLAGS -o $TARGET $SOURCES $_LIBDIRFLAGS $_LIBFLAGS'
-    env['SHLIBEMITTER']= None
+    # don't set up the emitter, cause AppendUnique will generate a list
+    # starting with None :-(
+    #env['SHLIBEMITTER']= None
     env['SMARTLINK']   = smart_link
     env['LINK']        = "$SMARTLINK"
     env['LINKFLAGS']   = SCons.Util.CLVar('')
index 01a5cf6c1eaa0e09e3cd9c83efefd18c2902ccf8..0a45e55160b616566af0a8691fce4344776b81c2 100644 (file)
@@ -41,10 +41,33 @@ import SCons.Defaults
 import SCons.Tool
 import SCons.Util
 
-header_extensions = [".h", ".hxx", ".hpp", ".hh"]
+class ToolQtWarning(SCons.Warnings.Warning):
+    pass
+
+class GeneratedMocFileNotIncluded(ToolQtWarning):
+    pass
+
+class QtdirNotFound(ToolQtWarning):
+    pass
 
+SCons.Warnings.enableWarningClass(ToolQtWarning)
+
+header_extensions = [".h", ".hxx", ".hpp", ".hh"]
 if SCons.Util.case_sensitive_suffixes('.h', '.H'):
     header_extensions.append('.H')
+cplusplus = __import__('c++', globals(), locals(), [])
+cxx_suffixes = cplusplus.CXXSuffixes
+
+def checkMocIncluded(target, source, env):
+    moc = target[0]
+    cpp = source[0]
+    # looks like cpp.includes is cleared before the build stage :-(
+    includes = SCons.Defaults.CScan(cpp, env)
+    if not moc in includes:
+        SCons.Warnings.warn(
+            GeneratedMocFileNotIncluded,
+            "Generated moc file '%s' is not included by '%s'" %
+            (str(moc), str(cpp)))
 
 class _Automoc:
     """
@@ -52,87 +75,105 @@ class _Automoc:
     StaticLibraries.
     """
 
-    def __init__(self, objBuilderName,uicDeclBuild,mocFromHBld,mocFromCppBld):
+    def __init__(self, objBuilderName):
         self.objBuilderName = objBuilderName
-        self.uicDeclBld = uicDeclBuild
-        self.mocFromHBld = mocFromHBld
-        self.mocFromCppBld = mocFromCppBld
         
     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 qt files.
         """
+        try:
+            if int(env.subst('$QT_AUTOSCAN')) == 0:
+                return target, source
+        except ValueError:
+            pass
+        try:
+            debug = int(env.subst('$QT_DEBUG'))
+        except ValueError:
+            debug = 0
+        
+        # some shortcuts used in the scanner
         FS = SCons.Node.FS.default_fs
         splitext = SCons.Util.splitext
-
+        objBuilder = getattr(env, self.objBuilderName)
+  
         # To make the following work, we assume that we stay in the
         # root directory
-        old_os_cwd = os.getcwd()
-        old_fs_cwd = FS.getcwd()
-        FS.chdir(FS.Dir('#'), change_os_dir=1)
-
-        # a regular expression for the Q_OBJECT macro
-        # currently fails, when Q_OBJECT is in comment (e.g. /* Q_OBJECT */)
-        q_object_search = re.compile(r'\sQ_OBJECT[\s;]')
-        # out_sources contains at least all sources for the Library or Prog
+        #old_os_cwd = os.getcwd()
+        #old_fs_cwd = FS.getcwd()
+        #FS.chdir(FS.Dir('#'), change_os_dir=1)
+
+        # some regular expressions:
+        # Q_OBJECT detection
+        q_object_search = re.compile(r'[^A-Za-z0-9]Q_OBJECT[^A-Za-z0-9]') 
+        # cxx and c comment 'eater'
+        comment = re.compile(r'(//.*)|(/\*(([^*])|(\*[^/]))*\*/)')
+
+        # The following is kind of hacky to get builders working properly (FIXME)
+        objBuilderEnv = objBuilder.env
+        objBuilder.env = env
+        mocBuilderEnv = env.Moc.env
+        env.Moc.env = env
+        
+        # make a deep copy for the result; MocH objects will be appended
         out_sources = source[:]
-        for s in source:
-            prefix, suffix = splitext(str(s))
-            # Nodes for header (h) / moc file (moc_cpp) / cpp file (cpp)
-            # and ui.h file (ui_h)
-            cpp = s.sources[0]
-            ui = None
-            if cpp.sources != None and len(cpp.sources) > 0:
-                src_src_suffix = splitext(str(cpp.sources[0]))[1]
-                if src_src_suffix == env.subst('$QT_UISUFFIX'):
-                    ui = cpp.sources[0]
-            
-            src_prefix, src_suffix = splitext(str(cpp.srcnode()))
+
+        for obj in source:
+            if not obj.has_builder():
+                # binary obj file provided
+                if debug:
+                    print "scons: qt: '%s' seems to be a binary. Discarded." % str(obj)
+                continue
+            cpp = obj.sources[0]
+            if not splitext(str(cpp))[1] in cxx_suffixes:
+                if debug:
+                    print "scons: qt: '%s' is no cxx file. Discarded." % str(cpp) 
+                # c or fortran source
+                continue
+            cpp_contents = comment.sub('', cpp.get_contents())
             h=None
             for h_ext in header_extensions:
-                if os.path.exists(src_prefix + h_ext):
-                    h = FS.File(src_prefix + h_ext)
-
-            if ui:
-                # file built from .ui file -> build also header from .ui
-                h = self.uicDeclBld(env, prefix, ui)
-                env.Depends(cpp, h)
-                ui_h_suff = env.subst('$QT_UIHSUFFIX')
-                if os.path.exists(src_prefix + ui_h_suff):
-                    # if a .ui.h file exists, we need to specify the dependecy ...
-                    ui_h = FS.File(src_prefix + ui_h_suff)
-                    env.Depends(cpp, ui_h)
-            if (h and q_object_search.search(h.get_contents())) or ui:
+                # try to find the header file in the corresponding source
+                # directory
+                hname = splitext(cpp.name)[0] + h_ext
+                h = SCons.Node.FS.find_file(hname,
+                                            (cpp.get_dir(),),
+                                            FS.File)
+                if h:
+                    if debug:
+                        print "scons: qt: Scanning '%s' (header of '%s')" % (str(h), str(cpp))
+                    h_contents = comment.sub('', h.get_contents())
+                    break
+            if not h and debug:
+                print "scons: qt: no header for '%s'." % (str(cpp))
+            if h and q_object_search.search(h_contents):
                 # h file with the Q_OBJECT macro found -> add moc_cpp
-                d,base = os.path.split(prefix)
-                src_ext = splitext(str(h))[1]
-                moc_cpp = FS.File(os.path.join(d, 
-                    env['QT_MOCNAMEGENERATOR'](base, src_ext, env)))
-                objBuilder = getattr(env, self.objBuilderName)
-                if env.get('QT_AUTOBUILD_MOC_SOURCES'):
-                    moc_o = objBuilder(source=moc_cpp)
-                    out_sources.append(moc_o)
-                    objBuilder(moc_o, moc_cpp)
-                self.mocFromHBld(env, moc_cpp, h)
+                moc_cpp = env.Moc(h)
+                moc_o = objBuilder(moc_cpp)
+                out_sources.append(moc_o)
                 #moc_cpp.target_scanner = SCons.Defaults.CScan
-            if cpp and q_object_search.search(cpp.get_contents()):
+                if debug:
+                    print "scons: qt: found Q_OBJECT macro in '%s', moc'ing to '%s'" % (str(h), str(moc_cpp))
+            if cpp and q_object_search.search(cpp_contents):
                 # cpp file with Q_OBJECT macro found -> add moc
                 # (to be included in cpp)
-                d,base = os.path.split(prefix)
-                src_ext = splitext(str(cpp))[1]
-                moc = FS.File(os.path.join(d, 
-                    env['QT_MOCNAMEGENERATOR'](base, src_ext, env)))
-                self.mocFromCppBld(env, moc, cpp)
+                moc = env.Moc(cpp)
                 env.Ignore(moc, moc)
+                if debug:
+                    print "scons: qt: found Q_OBJECT macro in '%s', moc'ing to '%s'" % (str(cpp), str(moc))
                 #moc.source_scanner = SCons.Defaults.CScan
+        # restore the original env attributes (FIXME)
+        objBuilder.env = objBuilderEnv
+        env.Moc.env = mocBuilderEnv
 
-        os.chdir(old_os_cwd)
-        FS.chdir(old_fs_cwd)
-        if env.get('QT_AUTOBUILD_MOC_SOURCES'):
-            return (target, out_sources)
-        else:
-            return (target, source)
+        #os.chdir(old_os_cwd)
+        #FS.chdir(old_fs_cwd)
+
+        return (target, out_sources)
+
+AutomocShared = _Automoc('SharedObject')
+AutomocStatic = _Automoc('StaticObject')
 
 def _detect(env):
     """Not really safe, but fast method to detect the QT library"""
@@ -142,85 +183,121 @@ def _detect(env):
     if not QTDIR:
         QTDIR = os.environ.get('QTDIR',None)
     if not QTDIR:
-        moc = env.Detect('moc')
+        moc = env.WhereIs('moc')
         if moc:
             QTDIR = os.path.dirname(os.path.dirname(moc))
+            SCons.Warnings.warn(
+                QtdirNotFound,
+                "Could not detect qt, using moc executable as a hint (QTDIR=%s)" % QTDIR)
         else:
             QTDIR = None
+            SCons.Warnings.warn(
+                QtdirNotFound,
+                "Could not detect qt, using empty QTDIR")
     return QTDIR
 
+
 def generate(env):
     """Add Builders and construction variables for qt to an Environment."""
+    CLVar = SCons.Util.CLVar
+    Action = SCons.Action.Action
+    Builder = SCons.Builder.Builder
+    splitext = SCons.Util.splitext
 
+    # the basics
     env['QTDIR']  = _detect(env)
     env['QT_MOC'] = os.path.join('$QTDIR','bin','moc')
     env['QT_UIC'] = os.path.join('$QTDIR','bin','uic')
-    env['QT_LIB'] = 'qt'
+    env['QT_LIB'] = 'qt' # may be set to qt-mt
 
-    # Should moc-generated sources be automatically compiled?
-    env['QT_AUTOBUILD_MOC_SOURCES'] = 1
+    # Should the qt tool try to figure out, which sources are to be moc'ed ?
+    env['QT_AUTOSCAN'] = 1
 
     # Some QT specific flags. I don't expect someone wants to
     # manipulate those ...
-    env['QT_UICIMPLFLAGS'] = SCons.Util.CLVar('')
-    env['QT_UICDECLFLAGS'] = SCons.Util.CLVar('')
-    env['QT_MOCFROMHFLAGS'] = SCons.Util.CLVar('')
-    env['QT_MOCFROMCXXFLAGS'] = SCons.Util.CLVar('-i')
+    env['QT_UICIMPLFLAGS'] = CLVar('')
+    env['QT_UICDECLFLAGS'] = CLVar('')
+    env['QT_MOCFROMHFLAGS'] = CLVar('')
+    env['QT_MOCFROMCXXFLAGS'] = CLVar('-i')
 
-    # Suffixes for the headers / sources to generate
-    env['QT_HSUFFIX'] = '.h'
+    # suffixes/prefixes for the headers / sources to generate
+    env['QT_UICDECLPREFIX'] = ''
+    env['QT_UICDECLSUFFIX'] = '.h'
+    env['QT_UICIMPLPREFIX'] = 'uic_'
+    env['QT_UICIMPLSUFFIX'] = '$CXXFILESUFFIX'
+    env['QT_MOCHPREFIX'] = 'moc_'
+    env['QT_MOCHSUFFIX'] = '$CXXFILESUFFIX'
+    env['QT_MOCCXXPREFIX'] = ''
+    env['QT_MOCCXXSUFFIX'] = '.moc'
     env['QT_UISUFFIX'] = '.ui'
-    env['QT_UIHSUFFIX'] = '.ui.h'
-    env['QT_MOCNAMEGENERATOR'] = \
-        lambda x, src_suffix, env: 'moc_' + x + env.get('CXXFILESUFFIX','.cc')
+
+    def uicEmitter(target, source, env):
+        adjustixes = SCons.Util.adjustixes
+        bs = SCons.Util.splitext(str(source[0]))[0]
+        # first target (header) is automatically added by builder
+        if len(target) < 2:
+            # second target is implementation
+            target.append(adjustixes(bs,
+                                     env.subst('$QT_UICIMPLPREFIX'),
+                                     env.subst('$QT_UICIMPLSUFFIX')))
+        if len(target) < 3:
+            # third target is moc file
+            target.append(adjustixes(bs,
+                                     env.subst('$QT_MOCHPREFIX'),
+                                     env.subst('$QT_MOCHSUFFIX')))
+        return target, source
 
     # Commands for the qt support ...
-    # command to generate implementation (cpp) file from a .ui file
-    env['QT_UICIMPLCOM'] = ('$QT_UIC $QT_UICIMPLFLAGS -impl '
-                            '${TARGETS[0].filebase}$QT_HSUFFIX '
-                            '-o $TARGET $SOURCES')
-    # command to generate declaration (h) file from a .ui file
-    env['QT_UICDECLCOM'] = ('$QT_UIC $QT_UICDECLFLAGS '
-                            '-o ${TARGETS[0].base}$QT_HSUFFIX $SOURCES')
+    # command to generate header, implementation and moc-file from a .ui file
+    env['QT_UICCOM'] = [
+        CLVar('$QT_UIC $QT_UICDECLFLAGS -o ${TARGETS[0]} $SOURCE'),
+        CLVar('$QT_UIC $QT_UICIMPLFLAGS -impl ${TARGETS[0].file} '
+              '-o ${TARGETS[1]} $SOURCE'),
+        CLVar('$QT_MOC $QT_MOCFROMHFLAGS -o ${TARGETS[2]} ${TARGETS[0]}')]
     # command to generate meta object information for a class declarated
     # in a header
-    env['QT_MOCFROMHCOM'] = '$QT_MOC $QT_MOCFROMHFLAGS -o $TARGET $SOURCE'
-    # command to generate meta object information for a class declatazed
+    env['QT_MOCFROMHCOM'] = (
+        '$QT_MOC $QT_MOCFROMHFLAGS -o ${TARGETS[0]} $SOURCE')
+    # command to generate meta object information for a class declarated
     # in a cpp file
-    env['QT_MOCFROMCXXCOM'] = '$QT_MOC $QT_MOCFROMCXXFLAGS -o $TARGET $SOURCE'
-
+    env['QT_MOCFROMCXXCOM'] = [
+        CLVar('$QT_MOC $QT_MOCFROMCXXFLAGS -o ${TARGETS[0]} $SOURCE'),
+        Action(checkMocIncluded,None)]
     # ... and the corresponding builders
-    uicDeclBld = SCons.Builder.Builder(action='$QT_UICDECLCOM',
-                                       src_suffix='$QT_UISUFFIX',
-                                       suffix='$QT_HSUFFIX')
-    mocFromHBld = SCons.Builder.Builder(action='$QT_MOCFROMHCOM',
-                                        src_suffix='$QT_HSUFFIX',
-                                        suffix='$QT_MOCSUFFIX')
-    mocFromCppBld = SCons.Builder.Builder(action='$QT_MOCFROMCXXCOM',
-                                          src_suffix='$QT_CXXSUFFIX',
-                                          suffix='$QT_MOCSUFFIX')
-
-    # we use CXXFile to generate .cpp files from .ui files
-    c_file, cxx_file = SCons.Tool.createCFileBuilders(env)
-    cxx_file.add_action('$QT_UISUFFIX', '$QT_UICIMPLCOM')
+    uicBld = Builder(action='$QT_UICCOM',
+                     emitter=uicEmitter,
+                     src_suffix='$QT_UISUFFIX',
+                     suffix='$QT_UICDECLSUFFIX',
+                     prefix='$QT_UICDECLPREFIX')
+    mocBld = Builder(action={}, prefix={}, suffix={})
+    for h in header_extensions:
+        mocBld.add_action(h, '$QT_MOCFROMHCOM')
+        mocBld.prefix[h] = '$QT_MOCHPREFIX'
+        mocBld.suffix[h] = '$QT_MOCHSUFFIX'
+    for cxx in cxx_suffixes:
+        mocBld.add_action(cxx, '$QT_MOCFROMCXXCOM')
+        mocBld.prefix[cxx] = '$QT_MOCCXXPREFIX'
+        mocBld.suffix[cxx] = '$QT_MOCCXXSUFFIX'
+
+    # register the builders 
+    env['BUILDERS']['Uic'] = uicBld
+    env['BUILDERS']['Moc'] = mocBld
+    static_obj, shared_obj = SCons.Tool.createObjBuilders(env)
+    static_obj.src_builder.append('Uic')
+    shared_obj.src_builder.append('Uic')
 
     # We use the emitters of Program / StaticLibrary / SharedLibrary
-    # to produce almost all builders except .cpp from .ui
-    # First, make sure the Environment has Object builders.
-    SCons.Tool.createObjBuilders(env)
+    # 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.Append(PROGEMITTER = [_Automoc('StaticObject',
-                                  uicDeclBld,mocFromHBld,mocFromCppBld)],
-               SHLIBEMITTER = [_Automoc('SharedObject',
-                                   uicDeclBld,mocFromHBld,mocFromCppBld)],
-               LIBEMITTER = [_Automoc('StaticObject',
-                                 uicDeclBld,mocFromHBld,mocFromCppBld)])
-    # Of course, we need to link against the qt libraries
-    env.AppendUnique(CPPPATH=[os.path.join('$QTDIR', 'include')])
-    env.AppendUnique(LIBPATH=[os.path.join('$QTDIR', 'lib')])
-    env.AppendUnique(LIBS=['$QT_LIB'])
+    env.AppendUnique(PROGEMITTER =[AutomocStatic],
+                     SHLIBEMITTER=[AutomocShared],
+                     LIBEMITTER  =[AutomocStatic],
+                     # Of course, we need to link against the qt libraries
+                     CPPPATH=[os.path.join('$QTDIR', 'include')],
+                     LIBPATH=[os.path.join('$QTDIR', 'lib')],
+                     LIBS=['$QT_LIB'])
 
 def exists(env):
     return _detect(env)
index cde13917938222da215822d7a9cf6fe2aa43d675..9ed7a1518677a5a85d1204ee377aa7c5704f39ea 100644 (file)
@@ -39,10 +39,11 @@ env = Environment()
 f1 = env.Object(target = 'f1', source = 'f1.c')
 f2 = Object(target = 'f2', source = 'f2.cpp')
 f3 = env.Object(target = 'f3', source = 'f3.c')
-env.Program(target = 'prog1', source = Split('f1%s f2%s f3%s prog.cpp'))
-env.Program(target = 'prog2', source = [f1, f2, f3, 'prog.cpp'])
+mult_o = env.Object(['f4.c', 'f5.c'])
+env.Program(target = 'prog1', source = Split('f1%s f2%s f3%s f4%s prog.cpp'))
+env.Program(target = 'prog2', source = mult_o + [f1, f2, f3, 'prog.cpp'])
 env.Program(target = 'prog3', source = ['f1%s', f2, 'f3%s', 'prog.cpp'])
-""" % (_obj, _obj, _obj, _obj, _obj))
+""" % (_obj, _obj, _obj, _obj, _obj, _obj))
 
 test.write('f1.c', r"""
 void
@@ -70,6 +71,22 @@ f3(void)
 }
 """)
 
+test.write('f4.c', r"""
+void
+f4(void)
+{
+       printf("f4.c\n");
+}
+""")
+
+test.write('f5.c', r"""
+void
+f5(void)
+{
+       printf("f5.c\n");
+}
+""")
+
 test.write('prog.cpp', r"""
 #include <stdio.h>
 
index af4c4af87be3dde6b3683b911613b87aec2925e2..6b378c463176ae1ab2416d44ea457b39a377fa01 100644 (file)
@@ -30,6 +30,7 @@ generation of qt's moc files.
 """
 
 import os.path
+import re
 import string
 
 import TestSCons
@@ -40,6 +41,7 @@ lib_ = TestSCons.lib_
 _lib = TestSCons._lib
 dll_ = TestSCons.dll_
 _dll = TestSCons._dll
+_shobj = TestSCons._shobj
 
 test = TestSCons.TestSCons()
 
@@ -72,6 +74,7 @@ sys.exit(0)
 
 test.write(['qt', 'bin', 'myuic.py'], """
 import sys
+import os.path
 import string
 output_arg = 0
 impl_arg = 0
@@ -92,8 +95,13 @@ for arg in sys.argv[1:]:
         if source:
             sys.exit(1)
         source = open(arg, 'rb')
+        sourceFile = arg
 if impl:
     output.write( '#include "' + impl + '"\\n' )
+    if string.find(source.read(), '// ui.h') != -1:
+        output.write(
+           '#include "' +
+           os.path.basename(os.path.splitext(sourceFile)[0]) + '.ui.h"\\n')
 else:
     output.write( '#include "my_qobject.h"\\n' + source.read() + " Q_OBJECT \\n" )
 output.close()
@@ -130,7 +138,9 @@ QT_UIC = '%s %s' % (python, test.workpath('qt','bin','myuic.py'))
 
 def createSConstruct(test,place):
     test.write(place, """
-env = Environment(QTDIR = r'%s',
+if ARGUMENTS.get('noqtdir', 0): QTDIR=None
+else: QTDIR=r'%s'
+env = Environment(QTDIR = QTDIR,
                   QT_LIB = r'%s',
                   QT_MOC = r'%s',
                   QT_UIC = r'%s',
@@ -149,7 +159,11 @@ SConscript( sconscript )
 """ % (QT, QT_LIB, QT_MOC, QT_UIC))
 
 test.subdir( 'work1', 'work2', 'work3', 'work4',
-             'work5', 'work6', 'work7', 'work8' )
+             'work5', 'work6', 'work7', 'work8',
+             'work9', ['work9', 'local_include'],
+             'work10', ['work10', 'sub'], ['work10', 'sub', 'local_include'],
+             'work11', ['work11', 'include'], ['work11', 'ui'],
+             'work12')
 
 ##############################################################################
 # 1. create a moc file from a header file.
@@ -199,7 +213,8 @@ test.fail_test( not os.path.exists(test.workpath('work1', 'build', moc)) )
 
 aaa_dll = dll_ + 'aaa' + _dll
 moc = 'moc_aaa.cc'
-cpp = 'aaa.cc'
+cpp = 'uic_aaa.cc'
+obj = os.path.splitext(cpp)[0] + _shobj
 h = 'aaa.h'
 
 createSConstruct(test, ['work2', 'SConstruct'])
@@ -209,7 +224,12 @@ env.SharedLibrary(target = 'aaa', source = ['aaa.ui', 'useit.cpp'])
 """)
 
 test.write(['work2', 'aaa.ui'], r"""
-void aaa(void)
+#if defined (_WIN32) || defined(__CYGWIN__)
+#define DLLEXPORT __declspec(dllexport)
+#else
+#define DLLEXPORT
+#endif
+DLLEXPORT void aaa(void)
 """)
 
 test.write(['work2', 'useit.cpp'], r"""
@@ -223,7 +243,12 @@ test.run(chdir='work2', arguments = aaa_dll)
 test.up_to_date(chdir='work2', options='-n',arguments = aaa_dll)
 test.write(['work2', 'aaa.ui'], r"""
 /* a change */
-void aaa(void)
+#if defined (_WIN32) || defined(__CYGWIN__)
+#define DLLEXPORT __declspec(dllexport)
+#else
+#define DLLEXPORT
+#endif
+DLLEXPORT void aaa(void)
 """)
 test.not_up_to_date(chdir='work2', options = '-n', arguments = moc)
 test.not_up_to_date(chdir='work2', options = '-n', arguments = cpp)
@@ -232,7 +257,16 @@ test.run(chdir='work2', arguments = aaa_dll)
 test.write(['work2', 'aaa.ui.h'], r"""
 /* test dependency to .ui.h */
 """)
-test.not_up_to_date(chdir='work2', options = '-n', arguments = cpp)
+test.write(['work2', 'aaa.ui'], r"""
+void aaa(void)
+// ui.h
+""")
+test.run(chdir='work2', arguments = aaa_dll)
+test.write(['work2', 'aaa.ui.h'], r"""
+/* changed */
+""")
+test.not_up_to_date(chdir='work2', options = '-n', arguments = obj)
+test.up_to_date(chdir='work2', options = '-n', arguments = cpp)
 test.up_to_date(chdir='work2', options = '-n', arguments = h)
 test.up_to_date(chdir='work2', options = '-n', arguments = moc)
 
@@ -251,7 +285,7 @@ test.fail_test(not os.path.exists(test.workpath('work2','build',moc)) or
 # 3. create a moc file from a cpp file
 
 lib_aaa = lib_ + 'aaa' + _lib
-moc = 'moc_aaa.cc'
+moc = 'aaa.moc'
 
 createSConstruct(test, ['work3', 'SConstruct'])
 test.write(['work3', 'SConscript'], """
@@ -398,10 +432,13 @@ foo6(void)
 }
 """)
 
-test.run(chdir='work6')
+# we can receive warnings about a non detected qt (empty QTDIR)
+# these are not critical, but maybe annoying
+test.run(chdir='work6', stderr=None)
 
 main_exe = 'main' + _exe
 test.run(program = test.workpath('work6', main_exe),
+         stderr = None,
          stdout = 'qt/include/foo6.h\n')
 
 ##############################################################################
@@ -465,7 +502,7 @@ class MyClass1 : public QObject {
 void mocFromCpp() {
   MyClass1 myclass;
 }
-#include "moc_mocFromCpp.cpp"
+#include "mocFromCpp.moc"
 """)
 
     test.write( ['work7', 'mocFromH.h'],"""
@@ -522,50 +559,287 @@ int main() {
     QTDIR=os.environ['QTDIR']
     del os.environ['QTDIR']
 
-    test.run(chdir='work7', arguments="-c test_realqt" + _exe)
-    test.run(chdir='work7', arguments="PATH=%s/bin test_realqt%s"%(QTDIR,_exe))
+    test.run(chdir='work7', stderr=None, arguments="-c test_realqt" + _exe)
+    test.fail_test(not test.match_re(test.stderr(), r"""
+scons: warning: Could not detect qt, using empty QTDIR
+File "SConstruct", line \d+, in .+
+"""))
+
+    test.run(chdir='work7', stderr=None,
+             arguments="PATH=%s/bin test_realqt%s"%(QTDIR,_exe))
+    
+    test.fail_test(not test.match_re(test.stderr(), r"""
+scons: warning: Could not detect qt, using moc executable as a hint \(QTDIR=%s\)
+File "SConstruct", line \d+, in .+
+""" % (re.escape(QTDIR))))
+
     
 else:
     print "Could not find QT, skipping test(s)."
 
 ##############################################################################
-# 8. test the $QT_AUTOBUUILD_MOC_SOURCES variable
+# 8. test the $QT_AUTOBUILD_MOC_SOURCES variable
+#
+# This has been removed, but I'm leaving the test here, commented out,
+# in case we ever resurrect this functionality again in the future.
+#
+#aaa_dll = dll_ + 'aaa' + _dll
+#moc = 'moc_aaa.cc'
+#
+#createSConstruct(test, ['work8', 'SConstruct'])
+#
+#test.write(['work8', 'SConscript'], """
+#Import("env")
+#env = env.Copy(QT_AUTOBUILD_MOC_SOURCES = 0)
+#env.SharedLibrary(target = 'aaa', source = ['aaa.ui', 'useit.cpp', 'aaa_function.cpp'])
+#""")
+#
+#test.write(['work8', 'aaa.ui'], r"""
+##if defined (_WIN32) || defined(__CYGWIN__)
+##define DLLEXPORT __declspec(dllexport)
+##else
+##define DLLEXPORT
+##endif
+#DLLEXPORT void aaa(void)
+#""")
+#
+#test.write(['work8', 'useit.cpp'], r"""
+##include "aaa.h"
+#void useit() {
+#  aaa();
+#}
+#""")
+#
+#test.write(['work8', 'aaa_function.cpp'], r"""
+##include "my_qobject.h"
+##if defined (_WIN32) || defined(__CYGWIN__)
+##define DLLEXPORT __declspec(dllexport)
+##else
+##define DLLEXPORT
+##endif
+#DLLEXPORT void aaa(void)
+# { my_qt_symbol( "aaa_function.cpp\n" ); }
+#""")
+#
+#test.run(chdir='work8', arguments = aaa_dll)
+#
+#test.must_not_exist(test.workpath('work8', moc))
+#
+#test.write(['work8', 'SConscript'], """
+#Import("env")
+#env = env.Copy(QT_AUTOBUILD_MOC_SOURCES = 1)
+#env.SharedLibrary(target = 'aaa', source = ['aaa.ui', 'useit.cpp'])
+#""")
+#
+#test.run(chdir='work8', arguments = aaa_dll)
+#
+#test.must_exist(test.workpath('work8', moc))
 
-aaa_dll = dll_ + 'aaa' + _dll
-moc = 'moc_aaa.cc'
+##############################################################################
+# 9. test that an overwritten CPPPATH is working with generated files
 
-createSConstruct(test, ['work8', 'SConstruct'])
+# this is basically test 1, but with an additional include
+aaa_exe = 'aaa' + _exe
 
-test.write(['work8', 'SConscript'], """
+createSConstruct(test, ['work9', 'SConstruct'])
+test.write( ['work9', 'SConscript'], """
 Import("env")
-env = env.Copy(QT_AUTOBUILD_MOC_SOURCES = 0)
-env.SharedLibrary(target = 'aaa', source = ['aaa.ui', 'useit.cpp'])
+env.Program(target = 'aaa', source = 'aaa.cpp', CPPPATH=['$CPPPATH', './local_include'])
 """)
 
-test.write(['work8', 'aaa.ui'], r"""
-void aaa(void)
+test.write(['work9', 'aaa.cpp'], r"""
+#include "aaa.h"
+int main() { aaa(); return 0; }
 """)
 
-test.write(['work8', 'useit.cpp'], r"""
+test.write(['work9', 'aaa.h'], r"""
+#include "my_qobject.h"
+#include "local_include.h"
+void aaa(void) Q_OBJECT;
+""")
+
+test.write(['work9', 'local_include', 'local_include.h'], r"""
+/* empty; just needs to be found */
+""")
+
+test.run(chdir='work9', arguments = aaa_exe)
+
+##############################################################################
+# 10. test that an appended relative CPPPATH is working with generated files
+
+# this is basically test 9, but the include path is env.Append-ed and
+# everything goes into sub directory "sub"
+aaa_exe = os.path.join('sub', 'aaa' + _exe)
+
+createSConstruct(test, ['work10', 'SConstruct'])
+test.write( ['work10', 'SConscript'], r"""
+SConscript('sub/SConscript')
+""")
+
+test.write( ['work10', 'sub', 'SConscript'], r"""
+Import("env")
+env.Append(CPPPATH=['./local_include'])
+env.Program(target = 'aaa', source = 'aaa.cpp')
+""")
+
+test.write(['work10', 'sub', 'aaa.cpp'], r"""
 #include "aaa.h"
-void useit() {
-  aaa();
-}
+int main() { aaa(); return 0; }
+""")
+
+test.write(['work10', 'sub', 'aaa.h'], r"""
+#include "my_qobject.h"
+#include "local_include.h"
+void aaa(void) Q_OBJECT;
+""")
+
+test.write(['work10', 'sub', 'local_include', 'local_include.h'], r"""
+/* empty; just needs to be found */
 """)
 
-test.run(chdir='work8', arguments = aaa_dll)
+test.run(chdir='work10', arguments = aaa_exe)
+
+###############################################################################
+# 11. test the manual QT builder calls
 
-test.must_not_exist(test.workpath('work8', moc))
+aaa_exe = 'aaa' + _exe
 
-test.write(['work8', 'SConscript'], """
+createSConstruct(test, ['work11', 'SConstruct'])
+test.write( ['work11', 'SConscript'], r"""
 Import("env")
-env = env.Copy(QT_AUTOBUILD_MOC_SOURCES = 1)
-env.SharedLibrary(target = 'aaa', source = ['aaa.ui', 'useit.cpp'])
+sources = ['aaa.cpp', 'bbb.cpp', 'ddd.cpp', 'eee.cpp', 'main.cpp']
+
+# normal invocation
+sources.append(env.Moc('include/aaa.h'))
+env.Moc('bbb.cpp')
+sources.extend(env.Uic('ui/ccc.ui')[1:])
+
+# manual target specification
+sources.append(env.Moc('moc-ddd.cpp', 'include/ddd.h',
+               QT_MOCHPREFIX='')) # Watch out !
+env.Moc('moc_eee.cpp', 'eee.cpp')
+sources.extend(env.Uic(['include/uic_fff.hpp', 'fff.cpp', 'fff.moc.cpp'],
+                       'ui/fff.ui')[1:])
+
+print map(str,sources)
+env.Program(target='aaa',
+            source=sources,
+            CPPPATH=['$CPPPATH', './include'],
+            QT_AUTOSCAN=0)
+""")
+
+test.write(['work11', 'aaa.cpp'], r"""
+#include "aaa.h"
+""")
+                     
+test.write(['work11', 'include', 'aaa.h'], r"""
+#include "my_qobject.h"
+void aaa(void) Q_OBJECT;
+""")
+
+test.write(['work11', 'bbb.h'], r"""
+void bbb(void);
+""")
+
+test.write(['work11', 'bbb.cpp'], r"""
+#include "my_qobject.h"
+void bbb(void) Q_OBJECT
+#include "bbb.moc"
+""")
+
+test.write(['work11', 'ui', 'ccc.ui'], r"""
+void ccc(void)
 """)
 
-test.run(chdir='work8', arguments = aaa_dll)
+test.write(['work11', 'ddd.cpp'], r"""
+#include "ddd.h"
+""")
+                     
+test.write(['work11', 'include', 'ddd.h'], r"""
+#include "my_qobject.h"
+void ddd(void) Q_OBJECT;
+""")
+
+test.write(['work11', 'eee.h'], r"""
+void eee(void);
+""")
+
+test.write(['work11', 'eee.cpp'], r"""
+#include "my_qobject.h"
+void eee(void) Q_OBJECT
+#include "moc_eee.cpp"
+""")
+
+test.write(['work11', 'ui', 'fff.ui'], r"""
+void fff(void)
+""")
 
-test.must_exist(test.workpath('work8', moc))
+test.write(['work11', 'main.cpp'], r"""
+#include "aaa.h"
+#include "bbb.h"
+#include "ui/ccc.h"
+#include "ddd.h"
+#include "eee.h"
+#include "uic_fff.hpp"
 
+int main() {
+  aaa(); bbb(); ccc(); ddd(); eee(); fff(); return 0;
+}
+""")
+
+test.run(chdir='work11', arguments = aaa_exe)
+
+# normal invocation
+test.must_exist(test.workpath('work11', 'include', 'moc_aaa.cc'))
+test.must_exist(test.workpath('work11', 'bbb.moc'))
+test.must_exist(test.workpath('work11', 'ui', 'ccc.h'))
+test.must_exist(test.workpath('work11', 'ui', 'uic_ccc.cc'))
+test.must_exist(test.workpath('work11', 'ui', 'moc_ccc.cc'))
+
+# manual target spec.
+test.must_exist(test.workpath('work11', 'moc-ddd.cpp'))
+test.must_exist(test.workpath('work11', 'moc_eee.cpp'))
+test.must_exist(test.workpath('work11', 'include', 'uic_fff.hpp'))
+test.must_exist(test.workpath('work11', 'fff.cpp'))
+test.must_exist(test.workpath('work11', 'fff.moc.cpp'))
+
+
+##############################################################################
+# 12. test the tool warings
+createSConstruct(test, ['work12', 'SConstruct'])
+
+test.write(['work12', 'aaa.cpp'], r"""
+#include "my_qobject.h"
+void aaa(void) Q_OBJECT
+""")
+
+test.write(['work12', 'SConscript'], r"""
+Import("env")
+import os
+env.StaticLibrary('aaa.cpp')
+""")
+
+test.run(chdir='work12', stderr=None)
+test.fail_test(not test.match_re(test.stderr(), r"""
+scons: warning: Generated moc file 'aaa.moc' is not included by 'aaa.cpp'
+File .+
+"""))
+
+os.environ['QTDIR'] = QT
+test.run(chdir='work12', arguments='-n noqtdir=1')
+
+# We'd like to eliminate $QTDIR from the environment as follows:
+#       del os.environ['QTDIR']
+# But unfortunately, in at least some versions of Python, the Environment
+# class doesn't implement a __delitem__() method to make the library
+# call to actually remove the deleted variable from the *external*
+# environment, so it only gets removed from the Python dictionary.
+# Consequently, we need to just wipe out its value as follows>
+os.environ['QTDIR'] = ''
+test.run(chdir='work12', stderr=None, arguments='-n noqtdir=1')
+test.fail_test(not test.match_re(test.stderr(), r"""
+scons: warning: Could not detect qt, using empty QTDIR
+File "SConstruct", line \d+, in .+
+"""))
 
 test.pass_test()
index 7964d4437ab2580a4ecc59066ca3849bd0c256e0..867dbfb79d5cd3754f760caabe3301f2eff3a1f5 100644 (file)
@@ -152,17 +152,21 @@ createSConstruct(test, ['SConstruct'],
                     QT_UICDECLFLAGS='-y',
                     QT_MOCFROMHFLAGS='-z',
                     QT_MOCFROMCXXFLAGS='-i -w',
-                    QT_HSUFFIX='.hpp',
-                    QT_MOCNAMEGENERATOR=lambda x,src_suffix,env: x + '.moc.cpp',
-                    QT_UISUFFIX='.des',
-                    QT_UIHSUFFUX='.des.hpp',
-                    CXXFILESUFFIX='.cpp',""")
+                    QT_UICDECLPREFIX='uic-',
+                    QT_UICDECLSUFFIX='.hpp',
+                    QT_UICIMPLPREFIX='',
+                    QT_UICIMPLSUFFIX='.cxx',
+                    QT_MOCHPREFIX='mmm',
+                    QT_MOCHSUFFIX='.cxx',
+                    QT_MOCCXXPREFIX='moc',
+                    QT_MOCCXXSUFFIX='.inl',
+                    QT_UISUFFIX='.myui',""")
 test.write('SConscript',"""
 Import("env")
 env.Program('mytest', ['mocFromH.cpp',
                        'mocFromCpp.cpp',
-                       'an_ui_file.des',
-                       'another_ui_file.des',
+                       'an_ui_file.myui',
+                       'another_ui_file.myui',
                        'main.cpp'])
 """)
 
@@ -178,14 +182,14 @@ test.write('mocFromH.cpp', """
 test.write('mocFromCpp.cpp', """
 #include "my_qobject.h"
 void mocFromCpp() Q_OBJECT
-#include "mocFromCpp.moc.cpp"
+#include "mocmocFromCpp.inl"
 """)
 
-test.write('an_ui_file.des', """
+test.write('an_ui_file.myui', """
 void an_ui_file()
 """)
 
-test.write('another_ui_file.des', """
+test.write('another_ui_file.myui', """
 void another_ui_file()
 """)
 
@@ -195,8 +199,8 @@ test.write('another_ui_file.desc.hpp', """
 
 test.write('main.cpp', """
 #include "mocFromH.hpp"
-#include "an_ui_file.hpp"
-#include "another_ui_file.hpp"
+#include "uic-an_ui_file.hpp"
+#include "uic-another_ui_file.hpp"
 void mocFromCpp();
 
 int main() {
@@ -213,14 +217,29 @@ def _existAll( test, files ):
     return reduce(lambda x,y: x and y,
                   map(os.path.exists,map(test.workpath, files)))
                        
-test.fail_test(not _existAll(test, ['mocFromH.moc.cpp',
-                                    'mocFromCpp.moc.cpp',
-                                    'an_ui_file.cpp',
-                                    'an_ui_file.hpp',
-                                    'an_ui_file.moc.cpp',
-                                    'another_ui_file.cpp',
-                                    'another_ui_file.hpp',
-                                    'another_ui_file.moc.cpp']))
+createSConstruct(test, ['SConstruct'],
+                 """QT_UICIMPLFLAGS='-x',
+                    QT_UICDECLFLAGS='-y',
+                    QT_MOCFROMHFLAGS='-z',
+                    QT_MOCFROMCXXFLAGS='-i -w',
+                    QT_UICDECLPREFIX='uic-',
+                    QT_UICDECLSUFFIX='.hpp',
+                    QT_UICIMPLPREFIX='',
+                    QT_UICIMPLSUFFIX='.cxx',
+                    QT_MOCHPREFIX='mmm',
+                    QT_MOCHSUFFIX='.cxx',
+                    QT_MOCCXXPREFIX='moc',
+                    QT_MOCCXXSUFFIX=`.inl',
+                    QT_UISUFFIX='.myui',""")
+
+test.fail_test(not _existAll(test, ['mmmmocFromH.cxx',
+                                    'mocmocFromCpp.inl',
+                                    'an_ui_file.cxx',
+                                    'uic-an_ui_file.hpp',
+                                    'mmman_ui_file.cxx',
+                                    'another_ui_file.cxx',
+                                    'uic-another_ui_file.hpp',
+                                    'mmmanother_ui_file.cxx']))
 
 def _flagTest(test,fileToContentsStart):
     import string
@@ -229,10 +248,10 @@ def _flagTest(test,fileToContentsStart):
             return 1
     return 0
 
-test.fail_test(_flagTest(test, {'mocFromH.moc.cpp':'/* mymoc.py -z */',
-                                'mocFromCpp.moc.cpp':'/* mymoc.py -w */',
-                                'an_ui_file.cpp':'/* myuic.py -x */',
-                                'an_ui_file.hpp':'/* myuic.py -y */',
-                                'an_ui_file.moc.cpp':'/* mymoc.py -z */'}))
+test.fail_test(_flagTest(test, {'mmmmocFromH.cxx':'/* mymoc.py -z */',
+                                'mocmocFromCpp.inl':'/* mymoc.py -w */',
+                                'an_ui_file.cxx':'/* myuic.py -x */',
+                                'uic-an_ui_file.hpp':'/* myuic.py -y */',
+                                'mmman_ui_file.cxx':'/* mymoc.py -z */'}))
 
 test.pass_test()
index 48ae5466764ce365aacda4fe525c5fcd07859ebb..e7e21f1ca3318ef840ef734e7992ea7445f03401 100644 (file)
@@ -119,7 +119,11 @@ error_output = {
     'icl' : """
 scons: warning: Intel license dir was not found.  Tried using the INTEL_LICENSE_FILE environment variable (), the registry () and the default path (C:\Program Files\Common Files\Intel\Licenses).  Using the default path as a last resort.
 File "SConstruct", line 1, in ?
-"""
+""",
+    'qt' : """
+scons: warning: Could not detect qt, using empty QTDIR
+File "SConstruct", line 1, in ?
+""",
 }
 
 # An SConstruct for importing Tool names that have illegal characters
@@ -138,6 +142,7 @@ import SCons.Tool.%s
 x = SCons.Tool.%s.generate
 """
 
+failures = []
 for tool in tools:
     if tool[0] in '0123456789' or '+' in tool:
         test.write('SConstruct', indirect_import % (tool, tool, tool))
@@ -148,6 +153,8 @@ for tool in tools:
     if stderr != '' and stderr != error_output.get(tool, ''):
         print "Failed importing '%s', stderr:" % tool
         print stderr
-        test.fail_test(1)
+        failures.append[tool]
+
+test.fail_test(len(failures))
 
 test.pass_test()