Several bug fixes from Charles Crain.
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Mon, 22 Apr 2002 19:21:26 +0000 (19:21 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Mon, 22 Apr 2002 19:21:26 +0000 (19:21 +0000)
git-svn-id: http://scons.tigris.org/svn/scons/trunk@338 fdb21ef1-2011-0410-befe-b5e4ea1792b1

13 files changed:
src/CHANGES.txt
src/engine/SCons/Action.py
src/engine/SCons/ActionTests.py
src/engine/SCons/Builder.py
src/engine/SCons/BuilderTests.py
src/engine/SCons/Defaults.py
src/engine/SCons/Node/FS.py
src/engine/SCons/Node/FSTests.py
src/engine/SCons/Script/__init__.py
src/engine/SCons/Util.py
src/engine/SCons/UtilTests.py
test/YACC.py
test/long-lines.py

index 29857f9eaf65d783f9b2c2a5ba656a517a2d013a..b7b07e4aa24a1d835387a6e5e4b890eb9883094f 100644 (file)
@@ -58,6 +58,13 @@ RELEASE 0.07 -
 
   - ListBuilder now passes all targets to the action, not just the first.
 
+  - Fix so that -c now deletes generated yacc .h files.
+
+  - Builder actions and emitter functions can now be initialized, through
+    construction variables, to things other than strings.
+
+  - Make top-relative '#/dir' lookups work like '#dir'.
+
   From Steven Knight:
 
   - Fix so that -c -n does *not* remove the targets!
index 38b5413d7f38c729ad7ca1f50526d41f3c844393..1941e1a38714fb27ef028b7493bef3c0c1f801fa 100644 (file)
@@ -198,6 +198,15 @@ def _do_create_action(act):
     elif callable(act):
         return FunctionAction(act)
     elif SCons.Util.is_String(act):
+        var=SCons.Util.get_environment_var(act)
+        if var:
+            # This looks like a string that is purely an Environment
+            # variable reference, like "$FOO" or "${FOO}".  We do
+            # something special here...we lazily evaluate the contents
+            # of that Environment variable, so a user could but something
+            # like a function or a CommandGenerator in that variable
+            # instead of a string.
+            return CommandGeneratorAction(LazyCmdGenerator(var))
         listCmds = map(lambda x: CommandAction(string.split(x)),
                        string.split(act, '\n'))
         if len(listCmds) == 1:
@@ -314,11 +323,19 @@ class EnvDictProxy(UserDict.UserDict):
     def __init__(self, env):
         UserDict.UserDict.__init__(self, env)
 
-    def subst(self, string):
-        return SCons.Util.scons_subst(string, self.data, {}, _rm)
+    def subst(self, string, raw=0):
+        if raw:
+            regex_remove = None
+        else:
+            regex_remove = _rm
+        return SCons.Util.scons_subst(string, self.data, {}, regex_remove)
 
-    def subst_list(self, string):
-        return SCons.Util.scons_subst_list(string, self.data, {}, _rm)
+    def subst_list(self, string, raw=0):
+        if raw:
+            regex_remove = None
+        else:
+            regex_remove = _rm
+        return SCons.Util.scons_subst_list(string, self.data, {}, regex_remove)
 
 class CommandAction(ActionBase):
     """Class for command-execution actions."""
@@ -410,6 +427,21 @@ class CommandGeneratorAction(ActionBase):
         """
         return apply(self.__generate(kw).get_contents, (), kw)
 
+class LazyCmdGenerator:
+    """This is a simple callable class that acts as a command generator.
+    It holds on to a key into an Environment dictionary, then waits
+    until execution time to see what type it is, then tries to
+    create an Action out of it."""
+    def __init__(self, var):
+        self.var = SCons.Util.to_String(var)
+
+    def __call__(self, env, **kw):
+        if env.has_key(self.var):
+            return env[self.var]
+        else:
+            # The variable reference substitutes to nothing.
+            return ''
+
 class FunctionAction(ActionBase):
     """Class for Python function actions."""
     def __init__(self, function):
index 648b77bed08d82f76f2b7122325430845ad903d0..cda565d3dff8d80f758a3d3cac404d40662ea1a0 100644 (file)
@@ -178,14 +178,21 @@ class CommandGeneratorActionTestCase(unittest.TestCase):
 
         def f(dummy, env, self=self):
             self.dummy = dummy
-            assert env.subst('$FOO') == 'foo baz\nbar ack', env.subst('$FOO')
-            assert env.subst_list('$FOO') == [ [ 'foo', 'baz' ],
-                                               [ 'bar', 'ack' ] ], env.subst_list('$FOO')
+            assert env.subst("$FOO $( bar $) baz") == 'foo baz\nbar ack bar baz', env.subst("$FOO $( bar $) baz")
+            assert env.subst("$FOO $( bar $) baz", raw=1) == 'foo baz\nbar ack $( bar $) baz', env.subst("$FOO $( bar $) baz", raw=1)
+            assert env.subst_list("$FOO $( bar $) baz") == [ [ 'foo', 'baz' ],
+                                                             [ 'bar', 'ack', 'bar', 'baz' ] ], env.subst_list("$FOO $( bar $) baz")
+            assert env.subst_list("$FOO $( bar $) baz",
+                                  raw=1) == [ [ 'foo', 'baz' ],
+                                              [ 'bar', 'ack', '$(', 'bar', '$)', 'baz' ] ], env.subst_list("$FOO $( bar $) baz", raw=1)
             return "$FOO"
         def func_action(env, dummy, self=self):
-            assert env.subst('$foo') == 'bar', env.subst('$foo')
-            assert env.subst_list('$foo') == [ [ 'bar' ] ], env.subst_list('$foo')
-            assert env.subst_list([ '$foo', 'bar' ]) == [[ 'bar', 'bar' ]], env.subst_list([ [ '$foo', 'bar' ] ])
+            assert env.subst('$foo $( bar $)') == 'bar bar', env.subst('$foo $( bar $)')
+            assert env.subst('$foo $( bar $)',
+                             raw=1) == 'bar $( bar $)', env.subst('$foo $( bar $)', raw=1)
+            assert env.subst_list([ '$foo', '$(', 'bar', '$)' ]) == [[ 'bar', 'bar' ]], env.subst_list([ '$foo', '$(', 'bar', '$)' ])
+            assert env.subst_list([ '$foo', '$(', 'bar', '$)' ],
+                                  raw=1) == [[ 'bar', '$(', 'bar', '$)' ]], env.subst_list([ '$foo', '$(', 'bar', '$)' ], raw=1)
             self.dummy=dummy
         def f2(dummy, env, f=func_action):
             return f
@@ -291,6 +298,39 @@ class ListActionTestCase(unittest.TestCase):
         c = a.get_contents(target=[], source=[])
         assert c == "xyz", c
 
+class LazyActionTestCase(unittest.TestCase):
+    def test_init(self):
+        """Test creation of a lazy-evaluation Action
+        """
+        # Environment variable references should create a special
+        # type of CommandGeneratorAction that lazily evaluates the
+        # variable.
+        a9 = SCons.Action.Action('$FOO')
+        assert isinstance(a9, SCons.Action.CommandGeneratorAction), a9
+        assert a9.generator.var == 'FOO', a9.generator.var
+
+        a10 = SCons.Action.Action('${FOO}')
+        assert isinstance(a9, SCons.Action.CommandGeneratorAction), a10
+        assert a10.generator.var == 'FOO', a10.generator.var
+
+    def test_execute(self):
+        """Test executing a lazy-evalueation Action
+        """
+        def f(s, env):
+            s.test=1
+            return 0
+        a = SCons.Action.Action('$BAR')
+        a.execute(s = self, env={'BAR':f})
+        assert self.test == 1, self.test
+
+    def test_get_contents(self):
+        """Test fetching the contents of a lazy-evaluation Action
+        """
+        a = SCons.Action.Action("${FOO}")
+        c = a.get_contents(target=[], source=[],
+                           env={'FOO':[["This", "is", "$(", "a", "$)", "test"]]})
+        assert c == "This is test", c
+
 
 if __name__ == "__main__":
     suite = unittest.TestSuite()
@@ -300,7 +340,8 @@ if __name__ == "__main__":
     for tclass in [CommandActionTestCase,
                    CommandGeneratorActionTestCase,
                    FunctionActionTestCase,
-                   ListActionTestCase]:
+                   ListActionTestCase,
+                   LazyActionTestCase]:
         for func in ["test_init", "test_execute", "test_get_contents"]:
             suite.addTest(tclass(func))
     if not unittest.TextTestRunner().run(suite).wasSuccessful():
index 2a1b7d817d5af1cda52d4ebdfde1bffa4cf11b3b..05330e4f3eaf1b7d2fbe2e98c888282ec8630189 100644 (file)
@@ -82,6 +82,17 @@ def Builder(**kw):
         action_dict = kw['action']
         kw['action'] = SCons.Action.CommandGenerator(DictCmdGenerator(action_dict))
         kw['src_suffix'] = action_dict.keys()
+
+    if kw.has_key('emitter') and \
+       SCons.Util.is_String(kw['emitter']):
+        # This allows users to pass in an Environment
+        # variable reference (like "$FOO") as an emitter.
+        # We will look in that Environment variable for
+        # a callable to use as the actual emitter.
+        var = SCons.Util.get_environment_var(kw['emitter'])
+        if not var:
+            raise UserError, "Supplied emitter '%s' does not appear to refer to an Environment variable" % kw['emitter']
+        kw['emitter'] = EmitterProxy(var)
         
     if kw.has_key('src_builder'):
         return apply(MultiStepBuilder, (), kw)
@@ -129,6 +140,33 @@ def _adjust_suffix(suff):
         return '.' + suff
     return suff
 
+class EmitterProxy:
+    """This is a callable class that can act as a
+    Builder emitter.  It holds on to a string that
+    is a key into an Environment dictionary, and will
+    look there at actual build time to see if it holds
+    a callable.  If so, we will call that as the actual
+    emitter."""
+    def __init__(self, var):
+        self.var = SCons.Util.to_String(var)
+
+    def __call__(self, target, source, env, **kw):
+        emitter = self.var
+
+        # Recursively substitue the variable.
+        # We can't use env.subst() because it deals only
+        # in strings.  Maybe we should change that?
+        while SCons.Util.is_String(emitter) and \
+              env.has_key(emitter):
+            emitter = env[emitter]
+        if not callable(emitter):
+            return (target, source)
+        args = { 'target':target,
+                 'source':source,
+                 'env':env }
+        args.update(kw)
+        return apply(emitter, (), args)
+
 class BuilderBase:
     """Base class for Builders, objects that create output
     nodes (files) from input nodes (files).
@@ -374,9 +412,15 @@ class MultiStepBuilder(BuilderBase):
                 dictArgs['env'] = env
                 tgt = apply(src_bld, (), dictArgs)
                 if not SCons.Util.is_List(tgt):
-                    final_sources.append(tgt)
-                else:
-                    final_sources.extend(tgt)
+                    tgt = [ tgt ]
+
+                # Only supply the builder with sources it is capable
+                # of building.
+                tgt = filter(lambda x,
+                             suf=self.src_suffixes(env, kw):
+                             os.path.splitext(SCons.Util.to_String(x))[1] in \
+                             suf, tgt)
+                final_sources.extend(tgt)
             else:
                 final_sources.append(snode)
         dictKwArgs = kw
index e30079c1db45aec7931aac5f807fa2d32e523bf6..12178ed4eb1dc8d094563fd9a0ca5ce230f7e0d9 100644 (file)
@@ -86,6 +86,10 @@ class Environment:
         return {}
     def autogenerate(self, dir=''):
         return {}
+    def __getitem__(self, item):
+        return self.d[item]
+    def has_key(self, item):
+        return self.d.has_key(item)
     
 env = Environment()
 
@@ -743,6 +747,25 @@ class BuilderTestCase(unittest.TestCase):
         assert 'foo' in map(str, tgt.sources), map(str, tgt.sources)
         assert 'bar' in map(str, tgt.sources), map(str, tgt.sources)
 
+        env2=Environment(FOO=emit)
+        builder2=SCons.Builder.Builder(name="builder2", action='foo',
+                                       emitter="$FOO")
+
+        tgt = builder2(env2, target='foo', source='bar')
+        assert str(tgt) == 'foo', str(tgt)
+        assert str(tgt.sources[0]) == 'bar', str(tgt.sources[0])
+
+        tgt = builder2(env2, target='foo', source='bar', foo=1)
+        assert len(tgt) == 2, len(tgt)
+        assert 'foo' in map(str, tgt), map(str, tgt)
+        assert 'bar' in map(str, tgt), map(str, tgt)
+
+        tgt = builder2(env2, target='foo', source='bar', bar=1)
+        assert str(tgt) == 'foo', str(tgt)
+        assert len(tgt.sources) == 2, len(tgt.sources)
+        assert 'foo' in map(str, tgt.sources), map(str, tgt.sources)
+        assert 'bar' in map(str, tgt.sources), map(str, tgt.sources)
+
 if __name__ == "__main__":
     suite = unittest.makeSuite(BuilderTestCase, 'test_')
     if not unittest.TextTestRunner().run(suite).wasSuccessful():
index 8856db527f1e64854b010f1169d770b70a7ad31a..6acf4dec3bb4304eac13e4140987b2f57bdcadc8 100644 (file)
@@ -77,17 +77,28 @@ class SharedCmdGenerator:
             return self.action_shared
         else:
             return self.action_static
-                         
+
+def yaccEmitter(target, source, env, **kw):
+    # Yacc can be configured to emit a .h file as well
+    # as a .c file.  Append that as a target.
+    if len(source) and os.path.splitext(SCons.Util.to_String(source[0]))[1] in \
+       [ '.y', '.yy']:
+        target.append(os.path.splitext(SCons.Util.to_String(target[0]))[0] + \
+                      '.h')
+    return (target, source)
+
 CFile = SCons.Builder.Builder(name = 'CFile',
                               action = { '.l'    : '$LEXCOM',
                                          '.y'    : '$YACCCOM',
                                        },
+                              emitter = yaccEmitter,
                               suffix = '$CFILESUFFIX')
 
 CXXFile = SCons.Builder.Builder(name = 'CXXFile',
                                 action = { '.ll' : '$LEXCOM',
                                            '.yy' : '$YACCCOM',
                                          },
+                                emitter = yaccEmitter,
                                 suffix = '$CXXFILESUFFIX')
 
 CXXAction = SCons.Action.Action("$CXXCOM")
@@ -134,22 +145,33 @@ Object = SCons.Builder.Builder(name = 'Object',
                                src_suffix = static_obj.src_suffixes(),
                                src_builder = [CFile, CXXFile])
 
-def win32LinkGenerator(env, target, source, **kw):
-    cmd = env.subst_list([ '$LINK', '$LINKFLAGS', '/OUT:' + str(target[0]) ])[0]
-    cmd.extend(['$('] + env.subst_list('$_LIBDIRFLAGS')[0] + ['$)'])
-    cmd.extend(env.subst_list('$_LIBFLAGS')[0])
-    cmd.extend(map(lambda x: str(x), source))
+def win32TempFileMunge(env, cmd_list):
+    """Given a list of command line arguments, see if it is too
+    long to pass to the win32 command line interpreter.  If so,
+    create a temp file, then pass "@tempfile" as the sole argument
+    to the supplied command (which is the first element of cmd_list).
+    Otherwise, just return [cmd_list]."""
+    cmd = env.subst_list(cmd_list)[0]
     cmdlen = reduce(lambda x, y: x + len(y), cmd, 0) + len(cmd)
     if cmdlen <= 2048:
-        return [cmd]
+        return [cmd_list]
     else:
         import tempfile
-        tmp = tempfile.mktemp()
-       args = filter(lambda x: x != '$(' and x != '$)', cmd[1:])
-        args = map(SCons.Util.quote_spaces, args)
+        # We do a normpath because mktemp() has what appears to be
+        # a bug in Win32 that will use a forward slash as a path
+        # delimiter.  Win32's link mistakes that for a command line
+        # switch and barfs.
+        tmp = os.path.normpath(tempfile.mktemp())
+        args = map(SCons.Util.quote_spaces, cmd[1:])
         open(tmp, 'w').write(string.join(args, " ") + "\n")
         return [ [cmd[0], '@' + tmp],
                  ['del', tmp] ]
+    
+def win32LinkGenerator(env, target, source, **kw):
+    args = [ '$LINK', '$LINKFLAGS', '/OUT:%s' % target[0],
+             '$(', '$_LIBDIRFLAGS', '$)', '$_LIBFLAGS' ]
+    args.extend(map(SCons.Util.to_String, source))
+    return win32TempFileMunge(env, args)
 
 kw = {
        'name'        : 'Program',
@@ -197,7 +219,7 @@ def win32LibGenerator(target, source, env, shared=1):
         else:
             # Just treat it as a generic source file.
             listCmd.append(str(src))
-    return [ listCmd ]
+    return win32TempFileMunge(env, listCmd)
 
 def win32LibEmitter(target, source, env, shared=0):
     if shared:
@@ -226,32 +248,19 @@ def win32LibEmitter(target, source, env, shared=0):
                                       env.subst("$LIBSUFFIX")))
     return (target, source)
 
