Commits

Anonymous committed 91b653c

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

  • Participants
  • Parent commits b671844

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("""\