SConf fixes. (Christoph Wiedemann)
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Fri, 16 May 2003 17:40:05 +0000 (17:40 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Fri, 16 May 2003 17:40:05 +0000 (17:40 +0000)
git-svn-id: http://scons.tigris.org/svn/scons/trunk@686 fdb21ef1-2011-0410-befe-b5e4ea1792b1

doc/man/scons.1
src/engine/SCons/ActionTests.py
src/engine/SCons/Node/FS.py
src/engine/SCons/Platform/posix.py
src/engine/SCons/Platform/win32.py
src/engine/SCons/SConf.py
src/engine/SCons/SConfTests.py
src/engine/SCons/Sig/__init__.py
test/Configure.py

index ec2866975c957de6cc1f9a4d68d9d1cd6ab708a0..056b31e8b867565507afc8404c1d192879a2734e 100644 (file)
@@ -3074,18 +3074,28 @@ The following Checks are predefined.
 goes by and developers contribute new useful tests.)
 
 .TP
-.RI Configure.CheckCHeader( self ", " header )
+.RI Configure.CheckCHeader( self ", " header ", [" include_quotes ])
 Checks if 
 .I header
-is usable in the C-language.
+is usable in the C-language. The optional argument 
+.I include_quotes 
+must be
+a two character string, where the first character denotes the opening
+quote and the second character denotes the closing quote (both default
+to \N'34')
 Returns 1 on success and 0 on failure.
 
 .TP
-.RI Configure.CheckCXXHeader( self ", " header )
+.RI Configure.CheckCXXHeader( self ", " header ", [" include_quotes ])
 Checks if 
 .I header
-is usable in the C++ language.
-Returns 1 on success and 0 on failure.
+is usable in the C++ language. The optional argument 
+.I include_quotes 
+must be
+a two character string, where the first character denotes the opening
+quote and the second character denotes the closing quote (both default
+to \N'34')
+Returns 1 on success and 0 on failure. 
 
 .TP 
 .RI Configure.CheckLib( self ", [" library ", " symbol ", " autoadd ])
index 3e325948e1afc2099b285e27dc1b65cb62c7b6b5..f980fb7a7dc287393b8106c3a6209a85cc0afd00 100644 (file)
@@ -72,6 +72,7 @@ act_py = test.workpath('act.py')
 
 outfile = test.workpath('outfile')
 outfile2 = test.workpath('outfile2')
+pipe_file = test.workpath('pipe.out')
 
 scons_env = SCons.Environment.Environment()
 
@@ -520,20 +521,47 @@ class CommandActionTestCase(unittest.TestCase):
     def test_pipe_execute(self):
         """Test capturing piped output from an action
         """
-        pipe_file = open( test.workpath('pipe.out'), "w" )
+        pipe = open( pipe_file, "w" )
         self.env = Environment(ENV = {'ACTPY_PIPE' : '1'}, PIPE_BUILD = 1,
-                               PSTDOUT = pipe_file, PSTDERR = pipe_file)
+                               PSTDOUT = pipe, PSTDERR = pipe)
         # everything should also work when piping output
         self.test_execute()
         self.env['PSTDOUT'].close()
-        pipe_out = test.read( test.workpath('pipe.out') )
+        pipe_out = test.read( pipe_file )
         if sys.platform == 'win32':
             cr = '\r'
         else:
             cr = ''
-        found = re.findall( "act.py: stdout: executed act.py%s\nact.py: stderr: executed act.py%s\n" % (cr, cr), pipe_out )
+        act_out = "act.py: stdout: executed act.py"
+        act_err = "act.py: stderr: executed act.py"
+        found = re.findall( "%s%s\n%s%s\n" % (act_out, cr, act_err, cr),
+                            pipe_out )
         assert len(found) == 8, found
 
+        # test redirection operators
+        def test_redirect(self, redir):
+            cmd = r'%s %s %s xyzzy %s' % (python, act_py, outfile, redir)
+            pipe = open( pipe_file, "w" )
+            act = SCons.Action.CommandAction(cmd)
+            r = act([], [], self.env.Copy(PSTDOUT = pipe, PSTDERR = pipe))
+            pipe.close()
+            assert r == 0
+            return (test.read(outfile2, 'r'), test.read(pipe_file, 'r'))
+        
+        (redirected, pipe_out) = test_redirect(self,'> %s' % outfile2 )
+        assert redirected == "%s\n" % act_out
+        assert pipe_out == "%s\n" % act_err
+
+        (redirected, pipe_out) = test_redirect(self,'2> %s' % outfile2 )
+        assert redirected == "%s\n" % act_err
+        assert pipe_out == "%s\n" % act_out
+
+        (redirected, pipe_out) = test_redirect(self,'> %s 2>&1' % outfile2 )
+        assert (redirected == "%s\n%s\n" % (act_out, act_err) or
+                redirected == "%s\n%s\n" % (act_err, act_out))
+        assert pipe_out == "" 
+        
+
     def test_set_handler(self):
         """Test setting the command handler...
         """
