Add a .win32 attribute for FS entries; preserve Literal() attributes when concatenati...
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Wed, 21 Jul 2004 13:06:51 +0000 (13:06 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Wed, 21 Jul 2004 13:06:51 +0000 (13:06 +0000)
git-svn-id: http://scons.tigris.org/svn/scons/trunk@1007 fdb21ef1-2011-0410-befe-b5e4ea1792b1

src/CHANGES.txt
src/engine/SCons/Node/FS.py
src/engine/SCons/Node/FSTests.py
src/engine/SCons/Util.py
src/engine/SCons/UtilTests.py

index ab32ceffe96b1ce207a8fb092ec4da94c73fc315..73c70475924001207087320d5d216f4a17c1b17d 100644 (file)
@@ -139,6 +139,14 @@ RELEASE 0.96 - XXX
     arguments when creating Builder objects.  Enhance Dir Nodes so that
     they can be created with user-specified Builder objects.
 
+  From Chris Murray:
+
+  - Add a .win32 attribute to force file names to expand with
+    Windows backslash path separators.
+
+  - Fix escaping file names on command lines when the expansion is
+    concatenated with another string.
+
   From Gary Oberbrunner:
 
   - Add a --debug=presub option to print actions prior to substitution.
index 72ed154dbe3394e7c1c3ca373d32ec5e2b48dc6a..1cb77792b2398fea5d34bbd901f100be1c9a922c 100644 (file)
@@ -348,15 +348,24 @@ class EntryProxy(SCons.Util.Proxy):
                                              entry.name + "_base")
 
     def __get_posix_path(self):
-        """Return the path with / as the path separator, regardless
-        of platform."""
+        """Return the path with / as the path separator,
+        regardless of platform."""
         if os.sep == '/':
             return self
         else:
             entry = self.get()
-            return SCons.Util.SpecialAttrWrapper(string.replace(entry.get_path(),
-                                                                os.sep, '/'),
-                                                 entry.name + "_posix")
+            r = string.replace(entry.get_path(), os.sep, '/')
+            return SCons.Util.SpecialAttrWrapper(r, entry.name + "_posix")
+
+    def __get_win32_path(self):
+        """Return the path with \ as the path separator,
+        regardless of platform."""
+        if os.sep == '\\':
+            return self
+        else:
+            entry = self.get()
+            r = string.replace(entry.get_path(), os.sep, '\\')
+            return SCons.Util.SpecialAttrWrapper(r, entry.name + "_win32")
 
     def __get_srcnode(self):
         return EntryProxy(self.get().srcnode())
