Commits

Anonymous committed 91b653c

Return lists of Nodes from all builders, not single Nodes when there's only one.

Comments (0)

Files changed (45)

 .B scons
 has determined are appropriate for the local system.
 
-All builder methods return a Node or a list of Nodes,
-representing the target or targets that will be built.
-A list of Nodes is returned if there is more than one target,
-and a single Node is returned if there is only one target.
+All builder methods return a list of Nodes
+that represent the target or targets that will be built.
 A
 .I Node
 is an internal SCons object
 flag when compiling one specific object file:
 
 .ES
-bar_obj = env.StaticObject('bar.c', CCFLAGS='-DBAR')
-env.Program(source = ['foo.c', bar_obj, 'main.c'])
+bar_obj_list = env.StaticObject('bar.c', CCFLAGS='-DBAR')
+env.Program(source = ['foo.c', bar_obj_list, 'main.c'])
 .EE
 
 Using a Node in this way
 a platform-specific object suffix
 when calling the Program() builder method.
 
+(Note that Builder calls will "flatten" the lists
+the source and target file list,
+so it's all right to have the bar_obj list
+return by the StaticObject() call
+in the middle of the source file list.)
+
 The path name for a Node's file may be used
 by passing the Node to the Python-builtin
 .B str()
 function:
 
 .ES
-bar_obj = env.StaticObject('bar.c', CCFLAGS='-DBAR')
-print "The path to bar_obj is:", str(bar_obj)
-.EE
+bar_obj_list = env.StaticObject('bar.c', CCFLAGS='-DBAR')
+print "The path to bar_obj is:", str(bar_obj_list[0])
+.EE
+
+Note again that because the Builder call returns a list,
+we have to access the first element in the list
+.B (bar_obj_list[0])
+to get at the Node that actually represents
+the object file.
 
 .B scons
 provides the following builder methods:
   - Don't blow up with stack trace when the external $PATH environment
     variable isn't set.
 
+  - Make Builder calls return lists all the time, even if there's only
+    one target.  This keeps things consistent and easier to program to
+    across platforms.
+
   From Chris Murray:
 
   - Add a .win32 attribute to force file names to expand with
 
   Please note the following important changes since release 0.95:
 
+    - All Builder calls (both built-in like Program(), Library(),
+      etc. and customer Builders) now always return a list of target
+      Nodes.   If the Builder only builds one target, the Builder
+      call will now return a list containing that target Node, not
+      the target Node itself as it used to do.
+
+      This change should be invisibile to most normal uses of the
+      return values from Builder calls.  It will cause an error if the
+      SConscript file was performing some direct manipulation of the
+      returned Node value.  For example, an attempt to print the name
+      of a target returned by the Object() Builder:
+
+            target = Object('foo.c')
+            # OLD WAY
+            print target
+
+      Will now need to access the first element in the list returned by
+      the Object() call:
+
+            target = Object('foo.c')
+            # NEW AY
+            print target[0]
+
+      This change was introduced to make the data type returned by Builder
+      calls consistent (always a list), regardless of platform or number
+      of returned targets.
+
     - The SConsignFile() function now uses an internally-supplied
       SCons.dblite module as the default DB scheme for the .sconsign file.
       If you are using the SConsignFile() function without an explicitly
           FortranScan.add_skey('.x') => env.Append(FORTRANSUFFIXES = ['.x'])
 
     - The internal "node_factory" keyword argument has been removed;
-      the seperate and more flexible "target_factory" and "source_factory"
+      the separate and more flexible "target_factory" and "source_factory"
       keywords should be used instead.
 
     - SCons now treats file "extensions" that contain all digits (for

src/engine/SCons/Action.py

                         # it is a path list, because that's a pretty
                         # common list like value to stick in an environment
                         # variable:
+                        value = SCons.Util.flatten(value)
                         ENV[key] = string.join(map(str, value), os.pathsep)
                     elif not SCons.Util.is_String(value):
                         # If it isn't a string or a list, then
                 return "unknown_python_function"
 
     def strfunction(self, target, source, env):
-        def quote(s):
-            return '"' + str(s) + '"'
-        def array(a, q=quote):
-            return '[' + string.join(map(lambda x, q=q: q(x), a), ", ") + ']'
+        def array(a):
+            def quote(s):
+                return '"' + str(s) + '"'
+            return '[' + string.join(map(quote, a), ", ") + ']'
         name = self.function_name()
-        tstr = len(target) == 1 and quote(target[0]) or array(target)
-        sstr = len(source) == 1 and quote(source[0]) or array(source)
+        tstr = array(target)
+        sstr = array(source)
         return "%s(%s, %s)" % (name, tstr, sstr)
 
     def __str__(self):

src/engine/SCons/ActionTests.py

             result = a("out", "in", env)
             assert result == 7, result
             s = sio.getvalue()
-            assert s == 'execfunc("out", "in")\n', s
+            assert s == 'execfunc(["out"], ["in"])\n', s
 
             SCons.Action.execute_actions = 0
 
             result = a("out", "in", env)
             assert result == 0, result
             s = sio.getvalue()
-            assert s == 'execfunc("out", "in")\n', s
+            assert s == 'execfunc(["out"], ["in"])\n', s
 
             SCons.Action.print_actions_presub = 1
 
             result = a("out", "in", env)
             assert result == 0, result
             s = sio.getvalue()
-            assert s == 'execfunc("out", "in")\n', s
+            assert s == 'execfunc(["out"], ["in"])\n', s
 
             sio = StringIO.StringIO()
             sys.stdout = sio
             result = a("out", "in", env, presub=1)
             assert result == 0, result
             s = sio.getvalue()
-            assert s == 'Building out with action(s):\n  execfunc(env, target, source)\nexecfunc("out", "in")\n', s
+            assert s == 'Building out with action(s):\n  execfunc(env, target, source)\nexecfunc(["out"], ["in"])\n', s
 
             a2 = SCons.Action.Action(execfunc)
 
             result = a2("out", "in", env)
             assert result == 0, result
             s = sio.getvalue()
-            assert s == 'Building out with action(s):\n  execfunc(env, target, source)\nexecfunc("out", "in")\n', s
+            assert s == 'Building out with action(s):\n  execfunc(env, target, source)\nexecfunc(["out"], ["in"])\n', s
 
             sio = StringIO.StringIO()
             sys.stdout = sio
             result = a2("out", "in", env, presub=0)
             assert result == 0, result
             s = sio.getvalue()
-            assert s == 'execfunc("out", "in")\n', s
+            assert s == 'execfunc(["out"], ["in"])\n', s
 
             sio = StringIO.StringIO()
             sys.stdout = sio

src/engine/SCons/Builder.py

 
         if len(tlist) == 1:
             builder = self
-            result = tlist[0]
         else:
             builder = ListBuilder(self, env, tlist)
-            result = tlist
         _init_nodes(builder, env, overwarn.data, tlist, slist)
 
-        return result
+        return tlist
 
     def __call__(self, env, target = None, source = _null, **kw):
         return self._execute(env, target, source, OverrideWarner(kw))
                 final_sources.append(snode)
             else:
                 tgt = subsidiary_builder._execute(env, None, snode, overwarn)
-                # Only supply the builder with sources it is capable
-                # of building.
-                if SCons.Util.is_List(tgt):
+                # If the subsidiary Builder returned more than one target,
+                # then filter out any sources that this Builder isn't
+                # capable of building.
+                if len(tgt) > 1:
                     tgt = filter(lambda x, self=self, suf=src_suffixes:
                                  self.splitext(SCons.Util.to_String(x))[1] in suf,
                                  tgt)
-                if not SCons.Util.is_List(tgt):
-                    final_sources.append(tgt)
-                else:
-                    final_sources.extend(tgt)
+                final_sources.extend(tgt)
 
         return BuilderBase._execute(self, env, target, final_sources, overwarn)
 

src/engine/SCons/BuilderTests.py

         assert n1.executor, "no executor found"
         assert not hasattr(n2, 'env')
 
-        target = builder(env, target = 'n3', source = 'n4')
+        target = builder(env, target = 'n3', source = 'n4')[0]
         assert target.name == 'n3'
         assert target.sources[0].name == 'n4'
 
-        target = builder(env, target = 'n4 n5', source = ['n6 n7'])
+        target = builder(env, target = 'n4 n5', source = ['n6 n7'])[0]
         assert target.name == 'n4 n5'
         assert target.sources[0].name == 'n6 n7'
 
-        target = builder(env, target = ['n8 n9'], source = 'n10 n11')
+        target = builder(env, target = ['n8 n9'], source = 'n10 n11')[0]
         assert target.name == 'n8 n9'
         assert target.sources[0].name == 'n10 n11'
 
             uni = unicode
 
         target = builder(env, target = uni('n12 n13'),
-                          source = [uni('n14 n15')])
+                          source = [uni('n14 n15')])[0]
         assert target.name == uni('n12 n13')
         assert target.sources[0].name == uni('n14 n15')
 
         target = builder(env, target = [uni('n16 n17')],
-                         source = uni('n18 n19'))
+                         source = uni('n18 n19'))[0]
         assert target.name == uni('n16 n17')
         assert target.sources[0].name == uni('n18 n19')
 
                                         source_factory=MyNode,
                                         prefix='p-',
                                         suffix='.s')
-        target = builder(env, source='n21')
+        target = builder(env, source='n21')[0]
         assert target.name == 'p-n21.s', target
 
     def test_mistaken_variables(self):
         assert builder.get_prefix(env) == 'lib.'
         builder = SCons.Builder.Builder(prefix = 'lib')
         assert builder.get_prefix(env) == 'lib'
-        tgt = builder(env, target = 'tgt1', source = 'src1')
+        tgt = builder(env, target = 'tgt1', source = 'src1')[0]
         assert tgt.path == 'libtgt1', \
                 "Target has unexpected name: %s" % tgt.path
-        tgt = builder(env, target = 'tgt2a tgt2b', source = 'src2')
+        tgt = builder(env, target = 'tgt2a tgt2b', source = 'src2')[0]
         assert tgt.path == 'libtgt2a tgt2b', \
                 "Target has unexpected name: %s" % tgt.path
-        tgt = builder(env, source = 'src3')
+        tgt = builder(env, source = 'src3')[0]
         assert tgt.path == 'libsrc3', \
                 "Target has unexpected name: %s" % tgt.path
-        tgt = builder(env, source = 'lib/src4')
+        tgt = builder(env, source = 'lib/src4')[0]
         assert tgt.path == os.path.join('lib', 'libsrc4'), \
                 "Target has unexpected name: %s" % tgt.path
