Fix the new signature dictionary so it handles slices and attributes such as abspath.
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Wed, 12 Mar 2003 17:48:40 +0000 (17:48 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Wed, 12 Mar 2003 17:48:40 +0000 (17:48 +0000)
git-svn-id: http://scons.tigris.org/svn/scons/trunk@610 fdb21ef1-2011-0410-befe-b5e4ea1792b1

src/engine/SCons/Environment.py
src/engine/SCons/Util.py
src/engine/SCons/UtilTests.py

index fdfa8b2e87dd805d3215f75c97e512637b968145..8336f7451b279bbfd7f87952c8d4719c5cfcd6c0 100644 (file)
@@ -549,31 +549,10 @@ class Environment:
         This fills in static TARGET, TARGETS, SOURCE and SOURCES
         variables so that signatures stay the same every time.
         """
-        class lister:
-            def __init__(self, fmt):
-                self.fmt = fmt
-            def _format(self, index):
-                # For some reason, I originally made the fake names of
-                # the targets and sources 1-based (['__t1__, '__t2__']),
-                # not 0-based.  We preserve this behavior by adding one
-                # to the returned item names, so everyone's targets
-                # won't get recompiled if they were using an old
-                # version.
-                return self.fmt % (index + 1)
-            def __str__(self):
-                return self._format(0) + " " + self._format(1)
-            def __getitem__(self, index):
-                return SCons.Util.PathList([self._format(index)])[0]
-            def __getslice__(self, i, j):
-                slice = []
-                for x in range(i, j):
-                    slice.append(self._format(x))
-                return SCons.Util.PathList(slice)
-
         dict = {}
         for k,v in self.items(): dict[k] = v
-        dict['TARGETS'] = lister('__t%d__')
+        dict['TARGETS'] = SCons.Util.Lister('__t%d__')
         dict['TARGET'] = dict['TARGETS'][0]
-        dict['SOURCES'] = lister('__s%d__')
+        dict['SOURCES'] = SCons.Util.Lister('__s%d__')
         dict['SOURCE'] = dict['SOURCES'][0]
         return dict
index fcadf48d3d19f7d899ba388719ed07b0ad47658e..b03ff0f9090df43c3cf4f0408933c8154d58a367 100644 (file)
@@ -133,8 +133,10 @@ class PathList(UserList.UserList):
             first_part, second_part = split_func(strPath)
             list1.append(first_part)
             list2.append(second_part)
-        return (self.__class__(list1),
-                self.__class__(list2))
+        # Note that we return explicit PathList() instances, not
+        # self.__class__().  This makes sure the right attributes are
+        # available even if this object is a Lister, not a PathList.
+        return (PathList(list1), PathList(list2))
 
     def __getBasePath(self):
         """Return the file's directory and file name, with the
@@ -159,7 +161,10 @@ class PathList(UserList.UserList):
 
     def __getAbsPath(self):
         """Return the absolute path"""
-        return map(lambda x: updrive(os.path.abspath(x)), self.data)
+        # Note that we return an explicit PathList() instance, not
+        # self.__class__().  This makes sure the right attributes are
+        # available even if this object is a Lister, not a PathList.
+        return PathList(map(lambda x: updrive(os.path.abspath(x)), self.data))
 
     dictSpecialAttrs = { "file" : __getFileName,
                          "base" : __getBasePath,
@@ -174,6 +179,12 @@ class PathList(UserList.UserList):
     def __str__(self):
         return string.join(self.data)
 
+    def to_String(self):
+        # Used by our variable-interpolation to interpolate a string.
+        # The interpolation doesn't use __str__() for this because then
+        # it interpolates other lists as "['x', 'y']".
+        return string.join(self.data)
+
     def __repr__(self):
         return repr(string.join(self.data))
 
@@ -183,6 +194,49 @@ class PathList(UserList.UserList):
         # suffix and basepath.
         return self.__class__([ UserList.UserList.__getitem__(self, item), ])
 
+class Lister(PathList):
+    """A special breed of fake list that not only supports the inherited
+    "path dissection" attributes of PathList, but also uses a supplied
+    format string to generate arbitrary (slices of) individually-named
+    elements on the fly.
+    """
+    def __init__(self, fmt):
+        self.fmt = fmt
+        PathList.__init__(self, [ self._element(0), self._element(1) ])
+    def __getitem__(self, index):
+        return PathList([self._element(index)])
+    def _element(self, index):
+        """Generate the index'th element in this list."""
+        # For some reason, I originally made the fake names of
+        # the targets and sources 1-based (['__t1__, '__t2__']),
+        # not 0-based.  We preserve this behavior by adding one
+        # to the returned item names, so everyone's targets
+        # won't get recompiled if they were using an old version.
+        return self.fmt % (index + 1)
+    def __iter__(self):
+        """Return an iterator object for Python 2.2."""
+        class Lister_iter:
+            def __init__(self, data):
+                self.data = data
+                self.index = 0
+            def __iter__(self):
+                return self
+            def next(self):
+                try:
+                    element = self.data[self.index]
+                except IndexError:
+                    raise StopIteration
+                self.index = self.index + 1
+                return element
+        return Lister_iter(self.data)
+    def __getslice__(self, i, j):
+        slice = []
+        if j == sys.maxint:
+            j = i + 2
+        for x in range(i, j):
+            slice.append(self._element(x))
+        return PathList(slice)
+
 _env_var = re.compile(r'^\$([_a-zA-Z]\w*|{[^}]*})$')
 
 def get_environment_var(varstr):
@@ -378,7 +432,10 @@ def scons_subst_list(strSubst, globals, locals, remove=None):
         elif is_String(x):
             return _space_sep.sub('\0', x)
         elif is_List(x):
-            return string.join(map(to_String, x), '\0')
+            try:
+                return x.to_String()
+            except AttributeError:
+                return string.join(map(to_String, x), '\0')
         else:
             return to_String(x)
 
@@ -439,7 +496,10 @@ def scons_subst(strSubst, globals, locals, remove=None):
             if e is None:
                 s = ''
             elif is_List(e):
-                s = string.join(map(to_String, e), ' ')
+                try:
+                    s = e.to_String()
+                except AttributeError:
+                    s = string.join(map(to_String, e), ' ')
             else:
                 s = to_String(e)
         except NameError:
index 0e81a1d8c21bac4201cb4502b720ff455fc65a5e..f77265a71c1e7a84f051dc12d0f18a2ad6ba8ad2 100644 (file)
@@ -45,9 +45,9 @@ class OutBuffer:
 
 
 class UtilTestCase(unittest.TestCase):
-    def test_subst(self):
-       """Test the subst function."""
-       loc = {}
+    def test_subst_PathList(self):
+        """Test the subst function with PathLists"""
+        loc = {}
         loc['TARGETS'] = PathList(map(os.path.normpath, [ "./foo/bar.exe",
                                                           "/bar/baz.obj",
                                                           "../foo/baz.obj" ]))
@@ -97,16 +97,18 @@ class UtilTestCase(unittest.TestCase):
         newcom = scons_subst("test ${TARGET.dir}", loc, {})
         assert newcom == cvt("test foo")
 
+        cwd = SCons.Util.updrive(os.getcwd())
+
         newcom = scons_subst("test ${TARGET.abspath}", loc, {})
-        assert newcom == cvt("test %s/foo/bar.exe"%SCons.Util.updrive(os.getcwd())), newcom
+        assert newcom == cvt("test %s/foo/bar.exe" % (cwd)), newcom
 
         newcom = scons_subst("test ${SOURCES.abspath}", loc, {})
-        assert newcom == cvt("test %s/foo/blah.cpp %s %s/foo/ack.c"%(SCons.Util.updrive(os.getcwd()),
+        assert newcom == cvt("test %s/foo/blah.cpp %s %s/foo/ack.c"%(cwd,
                                                                      SCons.Util.updrive(os.path.abspath(os.path.normpath("/bar/ack.cpp"))),
                                                                      SCons.Util.updrive(os.path.normpath(os.getcwd()+"/..")))), newcom
 
         newcom = scons_subst("test ${SOURCE.abspath}", loc, {})
-        assert newcom == cvt("test %s/foo/blah.cpp"%SCons.Util.updrive(os.getcwd())), newcom
+        assert newcom == cvt("test %s/foo/blah.cpp" % (cwd)), newcom
 
         newcom = scons_subst("test $xxx", loc, {})
         assert newcom == cvt("test"), newcom
@@ -151,6 +153,65 @@ class UtilTestCase(unittest.TestCase):
         newcom = scons_subst("$$FOO$BAZ", glob, {})
         assert newcom == "$FOOBLAT", newcom
 
+    def test_subst_Lister(self):
+        """Test the subst function with Listers"""
+        loc = {}
+        loc['TARGETS'] = Lister('t%d')
+        loc['TARGET'] = loc['TARGETS'][0]
+        loc['SOURCES'] = Lister('s%d')
+        loc['SOURCE'] = loc['SOURCES'][0]
+        loc['xxx'] = None
+        loc['zero'] = 0
+        loc['one'] = 1
+
+        if os.sep == '/':
+            def cvt(str):
+                return str
+        else:
+            def cvt(str):
+                return string.replace(str, '/', os.sep)
+
+        newcom = scons_subst("test $TARGETS $SOURCES", loc, {})
+        assert newcom == cvt("test t1 t2 s1 s2"), newcom
+
+        newcom = scons_subst("test ${TARGETS[:]} ${SOURCES[0]}", loc, {})
+        assert newcom == cvt("test t1 t2 s1"), newcom
+
+        newcom = scons_subst("test ${TARGETS[1:]}v", loc, {})
+        assert newcom == cvt("test t2 t3v"), newcom
+
+        newcom = scons_subst("test $TARGET", loc, {})
+        assert newcom == cvt("test t1"), newcom
+
+        newcom = scons_subst("test $TARGET$FOO[0]", loc, {})
+        assert newcom == cvt("test t1[0]"), newcom
+
+        newcom = scons_subst("test ${TARGET.file}", loc, {})
+        assert newcom == cvt("test t1"), newcom
+
+        newcom = scons_subst("test ${TARGET.filebase}", loc, {})
+        assert newcom == cvt("test t1"), newcom
+
+        newcom = scons_subst("test ${TARGET.suffix}", loc, {})
+        assert newcom == cvt("test"), newcom
+
+        newcom = scons_subst("test ${TARGET.base}", loc, {})
+        assert newcom == cvt("test t1"), newcom
+
+        newcom = scons_subst("test ${TARGET.dir}", loc, {})
+        assert newcom == cvt("test"), newcom
+
+        cwd = SCons.Util.updrive(os.getcwd())
+
+        newcom = scons_subst("test ${TARGET.abspath}", loc, {})
+        assert newcom == cvt("test %s/t1" % (cwd)), newcom
+
+        newcom = scons_subst("test ${SOURCES.abspath}", loc, {})
+        assert newcom == cvt("test %s/s1 %s/s2" % (cwd, cwd)), newcom
+
+        newcom = scons_subst("test ${SOURCE.abspath}", loc, {})
+        assert newcom == cvt("test %s/s1" % cwd), newcom
+
     def test_splitext(self):
         assert splitext('foo') == ('foo','')
         assert splitext('foo.bar') == ('foo','.bar')