-PosixLibrary = SCons.Builder.Builder(name = 'Library',
-                                     generator = \
-                                     SharedCmdGenerator(shared="$SHLINKCOM",
-                                                        static="$ARCOM"),
-                                     prefix = \
-                                     LibAffixGenerator(static='$LIBPREFIX',
-                                                       shared='$SHLIBPREFIX'),
-                                     suffix = \
-                                     LibAffixGenerator(static='$LIBSUFFIX',
-                                                       shared='$SHLIBSUFFIX'),
-                                     src_suffix = '$OBJSUFFIX',
-                                     src_builder = Object)
-
-Win32Library = SCons.Builder.Builder(name = 'Library',
-                                     generator = \
-                                     SharedCmdGenerator(shared=SCons.Action.CommandGeneratorAction(win32LibGenerator),
-                                                        static="$ARCOM"),
-                                     emitter = win32LibEmitter,
-                                     prefix = \
-                                     LibAffixGenerator(static='$LIBPREFIX',
-                                                       shared='$SHLIBPREFIX'),
-                                     suffix = \
-                                     LibAffixGenerator(static='$LIBSUFFIX',
-                                                       shared='$SHLIBSUFFIX'),
-                                     src_suffix = '$OBJSUFFIX',
-                                     src_builder = Object)
+Library = SCons.Builder.Builder(name = 'Library',
+                                generator = \
+                                SharedCmdGenerator(shared="$SHLINKCOM",
+                                                   static="$ARCOM"),
+                                emitter="$LIBEMITTER",
+                                prefix = \
+                                LibAffixGenerator(static='$LIBPREFIX',
+                                                  shared='$SHLIBPREFIX'),
+                                suffix = \
+                                LibAffixGenerator(static='$LIBSUFFIX',
+                                                  shared='$SHLIBSUFFIX'),
+                                src_suffix = '$OBJSUFFIX',
+                                src_builder = Object)
 
 LaTeXAction = SCons.Action.Action('$LATEXCOM')
 