-        tgt = builder(env, target = 'lib/tgt5', source = 'lib/src5')
+        tgt = builder(env, target = 'lib/tgt5', source = 'lib/src5')[0]
         assert tgt.path == os.path.join('lib', 'libtgt5'), \
                 "Target has unexpected name: %s" % tgt.path
 
                                                   '.x'   : 'y-',
                                                   '$FOO' : 'foo-',
                                                   '.zzz' : my_emit})
-        tgt = builder(my_env, source = 'f1')
+        tgt = builder(my_env, source = 'f1')[0]
         assert tgt.path == 'default-f1', tgt.path
-        tgt = builder(my_env, source = 'f2.c')
+        tgt = builder(my_env, source = 'f2.c')[0]
         assert tgt.path == 'default-f2', tgt.path
-        tgt = builder(my_env, source = 'f3.in')
+        tgt = builder(my_env, source = 'f3.in')[0]
         assert tgt.path == 'out-f3', tgt.path
-        tgt = builder(my_env, source = 'f4.x')
+        tgt = builder(my_env, source = 'f4.x')[0]
         assert tgt.path == 'y-f4', tgt.path
-        tgt = builder(my_env, source = 'f5.foo')
+        tgt = builder(my_env, source = 'f5.foo')[0]
         assert tgt.path == 'foo-f5', tgt.path
-        tgt = builder(my_env, source = 'f6.zzz')
+        tgt = builder(my_env, source = 'f6.zzz')[0]
         assert tgt.path == 'emit-f6', tgt.path
 
     def test_src_suffix(self):
         b1 = SCons.Builder.Builder(src_suffix = '.c')
         assert b1.src_suffixes(env) == ['.c'], b1.src_suffixes(env)
 
-        tgt = b1(env, target = 'tgt2', source = 'src2')
+        tgt = b1(env, target = 'tgt2', source = 'src2')[0]
         assert tgt.sources[0].path == 'src2.c', \
                 "Source has unexpected name: %s" % tgt.sources[0].path
 
-        tgt = b1(env, target = 'tgt3', source = 'src3a src3b')
+        tgt = b1(env, target = 'tgt3', source = 'src3a src3b')[0]
         assert len(tgt.sources) == 1
         assert tgt.sources[0].path == 'src3a src3b.c', \
                 "Unexpected tgt.sources[0] name: %s" % tgt.sources[0].path
         assert builder.get_suffix(env) == '.o', builder.get_suffix(env)
         builder = SCons.Builder.Builder(suffix = 'o')
         assert builder.get_suffix(env) == '.o', builder.get_suffix(env)
-        tgt = builder(env, target = 'tgt3', source = 'src3')
+        tgt = builder(env, target = 'tgt3', source = 'src3')[0]
         assert tgt.path == 'tgt3.o', \
                 "Target has unexpected name: %s" % tgt.path
-        tgt = builder(env, target = 'tgt4a tgt4b', source = 'src4')
+        tgt = builder(env, target = 'tgt4a tgt4b', source = 'src4')[0]
         assert tgt.path == 'tgt4a tgt4b.o', \
                 "Target has unexpected name: %s" % tgt.path
-        tgt = builder(env, source = 'src5')
+        tgt = builder(env, source = 'src5')[0]
         assert tgt.path == 'src5.o', \
                 "Target has unexpected name: %s" % tgt.path
 
                                                   '.x'   : '.y',
                                                   '$BAR' : '.new',
                                                   '.zzz' : my_emit})
-        tgt = builder(my_env, source = 'f1')
+        tgt = builder(my_env, source = 'f1')[0]
         assert tgt.path == 'f1.default', tgt.path
-        tgt = builder(my_env, source = 'f2.c')
+        tgt = builder(my_env, source = 'f2.c')[0]
         assert tgt.path == 'f2.default', tgt.path
-        tgt = builder(my_env, source = 'f3.in')
+        tgt = builder(my_env, source = 'f3.in')[0]
         assert tgt.path == 'f3.out', tgt.path
-        tgt = builder(my_env, source = 'f4.x')
+        tgt = builder(my_env, source = 'f4.x')[0]
         assert tgt.path == 'f4.y', tgt.path
-        tgt = builder(my_env, source = 'f5.bar')
+        tgt = builder(my_env, source = 'f5.bar')[0]
         assert tgt.path == 'f5.new', tgt.path
-        tgt = builder(my_env, source = 'f6.zzz')
+        tgt = builder(my_env, source = 'f6.zzz')[0]
         assert tgt.path == 'f6.emit', tgt.path
 
     def test_single_source(self):
         builder = SCons.Builder.Builder(action=SCons.Action.Action(func,None),
                                         single_source = 1, suffix='.out')
         env['CNT'] = [0]
-        tgt = builder(env, target=outfiles[0], source=infiles[0])
+        tgt = builder(env, target=outfiles[0], source=infiles[0])[0]
         tgt.prepare()
         tgt.build()
         assert env['CNT'][0] == 1, env['CNT'][0]
-        tgt = builder(env, outfiles[1], infiles[1])
+        tgt = builder(env, outfiles[1], infiles[1])[0]
         tgt.prepare()
         tgt.build()
         assert env['CNT'][0] == 2
                                                   src_builder = builder1,
                                                   src_suffix = '.foo')
 
-        tgt = builder2(env, target='baz', source=['test.bar', 'test2.foo', 'test3.txt'])
+        tgt = builder2(env, target='baz',
+                       source=['test.bar', 'test2.foo', 'test3.txt'])[0]
         assert str(tgt.sources[0]) == 'test.foo', str(tgt.sources[0])
         assert str(tgt.sources[0].sources[0]) == 'test.bar', \
                str(tgt.sources[0].sources[0])
         assert str(tgt.sources[1]) == 'test2.foo', str(tgt.sources[1])
         assert str(tgt.sources[2]) == 'test3.txt', str(tgt.sources[2])
 
-        tgt = builder2(env, 'aaa.bar')
+        tgt = builder2(env, 'aaa.bar')[0]
         assert str(tgt) == 'aaa', str(tgt)
         assert str(tgt.sources[0]) == 'aaa.foo', str(tgt.sources[0])
         assert str(tgt.sources[0].sources[0]) == 'aaa.bar', \
                                                   src_builder=builder5,
                                                   suffix='.exe',
                                                   src_suffix='.obj')
-        tgt = builder6(env, 'test', 'test.i')
+        tgt = builder6(env, 'test', 'test.i')[0]
         assert str(tgt) == 'test.exe', str(tgt)
         assert str(tgt.sources[0]) == 'test_wrap.obj', str(tgt.sources[0])
         assert str(tgt.sources[0].sources[0]) == 'test_wrap.c', \
         
         assert isinstance(builder, SCons.Builder.CompositeBuilder)
         assert isinstance(builder.action, SCons.Action.CommandGeneratorAction)
-        tgt = builder(env, target='test1', source='test1.foo')
+        tgt = builder(env, target='test1', source='test1.foo')[0]
         assert isinstance(tgt.builder, SCons.Builder.BuilderBase)
         assert tgt.builder.action is builder.action
-        tgt = builder(env, target='test2', source='test1.bar')
+        tgt = builder(env, target='test2', source='test1.bar')[0]
         assert isinstance(tgt.builder, SCons.Builder.BuilderBase)
         assert tgt.builder.action is builder.action
         flag = 0
-        tgt = builder(env, target='test3', source=['test2.bar', 'test1.foo'])
+        tgt = builder(env, target='test3', source=['test2.bar', 'test1.foo'])[0]
         try:
             tgt.build()
         except SCons.Errors.UserError, e:
         match = str(e) == "While building `['test3']' from `test1.foo': Cannot build multiple sources with different extensions: .bar, .foo"
         assert match, e
 
-        tgt = builder(env, target='test4', source=['test4.BAR2'])
+        tgt = builder(env, target='test4', source=['test4.BAR2'])[0]
         assert isinstance(tgt.builder, SCons.Builder.BuilderBase)
         try:
             tgt.build()
         env['FOO_SUFFIX'] = '.BAR2'
         builder.add_action('$NEW_SUFFIX', func_action)
         flag = 0
-        tgt = builder(env, target='test5', source=['test5.BAR2'])
+        tgt = builder(env, target='test5', source=['test5.BAR2'])[0]
         try:
             tgt.build()
         except SCons.Errors.UserError:
         assert isinstance(builder, SCons.Builder.CompositeBuilder)
         assert isinstance(builder.action, SCons.Action.CommandGeneratorAction)
 
-        tgt = builder(env, target='t1', source='t1a.ina t1b.ina')
+        tgt = builder(env, target='t1', source='t1a.ina t1b.ina')[0]
         assert isinstance(tgt.builder, SCons.Builder.BuilderBase)
 
-        tgt = builder(env, target='t2', source='t2a.foo t2b.ina')
+        tgt = builder(env, target='t2', source='t2a.foo t2b.ina')[0]
         assert isinstance(tgt.builder, SCons.Builder.MultiStepBuilder), tgt.builder.__dict__
 
         bar_bld = SCons.Builder.Builder(action = 'a-bar',
 
         builder.add_action('.bar', 'bar')
 
-        tgt = builder(env, target='t3-foo', source='t3a.foo t3b.ina')
+        tgt = builder(env, target='t3-foo', source='t3a.foo t3b.ina')[0]
         assert isinstance(tgt.builder, SCons.Builder.MultiStepBuilder)
 
-        tgt = builder(env, target='t3-bar', source='t3a.bar t3b.inb')
+        tgt = builder(env, target='t3-bar', source='t3a.bar t3b.inb')[0]
         assert isinstance(tgt.builder, SCons.Builder.MultiStepBuilder)
 
         flag = 0
-        tgt = builder(env, target='t5', source=['test5a.foo', 'test5b.inb'])
+        tgt = builder(env, target='t5', source=['test5a.foo', 'test5b.inb'])[0]
         try:
             tgt.build()
         except SCons.Errors.UserError, e:
         assert match, e
 
         flag = 0
-        tgt = builder(env, target='t6', source=['test6a.bar', 'test6b.ina'])
+        tgt = builder(env, target='t6', source=['test6a.bar', 'test6b.ina'])[0]
         try:
             tgt.build()
         except SCons.Errors.UserError, e:
         assert match, e
 
         flag = 0
-        tgt = builder(env, target='t4', source=['test4a.ina', 'test4b.inb'])
+        tgt = builder(env, target='t4', source=['test4a.ina', 'test4b.inb'])[0]
         try:
             tgt.build()
         except SCons.Errors.UserError, e:
         assert match, e
 
         flag = 0
-        tgt = builder(env, target='t7', source=['test7'])
+        tgt = builder(env, target='t7', source=['test7'])[0]
         try:
             tgt.build()
         except SCons.Errors.UserError, e:
         assert match, e
 
         flag = 0
-        tgt = builder(env, target='t8', source=['test8.unknown'])
+        tgt = builder(env, target='t8', source=['test8.unknown'])[0]
         try:
             tgt.build()
         except SCons.Errors.UserError, e:
         env = Environment()
         builder = SCons.Builder.Builder(target_scanner=tscan,
                                         source_scanner=sscan)
-        tgt = builder(env, target='foo2', source='bar')
+        tgt = builder(env, target='foo2', source='bar')[0]
         assert tgt.target_scanner == tscan, tgt.target_scanner
         assert tgt.source_scanner == sscan, tgt.source_scanner
 
                                          src_builder = builder1,
                                          target_scanner = tscan,
                                          source_scanner = tscan)