index 071a1d29c49324a5926eb3d3f30efce931c91f8e..ff4dfb5cfee5893672e912663097d5ff3da7aa37 100644 (file)
@@ -1041,7 +1041,7 @@ class Dir(Entry):
         creating it first if necessary."""
         if not self._sconsign:
             import SCons.Sig
-            self._sconsign = SCons.Sig.SConsignFile(self)
+            self._sconsign = SCons.Sig.SConsignFileFactory(self)
         return self._sconsign
 
     def srcnode(self):
index 03c86984bc24872d57fe87ec114270c92620cd2f..3546a174a702ec56f7be17988701dfaa06f0491f 100644 (file)
@@ -98,33 +98,18 @@ def piped_env_spawn(sh, escape, cmd, args, env, stdout, stderr):
     # the command name and the command's stdout is written to stdout
     # the command's stderr is written to stderr
     s = _get_env_command( sh, escape, cmd, args, env)
-    # write the command line out
-    if stdout != None:
-        stdout.write(string.join(args) + '\n')
     proc = popen2.Popen3(s, 1)
     # process stdout
     if stdout != None:
-        #for line in proc.fromchild.xreadlines():
-        #    stdout.write(line)
-        while 1:
-            line = proc.fromchild.readline()
-            if not line:
-                break
-            stdout.write(line)
+        stdout.write(proc.fromchild.read())
     # process stderr
     if stderr != None:
-        #for line in proc.childerr.xreadlines():
-        #    stderr.write(line)
-        while 1:
-            line = proc.childerr.readline()
-            if not line:
-                break
-            stderr.write(line)
+        stderr.write(proc.childerr.read())
     stat = proc.wait()
     if stat & 0xff:
         return stat | 0x80
     return stat >> 8
-    
+
 def piped_fork_spawn(sh, escape, cmd, args, env, stdout, stderr):
     # spawn using fork / exec and providing a pipe for the command's
     # stdout / stderr stream
@@ -135,9 +120,6 @@ def piped_fork_spawn(sh, escape, cmd, args, env, stdout, stderr):
         (rFdOut, wFdOut) = os.pipe()
         rFdErr = rFdOut
         wFdErr = wFdOut
-    # write the command line out
-    if stdout != None:
-        stdout.write(string.join(args) + '\n')
     # do the fork
     pid = os.fork()
     if not pid:
@@ -163,7 +145,7 @@ def piped_fork_spawn(sh, escape, cmd, args, env, stdout, stderr):
         pid, stat = os.waitpid(pid, 0)
         os.close( wFdOut )
         if stdout != stderr:
-            os.close( wFdErr )        
+            os.close( wFdErr )
         childOut = os.fdopen( rFdOut )
         if stdout != stderr:
             childErr = os.fdopen( rFdErr )
@@ -171,22 +153,10 @@ def piped_fork_spawn(sh, escape, cmd, args, env, stdout, stderr):
             childErr = childOut
         # process stdout
         if stdout != None:
-            #for line in childOut.xreadlines():
-            #    stdout.write(line)
-            while 1:
-                line = childOut.readline()
-                if not line:
-                    break
-                stdout.write(line)
+            stdout.write( childOut.read() )
         # process stderr
         if stderr != None:
-            #for line in childErr.xreadlines():
-            #    stderr.write(line)
-            while 1:
-                line = childErr.readline()
-                if not line:
-                    break
-                stdout.write(line)
+            stderr.write( childErr.read() )
         os.close( rFdOut )
         if stdout != stderr:
             os.close( rFdErr )
@@ -194,8 +164,8 @@ def piped_fork_spawn(sh, escape, cmd, args, env, stdout, stderr):
             return stat | 0x80
         return stat >> 8
 
-           
-    
+
+
 def generate(env):
 
     # If the env command exists, then we can use os.system()
index b36d6117e5c05ec24b3180e2fcc4267d7b09ea4f..bce50f493d0a4e3c99d1843fb0bae1891d9f2d87 100644 (file)
@@ -85,45 +85,62 @@ class TempFileMunge:
 # scons.
 
 def piped_spawn(sh, escape, cmd, args, env, stdout, stderr):
+    # There is no direct way to do that in python. What we do
+    # here should work for most cases:
+    #   In case stdout (stderr) is not redirected to a file,
+    #   we redirect it into a temporary file tmpFileStdout
+    #   (tmpFileStderr) and copy the contents of this file
+    #   to stdout (stderr) given in the argument
     if not sh:
         sys.stderr.write("scons: Could not find command interpreter, is it in your PATH?\n")
         return 127
     else:
-        # NOTE: This is just a big, big hack.  What we do is simply pipe the
-        # output to a temporary file and then write it to the streams.
-        # I DO NOT know the effect of adding these to a command line that
-        # already has indirection symbols.
-        tmpFile = os.path.normpath(tempfile.mktemp())
-        args.append(">" + str(tmpFile))
-        args.append("2>&1")
-        if stdout != None:
-            # ToDo: use the printaction instead of that
-            stdout.write(string.join(args) + "\n")
+        # one temporary file for stdout and stderr
+        tmpFileStdout = os.path.normpath(tempfile.mktemp())
+        tmpFileStderr = os.path.normpath(tempfile.mktemp())
+
+        # check if output is redirected
+        stdoutRedirected = 0
+        stderrRedirected = 0
+        for arg in args:
+            # are there more possibilities to redirect stdout ?
+            if (string.find( arg, ">", 0, 1 ) != -1 or
+                string.find( arg, "1>", 0, 2 ) != -1):
+                stdoutRedirected = 1
+            # are there more possibilities to redirect stderr ?
+            if string.find( arg, "2>", 0, 2 ) != -1:
+                stderrRedirected = 1
+
+        # redirect output of non-redirected streams to our tempfiles
+        if stdoutRedirected == 0:
+            args.append(">" + str(tmpFileStdout))
+        if stderrRedirected == 0:
+            args.append("2>" + str(tmpFileStderr))
+
+        # actually do the spawn
         try:
-            try:
-                args = [sh, '/C', escape(string.join(args)) ]
-                ret = os.spawnve(os.P_WAIT, sh, args, env)
-            except OSError, e:
-                ret = exitvalmap[e[0]]
+            args = [sh, '/C', escape(string.join(args)) ]
+            ret = os.spawnve(os.P_WAIT, sh, args, env)
+        except OSError, e:
+            # catch any error
+            ret = exitvalmap[e[0]]
+            if stderr != None:
                 stderr.write("scons: %s: %s\n" % (cmd, e[1]))
+        # copy child output from tempfiles to our streams
+        # and do clean up stuff
+        if stdout != None and stdoutRedirected == 0:
             try:
-                input = open( tmpFile, "r" )
-                while 1:
-                    line = input.readline()
-                    if not line:
-                        break
-                    if stdout != None:
-                        stdout.write(line)
-                    if stderr != None and stderr != stdout:
-                        stderr.write(line)
-            finally:
-                input.close()
-        finally:
-            try:
-                os.remove( tmpFile )
-            except OSError:
-                # What went wrong here ??
+                stdout.write(open( tmpFileStdout, "r" ).read())
+                os.remove( tmpFileStdout )
+            except:
                 pass
+
+        if stderr != None and stderrRedirected == 0:
+            try:
+                stderr.write(open( tmpFileStderr, "r" ).read())
+                os.remove( tmpFileStderr )
+            except:
+                    pass
         return ret
 
 def spawn(sh, escape, cmd, args, env):
index 830b94a78b90a722e6d43e76ec23c5481e2499f3..1546fb119c39b9d31707147c66fd0cd716229b1f 100644 (file)
@@ -32,6 +32,7 @@ import cPickle
 import os
 import shutil
 import sys
+import traceback
 from types import *
 
 import SCons.Action
@@ -42,6 +43,10 @@ import SCons.Taskmaster
 import SCons.Util
 import SCons.Warnings
 
+# First i thought of using a different filesystem as the default_fs,
+# but it showed up that there are too many side effects in doing that.
+SConfFS=SCons.Node.FS.default_fs
+
 _ac_build_counter = 0
 _ac_config_counter = 0
 _activeSConfObjects = {}
@@ -50,12 +55,17 @@ class SConfWarning(SCons.Warnings.Warning):
     pass
 SCons.Warnings.enableWarningClass( SConfWarning )
 
-
+# action to create the source
 def _createSource( target, source, env ):
-    fd = open(str(target[0]), "w")
+    fd = open(target[0].get_path(), "w")
     fd.write(env['SCONF_TEXT'])
     fd.close()
 
+def _stringSource( target, source, env ):
+    import string
+    return (target[0].get_path() + ' <- \n  |' +
+            string.replace( env['SCONF_TEXT'], "\n", "\n  |" ) )
+
 
 class SConf:
     """This is simply a class to represent a configure context. After