@@ -379,6 +388,7 @@ class EntryProxy(SCons.Util.Proxy):
     
     dictSpecialAttrs = { "base"     : __get_base_path,
                          "posix"    : __get_posix_path,
+                         "win32"    : __get_win32_path,
                          "srcpath"  : __get_srcnode,
                          "srcdir"   : __get_srcdir,
                          "dir"      : __get_dir,
index 231d736972e2d88a6bbe74f1ca25a9afe06c4952..4a868b90e1b936a4b217f4ad82dffec5aeea2213 100644 (file)
@@ -1522,7 +1522,6 @@ class prepareTestCase(unittest.TestCase):
         assert dir_made == [], dir_made
         xyz.set_state(0)
         xyz.prepare()
-        print "dir_made[0] =", dir_made[0]
         assert dir_made[0].path == "new_dir", dir_made[0]
 
         dir = fs.Dir("dir")
@@ -1836,6 +1835,13 @@ class SpecialAttrTestCase(unittest.TestCase):
             for_sig = f.posix.for_signature()
             assert for_sig == 'baz.blat_posix', for_sig
 
+        s = str(f.win32)
+        assert s == 'foo\\bar\\baz.blat', repr(s)
+        assert f.win32.is_literal(), f.win32
+        if f.win32 != f:
+            for_sig = f.win32.for_signature()
+            assert for_sig == 'baz.blat_win32', for_sig
+
         # And now, combinations!!!
         s = str(f.srcpath.base)
         assert s == os.path.normpath('foo/bar/baz'), s
@@ -1843,6 +1849,8 @@ class SpecialAttrTestCase(unittest.TestCase):
         assert s == str(f.srcdir), s
         s = str(f.srcpath.posix)
         assert s == 'foo/bar/baz.blat', s
+        s = str(f.srcpath.win32)
+        assert s == 'foo\\bar\\baz.blat', s
 
         # Test what happens with BuildDir()
         fs.BuildDir('foo', 'baz')
index 636a74fbe9d612a6452d82bafd602040c92ad79d..49e52ffa6ee3656575b07a5b4726e60c73afbfd8 100644 (file)
@@ -638,8 +638,8 @@ def scons_subst_list(strSubst, env, mode=SUBST_RAW, target=None, source=None, di
     class ListSubber(UserList.UserList):
         """A class to construct the results of a scons_subst_list() call.
 
-        Like StringSubber, this class binds a specific binds a specific
-        construction environment, mode, target and source with two methods
+        Like StringSubber, this class binds a specific construction 
+        environment, mode, target and source with two methods
         (substitute() and expand()) that handle the expansion.
 
         In addition, however, this class is used to track the state of
@@ -677,6 +677,7 @@ def scons_subst_list(strSubst, env, mode=SUBST_RAW, target=None, source=None, di
             the results of expansions of side-by-side strings still get
             re-evaluated separately, not smushed together.
             """
+
             if is_String(s):
                 try:
                     s0, s1 = s[:2]
@@ -737,6 +738,7 @@ def scons_subst_list(strSubst, env, mode=SUBST_RAW, target=None, source=None, di
             This serves as a wrapper for splitting up a string into
             separate tokens.
             """
+
             if is_String(args) and not isinstance(args, CmdStringHolder):
                 args = _separate_args.findall(args)
                 for a in args:
@@ -758,38 +760,58 @@ def scons_subst_list(strSubst, env, mode=SUBST_RAW, target=None, source=None, di
             another line to the result."""
             UserList.UserList.append(self, [])
             self.next_word()
+
         def this_word(self):
             """Arrange for the next word to append to the end of the
             current last word in the result."""
             self.append = self.add_to_current_word
+
         def next_word(self):
             """Arrange for the next word to start a new word."""
             self.append = self.add_new_word
 
         def add_to_current_word(self, x):
+            """Append the string x to the end of the current last word
+            in the result.  If that is not possible, then just add
+            it as a new word.  Make sure the entire concatenated string
+            inherits the object attributes of x (in particular, the 
+            escape function) by wrapping it as CmdStringHolder."""
+
             if not self.in_strip or self.mode != SUBST_SIG:
                 try:
-                    self[-1][-1] = self[-1][-1] + x
+                    y = self[-1][-1] + x
                 except IndexError:
                     self.add_new_word(x)
+                else:
+                    literal1 = self.literal(self[-1][-1])
+                    literal2 = self.literal(x)
+                    y = self.conv(y)
+                    if is_String(y):
+                        y = CmdStringHolder(y, literal1 or literal2)
+                    self[-1][-1] = y
+
         def add_new_word(self, x):
             if not self.in_strip or self.mode != SUBST_SIG:
-                try:
-                    l = x.is_literal
-                except AttributeError:
-                    literal = None
-                else:
-                    literal = l()
+                literal = self.literal(x)
                 x = self.conv(x)
                 if is_String(x):
                     x = CmdStringHolder(x, literal)
                 self[-1].append(x)
             self.append = self.add_to_current_word
 
+        def literal(self, x):
+            try:
+                l = x.is_literal
+            except AttributeError:
+                return None
+            else:
+                return l()
+
         def open_strip(self, x):
             """Handle the "open strip" $( token."""
             self.add_strip(x)
             self.in_strip = 1
+
         def close_strip(self, x):
             """Handle the "close strip" $) token."""
             self.add_strip(x)
@@ -909,14 +931,35 @@ else:
 
 class Proxy:
     """A simple generic Proxy class, forwarding all calls to
-    subject.  Inherit from this class to create a Proxy."""
+    subject.  So, for the benefit of the python newbie, what does
+    this really mean?  Well, it means that you can take an object, let's
+    call it 'objA', and wrap it in this Proxy class, with a statement
+    like this 
+
+                 proxyObj = Proxy(objA),   
+
+    Then, if in the future, you do something like this  
+
+                 x = proxyObj.var1, 
+
+    since Proxy does not have a 'var1' attribute (but presumably objA does), 
+    the request actually is equivalent to saying 
+                
+                 x = objA.var1
+
+    Inherit from this class to create a Proxy."""
+
     def __init__(self, subject):
+        """Wrap an object as a Proxy object"""
         self.__subject = subject
 
     def __getattr__(self, name):
+        """Retrieve an attribute from the wrapped object.  If the named
+           attribute doesn't exist, AttributeError is raised"""
         return getattr(self.__subject, name)
 
     def get(self):
+        """Retrieve the entire wrapped object"""
         return self.__subject
 
 # attempt to load the windows registry module:
index 846aaaa2bd41bef1e393cd9f9eb43c6864b028a0..713f522716207b00d291c5be04adfccdffe89ea2 100644 (file)
@@ -706,6 +706,12 @@ class UtilTestCase(unittest.TestCase):
         c = cmd_list[0][3].escape(escape_func)
         assert c == 'xyz', c
 
+        cmd_list = scons_subst_list("abc${LITERALS}xyz", env)
+        c = cmd_list[0][0].escape(escape_func)
+        assert c == '**abcfoo\nwith\nnewlines**', c
+        c = cmd_list[0][1].escape(escape_func)
+        assert c == '**bar\nwith\nnewlinesxyz**', c
+
         # Tests of the various SUBST_* modes of substitution.
         subst_list_cases = [
             "test $xxx",