-        tgt = builder2(env, target='baz2', source='test.bar test2.foo test3.txt')
+        tgt = builder2(env, target='baz2', source='test.bar test2.foo test3.txt')[0]
         assert tgt.target_scanner == tscan, tgt.target_scanner
         assert tgt.source_scanner == tscan, tgt.source_scanner
 
         # With no scanner specified, source_scanner and
         # backup_source_scanner are None.
         env1 = Environment()
-        tgt = builder(env1, target='foo1.x', source='bar.y')
+        tgt = builder(env1, target='foo1.x', source='bar.y')[0]
         src = tgt.sources[0]
         assert tgt.target_scanner != scanner, tgt.target_scanner
         assert src.source_scanner is None, src.source_scanner
         # has a scanner must still set the scanner.
         env2 = Environment()
         env2.scanner = scanner
-        tgt = builder(env2, target='foo2.x', source='bar.y')
+        tgt = builder(env2, target='foo2.x', source='bar.y')[0]
         src = tgt.sources[0]
         assert tgt.target_scanner != scanner, tgt.target_scanner
         assert src.source_scanner is None, src.source_scanner
         env=Environment(CC='cc')
 
         builder = SCons.Builder.Builder(action=buildFunc)
-        tgt = builder(env, target='foo', source='bar', foo=1, bar=2, CC='mycc')
+        tgt = builder(env, target='foo', source='bar', foo=1, bar=2, CC='mycc')[0]
         tgt.build()
         assert self.foo == 1, self.foo
         assert self.bar == 2, self.bar
                                         emitter=emit,
                                         target_factory=MyNode,
                                         source_factory=MyNode)
-        tgt = builder(env, target='foo2', source='bar')
+        tgt = builder(env, target='foo2', source='bar')[0]
         assert str(tgt) == 'foo2', str(tgt)
         assert str(tgt.sources[0]) == 'bar', str(tgt.sources[0])
 
         assert 'foo3' in map(str, tgt), map(str, tgt)
         assert 'bar1' in map(str, tgt), map(str, tgt)
 
-        tgt = builder(env, target='foo4', source='bar', bar=1)
+        tgt = builder(env, target='foo4', source='bar', bar=1)[0]
         assert str(tgt) == 'foo4', str(tgt)
         assert len(tgt.sources) == 2, len(tgt.sources)
         assert 'baz' in map(str, tgt.sources), map(str, tgt.sources)
                                        target_factory=MyNode,
                                        source_factory=MyNode)
 
-        tgt = builder2(env2, target='foo5', source='bar')
+        tgt = builder2(env2, target='foo5', source='bar')[0]
         assert str(tgt) == 'foo5', str(tgt)
         assert str(tgt.sources[0]) == 'bar', str(tgt.sources[0])
 
         assert 'foo6' in map(str, tgt), map(str, tgt)
         assert 'bar2' in map(str, tgt), map(str, tgt)
 
-        tgt = builder2(env2, target='foo7', source='bar', bar=1)
+        tgt = builder2(env2, target='foo7', source='bar', bar=1)[0]
         assert str(tgt) == 'foo7', str(tgt)
         assert len(tgt.sources) == 2, len(tgt.sources)
         assert 'baz' in map(str, tgt.sources), map(str, tgt.sources)
                                        emitter=emit3,
                                        target_factory=MyNode,
                                        source_factory=MyNode)
-        tgt = builder3(env, target=node, source='bar')
+        tgt = builder3(env, target=node, source='bar')[0]
         assert tgt is new_node, tgt
         assert tgt.builder is builder3, tgt.builder
         assert node.builder is new_builder, node.builder
                                                   '.4b':emit4b},
                                          target_factory=MyNode,
                                          source_factory=MyNode)
-        tgt = builder4(env, source='aaa.4a')
+        tgt = builder4(env, source='aaa.4a')[0]
         assert str(tgt) == 'emit4a-aaa', str(tgt)
-        tgt = builder4(env, source='bbb.4b')
+        tgt = builder4(env, source='bbb.4b')[0]
         assert str(tgt) == 'emit4b-bbb', str(tgt)
-        tgt = builder4(env, source='ccc.4c')
+        tgt = builder4(env, source='ccc.4c')[0]
         assert str(tgt) == 'ccc', str(tgt)
 
         def emit4c(target, source, env):
             target = map(lambda x: 'emit4c-' + x[:-3], source)
             return (target, source)
         builder4.add_emitter('.4c', emit4c)
-        tgt = builder4(env, source='ccc.4c')
+        tgt = builder4(env, source='ccc.4c')[0]
         assert str(tgt) == 'emit4c-ccc', str(tgt)
 
         # Test a list of emitter functions.
         env = Environment()
         b = SCons.Builder.Builder(action='foo', suffix='.o')
 
-        tgt = b(env, 'aaa')
+        tgt = b(env, 'aaa')[0]
         assert str(tgt) == 'aaa.o', str(tgt)
         assert len(tgt.sources) == 1, map(str, tgt.sources)
         assert str(tgt.sources[0]) == 'aaa', map(str, tgt.sources)
 
-        tgt = b(env, 'bbb.c')
+        tgt = b(env, 'bbb.c')[0]
         assert str(tgt) == 'bbb.o', str(tgt)
         assert len(tgt.sources) == 1, map(str, tgt.sources)
         assert str(tgt.sources[0]) == 'bbb.c', map(str, tgt.sources)
 
-        tgt = b(env, 'ccc.x.c')
+        tgt = b(env, 'ccc.x.c')[0]
         assert str(tgt) == 'ccc.x.o', str(tgt)
         assert len(tgt.sources) == 1, map(str, tgt.sources)
         assert str(tgt.sources[0]) == 'ccc.x.c', map(str, tgt.sources)
 
-        tgt = b(env, ['d0.c', 'd1.c'])
+        tgt = b(env, ['d0.c', 'd1.c'])[0]
         assert str(tgt) == 'd0.o', str(tgt)
         assert len(tgt.sources) == 2,  map(str, tgt.sources)
         assert str(tgt.sources[0]) == 'd0.c', map(str, tgt.sources)
         assert str(tgt.sources[1]) == 'd1.c', map(str, tgt.sources)
 
-        tgt = b(env, source='eee')
+        tgt = b(env, source='eee')[0]
         assert str(tgt) == 'eee.o', str(tgt)
         assert len(tgt.sources) == 1, map(str, tgt.sources)
         assert str(tgt.sources[0]) == 'eee', map(str, tgt.sources)
 
-        tgt = b(env, source='fff.c')
+        tgt = b(env, source='fff.c')[0]
         assert str(tgt) == 'fff.o', str(tgt)
         assert len(tgt.sources) == 1, map(str, tgt.sources)
         assert str(tgt.sources[0]) == 'fff.c', map(str, tgt.sources)
 
-        tgt = b(env, source='ggg.x.c')
+        tgt = b(env, source='ggg.x.c')[0]
         assert str(tgt) == 'ggg.x.o', str(tgt)
         assert len(tgt.sources) == 1, map(str, tgt.sources)
         assert str(tgt.sources[0]) == 'ggg.x.c', map(str, tgt.sources)
 
-        tgt = b(env, source=['h0.c', 'h1.c'])
+        tgt = b(env, source=['h0.c', 'h1.c'])[0]
         assert str(tgt) == 'h0.o', str(tgt)
         assert len(tgt.sources) == 2,  map(str, tgt.sources)
         assert str(tgt.sources[0]) == 'h0.c', map(str, tgt.sources)
         assert str(tgt.sources[1]) == 'h1.c', map(str, tgt.sources)
 
-        w = b(env, target='i0.w', source=['i0.x'])
-        y = b(env, target='i1.y', source=['i1.z'])
-        tgt = b(env, source=[w, y])
+        w = b(env, target='i0.w', source=['i0.x'])[0]
+        y = b(env, target='i1.y', source=['i1.z'])[0]
+        tgt = b(env, source=[w, y])[0]
         assert str(tgt) == 'i0.o', str(tgt)
         assert len(tgt.sources) == 2, map(str, tgt.sources)
         assert str(tgt.sources[0]) == 'i0.w', map(str, tgt.sources)

src/engine/SCons/Environment.py

         if not args:
             return []
 
-        if not SCons.Util.is_List(args):
+        if SCons.Util.is_List(args):
+            args = SCons.Util.flatten(args)
+        else:
             args = [args]
 
         nodes = []
                         n = self.subst(n, raw=1)
                         if node_factory:
                             n = node_factory(n)
-                    nodes.append(n)
+                    try:
+                        nodes.extend(n)
+                    except TypeError:
+                        nodes.append(n)
                 elif node_factory:
-                    v = self.subst(v, raw=1)
-                    nodes.append(node_factory(v))
+                    v = node_factory(self.subst(v, raw=1))
+                    try:
+                        nodes.extend(v)
+                    except TypeError:
+                        nodes.append(v)
             else:
                 nodes.append(v)
     
             s = self.arg2nodes(s, self.fs.Entry)
             for t in tlist:
                 AliasBuilder(self, t, s)
-        if len(tlist) == 1:
-            tlist = tlist[0]
         return tlist
 
     def AlwaysBuild(self, *targets):
         tlist = []
         for t in targets:
             tlist.extend(self.arg2nodes(t, self.fs.File))
-
         for t in tlist:
             t.set_always_build()
-
-        if len(tlist) == 1:
-            tlist = tlist[0]
         return tlist
 
     def BuildDir(self, build_dir, src_dir, duplicate=1):
         dlist = self.arg2nodes(dependency, self.fs.Entry)
         for t in tlist:
             t.add_dependency(dlist)
-
-        if len(tlist) == 1:
-            tlist = tlist[0]
         return tlist
 
     def Dir(self, name, *args, **kw):
         return SCons.Node.FS.find_file(file, nodes, self.fs.File)
 
     def GetBuildPath(self, files):
-        ret = map(str, self.arg2nodes(files, self.fs.Entry))
-        if len(ret) == 1:
-            return ret[0]
-        return ret
+        result = map(str, self.arg2nodes(files, self.fs.Entry))
+        if SCons.Util.is_List(files):
+            return result
+        else:
+            return result[0]
 
     def Ignore(self, target, dependency):
         """Ignore a dependency."""
         dlist = self.arg2nodes(dependency, self.fs.Entry)
         for t in tlist:
             t.add_ignore(dlist)
