summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorSteven Knight <knight@baldmt.com>2004-09-15 19:47:18 (GMT)
committerSteven Knight <knight@baldmt.com>2004-09-15 19:47:18 (GMT)
commit03ddfaadff09b7702a1406b11f90dbdc45516309 (patch)
tree853588be52f996ea8d6282fb47169aa21e57bb39 /src
parent09b8479c9e3be998942bb4c9d1e6e02b78ecf5e3 (diff)
downloadSCons-03ddfaadff09b7702a1406b11f90dbdc45516309.zip
SCons-03ddfaadff09b7702a1406b11f90dbdc45516309.tar.gz
SCons-03ddfaadff09b7702a1406b11f90dbdc45516309.tar.bz2
Fix suffix selection when there's no source file. (Kevin Quick)
Diffstat (limited to 'src')
-rw-r--r--src/CHANGES.txt6
-rw-r--r--src/engine/SCons/BuilderTests.py158
-rw-r--r--src/engine/SCons/Script/__init__.py12
-rw-r--r--src/engine/SCons/Util.py86
-rw-r--r--src/engine/SCons/UtilTests.py9
5 files changed, 247 insertions, 24 deletions
diff --git a/src/CHANGES.txt b/src/CHANGES.txt
index 83ec410..4064121 100644
--- a/src/CHANGES.txt
+++ b/src/CHANGES.txt
@@ -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
diff --git a/src/engine/SCons/BuilderTests.py b/src/engine/SCons/BuilderTests.py
index d7d7747..913e6d4 100644
--- a/src/engine/SCons/BuilderTests.py
+++ b/src/engine/SCons/BuilderTests.py
@@ -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):
diff --git a/src/engine/SCons/Script/__init__.py b/src/engine/SCons/Script/__init__.py
index 6ae76bf..5dd907c 100644
--- a/src/engine/SCons/Script/__init__.py
+++ b/src/engine/SCons/Script/__init__.py
@@ -70,18 +70,6 @@ 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()
diff --git a/src/engine/SCons/Util.py b/src/engine/SCons/Util.py
index 8707ed4..df2f604 100644
--- a/src/engine/SCons/Util.py
+++ b/src/engine/SCons/Util.py
@@ -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
-
-
diff --git a/src/engine/SCons/UtilTests.py b/src/engine/SCons/UtilTests.py
index d187390..2b5fdef 100644
--- a/src/engine/SCons/UtilTests.py
+++ b/src/engine/SCons/UtilTests.py
@@ -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'])