@@ -414,14 +423,10 @@ def make_win32_env_from_paths(include, lib, path):
         'SHF77PPCOM' : '$SHF77 $SHF77FLAGS $CPPFLAGS $_INCFLAGS /c $SOURCES /Fo$TARGET',
         'LINK'       : 'link',
         'LINKFLAGS'  : '/nologo',
-        # XXX - We'd like to do this as follows, but '$LINKCOM' in
-        # a Builder above gets expanded too soon to stick a function
-        # right in the environment like this.  Revisit this when this
-        # capability has been added (cf. bug report #537058).
-        #'LINKCOM'    : win32Link,
+        'LINKCOM'    : SCons.Action.CommandGenerator(win32LinkGenerator),
         'SHLINK'     : '$LINK',
         'SHLINKFLAGS': '$LINKFLAGS /dll',
-        'SHLINKCOM'  : '$SHLINK $SHLINKFLAGS /OUT:$TARGET $_LIBDIRFLAGS $_LIBFLAGS $SOURCES',
+        'SHLINKCOM'  : SCons.Action.CommandGenerator(win32LibGenerator),
         'AR'         : 'lib',
         'ARFLAGS'    : '/nologo',
         'ARCOM'      : '$AR $ARFLAGS /OUT:$TARGET $SOURCES',