-
-        if len(tlist) == 1:
-            tlist = tlist[0]
         return tlist
 
     def Install(self, dir, source):
         for dnode in dnodes:
             for src in sources:
                 target = self.fs.File(src.name, dnode)
-                tgt.append(InstallBuilder(self, target, src))
-        if len(tgt) == 1:
-            tgt = tgt[0]
+                tgt.extend(InstallBuilder(self, target, src))
         return tgt
 
     def InstallAs(self, target, source):
         """Install sources as targets."""
         sources = self.arg2nodes(source, self.fs.File)
         targets = self.arg2nodes(target, self.fs.File)
-        ret = []
+        result = []
         for src, tgt in map(lambda x, y: (x, y), sources, targets):
-            ret.append(InstallBuilder(self, tgt, src))
-        if len(ret) == 1:
-            ret = ret[0]
-        return ret
+            result.extend(InstallBuilder(self, tgt, src))
+        return result
 
     def Literal(self, string):
         return SCons.Util.Literal(string)
         tlist = []
         for t in targets:
             tlist.extend(self.arg2nodes(t, self.fs.Entry))
-
         for t in tlist:
             t.set_precious()
-
-        if len(tlist) == 1:
-            tlist = tlist[0]
         return tlist
 
     def Repository(self, *dirs, **kw):
             self.Precious(side_effect)
             for target in targets:
                 target.side_effects.append(side_effect)
-        if len(side_effects) == 1:
-            return side_effects[0]
-        else:
-            return side_effects
+        return side_effects
 
     def SourceCode(self, entry, builder):
         """Arrange for a source code builder for (part of) a tree."""
         entries = self.arg2nodes(entry, self.fs.Entry)
         for entry in entries:
             entry.set_src_builder(builder)
-        if len(entries) == 1:
-            return entries[0]
         return entries
 
     def SourceSignatures(self, type):

src/engine/SCons/EnvironmentTests.py

         """Test the Alias() method"""
         env = Environment(FOO='kkk', BAR='lll', EA='export_alias')
 
-        tgt = env.Alias('new_alias')
+        tgt = env.Alias('new_alias')[0]
         assert str(tgt) == 'new_alias', tgt
         assert tgt.sources == [], tgt.sources
 
-        tgt = env.Alias('None_alias', None)
+        tgt = env.Alias('None_alias', None)[0]
         assert str(tgt) == 'None_alias', tgt
         assert tgt.sources == [], tgt.sources
 
-        tgt = env.Alias('empty_list', [])
+        tgt = env.Alias('empty_list', [])[0]
         assert str(tgt) == 'empty_list', tgt
         assert tgt.sources == [], tgt.sources
 
-        tgt = env.Alias('export_alias', [ 'asrc1', '$FOO' ])
+        tgt = env.Alias('export_alias', [ 'asrc1', '$FOO' ])[0]
         assert str(tgt) == 'export_alias', tgt
         assert len(tgt.sources) == 2, map(str, tgt.sources)
         assert str(tgt.sources[0]) == 'asrc1', map(str, tgt.sources)
         assert str(tgt.sources[1]) == 'kkk', map(str, tgt.sources)
 
-        n = env.Alias(tgt, source = ['$BAR', 'asrc4'])
+        n = env.Alias(tgt, source = ['$BAR', 'asrc4'])[0]
         assert n is tgt, n
         assert len(tgt.sources) == 4, map(str, tgt.sources)
         assert str(tgt.sources[2]) == 'lll', map(str, tgt.sources)
         assert str(tgt.sources[3]) == 'asrc4', map(str, tgt.sources)
 
-        n = env.Alias('$EA', 'asrc5')
+        n = env.Alias('$EA', 'asrc5')[0]
         assert n is tgt, n
         assert len(tgt.sources) == 5, map(str, tgt.sources)
         assert str(tgt.sources[4]) == 'asrc5', map(str, tgt.sources)
         """Test the Command() method."""
         env = Environment()
         t = env.Command(target='foo.out', source=['foo1.in', 'foo2.in'],
-                        action='buildfoo $target $source')
+                        action='buildfoo $target $source')[0]
         assert not t.builder is None
         assert t.builder.action.__class__.__name__ == 'CommandAction'
         assert t.builder.action.cmd_list == 'buildfoo $target $source'
 
         sub = SCons.Node.FS.default_fs.Dir('sub')
         t = env.Command(target='bar.out', source='sub',
-                        action='buildbar $target $source')
+                        action='buildbar $target $source')[0]
         assert 'sub' in map(lambda x: x.path, t.sources)
 
         def testFunc(env, target, source):
             assert 'foo1.in' in map(str, source) and 'foo2.in' in map(str, source), map(str, source)
             return 0
         t = env.Command(target='foo.out', source=['foo1.in','foo2.in'],
-                        action=testFunc)
+                        action=testFunc)[0]
         assert not t.builder is None
         assert t.builder.action.__class__.__name__ == 'FunctionAction'
         t.build()
         env = Environment(TEST2 = test2)
         t = env.Command(target='baz.out', source='baz.in',
                         action='${TEST2(XYZ)}',
-                        XYZ='magic word')
+                        XYZ='magic word')[0]
         assert not t.builder is None
         t.build()
         assert x[0] == 'magic word', x
         env.Dir('dir2')
         env.File('xxx.py')
         env.File('yyy.py')
-        t = env.Depends(target='EnvironmentTest.py', dependency='Environment.py')
+        t = env.Depends(target='EnvironmentTest.py',
+                        dependency='Environment.py')[0]
         assert t.__class__.__name__ == 'Entry', t.__class__.__name__
         assert t.path == 'EnvironmentTest.py'
         assert len(t.depends) == 1
         assert d.__class__.__name__ == 'Entry', d.__class__.__name__
         assert d.path == 'Environment.py'
 
-        t = env.Depends(target='${FOO}.py', dependency='${BAR}.py')
+        t = env.Depends(target='${FOO}.py', dependency='${BAR}.py')[0]
         assert t.__class__.__name__ == 'File', t.__class__.__name__
         assert t.path == 'xxx.py'
         assert len(t.depends) == 1
         assert d.__class__.__name__ == 'File', d.__class__.__name__
         assert d.path == 'yyy.py'
 
-        t = env.Depends(target='dir1', dependency='dir2')
+        t = env.Depends(target='dir1', dependency='dir2')[0]
         assert t.__class__.__name__ == 'Dir', t.__class__.__name__
         assert t.path == 'dir1'
         assert len(t.depends) == 1
         env.File('yyyzzz')
         env.File('zzzyyy')
 
-        t = env.Ignore(target='targ.py', dependency='dep.py')
+        t = env.Ignore(target='targ.py', dependency='dep.py')[0]
         assert t.__class__.__name__ == 'Entry', t.__class__.__name__
         assert t.path == 'targ.py'
         assert len(t.ignore) == 1
         assert i.__class__.__name__ == 'Entry', i.__class__.__name__
         assert i.path == 'dep.py'
 
-        t = env.Ignore(target='$FOO$BAR', dependency='$BAR$FOO')
+        t = env.Ignore(target='$FOO$BAR', dependency='$BAR$FOO')[0]
         assert t.__class__.__name__ == 'File', t.__class__.__name__
         assert t.path == 'yyyzzz'
         assert len(t.ignore) == 1
         assert i.__class__.__name__ == 'File', i.__class__.__name__
         assert i.path == 'zzzyyy'
 
-        t = env.Ignore(target='dir1', dependency='dir2')
+        t = env.Ignore(target='dir1', dependency='dir2')[0]
         assert t.__class__.__name__ == 'Dir', t.__class__.__name__
         assert t.path == 'dir1'
         assert len(t.ignore) == 1
         for tnode in tgt:
             assert tnode.builder == InstallBuilder
 
-        tgt = env.InstallAs(target='${FOO}.t', source='${BAR}.s')
+        tgt = env.InstallAs(target='${FOO}.t', source='${BAR}.s')[0]
         assert tgt.path == 'iii.t'
         assert tgt.sources[0].path == 'jjj.s'
         assert tgt.builder == InstallBuilder
         env.File('mylll.pdb')
         env.Dir('mymmm.pdb')
 
-        foo = env.Object('foo.obj', 'foo.cpp')
-        bar = env.Object('bar.obj', 'bar.cpp')
-        s = env.SideEffect('mylib.pdb', ['foo.obj', 'bar.obj'])
+        foo = env.Object('foo.obj', 'foo.cpp')[0]
+        bar = env.Object('bar.obj', 'bar.cpp')[0]
+        s = env.SideEffect('mylib.pdb', ['foo.obj', 'bar.obj'])[0]
         assert s.__class__.__name__ == 'Entry', s.__class__.__name__
         assert s.path == 'mylib.pdb'
         assert s.side_effect
         assert s.depends_on([bar])
         assert s.depends_on([foo])
 
-        fff = env.Object('fff.obj', 'fff.cpp')
-        bbb = env.Object('bbb.obj', 'bbb.cpp')
-        s = env.SideEffect('my${LIB}.pdb', ['${FOO}.obj', '${BAR}.obj'])
+        fff = env.Object('fff.obj', 'fff.cpp')[0]
+        bbb = env.Object('bbb.obj', 'bbb.cpp')[0]
+        s = env.SideEffect('my${LIB}.pdb', ['${FOO}.obj', '${BAR}.obj'])[0]
         assert s.__class__.__name__ == 'File', s.__class__.__name__
         assert s.path == 'mylll.pdb'
         assert s.side_effect
         assert s.depends_on([bbb])
         assert s.depends_on([fff])
 
-        ggg = env.Object('ggg.obj', 'ggg.cpp')
-        ccc = env.Object('ccc.obj', 'ccc.cpp')
-        s = env.SideEffect('mymmm.pdb', ['ggg.obj', 'ccc.obj'])
+        ggg = env.Object('ggg.obj', 'ggg.cpp')[0]
+        ccc = env.Object('ccc.obj', 'ccc.cpp')[0]
+        s = env.SideEffect('mymmm.pdb', ['ggg.obj', 'ccc.obj'])[0]
         assert s.__class__.__name__ == 'Dir', s.__class__.__name__
         assert s.path == 'mymmm.pdb'
         assert s.side_effect
     def test_SourceCode(self):
         """Test the SourceCode() method."""
         env = Environment(FOO='mmm', BAR='nnn')
-        e = env.SourceCode('foo', None)
+        e = env.SourceCode('foo', None)[0]
         assert e.path == 'foo'
         s = e.src_builder()
         assert s is None, s
 
         b = Builder()