@@ -71,19 +81,22 @@ class SConf:
     """
 
     def __init__(self, env, custom_tests = {}, conf_dir='#/.sconf_temp',
-                 log_file='#config.log'):
+                 log_file='#/config.log'): 
         """Constructor. Pass additional tests in the custom_tests-dictinary,
         e.g. custom_tests={'CheckPrivate':MyPrivateTest}, where MyPrivateTest
         defines a custom test.
         Note also the conf_dir and log_file arguments (you may want to
         build tests in the BuildDir, not in the SourceDir)
         """
+        global SConfFS
+        if not SConfFS:
+            SConfFS = SCons.Node.FS.FS(SCons.Node.FS.default_fs.pathTop)
         if len(_activeSConfObjects.keys()) > 0:
             raise (SCons.Errors.UserError,
                    "Only one SConf object may be active at one time")
         self.env = env
         if log_file != None:
-            self.logfile = SCons.Node.FS.default_fs.File(log_file)
+            self.logfile = SConfFS.File(log_file)
         else:
             self.logfile = None
         self.logstream = None
@@ -98,7 +111,8 @@ class SConf:
                }
         self.AddTests(default_tests)
         self.AddTests(custom_tests)
-        self.confdir = SCons.Node.FS.default_fs.Dir(conf_dir)
+        self.confdir = SConfFS.Dir(conf_dir)
+        self.calc = None
         self.cache = {}
         self._startup()
 
@@ -110,7 +124,7 @@ class SConf:
         self._shutdown()
         return self.env
 
-    def setCache(self, nodes, already_done = []):
+    def _setCache(self, nodes, already_done = []):
         # Set up actions used for caching errors
         # Caching positive tests should not be necessary, cause
         # the build system knows, if test objects/programs/outputs
@@ -122,10 +136,12 @@ class SConf:
             # tests
             if (n.has_builder() and
                 not n in already_done):
-                    n.add_pre_action(SCons.Action.Action(self._preCache))
-                    n.add_post_action(SCons.Action.Action(self._postCache))
-                    already_done.append( n )
-            self.setCache(n.children())
+                n.add_pre_action(SCons.Action.Action(self._preCache,
+                                                     self._stringCache))
+                n.add_post_action(SCons.Action.Action(self._postCache,
+                                                      self._stringCache))
+                already_done.append( n )
+            self._setCache(n.children())
 
     def BuildNodes(self, nodes):
         """
@@ -147,24 +163,25 @@ class SConf:
             sys.stdout = self.logstream
             oldStderr = sys.stderr
             sys.stderr = self.logstream
-
-        self.setCache( nodes )
+        self._setCache( nodes ) 
         ret = 1
 
         try:
-            oldPwd = SCons.Node.FS.default_fs.getcwd()
-            SCons.Node.FS.default_fs.chdir(SCons.Node.FS.default_fs.Top)
             # ToDo: use user options for calc
-            calc = SCons.Sig.Calculator(max_drift=0)
+            self.calc = SCons.Sig.Calculator(max_drift=0)
             tm = SCons.Taskmaster.Taskmaster( nodes,
                                               SConfBuildTask,
-                                              calc )
+                                              self.calc )
             # we don't want to build tests in parallel
             jobs = SCons.Job.Jobs(1, tm )
             try:
                 jobs.run()
+            except SCons.Errors.BuildError, e:
+                sys.stderr.write("scons: *** [%s] %s\n" % (e.node, e.errstr))
+                if e.errstr == 'Exception':
+                    traceback.print_exception(e.args[0], e.args[1], e.args[2])
             except:
-                pass
+                raise
 
             for n in nodes:
                 state = n.get_state()
@@ -172,7 +189,6 @@ class SConf:
                     state != SCons.Node.up_to_date):
                     # the node could not be built. we return 0 in this case
                     ret = 0
-            SCons.Node.FS.default_fs.chdir(oldPwd)
         finally:
             if self.logstream != None:
                 # restore stdout / stderr
@@ -189,9 +205,10 @@ class SConf:
 
         nodesToBeBuilt = []
 
-        #target = self.confdir.File("conftest_" + str(_ac_build_counter))
         f = "conftest_" + str(_ac_build_counter)
-        target = os.path.join(str(self.confdir), f)
+        pref = self.env.subst( builder.builder.prefix )
+        suff = self.env.subst( builder.builder.suffix )
+        target = self.confdir.File(pref + f + suff)
         self.env['SCONF_TEXT'] = text
         if text != None:
             source = self.confdir.File(f + extension)
@@ -244,11 +261,7 @@ class SConf:
         compilation was successful, 0 otherwise. The target is saved in
         self.lastTarget (for further processing).
         """
-        #ok = self.TryCompile( text, extension)
-        #if( ok ):
         return self.TryBuild(self.env.Program, text, extension )
-        #else:
-        #    return 0
 
     def TryRun(self, text, extension ):
         """Compiles and runs the program given in text, using extension
@@ -259,9 +272,8 @@ class SConf:
         ok = self.TryLink(text, extension)
         if( ok ):
             prog = self.lastTarget
-            output = SCons.Node.FS.default_fs.File(str(prog)+'.out')
-            node = self.no_pipe_env.Command(output, prog, "%s >%s" % (str(prog),
-                                                              str(output)))
+            output = SConfFS.File(prog.get_path()+'.out')
+            node = self.env.Command(output, prog, "%s > $TARGET" % (prog.get_path()))
             ok = self.BuildNodes([node])
             if ok:
                 outputStr = output.get_contents()
