Fix suffix selection when there's no source file. (Kevin Quick)
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Wed, 15 Sep 2004 19:47:18 +0000 (19:47 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Wed, 15 Sep 2004 19:47:18 +0000 (19:47 +0000)
git-svn-id: http://scons.tigris.org/svn/scons/trunk@1072 fdb21ef1-2011-0410-befe-b5e4ea1792b1

src/CHANGES.txt
src/engine/SCons/BuilderTests.py
src/engine/SCons/Script/__init__.py
src/engine/SCons/Util.py
src/engine/SCons/UtilTests.py

index 83ec410f9e01f3cfe3c00725eebc4773a1ebd8e2..4064121e92e286dcfd778acf5974dc8165b33111 100644 (file)
@@ -59,6 +59,9 @@ RELEASE 0.97 - XXX
     $RPCGEN, $RPCGENFLAGS, $RPCGENCLIENTFLAGS, $RPCGENHEADERFLAGS,
     $RPCGENSERVICEFLAGS, $RPCGENXDRFLAGS.
 
+  - Update the man page to document that prefix and suffix Builder
+    keyword arguments can be strings, callables or dictionaries.
+
   - Provide more info in the error message when a user tries to build
     a target multiple ways.
 
@@ -69,6 +72,9 @@ RELEASE 0.97 - XXX
 
   - Only replace a Node's builder with a non-null source builder.
 
+  - Fix a stack trace when a suffix selection dictionary is passed
+    an empty source file list.
+
   From Christoph Wiedemann:
 
   - Add an Environment.SetDefault() method that only sets values if
index d7d7747d2484464fc389082f8e3324596f30b236..913e6d4fe3e9c674a3cb2ea7875921d79ed7545c 100644 (file)
@@ -855,6 +855,164 @@ class BuilderTestCase(unittest.TestCase):
         assert src.source_scanner is None, src.source_scanner
         assert src.backup_source_scanner == scanner, src.backup_source_scanner
 
+    def test_Builder_API(self):
+        """Test Builder interface.
+
+        Some of this is tested elsewhere in this file, but this is a
+        quick collection of common operations on builders with various
+        forms of component specifications."""
+
+        builder = SCons.Builder.Builder()
+
+        env = Environment(BUILDERS={'Bld':builder})
+        r = builder.get_name(env)
+        assert r == 'Bld', r
+        r = builder.get_prefix(env)
+        assert r == '', r
+        r = builder.get_suffix(env)
+        assert r == '', r
+        r = builder.get_src_suffix(env)
+        assert r == '', r
+        r = builder.src_suffixes(env)
+        assert r == [], r
+        r = builder.targets('foo')
+        assert r == ['foo'], r
+
+        # src_suffix can be a single string or a list of strings
+
+        builder.set_src_suffix('.foo')
+        r = builder.get_src_suffix(env)
+        assert r == '.foo', r
+        r = builder.src_suffixes(env)
+        assert r == ['.foo'], r
+
+        builder.set_src_suffix(['.foo', '.bar'])
+        r = builder.get_src_suffix(env)
+        assert r == '.foo', r
+        r = builder.src_suffixes(env)
+        assert r == ['.foo', '.bar'], r
+
+        builder.set_src_suffix(['.bar', '.foo'])
+        r = builder.get_src_suffix(env)
+        assert r == '.bar', r
+        r = builder.src_suffixes(env)
+        assert r == ['.bar', '.foo'], r
+
+        # adjust_suffix normalizes the suffix, adding a `.' if needed
+
+        r = builder.adjust_suffix('.foo')
+        assert r == '.foo', r
+        r = builder.adjust_suffix('_foo')
+        assert r == '_foo', r
+        r = builder.adjust_suffix('$foo')
+        assert r == '$foo', r
+        r = builder.adjust_suffix('foo')
+        assert r == '.foo', r
+        r = builder.adjust_suffix('f._$oo')
+        assert r == '.f._$oo', r
+
+        # prefix and suffix can be one of:
+        #   1. a string (adjusted and env variables substituted),
+        #   2. a function (passed (env,sources), returns suffix string)
+        #   3. a dict of src_suffix:suffix settings, key==None is
+        #      default suffix (special case of #2, so adjust_suffix
+        #      not applied)
+
+        builder = SCons.Builder.Builder(prefix='lib', suffix='foo')
+
+        env = Environment(BUILDERS={'Bld':builder})
+        r = builder.get_name(env)
+        assert r == 'Bld', r
+        r = builder.get_prefix(env)
+        assert r == 'lib', r
+        r = builder.get_suffix(env)
+        assert r == '.foo', r
+
+        mkpref = lambda env,sources: 'Lib'
+        mksuff = lambda env,sources: '.Foo'
+        builder = SCons.Builder.Builder(prefix=mkpref, suffix=mksuff)
+
+        env = Environment(BUILDERS={'Bld':builder})
+        r = builder.get_name(env)
+        assert r == 'Bld', r
+        r = builder.get_prefix(env)
+        assert r == 'Lib', r
+        r = builder.get_suffix(env)
+        assert r == '.Foo', r
+
+        builder = SCons.Builder.Builder(prefix='$PREF', suffix='$SUFF')
+
+        env = Environment(BUILDERS={'Bld':builder},PREF="LIB",SUFF=".FOO")
+        r = builder.get_name(env)
+        assert r == 'Bld', r
+        r = builder.get_prefix(env)
+        assert r == 'LIB', r
+        r = builder.get_suffix(env)
+        assert r == '.FOO', r
+
+        builder = SCons.Builder.Builder(prefix={None:'A_',
+                                                '.C':'E_'},
+                                        suffix={None:'.B',
+                                                '.C':'.D'})
+
+        env = Environment(BUILDERS={'Bld':builder})
+        r = builder.get_name(env)
+        assert r == 'Bld', r
+        r = builder.get_prefix(env)
+        assert r == 'A_', r
+        r = builder.get_suffix(env)
+        assert r == '.B', r
+        r = builder.get_prefix(env, ['X.C'])
+        assert r == 'E_', r
+        r = builder.get_suffix(env, ['X.C'])
+        assert r == '.D', r
+
+        builder = SCons.Builder.Builder(prefix='A_', suffix={}, action={})
+
+        env = Environment(BUILDERS={'Bld':builder})
+        r = builder.get_name(env)
+        assert r == 'Bld', r
+        r = builder.get_prefix(env)
+        assert r == 'A_', r
+        r = builder.get_suffix(env)
+        assert r == None, r
+        r = builder.get_src_suffix(env)
+        assert r == '', r
+        r = builder.src_suffixes(env)
+        assert r == [], r
+
+        # Builder actions can be a string, a list, or a dictionary
+        # whose keys are the source suffix.  The add_action()
+        # specifies a new source suffix/action binding.
+
+        builder.add_action('.src_sfx1', 'FOO')
+        r = builder.get_name(env)
+        assert r == 'Bld', r
+        r = builder.get_prefix(env)
+        assert r == 'A_', r
+        r = builder.get_suffix(env)
+        assert r == None, r
+        r = builder.get_suffix(env, ['X.src_sfx1'])
+        assert r == None, r
+        r = builder.get_src_suffix(env)
+        assert r == '.src_sfx1', r
+        r = builder.src_suffixes(env)
+        assert r == ['.src_sfx1'], r
+
+        builder.add_action('.src_sfx2', 'BAR')
+
+        r = builder.get_name(env)
+        assert r == 'Bld', r
+        r = builder.get_prefix(env)
+        assert r == 'A_', r
+        r = builder.get_suffix(env)
+        assert r ==  None, r
+        r = builder.get_src_suffix(env)
+        assert r == '.src_sfx1', r
+        r = builder.src_suffixes(env)
+        assert r == ['.src_sfx1', '.src_sfx2'], r
+
+
     def test_Builder_Args(self):
         """Testing passing extra args to a builder."""
         def buildFunc(target, source, env, s=self):
index 6ae76bf68d45f68d3de52ec333cb6e4ca6c7cd4a..5dd907c45953521d9be96bd2eb6603a29d6e5427 100644 (file)
@@ -69,18 +69,6 @@ import SCons.Taskmaster
 import SCons.Util
 import SCons.Warnings
 
-#
-import __builtin__
-try:
-    __builtin__.zip
-except AttributeError:
-    def zip(*lists):
-        result = []
-        for i in xrange(len(lists[0])):
-            result.append(tuple(map(lambda l, i=i: l[i], lists)))
-        return result
-    __builtin__.zip = zip
-
 #
 display = SCons.Util.display
 progress_display = SCons.Util.DisplayEngine()
index 8707ed4e03497e85dce5d922ec3d673e1b972e1a..df2f604053d1b3b65793b418a63c2faaad5c7bfc 100644 (file)
@@ -38,7 +38,7 @@ import stat
 import string
 import sys
 import types
-import UserDict
+from UserDict import UserDict
 import UserList
 
 import SCons.Errors
@@ -94,6 +94,18 @@ except ImportError:
             return self.__class__(self.data*n)
         __rmul__ = __mul__
 
+#
+import __builtin__
+try:
+    __builtin__.zip
+except AttributeError:
+    def zip(*lists):
+        result = []
+        for i in xrange(len(lists[0])):
+            result.append(tuple(map(lambda l, i=i: l[i], lists)))
+        return result
+    __builtin__.zip = zip
+
 _altsep = os.altsep
 if _altsep is None and sys.platform == 'win32':
     # My ActivePython 2.0.1 doesn't set os.altsep!  What gives?
@@ -952,7 +964,7 @@ def render_tree(root, child_func, prune=0, margin=[0], visited={}):
     return retval
 
 def is_Dict(e):
-    return type(e) is types.DictType or isinstance(e, UserDict.UserDict)
+    return type(e) is types.DictType or isinstance(e, UserDict)
 
 def is_List(e):
     return type(e) is types.ListType or isinstance(e, UserList.UserList)
@@ -1333,16 +1345,74 @@ class CLVar(UserList.UserList):
     def __str__(self):
         return string.join(self.data)
 
-class Selector(UserDict.UserDict):
-    """A callable dictionary that maps file suffixes to dictionary
-    values."""
+# A dictionary that preserves the order in which items are added.
+# Submitted by David Benjamin to ActiveState's Python Cookbook web site:
+#     http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/107747
+# Including fixes/enhancements from the follow-on discussions.
+class OrderedDict(UserDict):
+    def __init__(self, dict = None):
+        self._keys = []
+        UserDict.__init__(self, dict)
+
+    def __delitem__(self, key):
+        UserDict.__delitem__(self, key)
+        self._keys.remove(key)
+
+    def __setitem__(self, key, item):
+        UserDict.__setitem__(self, key, item)
+        if key not in self._keys: self._keys.append(key)
+
+    def clear(self):
+        UserDict.clear(self)
+        self._keys = []
+
+    def copy(self):
+        dict = OrderedDict()
+        dict.update(self)
+        return dict
+
+    def items(self):
+        return zip(self._keys, self.values())
+
+    def keys(self):
+        return self._keys[:]
+
+    def popitem(self):
+        try:
+            key = self._keys[-1]
+        except IndexError:
+            raise KeyError('dictionary is empty')
+
+        val = self[key]
+        del self[key]
+
+        return (key, val)
+
+    def setdefault(self, key, failobj = None):
+        UserDict.setdefault(self, key, failobj)
+        if key not in self._keys: self._keys.append(key)
+
+    def update(self, dict):
+        for (key, val) in dict.items():
+            self.__setitem__(key, val)
+
+    def values(self):
+        return map(self.get, self._keys)
+
+class Selector(OrderedDict):
+    """A callable ordered dictionary that maps file suffixes to
+    dictionary values.  We preserve the order in which items are added
+    so that get_suffix() calls always return the first suffix added."""
     def __call__(self, env, source):
-        ext = splitext(str(source[0]))[1]
+        try:
+            ext = splitext(str(source[0]))[1]
+        except IndexError:
+            ext = ""
         try:
             return self[ext]
         except KeyError:
             # Try to perform Environment substitution on the keys of
-            # emitter_dict before giving up.
+            # the dictionary before giving up.
             s_dict = {}
             for (k,v) in self.items():
                 if not k is None:
@@ -1450,5 +1520,3 @@ def unique(s):
         if x not in u:
             u.append(x)
     return u
-
-
index d1873908c110abafbf2e1f30476ff6363e1b149f..2b5fdefd0b2dfc0fc990f48156950dbdc0fe6532 100644 (file)
@@ -29,6 +29,8 @@ import string
 import sys
 import types
 import unittest
+from UserDict import UserDict
+
 from SCons.Util import *
 import TestCmd
 
@@ -960,8 +962,7 @@ class UtilTestCase(unittest.TestCase):
 
     def test_is_Dict(self):
         assert is_Dict({})
-        import UserDict
-        assert is_Dict(UserDict.UserDict())
+        assert is_Dict(UserDict())
         assert not is_Dict([])
         assert not is_Dict("")
         if hasattr(types, 'UnicodeType'):
@@ -1492,7 +1493,7 @@ class UtilTestCase(unittest.TestCase):
         s['c'] = 'CCC'
         assert s['c'] == 'CCC', s['c']
 
-        class DummyEnv(UserDict.UserDict):
+        class DummyEnv(UserDict):
             def subst(self, key):
                 if key[0] == '$':
                     return self[key[1:]]
@@ -1501,6 +1502,8 @@ class UtilTestCase(unittest.TestCase):
         env = DummyEnv()
 
         s = Selector({'.d' : 'DDD', '.e' : 'EEE'})
+        ret = s(env, [])
+        assert ret == None, ret
         ret = s(env, ['foo.d'])
         assert ret == 'DDD', ret
         ret = s(env, ['bar.e'])