-        e = env.SourceCode(e, b)
+        e = env.SourceCode(e, b)[0]
         assert e.path == 'foo'
         s = e.src_builder()
         assert s is b, s
 
-        e = env.SourceCode('$BAR$FOO', None)
+        e = env.SourceCode('$BAR$FOO', None)[0]
         assert e.path == 'nnnmmm'
         s = e.src_builder()
         assert s is None, s

src/engine/SCons/SConf.py

             source = self.confdir.File(f + extension)
             sourceNode = self.env.SConfSourceBuilder(target=source,
                                                      source=None)
-            nodesToBeBuilt.append(sourceNode)
+            nodesToBeBuilt.extend(sourceNode)
         else:
             source = None
 
             pname = str(prog)
             output = SConfFS.File(pname+'.out')
             node = self.env.Command(output, prog, [ [ pname, ">", "${TARGET}"] ])
-            ok = self.BuildNodes([node])
+            ok = self.BuildNodes(node)
             if ok:
                 outputStr = output.get_contents()
                 return( 1, outputStr)

src/engine/SCons/Util.py

     def is_String(e):
         return type(e) is types.StringType or isinstance(e, UserString)
 
+def is_Scalar(e):
+    return is_String(e) or not is_List(e)
+
+def flatten(sequence, scalarp=is_Scalar, result=None):
+    if result is None:
+        result = []
+    for item in sequence:
+        if scalarp(item):
+            result.append(item)
+        else:
+            flatten(item, scalarp, result)
+    return result
+
 class Proxy:
     """A simple generic Proxy class, forwarding all calls to
     subject.  So, for the benefit of the python newbie, what does

test/BitKeeper.py

 """,
                                        build_str = """\
 bk get -e aaa.in
-cat("aaa.out", "aaa.in")
-cat("bbb.out", "bbb.in")
+cat(["aaa.out"], ["aaa.in"])
+cat(["bbb.out"], ["bbb.in"])
 bk get -e ccc.in
-cat("ccc.out", "ccc.in")
-cat("all", ["aaa.out", "bbb.out", "ccc.out"])
+cat(["ccc.out"], ["ccc.in"])
+cat(["all"], ["aaa.out", "bbb.out", "ccc.out"])
 bk get -e sub/ddd.in
-cat("sub/ddd.out", "sub/ddd.in")
-cat("sub/eee.out", "sub/eee.in")
+cat(["sub/ddd.out"], ["sub/ddd.in"])
+cat(["sub/eee.out"], ["sub/eee.in"])
 bk get -e sub/fff.in
-cat("sub/fff.out", "sub/fff.in")
-cat("sub/all", ["sub/ddd.out", "sub/eee.out", "sub/fff.out"])
+cat(["sub/fff.out"], ["sub/fff.in"])
+cat(["sub/all"], ["sub/ddd.out", "sub/eee.out", "sub/fff.out"])
 """),
              stderr = """\
 sub/SConscript 1.1 -> 1.2: 5 lines
 sub/fff.in 1.1 -> 1.2: 1 lines
 """)
 
-    test.fail_test(test.read(['work1', 'all']) != "work1/aaa.in\nchecked-out work1/bbb.in\nwork1/ccc.in\n")
+    test.must_match(['work1', 'all'], "work1/aaa.in\nchecked-out work1/bbb.in\nwork1/ccc.in\n")
 
     test.fail_test(not is_writable(test.workpath('work1', 'sub', 'SConscript')))
     test.fail_test(not is_writable(test.workpath('work1', 'aaa.in')))
 """,
                                        build_str = """\
 bk co -q aaa.in
-cat("aaa.out", "aaa.in")
-cat("bbb.out", "bbb.in")
+cat(["aaa.out"], ["aaa.in"])
+cat(["bbb.out"], ["bbb.in"])
 bk co -q ccc.in
-cat("ccc.out", "ccc.in")
-cat("all", ["aaa.out", "bbb.out", "ccc.out"])
+cat(["ccc.out"], ["ccc.in"])
+cat(["all"], ["aaa.out", "bbb.out", "ccc.out"])
 bk co -q sub/ddd.in
-cat("sub/ddd.out", "sub/ddd.in")
-cat("sub/eee.out", "sub/eee.in")
+cat(["sub/ddd.out"], ["sub/ddd.in"])
+cat(["sub/eee.out"], ["sub/eee.in"])
 bk co -q sub/fff.in
-cat("sub/fff.out", "sub/fff.in")
-cat("sub/all", ["sub/ddd.out", "sub/eee.out", "sub/fff.out"])
+cat(["sub/fff.out"], ["sub/fff.in"])
+cat(["sub/all"], ["sub/ddd.out", "sub/eee.out", "sub/fff.out"])
 """))
 
-    test.fail_test(test.read(['work2', 'all']) != "work2/aaa.in\nchecked-out work2/bbb.in\nwork2/ccc.in\n")
+    test.must_match(['work2', 'all'], "work2/aaa.in\nchecked-out work2/bbb.in\nwork2/ccc.in\n")
 
-    test.fail_test(test.read(['work2', 'sub', 'all']) != "work2/sub/ddd.in\nchecked-out work2/sub/eee.in\nwork2/sub/fff.in\n")
+    test.must_match(['work2', 'sub', 'all'], "work2/sub/ddd.in\nchecked-out work2/sub/eee.in\nwork2/sub/fff.in\n")
 
     test.fail_test(is_writable(test.workpath('work2', 'sub', 'SConscript')))
     test.fail_test(is_writable(test.workpath('work2', 'aaa.in')))
 """ % bk,
                                    build_str = """\
 %s get aaa.in
-cat("aaa.out", "aaa.in")
-cat("bbb.out", "bbb.in")
+cat(["aaa.out"], ["aaa.in"])
+cat(["bbb.out"], ["bbb.in"])
 %s get ccc.in
-cat("ccc.out", "ccc.in")
-cat("all", ["aaa.out", "bbb.out", "ccc.out"])
+cat(["ccc.out"], ["ccc.in"])
+cat(["all"], ["aaa.out", "bbb.out", "ccc.out"])
 %s get sub/ddd.in
-cat("sub/ddd.out", "sub/ddd.in")
-cat("sub/eee.out", "sub/eee.in")
+cat(["sub/ddd.out"], ["sub/ddd.in"])
+cat(["sub/eee.out"], ["sub/eee.in"])
 %s get sub/fff.in
-cat("sub/fff.out", "sub/fff.in")
-cat("sub/all", ["sub/ddd.out", "sub/eee.out", "sub/fff.out"])
+cat(["sub/fff.out"], ["sub/fff.in"])
+cat(["sub/all"], ["sub/ddd.out", "sub/eee.out", "sub/fff.out"])
 """ % (bk, bk, bk, bk)),
          stderr = """\
 sub/SConscript 1.1: 5 lines
 sub/fff.in 1.1: 1 lines
 """)
 
-test.fail_test(test.read(['work3', 'all']) != "import/aaa.in\nwork3/bbb.in\nimport/ccc.in\n")
+test.must_match(['work3', 'all'], "import/aaa.in\nwork3/bbb.in\nimport/ccc.in\n")
 
-test.fail_test(test.read(['work3', 'sub', 'all']) != "import/sub/ddd.in\nwork3/sub/eee.in\nimport/sub/fff.in\n")
+test.must_match(['work3', 'sub', 'all'], "import/sub/ddd.in\nwork3/sub/eee.in\nimport/sub/fff.in\n")
 
 test.fail_test(is_writable(test.workpath('work3', 'sub', 'SConscript')))
 test.fail_test(is_writable(test.workpath('work3', 'aaa.in')))
 """ % (cvsroot),
                                    build_str = """\
 cvs -Q -d %s co foo/aaa.in
-cat("aaa.out", "%s")
-cat("bbb.out", "%s")
+cat(["aaa.out"], ["%s"])
+cat(["bbb.out"], ["%s"])
 cvs -Q -d %s co foo/ccc.in
-cat("ccc.out", "%s")
-cat("all", ["aaa.out", "bbb.out", "ccc.out"])
+cat(["ccc.out"], ["%s"])
+cat(["all"], ["aaa.out", "bbb.out", "ccc.out"])
 cvs -Q -d %s co foo/sub/ddd.in
-cat("%s", "%s")
-cat("%s", "%s")
+cat(["%s"], ["%s"])
+cat(["%s"], ["%s"])
 cvs -Q -d %s co foo/sub/fff.in
-cat("%s", "%s")
-cat("%s", ["%s", "%s", "%s"])
+cat(["%s"], ["%s"])
+cat(["%s"], ["%s", "%s", "%s"])
 """ % (cvsroot,
        foo_aaa_in,
        foo_bbb_in,
                                    build_str = """\
 cvs -q -d %s co -d . foo/aaa.in
 U ./aaa.in
-cat("aaa.out", "aaa.in")
-cat("bbb.out", "bbb.in")
+cat(["aaa.out"], ["aaa.in"])
+cat(["bbb.out"], ["bbb.in"])
 cvs -q -d %s co -d . foo/ccc.in
 U ./ccc.in
-cat("ccc.out", "ccc.in")
-cat("all", ["aaa.out", "bbb.out", "ccc.out"])
+cat(["ccc.out"], ["ccc.in"])
+cat(["all"], ["aaa.out", "bbb.out", "ccc.out"])
 cvs -q -d %s co -d sub foo/sub/ddd.in
 U sub/ddd.in
-cat("%s", "%s")
-cat("%s", "%s")
+cat(["%s"], ["%s"])
+cat(["%s"], ["%s"])
 cvs -q -d %s co -d sub foo/sub/fff.in
 U sub/fff.in
-cat("%s", "%s")
-cat("%s", ["%s", "%s", "%s"])
+cat(["%s"], ["%s"])
+cat(["%s"], ["%s", "%s", "%s"])
 """ % (cvsroot,
        cvsroot,
        cvsroot,
          stdout = test.wrap_stdout(build_str = """\
 cvs -q -d %s co -d . foo/aaa.in
 U ./aaa.in
-cat("aaa.out", "aaa.in")
+cat(["aaa.out"], ["aaa.in"])
 cvs -q -d %s co -d . foo/bbb.in
 U ./bbb.in
-cat("bbb.out", "bbb.in")
+cat(["bbb.out"], ["bbb.in"])
 cvs -q -d %s co -d . foo/ccc.in
 U ./ccc.in