@@ -300,26 +312,26 @@ class SConf:
         # We record errors in the cache. Only non-exisiting targets may
         # have recorded errors
         needs_rebuild = target[0].exists()
-        buildSig = target[0].builder.action.get_contents(target, source, env)
+        buildSig = target[0].calc_signature(self.calc)
         for node in source:
             if node.get_state() != SCons.Node.up_to_date:
                 # if any of the sources has changed, we cannot use our cache
                 needs_rebuild = 1
-        if not self.cache.has_key( str(target[0]) ):
+        if not self.cache.has_key( target[0].get_path() ):
             # We have no recorded error, so we try to build the target
             needs_rebuild = 1
         else:
-            lastBuildSig = self.cache[str(target[0])]['builder']
+            lastBuildSig = self.cache[target[0].get_path()]['builder']
             if lastBuildSig != buildSig:
                 needs_rebuild = 1
         if not needs_rebuild:
             # When we are here, we can savely pass the recorded error
             print ('(cached): Building "%s" failed in a previous run.' %
-                   str(target[0]))
+                   target[0].get_path())
             return 1
         else:
             # Otherwise, we try to record an error
-            self.cache[str(target[0])] = {
+            self.cache[target[0].get_path()] = {
                'builder' :  buildSig
             }
 
@@ -327,35 +339,36 @@ class SConf:
         # Action after target is successfully built
         #
         # No error during build -> remove the recorded error
-        del self.cache[str(target[0])]
+        del self.cache[target[0].get_path()]
+
+    def _stringCache(self, target, source, env):
+        return None
 
     def _loadCache(self):
         # try to load build-error cache
         try:
-            cacheDesc = cPickle.load(open(str(self.confdir.File(".cache"))))
+            cacheDesc = cPickle.load(open(self.confdir.File(".cache").get_path()))
             if cacheDesc['scons_version'] != SCons.__version__:
                 raise Exception, "version mismatch"
             self.cache = cacheDesc['data']
         except:
             self.cache = {}
-            #SCons.Warnings.warn( SConfWarning,
-            #                     "Couldn't load SConf cache (assuming empty)" )
 
     def _dumpCache(self):
         # try to dump build-error cache
         try:
             cacheDesc = {'scons_version' : SCons.__version__,
                          'data'          : self.cache }
-            cPickle.dump(cacheDesc, open(str(self.confdir.File(".cache")),"w"))
-        except:
-            SCons.Warnings.warn( SConfWarning,
-                                 "Couldn't dump SConf cache" )
-
-    def createDir(self, node):
-        if not node.up().exists():
-            self.createDir( node.up() )
-        if not node.exists():
-            SCons.Node.FS.Mkdir(node, None, self.env)
+            cPickle.dump(cacheDesc, open(self.confdir.File(".cache").get_path(),"w"))
+        except Exception, e:
+            # this is most likely not only an IO error, but an error
+            # inside SConf ...
+            SCons.Warnings.warn( SConfWarning, "Couldn't dump SConf cache" )
+
+    def _createDir( self, node ):
+        dirName = node.get_path()
+        if not os.path.isdir( dirName ):
+            os.makedirs( dirName )
             node._exists = 1
 
     def _startup(self):
@@ -364,23 +377,13 @@ class SConf:
         """
         global _ac_config_counter
         global _activeSConfObjects
-
-        #def createDir( node, self = self ):
-        #    if not node.up().exists():
-        #        createDir( node.up() )
-        #    if not node.exists():
-        #        SCons.Node.FS.Mkdir(node, None, self.env)
-        #        node._exists = 1
-        self.createDir(self.confdir)
-        # we don't want scons to build targets confdir automatically
-        # cause we are doing it 'by hand'
+        global SConfFS
+        
+        self.lastEnvFs = self.env.fs
+        self.env.fs = SConfFS
+        self._createDir(self.confdir)
         self.confdir.up().add_ignore( [self.confdir] )
-        self.confdir.set_state( SCons.Node.up_to_date )
-
-        self.no_pipe_env = self.env.Copy()
 
-        # piped spawn will print its own actions (CHANGE THIS!)
-        SCons.Action.print_actions = 0
         if self.logfile != None:
             # truncate logfile, if SConf.Configure is called for the first time
             # in a build
@@ -388,7 +391,7 @@ class SConf:
                 log_mode = "w"
             else:
                 log_mode = "a"
-            self.logstream = open(str(self.logfile), log_mode)
+            self.logstream = open(self.logfile.get_path(), log_mode)
             # logfile may stay in a build directory, so we tell
             # the build system not to override it with a eventually
             # existing file with the same name in the source directory
@@ -396,10 +399,17 @@ class SConf:
             self.env['PIPE_BUILD'] = 1
             self.env['PSTDOUT'] = self.logstream
             self.env['PSTDERR'] = self.logstream
-        else:
+
+            tb = traceback.extract_stack()[-3]
+            
+            self.logstream.write( '\nfile %s,line %d:\n\tConfigure( confdir = %s )\n\n' %
+                                  (tb[0], tb[1], self.confdir.get_path()) )
+        else: 
             self.logstream = None
         # we use a special builder to create source files from TEXT
-        action = SCons.Action.Action(_createSource,varlist=['SCONF_TEXT'])
+        action = SCons.Action.Action(_createSource,
+                                     _stringSource,
+                                     varlist=['SCONF_TEXT'])
         sconfSrcBld = SCons.Builder.Builder(action=action)
         self.env.Append( BUILDERS={'SConfSourceBuilder':sconfSrcBld} )
         self.active = 1
@@ -414,8 +424,6 @@ class SConf:
 
         if not self.active:
             raise SCons.Errors.UserError, "Finish may be called only once!"
-        # Piped Spawn print its own actions. CHANGE THIS!
-        SCons.Action.print_actions = 1
         if self.logstream != None:
             self.logstream.close()
             self.logstream = None
@@ -430,7 +438,7 @@ class SConf:
         self.active = 0
         del _activeSConfObjects[self]
         self._dumpCache()
-
+        self.env.fs = self.lastEnvFs
 
 class CheckContext:
     """Provides a context for configure tests. Defines how a test writes to the
@@ -507,27 +515,34 @@ class CheckContext:
     def __getattr__( self, attr ):
         if( attr == 'env' ):
             return self.sconf.env
+        elif( attr == 'lastTarget' ):
+            return self.sconf.lastTarget
         else:
             raise AttributeError, "CheckContext instance has no attribute '%s'" % attr
 
-def CheckCHeader(test, header):
+def _header_prog( header, include_quotes ):
+    return "#include %s%s%s\n\n" % (include_quotes[0],
+                                    header,
+                                    include_quotes[1])
+
+def CheckCHeader(test, header, include_quotes='""'):
     """
     A test for a c header file.
     """
     # ToDo: Support also system header files (i.e. #include <header.h>)
-    test.Message("Checking for C header %s... " % header)
-    ret = test.TryCompile("#include \"%s\"\n\n" % header, ".c")
+    test.Message("Checking for C header %s ... " % header)
+    ret = test.TryCompile(_header_prog(header, include_quotes), ".c")
     test.Result( ret )
     return ret
 
 
-def CheckCXXHeader(test, header):
+def CheckCXXHeader(test, header, include_quotes='""'):
     """
     A test for a c++ header file.
     """
     # ToDo: Support also system header files (i.e. #include <header.h>)
-    test.Message("Checking for C header %s... " % header)
-    ret = test.TryCompile("#include \"%s\"\n\n" % header, ".cpp")
+    test.Message("Checking for C++ header %s ... " % header)
+    ret = test.TryCompile(_header_prog(header, include_quotes), ".cpp")
     test.Result( ret )
     return ret
 
@@ -538,7 +553,7 @@ def CheckLib(test, library=None, symbol="main", autoadd=1):
     compiles without flags.
     """
     # ToDo: accept path for the library
-    test.Message("Checking for %s in library %s... " % (symbol, library))
+    test.Message("Checking for %s in library %s ... " % (symbol, library))
     oldLIBS = test.env.get( 'LIBS', [] )
 
     # NOTE: we allow this at in the case that we don't know what the
@@ -590,6 +605,7 @@ def CheckLibWithHeader(test, library, header, language, call="main();", autoadd=
 #include "%s"
 int main() {
   %s
+  return 0;
 }
 """ % (header, call)
 
index 0b737c05971f76f22e8a8a109fa779e8bc037234..cd4a31f9c64482aefc680cedb005fe2064d9d912 100644 (file)
 
 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 
+import os
 import re
+import string
 import StringIO
 import sys
+from types import *
 import unittest
 
 import TestCmd
 
-import SCons.Environment
-import SCons.SConf
-
-scons_env = SCons.Environment.Environment()
 sys.stdout = StringIO.StringIO()
 
 if sys.platform == 'win32':
@@ -41,84 +40,125 @@ if sys.platform == 'win32':
 else:
     existing_lib = "m"
 
-def clearFileCache(dir):
-    # mostly from FS.Dir.__clearRepositoryCache, but set also state
-    # of nodes to None
-    for node in dir.entries.values():
-        if node != dir.dir:
-            if node != dir and isinstance(node, SCons.Node.FS.Dir):
-                clearFileCache(node)
-            else:
-                node._srcreps = None
-                del node._srcreps
-                node._rfile = None
-                del node._rfile
-                node._rexists = None
-                del node._rexists
-                node._exists = None
-                del node._exists
-                node._srcnode = None
-                del node._srcnode
-                node.set_state(None)
-
 class SConfTestCase(unittest.TestCase):
 
     def setUp(self):
-        self.test = TestCmd.TestCmd(workdir = '')
+        # we always want to start with a clean directory
+        self.test = TestCmd.TestCmd(workdir = '') 
 
     def tearDown(self):
         self.test.cleanup()
 
     def _resetSConfState(self):
-                        
-        clearFileCache( SCons.Node.FS.default_fs.Dir(self.test.workpath()) )
-        SCons.SConf._ac_config_counter = 0
-        SCons.SConf._ac_build_counter = 0
-            
+        # Ok, this is tricky, and i do not know, if everything is sane.
+        # We try to reset scons' state (including all global variables)
+        import SCons.Sig
+        SCons.Sig.write() # simulate normal scons-finish
+        for n in sys.modules.keys():
+            if string.split(n, '.')[0] == 'SCons':
+                m = sys.modules[n]
+                if type(m) is ModuleType:
+                    # if this is really a scons module, clear its namespace
+                    del sys.modules[n]
+                    m.__dict__.clear()
+        # we only use SCons.Environment and SCons.SConf for these tests.
+        import SCons.Environment
+        import SCons.SConf
+        self.Environment = SCons.Environment
+        self.SConf = SCons.SConf
+        # and we need a new environment, cause references may point to
+        # old modules (well, at least this is safe ...)
+        self.scons_env = self.Environment.Environment()
+        self.scons_env['ENV']['PATH'] = os.environ['PATH']
+
+        # we want to do some autodetection here
+        # this stuff works with
+        #    - cygwin on win32 (using cmd.exe, not bash)
+        #    - posix
+        #    - msvc on win32 (hopefully)
+        if self.scons_env.subst('$CXX') == 'c++':
+            # better use g++ (which is normally no symbolic link
+            # --> the c++ call fails on cygwin
+            self.scons_env['CXX'] = 'g++'
+        if self.scons_env.subst('$LINK') == 'c++':
+            self.scons_env['LINK'] = 'g++'
+        if (not self.scons_env.Detect( self.scons_env.subst('$CXX') ) or
+            not self.scons_env.Detect( self.scons_env.subst('$CC') ) or
+            not self.scons_env.Detect( self.scons_env.subst('$LINK') )):
+            raise Exception, "This test needs an installed compiler!"
+        if self.scons_env['LINK'] == 'g++':
+            global existing_lib
+            existing_lib = 'm'
         
     def _baseTryXXX(self, TryFunc):
-        def checks(sconf, TryFunc):
+        # TryCompile and TryLink are much the same, so we can test them
+        # in one method, we pass the function as a string ('TryCompile',
+        # 'TryLink'), so we are aware of reloading modules.
+        
+        def checks(self, sconf, TryFuncString):
+            TryFunc = self.SConf.SConf.__dict__[TryFuncString]
             res1 = TryFunc( sconf, "int main() { return 0; }", ".c" )
-            res2 = TryFunc( sconf, "not a c program", ".c" )
+            res2 = TryFunc( sconf,
+                            '#include "no_std_header.h"\nint main() {return 0; }',
+                            '.c' )
             return (res1,res2)
-        
+
+        # 1. test initial behaviour (check ok / failed)
         self._resetSConfState()
-        sconf = SCons.SConf.SConf(scons_env,
-                                  conf_dir=self.test.workpath('config.tests'),
-                                  log_file=self.test.workpath('config.log'))
+        sconf = self.SConf.SConf(self.scons_env,
+                                 conf_dir=self.test.workpath('config.tests'),
+                                 log_file=self.test.workpath('config.log'))
         try:
-            res = checks( sconf, TryFunc )
+            res = checks( self, sconf, TryFunc )
             assert res[0] and not res[1] 
         finally:
             sconf.Finish()
-
-        # test the caching mechanism
+            
+        # 2.1 test the error caching mechanism (no dependencies have changed)
         self._resetSConfState()
-
-        sconf = SCons.SConf.SConf(scons_env,
-                                  conf_dir=self.test.workpath('config.tests'),
-                                  log_file=self.test.workpath('config.log'))
+        sconf = self.SConf.SConf(self.scons_env,
+                                 conf_dir=self.test.workpath('config.tests'),
+                                 log_file=self.test.workpath('config.log'))
         try:
-            res = checks( sconf, TryFunc )
+            res = checks( self, sconf, TryFunc )
             assert res[0] and not res[1] 
         finally:
             sconf.Finish()
-
         # we should have exactly one one error cached 
         log = self.test.read( self.test.workpath('config.log') )
         expr = re.compile( ".*(\(cached\))", re.DOTALL ) 
         firstOcc = expr.match( log )
         assert firstOcc != None 
         secondOcc = expr.match( log, firstOcc.end(0) )
-        assert secondOcc == None 
+        assert secondOcc == None
+
+        # 2.2 test the error caching mechanism (dependencies have changed)
+        self._resetSConfState()
+        sconf = self.SConf.SConf(self.scons_env,
+                                 conf_dir=self.test.workpath('config.tests'),
+                                 log_file=self.test.workpath('config.log'))
+        test_h = self.test.write( self.test.workpath('config.tests', 'no_std_header.h'),
+                                  "/* we are changing a dependency now */" );
+        try:
+            res = checks( self, sconf, TryFunc )
+            log = self.test.read( self.test.workpath('config.log') )
+            assert res[0] and res[1] 
+        finally:
+            sconf.Finish()
 
     def test_TryCompile(self):
-        self._baseTryXXX( SCons.SConf.SConf.TryCompile )
+        """Test SConf.TryCompile
+        """
+        self._baseTryXXX( "TryCompile" ) #self.SConf.SConf.TryCompile )
         
     def test_TryLink(self):
-        self._baseTryXXX( SCons.SConf.SConf.TryLink )
+        """Test SConf.TryLink
+        """
+        self._baseTryXXX( "TryLink" ) #self.SConf.SConf.TryLink ) 
 
     def test_TryRun(self):
+        """Test SConf.TryRun
+        """
         def checks(sconf):
             prog = """
 #include <stdio.h>
@@ -132,9 +172,9 @@ int main() {
             return (res1, res2)
         
         self._resetSConfState()
-        sconf = SCons.SConf.SConf(scons_env,
-                                  conf_dir=self.test.workpath('config.tests'),
-                                  log_file=self.test.workpath('config.log'))
+        sconf = self.SConf.SConf(self.scons_env,
+                                 conf_dir=self.test.workpath('config.tests'),
+                                 log_file=self.test.workpath('config.log'))
         try:
             res = checks(sconf)
             assert res[0][0] and res[0][1] == "Hello" 
@@ -144,17 +184,15 @@ int main() {
 
         # test the caching mechanism
         self._resetSConfState()
-
-        sconf = SCons.SConf.SConf(scons_env,
-                                  conf_dir=self.test.workpath('config.tests'),
-                                  log_file=self.test.workpath('config.log'))
+        sconf = self.SConf.SConf(self.scons_env,
+                                 conf_dir=self.test.workpath('config.tests'),
+                                 log_file=self.test.workpath('config.log'))
         try:
             res = checks(sconf)
             assert res[0][0] and res[0][1] == "Hello" 
             assert not res[1][0] and res[1][1] == ""
         finally:
             sconf.Finish()
-
         # we should have exactly one one error cached 
         log = self.test.read( self.test.workpath('config.log') )
         expr = re.compile( ".*(\(cached\))", re.DOTALL )
@@ -165,13 +203,15 @@ int main() {
 
 
     def test_TryAction(self):
+        """Test SConf.TryAction
+        """
         def actionOK(target, source, env):
             open(str(target[0]), "w").write( "RUN OK" )
             return None
         def actionFAIL(target, source, env):
             return 1
         self._resetSConfState()
-        sconf = SCons.SConf.SConf(scons_env,
+        sconf = self.SConf.SConf(self.scons_env,
                                   conf_dir=self.test.workpath('config.tests'),
                                   log_file=self.test.workpath('config.log'))
         try:
@@ -185,13 +225,15 @@ int main() {
         
 
     def test_StandardTests(self):
+        """Test standard checks
+        """
         def CHeaderChecks( sconf ):
-            res1 = sconf.CheckCHeader( "stdio.h" )
+            res1 = sconf.CheckCHeader( "stdio.h", include_quotes="<>" )
             res2 = sconf.CheckCHeader( "HopefullyNotCHeader.noh" )
             return (res1,res2)
 
         def CXXHeaderChecks(sconf):
-            res1 = sconf.CheckCXXHeader( "vector" )
+            res1 = sconf.CheckCXXHeader( "vector", include_quotes="<>" )
             res2 = sconf.CheckCXXHeader( "HopefullyNotCXXHeader.noh" )
             return (res1,res2)
 
@@ -236,9 +278,9 @@ int main() {
             return ((res1, libs1), (res2, libs2))
 
         self._resetSConfState()
-        sconf = SCons.SConf.SConf(scons_env,
-                                  conf_dir=self.test.workpath('config.tests'),
-                                  log_file=self.test.workpath('config.log'))
+        sconf = self.SConf.SConf(self.scons_env,
+                                 conf_dir=self.test.workpath('config.tests'),
+                                 log_file=self.test.workpath('config.log'))
         try:
             (res1, res2) = CHeaderChecks(sconf)
             assert res1 and not res2 
@@ -260,7 +302,8 @@ int main() {
             sconf.Finish()
 
     def test_CustomChecks(self):
-
+        """Test Custom Checks
+        """
         def CheckCustom(test):
             test.Message( "Checking UserTest ... " )
             prog = """
@@ -278,10 +321,10 @@ int main() {
         
 
         self._resetSConfState()
-        sconf = SCons.SConf.SConf(scons_env,
-                                  custom_tests={'CheckCustom': CheckCustom},
-                                  conf_dir=self.test.workpath('config.tests'),
-                                  log_file=self.test.workpath('config.log'))
+        sconf = self.SConf.SConf(self.scons_env,
+                                 custom_tests={'CheckCustom': CheckCustom},
+                                 conf_dir=self.test.workpath('config.tests'),
+                                 log_file=self.test.workpath('config.log'))
         try:
             ret = sconf.CheckCustom()
             assert ret 
@@ -291,6 +334,7 @@ int main() {
 
 if __name__ == "__main__":
     suite = unittest.makeSuite(SConfTestCase, 'test_')
-    if not unittest.TextTestRunner().run(suite).wasSuccessful ():
+    res = unittest.TextTestRunner().run(suite)
+    if not res.wasSuccessful():
         sys.exit(1)
-        
+
index cf7a86f05be409828f2f98b1527d871da8db32a2..1832e895c5a53f51f8bd8929edeb6956258b0909 100644 (file)
@@ -47,7 +47,7 @@ except ImportError:
 default_max_drift = 2*24*60*60
 
 #XXX Get rid of the global array so this becomes re-entrant.
-sig_files = []
+sig_files = {}
 
 # 1 means use build signature for derived source files
 # 0 means use content signature for derived source files
@@ -55,9 +55,18 @@ build_signature = 1
 
 def write():
     global sig_files
-    for sig_file in sig_files:
+    for sig_file in sig_files.values():
         sig_file.write()
 
+
+def SConsignFileFactory( dir, module=None):
+    try:
+        return sig_files[dir.path]
+    except KeyError:
+        sig_files[dir.path] = SConsignFile(dir, module)
+        return sig_files[dir.path]
+
+
 class SConsignEntry:
 
     """Objects of this type are pickled to the .sconsign file, so it
@@ -110,9 +119,6 @@ class SConsignFile:
                 SCons.Warnings.warn(SCons.Warnings.CorruptSConsignWarning,
                                     "Ignoring corrupt .sconsign file: %s"%self.sconsign)
 
-        global sig_files
-        sig_files.append(self)
-
     def get(self, filename):
         """
         Get the .sconsign entry for a file
@@ -279,7 +285,7 @@ class Calculator:
         bsig = cache.get_bsig()
         if bsig is not None:
             return bsig
-        
+
         sigs = map(lambda n, c=self: n.calc_signature(c), children)
         if node.has_builder():
             sigs.append(self.module.signature(node.get_executor()))
index b78cdcb1fa2ce486439425ec9fc51454eb0ed229..97333ab489df54ea08e04aee481fa049773364c6 100644 (file)
@@ -36,23 +36,48 @@ if sys.platform == 'win32':
 else:
     lib = 'm'
 
-test = TestSCons.TestSCons() 
+# to use cygwin compilers on cmd.exe -> uncomment following line
+#lib = 'm'
+
+oldPwd = os.getcwd()
 python = TestSCons.python
+test = None
+
+def reset(dot = 1):
+    global test, oldPwd
+    os.chdir( oldPwd )
+    TestSCons.scons = None
+    test = TestSCons.TestSCons()
+    if dot == 1:
+        test.match_func = TestCmd.match_re_dotall
+    
+
+def checkFiles(test, files):
+    for f in files:
+        test.fail_test( not os.path.isfile( test.workpath(f) ) )
+
 
 def checkLog( test, logfile, numUpToDate, numCache ):
-    test.fail_test(not os.path.exists(test.workpath('config.log')))
+    test.fail_test(not os.path.exists(test.workpath(logfile)))
     log = test.read(test.workpath(logfile))
-    test.fail_test( len( re.findall( "is up to date", log ) ) != numUpToDate )
-    test.fail_test( len( re.findall( "\(cached\): Building \S+ failed in a previous run.", log ) ) != numCache )
-    
+    try:
+        test.fail_test( len( re.findall( "is up to date", log ) ) != numUpToDate )
+        test.fail_test( len( re.findall( "\(cached\): Building \S+ failed in a previous run.", log ) ) != numCache )
+    except:
+        #print "contents of log ", test.workpath(logfile), "\n", log
+        raise 
 
 
 try:
 
     # 1.1 if checks are ok, the cache mechanism should work
 
+    reset(dot=0)
+    
     test.write( 'SConstruct', """
 env = Environment()
+import os
+env['ENV']['PATH'] = os.environ['PATH']
 conf = Configure(env)
 r1 = conf.CheckLibWithHeader( '%s', 'math.h', 'c' )
 r2 = conf.CheckLibWithHeader( None, 'math.h', 'c' )
@@ -63,16 +88,16 @@ r6 = conf.CheckCXXHeader( 'vector' )
 env = conf.Finish()
 if not (r1 and r2 and r3 and r4 and r5 and r6):
      Exit(1)
-""" % (lib, lib))
+""" % (lib,lib))
 
     required_stdout = test.wrap_stdout(build_str='scons: "." is up to date.\n',
                                        read_str=
     """Checking for main(); in library %s (header math.h) ... ok
 Checking for main(); in library None (header math.h) ... ok
-Checking for main in library %s... ok
-Checking for main in library None... ok
-Checking for C header math.h... ok
-Checking for C header vector... ok
+Checking for main in library %s ... ok
+Checking for main in library None ... ok
+Checking for C header math.h ... ok
+Checking for C++ header vector ... ok
 """ % (lib, lib))
 
     test.run(stdout = required_stdout)
@@ -83,9 +108,13 @@ Checking for C header vector... ok
 
     # 1.2 if checks are not ok, the cache mechanism should work as well
     #     (via explicit cache)
+    reset()
+    
 
     test.write( 'SConstruct', """
 env = Environment()
+import os
+env['ENV']['PATH'] = os.environ['PATH']
 conf = Configure(env)
 r1 = conf.CheckCHeader( 'no_std_c_header.h' ) # leads to compile error
 r2 = conf.CheckLib( 'no_c_library_SAFFDG' )   # leads to link error
@@ -97,8 +126,8 @@ if not (not r1 and not r2):
 
     required_stdout = test.wrap_stdout(build_str='scons: "." is up to date.\n',
                                        read_str=
-    """Checking for C header no_std_c_header.h... failed
-Checking for main in library no_c_library_SAFFDG... failed
+    """Checking for C header no_std_c_header.h ... failed
+Checking for main in library no_c_library_SAFFDG ... failed
 """)
 
     test.run(stdout = required_stdout)
@@ -109,8 +138,13 @@ Checking for main in library no_c_library_SAFFDG... failed
 
 
     # 2.1 test that normal builds work together with Sconf
+    reset()
+    
+
     test.write( 'SConstruct', """
 env = Environment()
+import os
+env['ENV']['PATH'] = os.environ['PATH']
 conf = Configure(env)
 r1 = conf.CheckCHeader( 'math.h' )
 r2 = conf.CheckCHeader( 'no_std_c_header.h' ) # leads to compile error
@@ -132,8 +166,8 @@ int main() {
     test.match_func = TestCmd.match_re_dotall
     required_stdout = test.wrap_stdout(build_str='.*',
                                        read_str=
-    """Checking for C header math.h... ok
-Checking for C header no_std_c_header.h... failed
+    """Checking for C header math.h ... ok
+Checking for C header no_std_c_header.h ... failed
 """)
     test.run( stdout = required_stdout )
     checkLog( test, 'config.log', 0, 0 )
@@ -143,8 +177,13 @@ Checking for C header no_std_c_header.h... failed
 
 
     # 2.2 test that BuildDir builds work together with Sconf
+    reset()
+    
+
     test.write( 'SConstruct', """
 env = Environment()
+import os
+env['ENV']['PATH'] = os.environ['PATH']
 BuildDir( 'build', '.' )
 conf = Configure(env, conf_dir='build/config.tests', log_file='build/config.log')
 r1 = conf.CheckCHeader( 'math.h' )
@@ -167,8 +206,8 @@ int main() {
 """)
     required_stdout = test.wrap_stdout(build_str='.*',
                                        read_str=
-    """Checking for C header math.h... ok
-Checking for C header no_std_c_header.h... failed
+    """Checking for C header math.h ... ok
+Checking for C header no_std_c_header.h ... failed
 """)
     test.run( stdout = required_stdout )
     checkLog( test, 'build/config.log', 0, 0 )
@@ -176,26 +215,64 @@ Checking for C header no_std_c_header.h... failed
     test.run( stdout = required_stdout )
     checkLog( test, 'build/config.log', 3, 1 )
     
+    # 2.3 test that Configure calls in SConscript files work
+    #     even if BuildDir is set
+    reset()
+    
+
+    test.subdir( 'sub' )
+    test.write( 'SConstruct', """
+BuildDir( 'build', '.' )
+SConscript( 'build/SConscript' )
+""")
+    test.write( 'SConscript', """
+SConscript( 'sub/SConscript' )
+""")
+    test.write( 'sub/SConscript', """
+env = Environment()
+import os
+env['ENV']['PATH'] = os.environ['PATH']
+conf = Configure( env )
+conf.CheckCHeader( 'math.h' )
+env = conf.Finish()
+env.Program( 'TestProgram', 'TestProgram.c' )
+""")
+    test.write( 'sub/TestProgram.h', """
+/* Just a test header */
+""")
+    test.write( 'sub/TestProgram.c', """
+#include "TestProgram.h"
+#include <stdio.h>
+
+int main() {
+  printf( "Hello\\n" );
+}
+""")
+    test.run()
+    checkFiles( test, [".sconf_temp/.cache", "config.log"] )
 
     # 3.1 test custom tests
+    reset()
+    
+
     compileOK = '#include <stdio.h>\\nint main() {printf("Hello");return 0;}'
     compileFAIL = "syntax error" 
     linkOK = compileOK
     linkFAIL = "void myFunc(); int main() { myFunc(); }"
     runOK = compileOK
     runFAIL = "int main() { return 1; }"
-    test.write( 'pyAct.py', 'import sys\nopen(sys.argv[1], "w").write(sys.argv[2] + "\\n"),\nsys.exit(int(sys.argv[2]))\n' 
-    test.write( 'SConstruct', """ 
+    test.write('pyAct.py', 'import sys\nprint sys.argv[1]\nsys.exit(int(sys.argv[1]))\n'
+    test.write('SConstruct', """ 
 def CheckCustom(test):
-    test.Message( 'Executing MyTest...' )
+    test.Message( 'Executing MyTest ... ' )
     retCompileOK = test.TryCompile( '%s', '.c' )
     retCompileFAIL = test.TryCompile( '%s', '.c' )
     retLinkOK = test.TryLink( '%s', '.c' )
     retLinkFAIL = test.TryLink( '%s', '.c' )
     (retRunOK, outputRunOK) = test.TryRun( '%s', '.c' )
     (retRunFAIL, outputRunFAIL) = test.TryRun( '%s', '.c' )
-    (retActOK, outputActOK) = test.TryAction( '%s pyAct.py $TARGET 0' )
-    (retActFAIL, outputActFAIL) = test.TryAction( '%s pyAct.py $TARGET 1' )
+    (retActOK, outputActOK) = test.TryAction( '%s pyAct.py 0 > $TARGET' )
+    (retActFAIL, outputActFAIL) = test.TryAction( '%s pyAct.py 1 > $TARGET' )
     resOK = retCompileOK and retLinkOK and retRunOK and outputRunOK=="Hello"
     resOK = resOK and retActOK and int(outputActOK)==0
     resFAIL = retCompileFAIL or retLinkFAIL or retRunFAIL or outputRunFAIL!=""
@@ -204,13 +281,15 @@ def CheckCustom(test):
     return resOK and not resFAIL
 
 env = Environment()
+import os
+env['ENV']['PATH'] = os.environ['PATH']
 conf = Configure( env, custom_tests={'CheckCustom' : CheckCustom} )
 conf.CheckCustom()
 env = conf.Finish()
 """ % (compileOK, compileFAIL, linkOK, linkFAIL, runOK, runFAIL,
        python, python ) )
     required_stdout = test.wrap_stdout(build_str='.*',
-                                       read_str="Executing MyTest...ok\n")
+                                       read_str="Executing MyTest ... ok\n")
     test.run(stdout = required_stdout)
     checkLog( test, 'config.log', 0, 0 )