@@ -449,7 +454,7 @@ def make_win32_env_from_paths(include, lib, path):
         'PSCOM'      : '$DVIPS $DVIPSFLAGS -o $TARGET $SOURCES',
         'PSPREFIX'   : '',
         'PSSUFFIX'   : '.ps',
-        'BUILDERS'   : [Alias, CFile, CXXFile, DVI, Win32Library, Object,
+        'BUILDERS'   : [Alias, CFile, CXXFile, DVI, Library, Object,
                         PDF, PostScript, Program],
         'SCANNERS'   : [CScan],
         'OBJPREFIX'  : '',
@@ -464,6 +469,7 @@ def make_win32_env_from_paths(include, lib, path):
         'LIBDIRSUFFIX'          : '',
         'LIBLINKPREFIX'         : '',
         'LIBLINKSUFFIX'         : '$LIBSUFFIX',
+        'LIBEMITTER'            : win32LibEmitter,
         'INCPREFIX'             : '/I',
         'INCSUFFIX'             : '',
         'WIN32DEFPREFIX'        : '/def:',
@@ -491,8 +497,6 @@ def make_win32_env(version):
 
 
 if os.name == 'posix':
-    Library = PosixLibrary
-    
     arcom = '$AR $ARFLAGS $TARGET $SOURCES'
     ranlib = 'ranlib'
     if SCons.Util.WhereIs(ranlib):
@@ -557,7 +561,7 @@ if os.name == 'posix':
         'PSCOM'      : '$DVIPS $DVIPSFLAGS -o $TARGET $SOURCES',
         'PSPREFIX'   : '',
         'PSSUFFIX'   : '.ps',
-        'BUILDERS'   : [Alias, CFile, CXXFile, DVI, PosixLibrary, Object,
+        'BUILDERS'   : [Alias, CFile, CXXFile, DVI, Library, Object,
                         PDF, PostScript, Program],
         'SCANNERS'   : [CScan],
         'OBJPREFIX'  : '',
@@ -578,8 +582,6 @@ if os.name == 'posix':
     }
 
 elif os.name == 'nt':
-    Library = Win32Library
-    
     versions = None
     try:
         versions = get_devstudio_versions()
index 1c506c91863917f9a356be302329b6dc433a1bff..861fbaf7983d4e513c8ff823cc39ca0a629a9e81 100644 (file)
@@ -204,7 +204,11 @@ class FS:
         self.__setTopLevelDir()
         if name[0] == '#':
             directory = self.Top
-            name = os.path.join('./', name[1:])
+            name = os.path.normpath(name[1:])
+            if name[0] == os.sep:
+                # Correct such that '#/foo' is equivalent
+                # to '#foo'.
+                name = name[1:]
         elif not directory:
             directory = self._cwd
         return (os.path.normpath(name), directory)
index 6b35adbfc17dac145b0626883d1a29d2ec0ae396..0259debf751e0e69184470779bc35956053b4e7b 100644 (file)
@@ -232,6 +232,8 @@ class FSTestCase(unittest.TestCase):
             Dir_test('.',           './',          sub_dir,           sub)
             Dir_test('./.',         './',          sub_dir,           sub)
             Dir_test('foo/./bar',   'foo/bar/',    sub_dir_foo_bar,   'foo/')
+            Dir_test('#foo/bar',    'foo/bar/',    sub_dir_foo_bar,   'foo/')
+            Dir_test('#/foo/bar',   'foo/bar/',    sub_dir_foo_bar,   'foo/')
 
             try:
                 f2 = fs.File(string.join(['f1', 'f2'], sep), directory = d1)
index 09b85be9a84293b09bea3e3844942a9a35114cb0..105c28f60cef13a80697431dea8492c79d39780f 100644 (file)
@@ -124,6 +124,8 @@ class CleanTask(SCons.Taskmaster.Task):
                         os.unlink(t.path)
                     except OSError:
                         pass
+                    else:
+                        print "Removed " + t.path
             except IndexError:
                 pass
 
index 0abbd1e79c8443ebb7088f41721f20e2c1d9050b..08edde783a512fe8a07b99cedd6300f37a066813 100644 (file)
@@ -139,6 +139,23 @@ class PathList(UserList.UserList):
         # suffix and basepath.
         return self.__class__([ UserList.UserList.__getitem__(self, item), ])
 
+_env_var = re.compile(r'^\$([_a-zA-Z]\w*|{[^}]*})$')
+
+def get_environment_var(varstr):
+    """Given a string, first determine if it looks like a reference
+    to a single environment variable, like "$FOO" or "${FOO}".
+    If so, return that variable with no decorations ("FOO").
+    If not, return None."""
+    mo=_env_var.match(to_String(varstr))
+    if mo:
+        var = mo.group(1)
+        if var[0] == '{':
+            return var[1:-1]
+        else:
+            return var
+    else:
+        return None
+
 def quote_spaces(arg):
     if ' ' in arg or '\t' in arg:
         return '"%s"' % arg
@@ -272,8 +289,10 @@ def is_List(e):
 def to_String(s):
     """Better than str() because it will preserve a unicode
     object without converting it to ASCII."""
-    if is_String(s):
-        return s
+    if hasattr(types, 'UnicodeType') and \
+       (type(s) is types.UnicodeType or \
+        (isinstance(s, UserString) and type(s.data) is types.UnicodeType)):
+        return unicode(s)
     else:
         return str(s)
 
index b42450699e3b0710fe16bcfd9169f711af895ce7..34dba99a400f66a15f22c85fc953a2d6e5117e71 100644 (file)
@@ -268,25 +268,29 @@ class UtilTestCase(unittest.TestCase):
             import UserString
 
             s1=UserString.UserString('blah')
-            assert to_String(s1) is s1, s1
+            assert to_String(s1) == s1, s1
             assert to_String(s1) == 'blah', s1
 
             class Derived(UserString.UserString):
                 pass
             s2 = Derived('foo')
-            assert to_String(s2) is s2, s2
+            assert to_String(s2) == s2, s2
             assert to_String(s2) == 'foo', s2
 
             if hasattr(types, 'UnicodeType'):
                 s3=UserString.UserString(unicode('bar'))
-                assert to_String(s3) is s3, s3
+                assert to_String(s3) == s3, s3
                 assert to_String(s3) == unicode('bar'), s3
+                assert type(to_String(s3)) is types.UnicodeType, \
+                       type(to_String(s3))
         except ImportError:
             pass
 
         if hasattr(types, 'UnicodeType'):
             s4 = unicode('baz')
             assert to_String(s4) == unicode('baz'), to_String(s4)
+            assert type(to_String(s4)) is types.UnicodeType, \
+                   type(to_String(s4))
 
     def test_WhereIs(self):
         test = TestCmd.TestCmd(workdir = '')
@@ -349,6 +353,14 @@ class UtilTestCase(unittest.TestCase):
            wi = WhereIs('xxx', path = pathdirs_1234, pathext = '.BAT;.EXE')
            assert string.lower(wi) == string.lower(test.workpath(sub3_xxx_exe)), wi
 
+    def test_get_env_var(self):
+        """Testing get_environment_var()."""
+        assert get_environment_var("$FOO") == "FOO", get_environment_var("$FOO")
+        assert get_environment_var("${BAR}") == "BAR", get_environment_var("${BAR}")
+        assert get_environment_var("${BAR}FOO") == None, get_environment_var("${BAR}FOO")
+        assert get_environment_var("$BAR ") == None, get_environment_var("$BAR ")
+        assert get_environment_var("FOO$BAR") == None, get_environment_var("FOO$BAR")
+
 if __name__ == "__main__":
     suite = unittest.makeSuite(UtilTestCase, 'test_')
     if not unittest.TextTestRunner().run(suite).wasSuccessful():
index 3e8ea35611bc1c2210d5161ddd436ab28a82e7a7..486200c64f12568b6f304369a9420cc5e5945d13 100644 (file)
@@ -98,7 +98,7 @@ os.system(string.join(sys.argv[1:], " "))
 """ % string.replace(test.workpath('wrapper.out'), '\\', '\\\\'))
 
     test.write('SConstruct', """
-foo = Environment()
+foo = Environment(YACCFLAGS='-d')
 yacc = foo.Dictionary('YACC')
 bar = Environment(YACC = r'%s wrapper.py ' + yacc)
 foo.Program(target = 'foo', source = 'foo.y')
@@ -150,4 +150,10 @@ newline: '\n';
 
     test.run(program = test.workpath('bar'), stdin = "b\n", stdout = "bar.y\n")
 
+    test.fail_test(not os.path.exists(test.workpath('foo.h')))
+
+    test.run(arguments = '-c .')
+
+    test.fail_test(os.path.exists(test.workpath('foo.h')))
+
 test.pass_test()
index bde101a31f0562f9184bb83dc0dee70564fdb93d..9cf24d875cb8fcf1666fb5e735cff14c00e02e61 100644 (file)
@@ -25,6 +25,7 @@
 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 
 import os
+import os.path
 import string
 import sys
 import TestSCons
@@ -32,8 +33,12 @@ import TestSCons
 test = TestSCons.TestSCons()
 
 if sys.platform == 'win32':
+    lib_=''
+    _dll = '.dll'
     linkflag = '/LIBPATH:' + test.workpath()
 else:
+    lib_='lib'
+    _dll='.so'
     linkflag = '-L' + test.workpath()
 
 test.write('SConstruct', """
@@ -42,6 +47,8 @@ while len(linkflags) <= 8100:
     linkflags = linkflags + r' %s'
 env = Environment(LINKFLAGS = '$LINKXXX', LINKXXX = linkflags)
 env.Program(target = 'foo', source = 'foo.c')
+# Library(shared=1) uses $LINKFLAGS by default.
+env.Library(target = 'bar', source = 'foo.c', shared=1)
 """ % (linkflag, linkflag))
 
 test.write('foo.c', r"""
@@ -58,4 +65,6 @@ test.run(arguments = '.')
 
 test.run(program = test.workpath('foo'), stdout = "foo.c\n")
 
+test.fail_test(not os.path.exists(lib_+'bar'+_dll))
+
 test.pass_test()