-cat("ccc.out", "ccc.in")
-cat("all", ["aaa.out", "bbb.out", "ccc.out"])
+cat(["ccc.out"], ["ccc.in"])
+cat(["all"], ["aaa.out", "bbb.out", "ccc.out"])
 """ % (cvsroot,
        cvsroot,
        cvsroot)))
 
 test.run(chdir = 'work4', arguments = '.')
 
-test.fail_test(not os.path.exists(test.workpath('work4', 'install', 'SConstruct')))
+test.must_exist(test.workpath('work4', 'install', 'SConstruct'))
 
 
 test.pass_test()
 # This should populate the cache with our derived files.
 test.run(chdir = 'src', arguments = '.')
 
-test.fail_test(test.read(['src', 'all']) != "aaa.in\nbbb.in\nccc.in\n")
-test.fail_test(test.read(['src', 'cat.out']) != "aaa.out\nbbb.out\nccc.out\nall\n")
+test.must_match(['src', 'all'], "aaa.in\nbbb.in\nccc.in\n")
+test.must_match(['src', 'cat.out'], "aaa.out\nbbb.out\nccc.out\nall\n")
 
 test.up_to_date(chdir = 'src', arguments = '.')
 
 Retrieved `all' from cache
 """))
 
-test.fail_test(os.path.exists(test.workpath('src', 'cat.out')))
+test.must_not_exist(test.workpath('src', 'cat.out'))
 
 test.up_to_date(chdir = 'src', arguments = '.')
 
 Retrieved `all' from cache
 """))
 
-test.fail_test(os.path.exists(test.workpath('src', 'aaa.out')))
-test.fail_test(os.path.exists(test.workpath('src', 'bbb.out')))
-test.fail_test(os.path.exists(test.workpath('src', 'ccc.out')))
-test.fail_test(os.path.exists(test.workpath('src', 'all')))
+test.must_not_exist(test.workpath('src', 'aaa.out'))
+test.must_not_exist(test.workpath('src', 'bbb.out'))
+test.must_not_exist(test.workpath('src', 'ccc.out'))
+test.must_not_exist(test.workpath('src', 'all'))
 
 # Verify that rebuilding with -s retrieves everything from the cache
 # even though it doesn't report anything.
 test.run(chdir = 'src', arguments = '-s .', stdout = "")
 
-test.fail_test(test.read(['src', 'all']) != "aaa.in\nbbb.in\nccc.in\n")
-test.fail_test(os.path.exists(test.workpath('src', 'cat.out')))
+test.must_match(['src', 'all'], "aaa.in\nbbb.in\nccc.in\n")
+test.must_not_exist(test.workpath('src', 'cat.out'))
 
 test.up_to_date(chdir = 'src', arguments = '.')
 
 
 test.run(chdir = 'src', arguments = '.', stdout = test.wrap_stdout("""\
 Retrieved `aaa.out' from cache
-cat("bbb.out", "bbb.in")
+cat(["bbb.out"], ["bbb.in"])
 Retrieved `ccc.out' from cache
-cat("all", ["aaa.out", "bbb.out", "ccc.out"])
+cat(["all"], ["aaa.out", "bbb.out", "ccc.out"])
 """))
 
-test.fail_test(test.read(['src', 'all']) != "aaa.in\nbbb.in 2\nccc.in\n")
-test.fail_test(test.read(['src', 'cat.out']) != "bbb.out\nall\n")
+test.must_match(['src', 'all'], "aaa.in\nbbb.in 2\nccc.in\n")
+test.must_match(['src', 'cat.out'], "bbb.out\nall\n")
 
 test.up_to_date(chdir = 'src', arguments = '.')
 
 # This should populate the cache with our derived files.
 test.run()
 
-test.fail_test(test.read(['build', 'all']) != "aaa.in\nbbb.in\nccc.in\n")
-test.fail_test(test.read('cat.out') != "%s\n%s\n%s\n%s\n" % (build_aaa_out, build_bbb_out, build_ccc_out, build_all))
+test.must_match(['build', 'all'], "aaa.in\nbbb.in\nccc.in\n")
+test.must_match('cat.out', "%s\n%s\n%s\n%s\n" % (build_aaa_out, build_bbb_out, build_ccc_out, build_all))
 
 test.up_to_date(arguments = '.')
 
 Retrieved `%s' from cache
 """ % (build_aaa_out, build_bbb_out, build_ccc_out, build_all)))
 
-test.fail_test(os.path.exists(test.workpath('cat.out')))
+test.must_not_exist(test.workpath('cat.out'))
 
 test.up_to_date(arguments = '.')
 
 Retrieved `%s' from cache
 """ % (build_aaa_out, build_bbb_out, build_ccc_out, build_all)))
 
-test.fail_test(os.path.exists(test.workpath('build', 'aaa.out')))
-test.fail_test(os.path.exists(test.workpath('build', 'bbb.out')))
-test.fail_test(os.path.exists(test.workpath('build', 'ccc.out')))
-test.fail_test(os.path.exists(test.workpath('build', 'all')))
+test.must_not_exist(test.workpath('build', 'aaa.out'))
+test.must_not_exist(test.workpath('build', 'bbb.out'))
+test.must_not_exist(test.workpath('build', 'ccc.out'))
+test.must_not_exist(test.workpath('build', 'all'))
 
 # Verify that rebuilding with -s retrieves everything from the cache
 # even though it doesn't report anything.
 test.run(arguments = '-s .', stdout = "")
 
-test.fail_test(test.read(['build', 'all']) != "aaa.in\nbbb.in\nccc.in\n")
-test.fail_test(os.path.exists(test.workpath('cat.out')))
+test.must_match(['build', 'all'], "aaa.in\nbbb.in\nccc.in\n")
+test.must_not_exist(test.workpath('cat.out'))
 
 test.up_to_date(arguments = '.')
 
 
 test.run(stdout = test.wrap_stdout("""\
 Retrieved `%s' from cache
-cat("%s", "%s")
+cat(["%s"], ["%s"])
 Retrieved `%s' from cache
-cat("%s", ["%s", "%s", "%s"])
+cat(["%s"], ["%s", "%s", "%s"])
 """ % (build_aaa_out,
        build_bbb_out, os.path.join('src', 'bbb.in'),
        build_ccc_out,
        build_all, build_aaa_out, build_bbb_out, build_ccc_out)))
 
-test.fail_test(test.read(['build', 'all']) != "aaa.in\nbbb.in 2\nccc.in\n")
-test.fail_test(test.read('cat.out') != "%s\n%s\n" % (build_bbb_out, build_all))
+test.must_match(['build', 'all'], "aaa.in\nbbb.in 2\nccc.in\n")
+test.must_match('cat.out', "%s\n%s\n" % (build_bbb_out, build_all))
 
 test.up_to_date(arguments = '.')
 
 
 test.run(chdir = 'subdir')
 
-test.fail_test(os.path.exists(test.workpath('cache3', 'N', 'None')))
+test.must_not_exist(test.workpath('cache3', 'N', 'None'))
 
 #############################################################################
 # Test that multiple target files get retrieved from cache correctly.
 
 test.run(chdir = 'multiple')
 
-test.fail_test(not os.path.exists(test.workpath('multiple', 'foo')))
-test.fail_test(not os.path.exists(test.workpath('multiple', 'bar')))
+test.must_exist(test.workpath('multiple', 'foo'))
+test.must_exist(test.workpath('multiple', 'bar'))
 
 test.run(chdir = 'multiple', arguments = '-c')
 
-test.fail_test(os.path.exists(test.workpath('multiple', 'foo')))
-test.fail_test(os.path.exists(test.workpath('multiple', 'bar')))
+test.must_not_exist(test.workpath('multiple', 'foo'))
+test.must_not_exist(test.workpath('multiple', 'bar'))
 
 test.run(chdir = 'multiple')
 
-test.fail_test(not os.path.exists(test.workpath('multiple', 'foo')))
-test.fail_test(not os.path.exists(test.workpath('multiple', 'bar')))
+test.must_exist(test.workpath('multiple', 'foo'))
+test.must_exist(test.workpath('multiple', 'bar'))
 
 # All done.
 test.pass_test()
 
 expect = test.wrap_stdout(read_str = 'Chmod("f1", 0666)\nChmod("d2", 0777)\n',
                           build_str = """\
-cat("bar.out", "bar.in")
+cat(["bar.out"], ["bar.in"])
 Chmod("f3", 0666)
 Chmod("d4", 0777)
 Chmod("f5", 0666)
-cat("f6.out", "f6.in")
-cat("f7.out", "f7.in")
+cat(["f6.out"], ["f6.in"])
+cat(["f7.out"], ["f7.in"])
 Chmod("Chmod-f7.in", 0666)
 Chmod("f7.out-Chmod", 0666)
 """)
 Copy("d3.out", "f3.in")
 """,
                           build_str = """\
-cat("bar.out", "bar.in")
+cat(["bar.out"], ["bar.in"])
 Copy("f4.out", "f4.in")
 Copy("d5.out", "d5.in")
 Copy("d6.out", "f6.in")
 Copy("f7.out", "f7.in")
-cat("f8.out", "f8.in")
-cat("f9.out", "f9.in")
+cat(["f8.out"], ["f8.in"])
+cat(["f9.out"], ["f9.in"])
 Copy("f9.out-Copy", "f9.in")
 """)
 test.run(options = '-n', arguments = '.', stdout = expect)
 Delete("d2")
 """,
                           build_str = """\
-cat("f3.out", "f3.in")
+cat(["f3.out"], ["f3.in"])
 Delete("f4")
 Delete("d5")
 Delete("f6")
 Delete("d7")
-cat("f8.out", "f8.in")
-cat("f9.out", "f9.in")
+cat(["f8.out"], ["f8.in"])
+cat(["f9.out"], ["f9.in"])
 Delete("Delete-f9.in")
 Delete("f9.out-Delete")
 """)
 
 test.run(arguments = '.')
 
-test.fail_test(test.read('f1.out') != "f1.in\nsubdir/foo.dep 1\n")
-test.fail_test(test.read('f2.out') != "f2.in\nsubdir/foo.dep 1\n")
-test.fail_test(test.read(['subdir', 'f3.out']) != "f3.in\nsubdir/bar.dep 1\n")
-test.fail_test(test.read(['subdir', 'f4.out']) !=
-               "subdir/f4.in\nsubdir/bar.dep 1\n")
-test.fail_test(test.read('f5.out') != "f5.in\nsubdir/foo.dep 1\n")
-test.fail_test(test.read(['sub2', 'f6.out']) != "f6.in\nsubdir/bar.dep 1\n")
+test.must_match('f1.out', "f1.in\nsubdir/foo.dep 1\n")
+test.must_match('f2.out', "f2.in\nsubdir/foo.dep 1\n")
+test.must_match(['subdir', 'f3.out'], "f3.in\nsubdir/bar.dep 1\n")
+test.must_match(['subdir', 'f4.out'], "subdir/f4.in\nsubdir/bar.dep 1\n")
+test.must_match('f5.out', "f5.in\nsubdir/foo.dep 1\n")
+test.must_match(['sub2', 'f6.out'], "f6.in\nsubdir/bar.dep 1\n")
 
 #
 test.write(['subdir', 'foo.dep'], "subdir/foo.dep 2\n")
 
 test.run(arguments = '.')
 
-test.fail_test(test.read('f1.out') != "f1.in\nsubdir/foo.dep 2\n")
-test.fail_test(test.read('f2.out') != "f2.in\nsubdir/foo.dep 2\n")
-test.fail_test(test.read(['subdir', 'f3.out']) != "f3.in\nsubdir/bar.dep 2\n")
-test.fail_test(test.read(['subdir', 'f4.out']) !=
-               "subdir/f4.in\nsubdir/bar.dep 2\n")
-test.fail_test(test.read('f5.out') != "f5.in\nsubdir/foo.dep 2\n")
-test.fail_test(test.read(['sub2', 'f6.out']) != "f6.in 2\nsubdir/bar.dep 2\n")
+test.must_match('f1.out', "f1.in\nsubdir/foo.dep 2\n")
+test.must_match('f2.out', "f2.in\nsubdir/foo.dep 2\n")
+test.must_match(['subdir', 'f3.out'], "f3.in\nsubdir/bar.dep 2\n")
+test.must_match(['subdir', 'f4.out'], "subdir/f4.in\nsubdir/bar.dep 2\n")
+test.must_match('f5.out', "f5.in\nsubdir/foo.dep 2\n")
+test.must_match(['sub2', 'f6.out'], "f6.in 2\nsubdir/bar.dep 2\n")
 
 #
 test.write(['subdir', 'foo.dep'], "subdir/foo.dep 3\n")
 
 test.run(arguments = '.')
 
-test.fail_test(test.read('f1.out') != "f1.in\nsubdir/foo.dep 3\n")
-test.fail_test(test.read('f2.out') != "f2.in\nsubdir/foo.dep 3\n")
-test.fail_test(test.read(['subdir', 'f3.out']) != "f3.in\nsubdir/bar.dep 2\n")
-test.fail_test(test.read(['subdir', 'f4.out']) !=
-               "subdir/f4.in\nsubdir/bar.dep 2\n")
-test.fail_test(test.read('f5.out') != "f5.in\nsubdir/foo.dep 2\n")
-test.fail_test(test.read(['sub2', 'f6.out']) != "f6.in 2\nsubdir/bar.dep 2\n")
+test.must_match('f1.out', "f1.in\nsubdir/foo.dep 3\n")
+test.must_match('f2.out', "f2.in\nsubdir/foo.dep 3\n")
+test.must_match(['subdir', 'f3.out'], "f3.in\nsubdir/bar.dep 2\n")
+test.must_match(['subdir', 'f4.out'], "subdir/f4.in\nsubdir/bar.dep 2\n")
+test.must_match('f5.out', "f5.in\nsubdir/foo.dep 2\n")
+test.must_match(['sub2', 'f6.out'], "f6.in 2\nsubdir/bar.dep 2\n")
 
 #
 test.write(['subdir', 'bar.dep'], "subdir/bar.dep 3\n")
 
 test.run(arguments = '.')
 
-test.fail_test(test.read('f1.out') != "f1.in\nsubdir/foo.dep 3\n")
-test.fail_test(test.read('f2.out') != "f2.in\nsubdir/foo.dep 3\n")
-test.fail_test(test.read(['subdir', 'f3.out']) != "f3.in\nsubdir/bar.dep 3\n")
-test.fail_test(test.read(['subdir', 'f4.out']) !=
-               "subdir/f4.in\nsubdir/bar.dep 3\n")
-test.fail_test(test.read('f5.out') != "f5.in\nsubdir/foo.dep 2\n")
-test.fail_test(test.read(['sub2', 'f6.out']) != "f6.in 2\nsubdir/bar.dep 2\n")
+test.must_match('f1.out', "f1.in\nsubdir/foo.dep 3\n")
+test.must_match('f2.out', "f2.in\nsubdir/foo.dep 3\n")
+test.must_match(['subdir', 'f3.out'], "f3.in\nsubdir/bar.dep 3\n")
+test.must_match(['subdir', 'f4.out'], "subdir/f4.in\nsubdir/bar.dep 3\n")
+test.must_match('f5.out', "f5.in\nsubdir/foo.dep 2\n")
+test.must_match(['sub2', 'f6.out'], "f6.in 2\nsubdir/bar.dep 2\n")
 
 #
 test.write('f6.in', "f6.in 3\n")
 
 test.run(arguments = '.')
 
-test.fail_test(test.read('f1.out') != "f1.in\nsubdir/foo.dep 3\n")
-test.fail_test(test.read('f2.out') != "f2.in\nsubdir/foo.dep 3\n")
-test.fail_test(test.read(['subdir', 'f3.out']) != "f3.in\nsubdir/bar.dep 3\n")
-test.fail_test(test.read(['subdir', 'f4.out']) !=
-               "subdir/f4.in\nsubdir/bar.dep 3\n")
-test.fail_test(test.read('f5.out') != "f5.in\nsubdir/foo.dep 3\n")
-test.fail_test(test.read(['sub2', 'f6.out']) != "f6.in 3\nsubdir/bar.dep 3\n")
+test.must_match('f1.out', "f1.in\nsubdir/foo.dep 3\n")
+test.must_match('f2.out', "f2.in\nsubdir/foo.dep 3\n")
+test.must_match(['subdir', 'f3.out'], "f3.in\nsubdir/bar.dep 3\n")
+test.must_match(['subdir', 'f4.out'], "subdir/f4.in\nsubdir/bar.dep 3\n")
+test.must_match('f5.out', "f5.in\nsubdir/foo.dep 3\n")
+test.must_match(['sub2', 'f6.out'], "f6.in 3\nsubdir/bar.dep 3\n")
 
 #
 test.write('SConstruct', """\
 env.Depends(file1, [[file2, 'file3']])
 """)
 
-test.run(status = 2, stderr = """
-scons: *** attempted to add a non-Node dependency to file1:
-\t['file2', 'file3'] is a <type 'list'>, not a Node
-File "SConstruct", line 4, in ?
-""")
+test.up_to_date(arguments = '.')
 
 test.pass_test()

test/Environment.py

                      'foo.in',
                      r'%s build.py $SOURCE $TARGET ${File(BAR)} ${Dir(BLAT)}')
 
+target = target[0]
 assert target == Dir('.').File('foo.out')
 assert Dir('.') == Dir('.').Dir('.')
 assert target == target.File('foo.out')
 
 test.run(status = 27,
          stdout = test.wrap_stdout("""\
-exit_builder("%s", "%s")
+exit_builder(["%s"], ["%s"])
 """ % (subdir_foo_out, subdir_foo_in), error=1),
          stderr = """\
 scons: *** [%s] Explicit exit, status 27
 """ % (subdir_foo_out))
 
-test.fail_test(test.read(['subdir', 'foo.out']) != "subdir/foo.in\n")
+test.must_match(['subdir', 'foo.out'], "subdir/foo.in\n")
 
 test.write('SConstruct', """\
 def exit_scanner(node, env, target):
 
 test.run(arguments = '.')
 
-test.fail_test(test.read('f1.out') != "f1a.in\nf1b.in\n")
-test.fail_test(test.read(['subdir', 'f2.out']) !=
-               "subdir/f2a.in\nsubdir/f2b.in\n")
-test.fail_test(test.read(['subdir', 'f3.out']) !=
-               "subdir/f3a.in\nsubdir/f3b.in\n")
+test.must_match('f1.out', "f1a.in\nf1b.in\n")
+test.must_match(['subdir', 'f2.out'], "subdir/f2a.in\nsubdir/f2b.in\n")
+test.must_match(['subdir', 'f3.out'], "subdir/f3a.in\nsubdir/f3b.in\n")
 
 test.up_to_date(arguments = '.')
 
 
 test.up_to_date(arguments = '.')
 
-test.fail_test(test.read('f1.out') != "f1a.in\nf1b.in\n")
-test.fail_test(test.read(['subdir', 'f2.out']) !=
-               "subdir/f2a.in\nsubdir/f2b.in\n")
-test.fail_test(test.read(['subdir', 'f3.out']) !=
-               "subdir/f3a.in\nsubdir/f3b.in\n")
+test.must_match('f1.out', "f1a.in\nf1b.in\n")
+test.must_match(['subdir', 'f2.out'], "subdir/f2a.in\nsubdir/f2b.in\n")
+test.must_match(['subdir', 'f3.out'], "subdir/f3a.in\nsubdir/f3b.in\n")
 
 test.write('f1a.in', "f1a.in 2\n")
 test.write(['subdir', 'f2b.in'], "subdir/f2b.in 2\n")
 
 test.run(arguments = '.')
 
-test.fail_test(test.read('f1.out') != "f1a.in 2\nf1b.in 2\n")
-test.fail_test(test.read(['subdir', 'f2.out']) !=
-               "subdir/f2a.in 2\nsubdir/f2b.in 2\n")
-test.fail_test(test.read(['subdir', 'f3.out']) !=
-               "subdir/f3a.in 2\nsubdir/f3b.in 2\n")
+test.must_match('f1.out', "f1a.in 2\nf1b.in 2\n")
+test.must_match(['subdir', 'f2.out'], "subdir/f2a.in 2\nsubdir/f2b.in 2\n")
+test.must_match(['subdir', 'f3.out'], "subdir/f3a.in 2\nsubdir/f3b.in 2\n")
 
 test.up_to_date(arguments = '.')
 
 env.Ignore(file1, [[file2, 'file3']])
 """)
 
-test.run(status = 2, stderr = """
-scons: *** attempted to ignore a non-Node dependency of file1:
-\t['file2', 'file3'] is a <type 'list'>, not a Node
-File "SConstruct", line 4, in ?
-""")
+test.up_to_date(arguments = '.')
 
 test.pass_test()
 
 expect = test.wrap_stdout(read_str = 'Mkdir("d1")\n',
                           build_str = """\
-cat("f2.out", "f2.in")
+cat(["f2.out"], ["f2.in"])
 Mkdir("d3")
 Mkdir("d4")
-cat("f5.out", "f5.in")
-cat("f6.out", "f6.in")
+cat(["f5.out"], ["f5.in"])
+cat(["f6.out"], ["f6.in"])
 Mkdir("Mkdir-f6.in")
 Mkdir("f6.out-Mkdir")
 """)
 
 expect = test.wrap_stdout(read_str = 'Move("f1.out", "f1.in")\n',
                           build_str = """\
-cat("f2.out", "f2.in")
+cat(["f2.out"], ["f2.in"])
 Move("f3.out", "f3.in")
 Move("f4.out", "f4.in")
-cat("f5.out", "f5.in")
-cat("f6.out", "f6.in")
+cat(["f5.out"], ["f5.in"])
+cat(["f6.out"], ["f6.in"])
 Move("Move-f6.out", "f6.in-Move")
 """)
 test.run(options = '-n', arguments = '.', stdout = expect)
 env = Environment()
 env.Program(target = 'foo1', source = 'f1.c')
 env.Program(target = 'foo2', source = Split('f2a.c f2b.c f2c.c'))
-Program(target = 'foo3', source = ['f3a.c', 'f3b.c', 'f3c.c'])
+f3a = File('f3a.c')
+f3b = File('f3b.c')
+Program(target = 'foo3', source = [f3a, [f3b, 'f3c.c']])
 env.Program('foo4', 'f4.c')
 env.Program('foo5.c')
 """)
 test.fail_test(not (oldtime4 == os.path.getmtime(foo4)))
 test.fail_test(not (oldtime5 == os.path.getmtime(foo5)))
 
-# 
-test.write('SConstruct', """\
-file1 = File('file1.c')
-file2 = File('file2.c')
-Program('foo', [file1, [file2, 'file3.c']])
-""")
-
-foo_exe = 'foo'+_exe
-
-test.run(status = 2, stderr = """
-scons: *** attempted to add a non-Node as source of %s:
-\t['file2.c', 'file3.c'] is a <type 'list'>, not a Node
-File "SConstruct", line 3, in ?
-""" % foo_exe)
-
 test.pass_test()
 """,
                                    build_str = """\
 co -q aaa.in
-cat("aaa.out", "aaa.in")
-cat("bbb.out", "bbb.in")
+cat(["aaa.out"], ["aaa.in"])
+cat(["bbb.out"], ["bbb.in"])
 co -q ccc.in
-cat("ccc.out", "ccc.in")
-cat("all", ["aaa.out", "bbb.out", "ccc.out"])
+cat(["ccc.out"], ["ccc.in"])
+cat(["all"], ["aaa.out", "bbb.out", "ccc.out"])
 co -q sub/ddd.in
-cat("sub/ddd.out", "sub/ddd.in")
-cat("sub/eee.out", "sub/eee.in")
+cat(["sub/ddd.out"], ["sub/ddd.in"])
+cat(["sub/eee.out"], ["sub/eee.in"])
 co -q sub/fff.in
-cat("sub/fff.out", "sub/fff.in")
-cat("sub/all", ["sub/ddd.out", "sub/eee.out", "sub/fff.out"])
+cat(["sub/fff.out"], ["sub/fff.in"])
+cat(["sub/all"], ["sub/ddd.out", "sub/eee.out", "sub/fff.out"])
 """))
 
-test.fail_test(test.read(['work1', 'all']) != "work1/aaa.in\nchecked-out work1/bbb.in\nwork1/ccc.in\n")
+test.must_match(['work1', 'all'], "work1/aaa.in\nchecked-out work1/bbb.in\nwork1/ccc.in\n")
 
-test.fail_test(test.read(['work1', 'sub', 'all']) != "work1/sub/ddd.in\nchecked-out work1/sub/eee.in\nwork1/sub/fff.in\n")
+test.must_match(['work1', 'sub', 'all'], "work1/sub/ddd.in\nchecked-out work1/sub/eee.in\nwork1/sub/fff.in\n")
 
 test.fail_test(is_writable(test.workpath('work1', 'sub', 'SConscript')))
 test.fail_test(is_writable(test.workpath('work1', 'aaa.in')))
 """,
                                    build_str = """\
 co -l aaa.in
-cat("aaa.out", "aaa.in")
-cat("bbb.out", "bbb.in")
+cat(["aaa.out"], ["aaa.in"])
+cat(["bbb.out"], ["bbb.in"])
 co -l ccc.in
-cat("ccc.out", "ccc.in")
-cat("all", ["aaa.out", "bbb.out", "ccc.out"])
+cat(["ccc.out"], ["ccc.in"])
+cat(["all"], ["aaa.out", "bbb.out", "ccc.out"])
 co -l sub/ddd.in
-cat("sub/ddd.out", "sub/ddd.in")
-cat("sub/eee.out", "sub/eee.in")
+cat(["sub/ddd.out"], ["sub/ddd.in"])
+cat(["sub/eee.out"], ["sub/eee.in"])
 co -l sub/fff.in
-cat("sub/fff.out", "sub/fff.in")
-cat("sub/all", ["sub/ddd.out", "sub/eee.out", "sub/fff.out"])
+cat(["sub/fff.out"], ["sub/fff.in"])
+cat(["sub/all"], ["sub/ddd.out", "sub/eee.out", "sub/fff.out"])
 """),
          stderr = """\
 sub/RCS/SConscript,v  -->  sub/SConscript
 done
 """)
 
-test.fail_test(test.read(['work2', 'all']) != "work2/aaa.in\nchecked-out work2/bbb.in\nwork2/ccc.in\n")
+test.must_match(['work2', 'all'], "work2/aaa.in\nchecked-out work2/bbb.in\nwork2/ccc.in\n")
 
-test.fail_test(test.read(['work2', 'sub', 'all']) != "work2/sub/ddd.in\nchecked-out work2/sub/eee.in\nwork2/sub/fff.in\n")
+test.must_match(['work2', 'sub', 'all'], "work2/sub/ddd.in\nchecked-out work2/sub/eee.in\nwork2/sub/fff.in\n")
 
 test.fail_test(not is_writable(test.workpath('work2', 'sub', 'SConscript')))
 test.fail_test(not is_writable(test.workpath('work2', 'aaa.in')))
 """,
                                    build_str = """\
 sccs get -e aaa.in
-cat("aaa.out", "aaa.in")
-cat("bbb.out", "bbb.in")
+cat(["aaa.out"], ["aaa.in"])
+cat(["bbb.out"], ["bbb.in"])
 sccs get -e ccc.in
-cat("ccc.out", "ccc.in")
-cat("all", ["aaa.out", "bbb.out", "ccc.out"])
+cat(["ccc.out"], ["ccc.in"])
+cat(["all"], ["aaa.out", "bbb.out", "ccc.out"])
 sccs get -e sub/ddd.in
-cat("sub/ddd.out", "sub/ddd.in")
-cat("sub/eee.out", "sub/eee.in")
+cat(["sub/ddd.out"], ["sub/ddd.in"])
+cat(["sub/eee.out"], ["sub/eee.in"])
 sccs get -e sub/fff.in
-cat("sub/fff.out", "sub/fff.in")
-cat("sub/all", ["sub/ddd.out", "sub/eee.out", "sub/fff.out"])
+cat(["sub/fff.out"], ["sub/fff.in"])
+cat(["sub/all"], ["sub/ddd.out", "sub/eee.out", "sub/fff.out"])
 """),
          stderr = """\
 sub/SConscript 1.1 -> 1.2: 5 lines
 sub/fff.in 1.1 -> 1.2: 1 lines
 """)
 
-test.fail_test(test.read(['work1', 'all']) != "work1/aaa.in\nchecked-out work1/bbb.in\nwork1/ccc.in\n")
+test.must_match(['work1', 'all'], "work1/aaa.in\nchecked-out work1/bbb.in\nwork1/ccc.in\n")
 
 test.fail_test(not is_writable(test.workpath('work1', 'sub', 'SConscript')))
 test.fail_test(not is_writable(test.workpath('work1', 'aaa.in')))
 """,
                                    build_str = """\
 sccs get aaa.in
-cat("aaa.out", "aaa.in")
-cat("bbb.out", "bbb.in")
+cat(["aaa.out"], ["aaa.in"])
+cat(["bbb.out"], ["bbb.in"])
 sccs get ccc.in
-cat("ccc.out", "ccc.in")
-cat("all", ["aaa.out", "bbb.out", "ccc.out"])
+cat(["ccc.out"], ["ccc.in"])
+cat(["all"], ["aaa.out", "bbb.out", "ccc.out"])
 sccs get sub/ddd.in
-cat("sub/ddd.out", "sub/ddd.in")
-cat("sub/eee.out", "sub/eee.in")
+cat(["sub/ddd.out"], ["sub/ddd.in"])
+cat(["sub/eee.out"], ["sub/eee.in"])
 sccs get sub/fff.in
-cat("sub/fff.out", "sub/fff.in")
-cat("sub/all", ["sub/ddd.out", "sub/eee.out", "sub/fff.out"])
+cat(["sub/fff.out"], ["sub/fff.in"])
+cat(["sub/all"], ["sub/ddd.out", "sub/eee.out", "sub/fff.out"])
 """),
          stderr = """\
 sub/SConscript 1.1: 5 lines
 sub/fff.in 1.1: 1 lines
 """)
 
-test.fail_test(test.read(['work2', 'all']) != "work2/aaa.in\nchecked-out work2/bbb.in\nwork2/ccc.in\n")
+test.must_match(['work2', 'all'], "work2/aaa.in\nchecked-out work2/bbb.in\nwork2/ccc.in\n")
 
 test.fail_test(is_writable(test.workpath('work2', 'sub', 'SConscript')))
 test.fail_test(is_writable(test.workpath('work2', 'aaa.in')))
 env2.Command('junk', 'junk.k2', r'%s build.py $SOURCES $TARGET')
 
 bar = env.Command('bar', 'bar.in', r'%s build.py $SOURCES  $TARGET')
-bar.source_scanner = kscan
+bar[0].source_scanner = kscan
 """ % (python, python, python))
 
 test.write('foo.k', 

test/SetBuildSignatureType.py

 
 test.run(arguments='foo.out.out',
          stdout=test.wrap_stdout("""\
-copy2("foo.out", "foo.in")
-copy1("foo.out.out", "foo.out")
+copy2(["foo.out"], ["foo.in"])
+copy1(["foo.out.out"], ["foo.out"])
 """),
          stderr=warning%16)
 
 
 test.run(arguments='foo.out.out',
          stdout=test.wrap_stdout("""\
-copy2("foo.out", "foo.in")
+copy2(["foo.out"], ["foo.in"])
 scons: `foo.out.out' is up to date.
 """),
          stderr=warning%17)
 
 test.run(arguments='foo.out.out',
          stdout=test.wrap_stdout("""\
-copy1("foo.out.out", "foo.out")
+copy1(["foo.out.out"], ["foo.out"])
 """),
          stderr=warning%17)
 
 
 test.run(arguments='foo.out.out',
          stdout=test.wrap_stdout("""\
-copy2("foo.out", "foo.in")
-copy1("foo.out.out", "foo.out")
+copy2(["foo.out"], ["foo.in"])
+copy1(["foo.out.out"], ["foo.out"])
 """),
          stderr=warning%16)