Rearrange the Environment methods and tests.
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Sun, 7 Sep 2003 04:14:49 +0000 (04:14 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Sun, 7 Sep 2003 04:14:49 +0000 (04:14 +0000)
git-svn-id: http://scons.tigris.org/svn/scons/trunk@789 fdb21ef1-2011-0410-befe-b5e4ea1792b1

src/engine/SCons/Environment.py
src/engine/SCons/EnvironmentTests.py

index f72bc9537508d36254c2c0492d57ba3415c2cff3..e7407ee67deb31e38f659254806a926be13ea5ff 100644 (file)
@@ -59,6 +59,8 @@ class _Null:
 
 _null = _Null
 
+DefaultTargets = None
+
 def installFunc(target, source, env):
     """Install a source file into a target using the function specified
     as the INSTALL construction variable."""
@@ -169,6 +171,20 @@ class Environment:
     Environment.
     """
 
+    #######################################################################
+    # This is THE class for interacting with the SCons build engine,
+    # and it contains a lot of stuff, so we're going to try to keep this
+    # a little organized by grouping the methods.
+    #######################################################################
+
+    #######################################################################
+    # Methods that make an Environment act like a dictionary.  These have
+    # the expected standard names for Python mapping objects.  Note that
+    # we don't actually make an Environment a subclass of UserDict for
+    # performance reasons.  Note also that we only supply methods for
+    # dictionary functionality that we actually need and use.
+    #######################################################################
+
     def __init__(self,
                  platform=None,
                  tools=None,
@@ -217,6 +233,46 @@ class Environment:
     def __cmp__(self, other):
        return cmp(self._dict, other._dict)
 
+    def __getitem__(self, key):
+        return self._dict[key]
+
+    def __setitem__(self, key, value):
+        if key in ['TARGET', 'TARGETS', 'SOURCE', 'SOURCES']:
+            SCons.Warnings.warn(SCons.Warnings.ReservedVariableWarning,
+                                "Ignoring attempt to set reserved variable `%s'" % key)
+        elif key == 'BUILDERS':
+            try:
+                bd = self._dict[key]
+                for k in bd.keys():
+                    del bd[k]
+            except KeyError:
+                self._dict[key] = BuilderDict(kwbd, self)
+            self._dict[key].update(value)
+        else:
+            if not SCons.Util.is_valid_construction_var(key):
+                raise SCons.Errors.UserError, "Illegal construction variable `%s'" % key
+            self._dict[key] = value
+
+    def __delitem__(self, key):
+        del self._dict[key]
+
+    def items(self):
+        "Emulates the items() method of dictionaries."""
+        return self._dict.items()
+
+    def has_key(self, key):
+        return self._dict.has_key(key)
+
+    def get(self, key, default=None):
+        "Emulates the get() method of dictionaries."""
+        return self._dict.get(key, default)
+
+    #######################################################################
+    # Utility methods that are primarily for internal use by SCons.
+    # These begin with lower-case letters.  Note that the subst() method
+    # is actually already out of the closet and used by people.
+    #######################################################################
+
     def arg2nodes(self, args, node_factory=_null, lookup_list=_null):
         if node_factory is _null:
             node_factory = self.fs.File
@@ -251,8 +307,99 @@ class Environment:
     
         return nodes
 
-    def Builders(self):
-       pass    # XXX
+    def get_builder(self, name):
+        """Fetch the builder with the specified name from the environment.
+        """
+        try:
+            return self._dict['BUILDERS'][name]
+        except KeyError:
+            return None
+
+    def get_scanner(self, skey):
+        """Find the appropriate scanner given a key (usually a file suffix).
+        Does a linear search. Could be sped up by creating a dictionary if
+        this proves too slow.
+        """
+        if self._dict['SCANNERS']:
+            for scanner in self._dict['SCANNERS']:
+                if skey in scanner.skeys:
+                    return scanner
+        return None
+
+    def subst(self, string, raw=0, target=None, source=None):
+       """Recursively interpolates construction variables from the
+       Environment into the specified string, returning the expanded
+       result.  Construction variables are specified by a $ prefix
+       in the string and begin with an initial underscore or
+       alphabetic character followed by any number of underscores
+       or alphanumeric characters.  The construction variable names
+       may be surrounded by curly braces to separate the name from
+       trailing characters.
+       """
+        if raw:
+            mode = SCons.Util.SUBST_RAW
+        else:
+            mode = SCons.Util.SUBST_CMD
+        return SCons.Util.scons_subst(string, self, mode,
+                                      target, source)
+    
+    def subst_list(self, string, raw=0, target=None, source=None):
+        """Calls through to SCons.Util.scons_subst_list().  See
+        the documentation for that function."""
+        if raw:
+            mode = SCons.Util.SUBST_RAW
+        else:
+            mode = SCons.Util.SUBST_CMD
+        return SCons.Util.scons_subst_list(string, self, mode,
+                                           target, source)
+
+    #######################################################################
+    # Public methods for manipulating an Environment.  These begin with
+    # upper-case letters.  The essential characteristic of methods in
+    # this section is that they do *not* have corresponding same-named
+    # global functions.  For example, a stand-alone Append() function
+    # makes no sense, because Append() is all about appending values to
+    # an Environment's construction variables.
+    #######################################################################
+
+    def Append(self, **kw):
+        """Append values to existing construction variables
+        in an Environment.
+        """
+        kw = our_deepcopy(kw)
+        for key in kw.keys():
+            if not self._dict.has_key(key):
+                self._dict[key] = kw[key]
+            elif SCons.Util.is_List(self._dict[key]) and not \
+                 SCons.Util.is_List(kw[key]):
+                self._dict[key] = self._dict[key] + [ kw[key] ]
+            elif SCons.Util.is_List(kw[key]) and not \
+                 SCons.Util.is_List(self._dict[key]):
+                self._dict[key] = [ self._dict[key] ] + kw[key]
+            elif SCons.Util.is_Dict(self._dict[key]) and \
+                 SCons.Util.is_Dict(kw[key]):
+                self._dict[key].update(kw[key])
+            else:
+                self._dict[key] = self._dict[key] + kw[key]
+
+    def AppendENVPath(self, name, newpath, envname = 'ENV', sep = os.pathsep):
+        """Append path elements to the path 'name' in the 'ENV'
+        dictionary for this environment.  Will only add any particular
+        path once, and will normpath and normcase all paths to help
+        assure this.  This can also handle the case where the env
+        variable is a list instead of a string.
+        """
+
+        orig = ''
+        if self._dict.has_key(envname) and self._dict[envname].has_key(name):
+            orig = self._dict[envname][name]
+
+        nv = SCons.Util.AppendPath(orig, newpath, sep)
+            
+        if not self._dict.has_key(envname):
+            self._dict[envname] = {}
+
+        self._dict[envname][name] = nv
 
     def Copy(self, tools=None, **kw):
         """Return a copy of a construction Environment.  The
@@ -276,40 +423,62 @@ class Environment:
         apply(clone.Replace, (), kw)
         return clone
 
-    def Scanners(self):
-       pass    # XXX
+    def Detect(self, progs):
+        """Return the first available program in progs.
+        """
+        if not SCons.Util.is_List(progs):
+            progs = [ progs ]
+        for prog in progs:
+            path = self.WhereIs(prog)
+            if path: return prog
+        return None
+
+    def Dictionary(self, *args):
+       if not args:
+           return self._dict
+       dlist = map(lambda x, s=self: s._dict[x], args)
+       if len(dlist) == 1:
+           dlist = dlist[0]
+       return dlist
 
-    def Replace(self, **kw):
-        """Replace existing construction variables in an Environment
-        with new construction variables and/or values.
+    def FindIxes(self, paths, prefix, suffix):
         """
-        try:
-            kwbd = our_deepcopy(kw['BUILDERS'])
-            del kw['BUILDERS']
-            self.__setitem__('BUILDERS', kwbd)
-        except KeyError:
-            pass
-        self._dict.update(our_deepcopy(kw))
+        Search a list of paths for something that matches the prefix and suffix.
 
-    def Append(self, **kw):
-        """Append values to existing construction variables
-        in an Environment.
+        paths - the list of paths or nodes.
+        prefix - construction variable for the prefix.
+        suffix - construction variable for the suffix.
         """
-        kw = our_deepcopy(kw)
-        for key in kw.keys():
-            if not self._dict.has_key(key):
-                self._dict[key] = kw[key]
-            elif SCons.Util.is_List(self._dict[key]) and not \
-                 SCons.Util.is_List(kw[key]):
-                self._dict[key] = self._dict[key] + [ kw[key] ]
-            elif SCons.Util.is_List(kw[key]) and not \
-                 SCons.Util.is_List(self._dict[key]):
-                self._dict[key] = [ self._dict[key] ] + kw[key]
-            elif SCons.Util.is_Dict(self._dict[key]) and \
-                 SCons.Util.is_Dict(kw[key]):
-                self._dict[key].update(kw[key])
-            else:
-                self._dict[key] = self._dict[key] + kw[key]
+
+        suffix = self.subst('$%s'%suffix)
+        prefix = self.subst('$%s'%prefix)
+
+        for path in paths:
+            dir,name = os.path.split(str(path))
+            if name[:len(prefix)] == prefix and name[-len(suffix):] == suffix: 
+                return path
+
+    def Override(self, overrides):
+        """
+        Produce a modified environment whose variables
+        are overriden by the overrides dictionaries.
+
+        overrides - a dictionary that will override
+        the variables of this environment.
+
+        This function is much more efficient than Copy()
+        or creating a new Environment because it doesn't do
+        a deep copy of the dictionary, and doesn't do a copy
+        at all if there are no overrides.
+        """
+
+        if overrides:
+            env = copy.copy(self)
+            env._dict = copy.copy(self._dict)
+            env._dict.update(overrides)
+            return env
+        else:
+            return self
 
     def Prepend(self, **kw):
         """Prepend values to existing construction variables
@@ -350,47 +519,63 @@ class Environment:
 
         self._dict[envname][name] = nv
 
-    def AppendENVPath(self, name, newpath, envname = 'ENV', sep = os.pathsep):
-        """Append path elements to the path 'name' in the 'ENV'
-        dictionary for this environment.  Will only add any particular
-        path once, and will normpath and normcase all paths to help
-        assure this.  This can also handle the case where the env
-        variable is a list instead of a string.
+    def Replace(self, **kw):
+        """Replace existing construction variables in an Environment
+        with new construction variables and/or values.
         """
+        try:
+            kwbd = our_deepcopy(kw['BUILDERS'])
+            del kw['BUILDERS']
+            self.__setitem__('BUILDERS', kwbd)
+        except KeyError:
+            pass
+        self._dict.update(our_deepcopy(kw))
 
-        orig = ''
-        if self._dict.has_key(envname) and self._dict[envname].has_key(name):
-            orig = self._dict[envname][name]
-
-        nv = SCons.Util.AppendPath(orig, newpath, sep)
-            
-        if not self._dict.has_key(envname):
-            self._dict[envname] = {}
+    def ReplaceIxes(self, path, old_prefix, old_suffix, new_prefix, new_suffix):
+        """
+        Replace old_prefix with new_prefix and old_suffix with new_suffix.
 
-        self._dict[envname][name] = nv
+        env - Environment used to interpolate variables.
+        path - the path that will be modified.
+        old_prefix - construction variable for the old prefix.
+        old_suffix - construction variable for the old suffix.
+        new_prefix - construction variable for the new prefix.
+        new_suffix - construction variable for the new suffix.
+        """
+        old_prefix = self.subst('$%s'%old_prefix)
+        old_suffix = self.subst('$%s'%old_suffix)
 
+        new_prefix = self.subst('$%s'%new_prefix)
+        new_suffix = self.subst('$%s'%new_suffix)
 
-    def Depends(self, target, dependency):
-        """Explicity specify that 'target's depend on 'dependency'."""
-        tlist = self.arg2nodes(target, self.fs.File)
-        dlist = self.arg2nodes(dependency, self.fs.File)
-        for t in tlist:
-            t.add_dependency(dlist)
+        dir,name = os.path.split(str(path))
+        if name[:len(old_prefix)] == old_prefix:
+            name = name[len(old_prefix):]
+        if name[-len(old_suffix):] == old_suffix:
+            name = name[:-len(old_suffix)]
+        return os.path.join(dir, new_prefix+name+new_suffix)
 
-        if len(tlist) == 1:
-            tlist = tlist[0]
-        return tlist
-
-    def Ignore(self, target, dependency):
-        """Ignore a dependency."""
-        tlist = self.arg2nodes(target, self.fs.File)
-        dlist = self.arg2nodes(dependency, self.fs.File)
-        for t in tlist:
-            t.add_ignore(dlist)
+    def WhereIs(self, prog):
+        """Find prog in the path.  
+        """
+        path = None
+        pathext = None
+        if self.has_key('ENV'):
+            if self['ENV'].has_key('PATH'):
+                path = self['ENV']['PATH']
+            if self['ENV'].has_key('PATHEXT'):
+                pathext = self['ENV']['PATHEXT']
+        path = SCons.Util.WhereIs(prog, path, pathext)
+        if path: return path
+        return None
 
-        if len(tlist) == 1:
-            tlist = tlist[0]
-        return tlist
+    #######################################################################
+    # Public methods for doing real "SCons stuff" (manipulating
+    # dependencies, setting attributes on targets, etc.).  These begin
+    # with upper-case letters.  The essential characteristic of methods
+    # in this section is that they all *should* have corresponding
+    # same-named global functions.
+    #######################################################################
 
     def AlwaysBuild(self, *targets):
         tlist = []
@@ -404,52 +589,6 @@ class Environment:
             tlist = tlist[0]
         return tlist
 
-    def Precious(self, *targets):
-        tlist = []
-        for t in targets:
-            tlist.extend(self.arg2nodes(t, self.fs.File))
-
-        for t in tlist:
-            t.set_precious()
-
-        if len(tlist) == 1:
-            tlist = tlist[0]
-        return tlist
-
-    def Dictionary(self, *args):
-       if not args:
-           return self._dict
-       dlist = map(lambda x, s=self: s._dict[x], args)
-       if len(dlist) == 1:
-           dlist = dlist[0]
-       return dlist
-
-    def __setitem__(self, key, value):
-        if key in ['TARGET', 'TARGETS', 'SOURCE', 'SOURCES']:
-            SCons.Warnings.warn(SCons.Warnings.ReservedVariableWarning,
-                                "Ignoring attempt to set reserved variable `%s'" % key)
-        elif key == 'BUILDERS':
-            try:
-                bd = self._dict[key]
-                for k in bd.keys():
-                    del bd[k]
-            except KeyError:
-                self._dict[key] = BuilderDict(kwbd, self)
-            self._dict[key].update(value)
-        else:
-            if not SCons.Util.is_valid_construction_var(key):
-                raise SCons.Errors.UserError, "Illegal construction variable `%s'" % key
-            self._dict[key] = value
-
-    def __getitem__(self, key):
-        return self._dict[key]
-
-    def __delitem__(self, key):
-        del self._dict[key]
-
-    def has_key(self, key):
-        return self._dict.has_key(key)
-
     def Command(self, target, source, action):
         """Builds the supplied target files from the supplied
         source files using the supplied action.  Action may
@@ -459,6 +598,28 @@ class Environment:
                                     source_factory=SCons.Node.FS.default_fs.Entry)
         return bld(self, target, source)
 
+    def Depends(self, target, dependency):
+        """Explicity specify that 'target's depend on 'dependency'."""
+        tlist = self.arg2nodes(target, self.fs.File)
+        dlist = self.arg2nodes(dependency, self.fs.File)
+        for t in tlist:
+            t.add_dependency(dlist)
+
+        if len(tlist) == 1:
+            tlist = tlist[0]
+        return tlist
+
+    def Ignore(self, target, dependency):
+        """Ignore a dependency."""
+        tlist = self.arg2nodes(target, self.fs.File)
+        dlist = self.arg2nodes(dependency, self.fs.File)
+        for t in tlist:
+            t.add_ignore(dlist)
+
+        if len(tlist) == 1:
+            tlist = tlist[0]
+        return tlist
+
     def Install(self, dir, source):
         """Install specified files in the given directory."""
         try:
@@ -492,14 +653,17 @@ class Environment:
             ret = ret[0]
         return ret
 
-    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 Precious(self, *targets):
+        tlist = []
+        for t in targets:
+            tlist.extend(self.arg2nodes(t, self.fs.File))
+
+        for t in tlist:
+            t.set_precious()
+
+        if len(tlist) == 1:
+            tlist = tlist[0]
+        return tlist
 
     def SideEffect(self, side_effect, target):
         """Tell scons that side_effects are built as side 
@@ -523,143 +687,11 @@ class Environment:
         else:
             return side_effects
 
-    def subst(self, string, raw=0, target=None, source=None):
-       """Recursively interpolates construction variables from the
-       Environment into the specified string, returning the expanded
-       result.  Construction variables are specified by a $ prefix
-       in the string and begin with an initial underscore or
-       alphabetic character followed by any number of underscores
-       or alphanumeric characters.  The construction variable names
-       may be surrounded by curly braces to separate the name from
-       trailing characters.
-       """
-        if raw:
-            mode = SCons.Util.SUBST_RAW
-        else:
-            mode = SCons.Util.SUBST_CMD
-        return SCons.Util.scons_subst(string, self, mode,
-                                      target, source)
-    
-    def subst_list(self, string, raw=0, target=None, source=None):
-        """Calls through to SCons.Util.scons_subst_list().  See
-        the documentation for that function."""
-        if raw:
-            mode = SCons.Util.SUBST_RAW
-        else:
-            mode = SCons.Util.SUBST_CMD
-        return SCons.Util.scons_subst_list(string, self, mode,
-                                           target, source)
-
-    def get_scanner(self, skey):
-        """Find the appropriate scanner given a key (usually a file suffix).
-        Does a linear search. Could be sped up by creating a dictionary if
-        this proves too slow.
-        """
-        if self._dict['SCANNERS']:
-            for scanner in self._dict['SCANNERS']:
-                if skey in scanner.skeys:
-                    return scanner
-        return None
-
-    def get_builder(self, name):
-        """Fetch the builder with the specified name from the environment.
-        """
-        try:
-            return self._dict['BUILDERS'][name]
-        except KeyError:
-            return None
-
-    def Detect(self, progs):
-        """Return the first available program in progs.
-        """
-        if not SCons.Util.is_List(progs):
-            progs = [ progs ]
-        for prog in progs:
-            path = self.WhereIs(prog)
-            if path: return prog
-        return None
-
-    def WhereIs(self, prog):
-        """Find prog in the path.  
-        """
-        path = None
-        pathext = None
-        if self.has_key('ENV'):
-            if self['ENV'].has_key('PATH'):
-                path = self['ENV']['PATH']
-            if self['ENV'].has_key('PATHEXT'):
-                pathext = self['ENV']['PATHEXT']
-        path = SCons.Util.WhereIs(prog, path, pathext)
-        if path: return path
-        return None
-
-    def Override(self, overrides):
-        """
-        Produce a modified environment whose variables
-        are overriden by the overrides dictionaries.
-
-        overrides - a dictionary that will override
-        the variables of this environment.
-
-        This function is much more efficient than Copy()
-        or creating a new Environment because it doesn't do
-        a deep copy of the dictionary, and doesn't do a copy
-        at all if there are no overrides.
-        """
-
-        if overrides:
-            env = copy.copy(self)
-            env._dict = copy.copy(self._dict)
-            env._dict.update(overrides)
-            return env
-        else:
-            return self
-
-    def get(self, key, default=None):
-        "Emulates the get() method of dictionaries."""
-        return self._dict.get(key, default)
-
-    def items(self):
-        "Emulates the items() method of dictionaries."""
-        return self._dict.items()
-
-    def FindIxes(self, paths, prefix, suffix):
-        """
-        Search a list of paths for something that matches the prefix and suffix.
-
-        paths - the list of paths or nodes.
-        prefix - construction variable for the prefix.
-        suffix - construction variable for the suffix.
-        """
-
-        suffix = self.subst('$%s'%suffix)
-        prefix = self.subst('$%s'%prefix)
-
-        for path in paths:
-            dir,name = os.path.split(str(path))
-            if name[:len(prefix)] == prefix and name[-len(suffix):] == suffix: 
-                return path
-
-    def ReplaceIxes(self, path, old_prefix, old_suffix, new_prefix, new_suffix):
-        """
-        Replace old_prefix with new_prefix and old_suffix with new_suffix.
-
-        env - Environment used to interpolate variables.
-        path - the path that will be modified.
-        old_prefix - construction variable for the old prefix.
-        old_suffix - construction variable for the old suffix.
-        new_prefix - construction variable for the new prefix.
-        new_suffix - construction variable for the new suffix.
-        """
-        old_prefix = self.subst('$%s'%old_prefix)
-        old_suffix = self.subst('$%s'%old_suffix)
-
-        new_prefix = self.subst('$%s'%new_prefix)
-        new_suffix = self.subst('$%s'%new_suffix)
-
-        dir,name = os.path.split(str(path))
-        if name[:len(old_prefix)] == old_prefix:
-            name = name[len(old_prefix):]
-        if name[-len(old_suffix):] == old_suffix:
-            name = name[:-len(old_suffix)]
-        return os.path.join(dir, new_prefix+name+new_suffix)
+    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
index 12766ecbbfcfbd8d12d92b556da4c18a696d3f7f..d3807f50e2dc5e6610e46257e63bfc38e5ddb2fe 100644 (file)
@@ -116,18 +116,28 @@ class Scanner:
 
 class EnvironmentTestCase(unittest.TestCase):
 
-    def test_Override(self):
-        "Test overriding construction variables"
-        env = Environment(ONE=1, TWO=2)
-        assert env['ONE'] == 1
-        assert env['TWO'] == 2
-        env2 = env.Override({'TWO':'10'})
-        assert env2['ONE'] == 1
-        assert env2['TWO'] == '10'
-        assert env['TWO'] == 2
-        env2.Replace(ONE = "won")
-        assert env2['ONE'] == "won"
-        assert env['ONE'] == 1
+    def test___init__(self):
+       """Test construction Environments creation
+       
+       Create two with identical arguments and check that
+       they compare the same.
+       """
+       env1 = Environment(XXX = 'x', YYY = 'y')
+       env2 = Environment(XXX = 'x', YYY = 'y')
+       assert env1 == env2, diff_env(env1, env2)
+
+    def test_get(self):
+        """Test the get() method."""
+        env = Environment(aaa = 'AAA')
+
+        x = env.get('aaa')
+        assert x == 'AAA', x
+        x = env.get('aaa', 'XXX')
+        assert x == 'AAA', x
+        x = env.get('bbb')
+        assert x is None, x
+        x = env.get('bbb', 'XXX')
+        assert x == 'XXX', x
 
     def test_arg2nodes(self):
         """Test the arg2nodes method
@@ -250,6 +260,54 @@ class EnvironmentTestCase(unittest.TestCase):
         assert not hasattr(nodes[1], 'bbbb'), nodes[0]
         assert nodes[1].c == 1, nodes[1]
 
+    def test_subst(self):
+       """Test substituting construction variables within strings
+       
+       Check various combinations, including recursive expansion
+       of variables into other variables.
+       """
+       env = Environment(AAA = 'a', BBB = 'b')
+       mystr = env.subst("$AAA ${AAA}A $BBBB $BBB")
+       assert mystr == "a aA b", str
+
+        # Changed the tests below to reflect a bug fix in
+        # subst()
+        env = Environment(AAA = '$BBB', BBB = 'b', BBBA = 'foo')
+       mystr = env.subst("$AAA ${AAA}A ${AAA}B $BBB")
+       assert mystr == "b bA bB b", str
+       env = Environment(AAA = '$BBB', BBB = '$CCC', CCC = 'c')
+       mystr = env.subst("$AAA ${AAA}A ${AAA}B $BBB")
+       assert mystr == "c cA cB c", str
+
+        env = Environment(AAA = '$BBB', BBB = '$CCC', CCC = [ 'a', 'b\nc' ])
+        lst = env.subst_list([ "$AAA", "B $CCC" ])
+        assert lst == [ [ "a", "b" ], [ "c", "B a", "b" ], [ "c" ] ], lst
+
+        class DummyNode:
+            def __init__(self, name):
+                self.name = name
+            def __str__(self):
+                return self.name
+            def rfile(self):
+                return self
+            def get_subst_proxy(self):
+                return self
+
+        # Test callables in the Environment
+        def foo(target, source, env, for_signature):
+            assert str(target) == 't', target
+            assert str(source) == 's', source
+            return env["FOO"]
+
+        env = Environment(BAR=foo, FOO='baz')
+
+        subst = env.subst('test $BAR', target=DummyNode('t'), source=DummyNode('s'))
+        assert subst == 'test baz', subst
+
+        lst = env.subst_list('test $BAR', target=DummyNode('t'), source=DummyNode('s'))
+        assert lst[0][0] == 'test', lst[0][0]
+        assert lst[0][1] == 'baz', lst[0][1]
+
     def test_Builder_calls(self):
         """Test Builder calls through different environments
         """
@@ -278,6 +336,8 @@ class EnvironmentTestCase(unittest.TestCase):
         assert not called_it.has_key('target')
         assert not called_it.has_key('source')
 
+
+
     def test_Builder_execs(self):
        """Test Builder execution through different environments
 
@@ -362,98 +422,6 @@ class EnvironmentTestCase(unittest.TestCase):
 #      s = env3.get_scanner(".cxx")
 #      assert s == None, s
 
-    def test_Copy(self):
-       """Test construction Environment copying
-
-       Update the copy independently afterwards and check that
-       the original remains intact (that is, no dangling
-       references point to objects in the copied environment).
-       Copy the original with some construction variable
-       updates and check that the original remains intact
-       and the copy has the updated values.
-       """
-       env1 = Environment(XXX = 'x', YYY = 'y')
-       env2 = env1.Copy()
-       env1copy = env1.Copy()
-       env2.Replace(YYY = 'yyy')
-       assert env1 != env2
-       assert env1 == env1copy
-
-       env3 = env1.Copy(XXX = 'x3', ZZZ = 'z3')
-       assert env3.Dictionary('XXX') == 'x3'
-       assert env3.Dictionary('YYY') == 'y'
-       assert env3.Dictionary('ZZZ') == 'z3'
-       assert env1 == env1copy
-
-        # Ensure that lists and dictionaries are
-        # deep copied, but not instances.
-        class TestA:
-            pass
-        env1 = Environment(XXX=TestA(), YYY = [ 1, 2, 3 ],
-                           ZZZ = { 1:2, 3:4 })
-        env2=env1.Copy()
-        env2.Dictionary('YYY').append(4)
-        env2.Dictionary('ZZZ')[5] = 6
-        assert env1.Dictionary('XXX') is env2.Dictionary('XXX')
-        assert 4 in env2.Dictionary('YYY')
-        assert not 4 in env1.Dictionary('YYY')
-        assert env2.Dictionary('ZZZ').has_key(5)
-        assert not env1.Dictionary('ZZZ').has_key(5)
-
-        #
-        env1 = Environment(BUILDERS = {'b1' : 1})
-        assert hasattr(env1, 'b1'), "env1.b1 was not set"
-        assert env1.b1.env == env1, "b1.env doesn't point to env1"
-        env2 = env1.Copy(BUILDERS = {'b2' : 2})
-        assert hasattr(env1, 'b1'), "b1 was mistakenly cleared from env1"
-        assert env1.b1.env == env1, "b1.env was changed"
-        assert not hasattr(env2, 'b1'), "b1 was not cleared from env2"
-        assert hasattr(env2, 'b2'), "env2.b2 was not set"
-        assert env2.b2.env == env2, "b2.env doesn't point to env2"
-
-        # Ensure that specifying new tools in a copied environment
-        # works.
-        def foo(env): env['FOO'] = 1
-        def bar(env): env['BAR'] = 2
-        def baz(env): env['BAZ'] = 3
-        env1 = Environment(tools=[foo])
-        env2 = env1.Copy()
-        env3 = env1.Copy(tools=[bar, baz])
-        
-        assert env1.get('FOO') is 1
-        assert env1.get('BAR') is None
-        assert env1.get('BAZ') is None
-        assert env2.get('FOO') is 1
-        assert env2.get('BAR') is None
-        assert env2.get('BAZ') is None
-        assert env3.get('FOO') is 1
-        assert env3.get('BAR') is 2
-        assert env3.get('BAZ') is 3
-
-    def test_Dictionary(self):
-       """Test retrieval of known construction variables
-
-       Fetch them from the Dictionary and check for well-known
-       defaults that get inserted.
-       """
-       env = Environment(XXX = 'x', YYY = 'y', ZZZ = 'z')
-       assert env.Dictionary('XXX') == 'x'
-       assert env.Dictionary('YYY') == 'y'
-       assert env.Dictionary('XXX', 'ZZZ') == ['x', 'z']
-       xxx, zzz = env.Dictionary('XXX', 'ZZZ')
-       assert xxx == 'x'
-       assert zzz == 'z'
-       assert env.Dictionary().has_key('BUILDERS')
-       assert env.Dictionary().has_key('CC')
-       assert env.Dictionary().has_key('CCFLAGS')
-       assert env.Dictionary().has_key('ENV')
-
-       assert env['XXX'] == 'x'
-       env['XXX'] = 'foo'
-       assert env.Dictionary('XXX') == 'foo'
-       del env['XXX']
-       assert not env.Dictionary().has_key('XXX')
-
     def test_ENV(self):
        """Test setting the external ENV in Environments
        """
@@ -463,78 +431,6 @@ class EnvironmentTestCase(unittest.TestCase):
        env = Environment(ENV = { 'PATH' : '/foo:/bar' })
        assert env.Dictionary('ENV')['PATH'] == '/foo:/bar'
 
-    def test_Environment(self):
-       """Test construction Environments creation
-       
-       Create two with identical arguments and check that
-       they compare the same.
-       """
-       env1 = Environment(XXX = 'x', YYY = 'y')
-       env2 = Environment(XXX = 'x', YYY = 'y')
-       assert env1 == env2, diff_env(env1, env2)
-
-    def test_Install(self):
-       """Test Install and InstallAs methods"""
-        env = Environment(FOO='iii', BAR='jjj')
-
-        tgt = env.Install('export', [ 'build/foo1', 'build/foo2' ])
-        paths = map(str, tgt)
-        paths.sort()
-        expect = map(os.path.normpath, [ 'export/foo1', 'export/foo2' ])
-        assert paths == expect, paths
-        for tnode in tgt:
-            assert tnode.builder == InstallBuilder
-
-        tgt = env.Install('$FOO', [ 'build/${BAR}1', 'build/${BAR}2' ])
-        paths = map(str, tgt)
-        paths.sort()
-        expect = map(os.path.normpath, [ 'iii/jjj1', 'iii/jjj2' ])
-        assert paths == expect, paths
-        for tnode in tgt:
-            assert tnode.builder == InstallBuilder
-
-        exc_caught = None
-        try:
-            tgt = env.Install('export', 'export')
-        except SCons.Errors.UserError, e:
-            exc_caught = 1
-        assert exc_caught, "UserError should be thrown when Install() target is not a file."
-        match = str(e) == "Source `export' of Install() is not a file.  Install() source must be one or more files."
-        assert match, e
-
-        exc_caught = None
-        try:
-            tgt = env.Install('export', ['export', 'build/foo1'])
-        except SCons.Errors.UserError, e:
-            exc_caught = 1
-        assert exc_caught, "UserError should be thrown when Install() target containins non-files."
-        match = str(e) == "Source `['export', 'build/foo1']' of Install() contains one or more non-files.  Install() source must be one or more files."
-        assert match, e
-
-        exc_caught = None
-        try:
-            tgt = env.Install('export/foo1', 'build/foo1')
-        except SCons.Errors.UserError, e:
-            exc_caught = 1
-        assert exc_caught, "UserError should be thrown reversing the order of Install() targets."
-        match = str(e) == "Target `export/foo1' of Install() is a file, but should be a directory.  Perhaps you have the Install() arguments backwards?"
-        assert match, e
-
-        tgt = env.InstallAs(target=string.split('foo1 foo2'),
-                            source=string.split('bar1 bar2'))
-        assert len(tgt) == 2, len(tgt)
-        paths = map(lambda x: str(x.sources[0]), tgt)
-        paths.sort()
-        expect = map(os.path.normpath, [ 'bar1', 'bar2' ])
-        assert paths == expect, paths
-        for tnode in tgt:
-            assert tnode.builder == InstallBuilder
-
-        tgt = env.InstallAs(target='${FOO}.t', source='${BAR}.s')
-        assert tgt.path == 'iii.t'
-        assert tgt.sources[0].path == 'jjj.s'
-        assert tgt.builder == InstallBuilder
-
     def test_ReservedVariables(self):
         """Test generation of warnings when reserved variable names
         are set in an environment."""
@@ -571,22 +467,222 @@ class EnvironmentTestCase(unittest.TestCase):
         test_it('foo.bar')
         test_it('foo-bar')
 
-    def test_Replace(self):
-        """Test replacing construction variables in an Environment
-
-        After creation of the Environment, of course.
-        """
-        env1 = Environment(AAA = 'a', BBB = 'b')
-        env1.Replace(BBB = 'bbb', CCC = 'ccc')
+    def test_autogenerate(dict):
+        """Test autogenerating variables in a dictionary."""
+
+        def RDirs(pathlist):
+            return SCons.Node.FS.default_fs.Rsearchall(pathlist,
+                                                       clazz=SCons.Node.FS.Dir,
+                                                       must_exist=0,
+                                                       cwd=SCons.Node.FS.default_fs.Dir('xx'))
+        
+        env = Environment(LIBS = [ 'foo', 'bar', 'baz' ],
+                          LIBLINKPREFIX = 'foo',
+                          LIBLINKSUFFIX = 'bar',
+                          RDirs=RDirs)
+        flags = env.subst_list('$_LIBFLAGS', 1)[0]
+        assert len(flags) == 3, flags
+        assert flags[0] == 'foofoobar', \
+               flags[0]
+        assert flags[1] == 'foobarbar', \
+               flags[1]
+        assert flags[2] == 'foobazbar', \
+               flags[2]
+
+        blat = SCons.Node.FS.default_fs.Dir('blat')
+
+        env = Environment(CPPPATH = [ 'foo', '$FOO/bar', blat ],
+                          INCPREFIX = 'foo ',
+                          INCSUFFIX = 'bar',
+                          FOO = 'baz',
+                          RDirs=RDirs)
+        flags = env.subst_list('$_CPPINCFLAGS', 1)[0]
+        assert len(flags) == 8, flags
+        assert flags[0] == '$(', \
+               flags[0]
+        assert flags[1] == os.path.normpath('foo'), \
+               flags[1]
+        assert flags[2] == os.path.normpath('xx/foobar'), \
+               flags[2]
+        assert flags[3] == os.path.normpath('foo'), \
+               flags[3]
+        assert flags[4] == os.path.normpath('xx/baz/barbar'), \
+               flags[4]
+        assert flags[5] == os.path.normpath('foo'), \
+               flags[5]
+        assert flags[6] == os.path.normpath('blatbar'), \
+               flags[6]
+        assert flags[7] == '$)', \
+               flags[7]
+
+        env = Environment(F77PATH = [ 'foo', '$FOO/bar', blat ],
+                          INCPREFIX = 'foo ',
+                          INCSUFFIX = 'bar',
+                          FOO = 'baz',
+                          RDirs=RDirs)
+        flags = env.subst_list('$_F77INCFLAGS', 1)[0]
+        assert len(flags) == 8, flags
+        assert flags[0] == '$(', \
+               flags[0]
+        assert flags[1] == os.path.normpath('foo'), \
+               flags[1]
+        assert flags[2] == os.path.normpath('xx/foobar'), \
+               flags[2]
+        assert flags[3] == os.path.normpath('foo'), \
+               flags[3]
+        assert flags[4] == os.path.normpath('xx/baz/barbar'), \
+               flags[4]
+        assert flags[5] == os.path.normpath('foo'), \
+               flags[5]
+        assert flags[6] == os.path.normpath('blatbar'), \
+               flags[6]
+        assert flags[7] == '$)', \
+               flags[7]
+
+        env = Environment(CPPPATH = '', F77PATH = '', LIBPATH = '',
+                          RDirs=RDirs)
+        assert len(env.subst_list('$_CPPINCFLAGS')[0]) == 0
+        assert len(env.subst_list('$_F77INCFLAGS')[0]) == 0
+        assert len(env.subst_list('$_LIBDIRFLAGS')[0]) == 0
+
+        SCons.Node.FS.default_fs.Repository('/rep1')
+        SCons.Node.FS.default_fs.Repository('/rep2')
+        env = Environment(CPPPATH = [ 'foo', '/a/b', '$FOO/bar', blat],
+                          INCPREFIX = '-I ',
+                          INCSUFFIX = 'XXX',
+                          FOO = 'baz',
+                          RDirs=RDirs)
+        flags = env.subst_list('$_CPPINCFLAGS', 1)[0]
+        assert flags[0] == '$(', \
+               flags[0]
+        assert flags[1] == '-I', \
+               flags[1]
+        assert flags[2] == os.path.normpath('xx/fooXXX'), \
+               flags[2]
+        assert flags[3] == '-I', \
+               flags[3]
+        assert flags[4] == os.path.normpath('/rep1/xx/fooXXX'), \
+               flags[4]
+        assert flags[5] == '-I', \
+               flags[5]
+        assert flags[6] == os.path.normpath('/rep2/xx/fooXXX'), \
+               flags[6]
+        assert flags[7] == '-I', \
+               flags[7]
+        assert flags[8] == os.path.normpath('/a/bXXX'), \
+               flags[8]
+        assert flags[9] == '-I', \
+               flags[9]
+        assert flags[10] == os.path.normpath('xx/baz/barXXX'), \
+               flags[10]
+        assert flags[11] == '-I', \
+               flags[11]
+        assert flags[12] == os.path.normpath('/rep1/xx/baz/barXXX'), \
+               flags[12]
+        assert flags[13] == '-I', \
+               flags[13]
+        assert flags[14] == os.path.normpath('/rep2/xx/baz/barXXX'), \
+               flags[14]
+        assert flags[15] == '-I', \
+               flags[15]
+        assert flags[16] == os.path.normpath('blatXXX'), \
+               flags[16]
+        assert flags[17] == '$)', \
+               flags[17]
+
+    def test_platform(self):
+        """Test specifying a platform callable when instantiating."""
+        class platform:
+            def __str__(self):        return "TestPlatform"
+            def __call__(self, env):  env['XYZZY'] = 777
+
+        def tool(env):
+            env['SET_TOOL'] = 'initialized'
+            assert env['PLATFORM'] == "TestPlatform"
+
+        env = Environment(platform = platform(), tools = [tool])
+        assert env['XYZZY'] == 777, env
+        assert env['PLATFORM'] == "TestPlatform"
+        assert env['SET_TOOL'] == "initialized"
+
+    def test_Default_PLATFORM(self):
+        """Test overriding the default PLATFORM variable"""
+        class platform:
+            def __str__(self):        return "DefaultTestPlatform"
+            def __call__(self, env):  env['XYZZY'] = 888
+
+        def tool(env):
+            env['SET_TOOL'] = 'abcde'
+            assert env['PLATFORM'] == "DefaultTestPlatform"
+
+        import SCons.Defaults
+        save = SCons.Defaults.ConstructionEnvironment.copy()
+        try:
+            import SCons.Defaults
+            SCons.Defaults.ConstructionEnvironment.update({
+                'PLATFORM' : platform(),
+            })
+            env = Environment(tools = [tool])
+            assert env['XYZZY'] == 888, env
+            assert env['PLATFORM'] == "DefaultTestPlatform"
+            assert env['SET_TOOL'] == "abcde"
+        finally:
+            SCons.Defaults.ConstructionEnvironment = save
+
+    def test_tools(self):
+        """Test specifying a tool callable when instantiating."""
+        def t1(env):
+            env['TOOL1'] = 111
+        def t2(env):
+            env['TOOL2'] = 222
+        def t3(env):
+            env['AAA'] = env['XYZ']
+        def t4(env):
+            env['TOOL4'] = 444
+        env = Environment(tools = [t1, t2, t3], XYZ = 'aaa')
+        assert env['TOOL1'] == 111, env['TOOL1']
+        assert env['TOOL2'] == 222, env
+        assert env['AAA'] == 'aaa', env        
+        t4(env)
+        assert env['TOOL4'] == 444, env
+        
+    def test_Default_TOOLS(self):
+        """Test overriding the default TOOLS variable"""
+        def t5(env):
+            env['TOOL5'] = 555
+        def t6(env):
+            env['TOOL6'] = 666
+        def t7(env):
+            env['BBB'] = env['XYZ']
+        def t8(env):
+            env['TOOL8'] = 888
+
+        import SCons.Defaults
+        save = SCons.Defaults.ConstructionEnvironment.copy()
+        try:
+            SCons.Defaults.ConstructionEnvironment.update({
+                'TOOLS' : [t5, t6, t7],
+            })
+            env = Environment(XYZ = 'bbb')
+            assert env['TOOL5'] == 555, env['TOOL5']
+            assert env['TOOL6'] == 666, env
+            assert env['BBB'] == 'bbb', env        
+            t8(env)
+            assert env['TOOL8'] == 888, env
+        finally:
+            SCons.Defaults.ConstructionEnvironment = save
+
+    def test_concat(self):
+        "Test _concat()"
+        e1 = Environment(PRE='pre', SUF='suf', STR='a b', LIST=['a', 'b'])
+        s = e1.subst
+        assert s("${_concat('', '', '', __env__)}") == ''
+        assert s("${_concat('', [], '', __env__)}") == ''
+        assert s("${_concat(PRE, '', SUF, __env__)}") == ''
+        assert s("${_concat(PRE, STR, SUF, __env__)}") == 'prea bsuf'
+        assert s("${_concat(PRE, LIST, SUF, __env__)}") == 'preasuf prebsuf'
 
-        env2 = Environment(AAA = 'a', BBB = 'bbb', CCC = 'ccc')
-        assert env1 == env2, diff_env(env1, env2)
 
-        env3 = Environment(BUILDERS = {'b1' : 1})
-        assert hasattr(env3, 'b1'), "b1 was not set"
-        env3.Replace(BUILDERS = {'b2' : 2})
-        assert not hasattr(env3, 'b1'), "b1 was not cleared"
-        assert hasattr(env3, 'b2'), "b2 was not set"
 
     def test_Append(self):
         """Test appending to construction variables in an Environment
@@ -624,75 +720,8 @@ class EnvironmentTestCase(unittest.TestCase):
         assert hasattr(env4, 'z1')
         assert hasattr(env4, 'z2')
 
-    def test_Prepend(self):
-        """Test prepending to construction variables in an Environment
-        """
-        import UserList
-        UL = UserList.UserList
-        env1 = Environment(AAA = 'a', BBB = 'b', CCC = 'c', DDD = 'd',
-                           EEE = ['e'], FFF = ['f'], GGG = ['g'], HHH = ['h'],
-                           III = UL(['i']), JJJ = UL(['j']),
-                           KKK = UL(['k']), LLL = UL(['l']))
-        env1.Prepend(BBB = 'B', CCC = ['C'], DDD = UL(['D']),
-                    FFF = 'F', GGG = ['G'], HHH = UL(['H']),
-                    JJJ = 'J', KKK = ['K'], LLL = UL(['L']))
-        env2 = Environment(AAA = 'a', BBB = 'Bb',
-                           CCC = ['C', 'c'], DDD = UL(['D', 'd']),
-                           EEE = ['e'], FFF = ['F', 'f'],
-                           GGG = ['G', 'g'], HHH = UL(['H', 'h']),
-                           III = UL(['i']), JJJ = UL(['J', 'j']),
-                           KKK = UL(['K', 'k']), LLL = UL(['L', 'l']))
-        assert env1 == env2, diff_env(env1, env2)
-
-        env3 = Environment(X = {'x1' : 7})
-        env3.Prepend(X = {'x1' : 8, 'x2' : 9}, Y = {'y1' : 10})
-        assert env3['X'] == {'x1': 8, 'x2' : 9}, env3['X']
-        assert env3['Y'] == {'y1': 10}, env3['Y']
-
-        env4 = Environment(BUILDERS = {'z1' : 11})
-        env4.Prepend(BUILDERS = {'z2' : 12})
-        assert env4['BUILDERS'] == {'z1' : 11, 'z2' : 12}, env4['BUILDERS']
-        assert hasattr(env4, 'z1')
-        assert hasattr(env4, 'z2')
-
-    def test_PrependENVPath(self):
-        """Test prepending to an ENV path."""
-        env1 = Environment(ENV = {'PATH': r'C:\dir\num\one;C:\dir\num\two'},
-                           MYENV = {'MYPATH': r'C:\mydir\num\one;C:\mydir\num\two'})
-        # have to include the pathsep here so that the test will work on UNIX too.
-        env1.PrependENVPath('PATH',r'C:\dir\num\two',sep = ';') 
-        env1.PrependENVPath('PATH',r'C:\dir\num\three',sep = ';')
-        env1.PrependENVPath('MYPATH',r'C:\mydir\num\three','MYENV',sep = ';')
-        env1.PrependENVPath('MYPATH',r'C:\mydir\num\one','MYENV',sep = ';')
-        assert(env1['ENV']['PATH'] == r'C:\dir\num\three;C:\dir\num\two;C:\dir\num\one')
-        assert(env1['MYENV']['MYPATH'] == r'C:\mydir\num\one;C:\mydir\num\three;C:\mydir\num\two')
-
-    def test_AppendENVPath(self):
-        """Test appending to an ENV path."""
-        env1 = Environment(ENV = {'PATH': r'C:\dir\num\one;C:\dir\num\two'},
-                           MYENV = {'MYPATH': r'C:\mydir\num\one;C:\mydir\num\two'})
-        # have to include the pathsep here so that the test will work on UNIX too.
-        env1.AppendENVPath('PATH',r'C:\dir\num\two', sep = ';')
-        env1.AppendENVPath('PATH',r'C:\dir\num\three', sep = ';')
-        env1.AppendENVPath('MYPATH',r'C:\mydir\num\three','MYENV', sep = ';')
-        env1.AppendENVPath('MYPATH',r'C:\mydir\num\one','MYENV', sep = ';')
-        assert(env1['ENV']['PATH'] == r'C:\dir\num\one;C:\dir\num\two;C:\dir\num\three')
-        assert(env1['MYENV']['MYPATH'] == r'C:\mydir\num\two;C:\mydir\num\three;C:\mydir\num\one')
-
-    def test_AppendENVPath(self):
-        """Test prepending to an ENV path."""
-        env1 = Environment(ENV = {'PATH': r'C:\dir\num\one;C:\dir\num\two'},
-                           MYENV = {'MYPATH': r'C:\mydir\num\one;C:\mydir\num\two'})
-        # have to include the pathsep here so that the test will work on UNIX too.
-        env1.PrependENVPath('PATH',r'C:\dir\num\two',sep = ';') 
-        env1.PrependENVPath('PATH',r'C:\dir\num\three',sep = ';')
-        env1.PrependENVPath('MYPATH',r'C:\mydir\num\three','MYENV',sep = ';')
-        env1.PrependENVPath('MYPATH',r'C:\mydir\num\one','MYENV',sep = ';')
-        assert(env1['ENV']['PATH'] == r'C:\dir\num\three;C:\dir\num\two;C:\dir\num\one')
-        assert(env1['MYENV']['MYPATH'] == r'C:\mydir\num\one;C:\mydir\num\three;C:\mydir\num\two')
-
-    def test_AppendENVPath(self):
-        """Test appending to an ENV path."""
+    def test_AppendENVPath(self):
+        """Test appending to an ENV path."""
         env1 = Environment(ENV = {'PATH': r'C:\dir\num\one;C:\dir\num\two'},
                            MYENV = {'MYPATH': r'C:\mydir\num\one;C:\mydir\num\two'})
         # have to include the pathsep here so that the test will work on UNIX too.
@@ -703,324 +732,73 @@ class EnvironmentTestCase(unittest.TestCase):
         assert(env1['ENV']['PATH'] == r'C:\dir\num\one;C:\dir\num\two;C:\dir\num\three')
         assert(env1['MYENV']['MYPATH'] == r'C:\mydir\num\two;C:\mydir\num\three;C:\mydir\num\one')
 
-    def test_Depends(self):
-       """Test the explicit Depends method."""
-       env = Environment(FOO = 'xxx', BAR='yyy')
-       t = env.Depends(target='EnvironmentTest.py', dependency='Environment.py')
-       assert t.__class__.__name__ == 'File'
-       assert t.path == 'EnvironmentTest.py'
-       assert len(t.depends) == 1
-       d = t.depends[0]
-       assert d.__class__.__name__ == 'File'
-       assert d.path == 'Environment.py'
-
-       t = env.Depends(target='${FOO}.py', dependency='${BAR}.py')
-       assert t.__class__.__name__ == 'File'
-       assert t.path == 'xxx.py'
-       assert len(t.depends) == 1
-       d = t.depends[0]
-       assert d.__class__.__name__ == 'File'
-       assert d.path == 'yyy.py'
-
-    def test_Ignore(self):
-        """Test the explicit Ignore method."""
-        env = Environment(FOO='yyy', BAR='zzz')
-        t = env.Ignore(target='targ.py', dependency='dep.py')
-        assert t.__class__.__name__ == 'File'
-        assert t.path == 'targ.py'
-        assert len(t.ignore) == 1
-        i = t.ignore[0]
-        assert i.__class__.__name__ == 'File'
-        assert i.path == 'dep.py'
-        t = env.Ignore(target='$FOO$BAR', dependency='$BAR$FOO')
-        assert t.__class__.__name__ == 'File'
-        assert t.path == 'yyyzzz'
-        assert len(t.ignore) == 1
-        i = t.ignore[0]
-        assert i.__class__.__name__ == 'File'
-        assert i.path == 'zzzyyy'
-
-    def test_AlwaysBuild(self):
-        """Test the AlwaysBuild() method"""
-        env = Environment(FOO='fff', BAR='bbb')
-        t = env.AlwaysBuild('a', 'b$FOO', ['c', 'd'], '$BAR')
-        assert t[0].__class__.__name__ == 'File'
-        assert t[0].path == 'a'
-        assert t[0].always_build
-        assert t[1].__class__.__name__ == 'File'
-        assert t[1].path == 'bfff'
-        assert t[1].always_build
-        assert t[2].__class__.__name__ == 'File'
-        assert t[2].path == 'c'
-        assert t[2].always_build
-        assert t[3].__class__.__name__ == 'File'
-        assert t[3].path == 'd'
-        assert t[3].always_build
-        assert t[4].__class__.__name__ == 'File'
-        assert t[4].path == 'bbb'
-        assert t[4].always_build
-
-    def test_Precious(self):
-        """Test the Precious() method."""
-        env = Environment(FOO='ggg', BAR='hhh')
-        t = env.Precious('a', '${BAR}b', ['c', 'd'], '$FOO')
-        assert t[0].__class__.__name__ == 'File'
-        assert t[0].path == 'a'
-        assert t[0].precious
-        assert t[1].__class__.__name__ == 'File'
-        assert t[1].path == 'hhhb'
-        assert t[1].precious
-        assert t[2].__class__.__name__ == 'File'
-        assert t[2].path == 'c'
-        assert t[2].precious
-        assert t[3].__class__.__name__ == 'File'
-        assert t[3].path == 'd'
-        assert t[3].precious
-        assert t[4].__class__.__name__ == 'File'
-        assert t[4].path == 'ggg'
-        assert t[4].precious
-
-    def test_Command(self):
-        """Test the Command() method."""
-        env = Environment()
-        t = env.Command(target='foo.out', source=['foo1.in', 'foo2.in'],
-                        action='buildfoo $target $source')
-        assert not t.builder is None
-        assert t.builder.action.__class__.__name__ == 'CommandAction'
-        assert t.builder.action.cmd_list == 'buildfoo $target $source'
-        assert 'foo1.in' in map(lambda x: x.path, t.sources)
-        assert 'foo2.in' in map(lambda x: x.path, t.sources)
-
-        sub = SCons.Node.FS.default_fs.Dir('sub')
-        t = env.Command(target='bar.out', source='sub',
-                        action='buildbar $target $source')
-        assert 'sub' in map(lambda x: x.path, t.sources)
-
-        def testFunc(env, target, source):
-            assert str(target[0]) == 'foo.out'
-            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)
-        assert not t.builder is None
-        assert t.builder.action.__class__.__name__ == 'FunctionAction'
-        t.build()
-        assert 'foo1.in' in map(lambda x: x.path, t.sources)
-        assert 'foo2.in' in map(lambda x: x.path, t.sources)
-
-    def test_SourceCode(self):
-        """Test the SourceCode() method."""
-        env = Environment(FOO='mmm', BAR='nnn')
-        e = env.SourceCode('foo', None)
-        assert e.path == 'foo'
-        s = e.src_builder()
-        assert s is None, s
-
-        b = Builder()
-        e = env.SourceCode(e, b)
-        assert e.path == 'foo'
-        s = e.src_builder()
-        assert s is b, s
-
-        e = env.SourceCode('$BAR$FOO', None)
-        assert e.path == 'nnnmmm'
-        s = e.src_builder()
-        assert s is None, s
-
-    def test_SideEffect(self):
-        """Test the SideEffect() method"""
-        env = Environment(LIB='lll', FOO='fff', BAR='bbb')
-
-        foo = env.Object('foo.obj', 'foo.cpp')
-        bar = env.Object('bar.obj', 'bar.cpp')
-        s = env.SideEffect('mylib.pdb', ['foo.obj', 'bar.obj'])
-        assert s.path == 'mylib.pdb'
-        assert s.side_effect
-        assert foo.side_effects == [s]
-        assert bar.side_effects == [s]
-        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'])
-        assert s.path == 'mylll.pdb'
-        assert s.side_effect
-        assert fff.side_effects == [s], fff.side_effects
-        assert bbb.side_effects == [s], bbb.side_effects
-        assert s.depends_on([bbb])
-        assert s.depends_on([fff])
+    def test_Copy(self):
+       """Test construction Environment copying
 
-    def test_subst(self):
-       """Test substituting construction variables within strings
-       
-       Check various combinations, including recursive expansion
-       of variables into other variables.
+       Update the copy independently afterwards and check that
+       the original remains intact (that is, no dangling
+       references point to objects in the copied environment).
+       Copy the original with some construction variable
+       updates and check that the original remains intact
+       and the copy has the updated values.
        """
-       env = Environment(AAA = 'a', BBB = 'b')
-       mystr = env.subst("$AAA ${AAA}A $BBBB $BBB")
-       assert mystr == "a aA b", str
-
-        # Changed the tests below to reflect a bug fix in
-        # subst()
-        env = Environment(AAA = '$BBB', BBB = 'b', BBBA = 'foo')
-       mystr = env.subst("$AAA ${AAA}A ${AAA}B $BBB")
-       assert mystr == "b bA bB b", str
-       env = Environment(AAA = '$BBB', BBB = '$CCC', CCC = 'c')
-       mystr = env.subst("$AAA ${AAA}A ${AAA}B $BBB")
-       assert mystr == "c cA cB c", str
-
-        env = Environment(AAA = '$BBB', BBB = '$CCC', CCC = [ 'a', 'b\nc' ])
-        lst = env.subst_list([ "$AAA", "B $CCC" ])
-        assert lst == [ [ "a", "b" ], [ "c", "B a", "b" ], [ "c" ] ], lst
-
-        class DummyNode:
-            def __init__(self, name):
-                self.name = name
-            def __str__(self):
-                return self.name
-            def rfile(self):
-                return self
-            def get_subst_proxy(self):
-                return self
-
-        # Test callables in the Environment
-        def foo(target, source, env, for_signature):
-            assert str(target) == 't', target
-            assert str(source) == 's', source
-            return env["FOO"]
-
-        env = Environment(BAR=foo, FOO='baz')
-
-        subst = env.subst('test $BAR', target=DummyNode('t'), source=DummyNode('s'))
-        assert subst == 'test baz', subst
-
-        lst = env.subst_list('test $BAR', target=DummyNode('t'), source=DummyNode('s'))
-        assert lst[0][0] == 'test', lst[0][0]
-        assert lst[0][1] == 'baz', lst[0][1]
-
-    def test_autogenerate(dict):
-        """Test autogenerating variables in a dictionary."""
-
-        def RDirs(pathlist):
-            return SCons.Node.FS.default_fs.Rsearchall(pathlist,
-                                                       clazz=SCons.Node.FS.Dir,
-                                                       must_exist=0,
-                                                       cwd=SCons.Node.FS.default_fs.Dir('xx'))
-        
-        env = Environment(LIBS = [ 'foo', 'bar', 'baz' ],
-                          LIBLINKPREFIX = 'foo',
-                          LIBLINKSUFFIX = 'bar',
-                          RDirs=RDirs)
-        flags = env.subst_list('$_LIBFLAGS', 1)[0]
-        assert len(flags) == 3, flags
-        assert flags[0] == 'foofoobar', \
-               flags[0]
-        assert flags[1] == 'foobarbar', \
-               flags[1]
-        assert flags[2] == 'foobazbar', \
-               flags[2]
-
-        blat = SCons.Node.FS.default_fs.Dir('blat')
+       env1 = Environment(XXX = 'x', YYY = 'y')
+       env2 = env1.Copy()
+       env1copy = env1.Copy()
+       env2.Replace(YYY = 'yyy')
+       assert env1 != env2
+       assert env1 == env1copy
 
-        env = Environment(CPPPATH = [ 'foo', '$FOO/bar', blat ],
-                          INCPREFIX = 'foo ',
-                          INCSUFFIX = 'bar',
-                          FOO = 'baz',
-                          RDirs=RDirs)
-        flags = env.subst_list('$_CPPINCFLAGS', 1)[0]
-        assert len(flags) == 8, flags
-        assert flags[0] == '$(', \
-               flags[0]
-        assert flags[1] == os.path.normpath('foo'), \
-               flags[1]
-        assert flags[2] == os.path.normpath('xx/foobar'), \
-               flags[2]
-        assert flags[3] == os.path.normpath('foo'), \
-               flags[3]
-        assert flags[4] == os.path.normpath('xx/baz/barbar'), \
-               flags[4]
-        assert flags[5] == os.path.normpath('foo'), \
-               flags[5]
-        assert flags[6] == os.path.normpath('blatbar'), \
-               flags[6]
-        assert flags[7] == '$)', \
-               flags[7]
+       env3 = env1.Copy(XXX = 'x3', ZZZ = 'z3')
+       assert env3.Dictionary('XXX') == 'x3'
+       assert env3.Dictionary('YYY') == 'y'
+       assert env3.Dictionary('ZZZ') == 'z3'
+       assert env1 == env1copy
 
-        env = Environment(F77PATH = [ 'foo', '$FOO/bar', blat ],
-                          INCPREFIX = 'foo ',
-                          INCSUFFIX = 'bar',
-                          FOO = 'baz',
-                          RDirs=RDirs)
-        flags = env.subst_list('$_F77INCFLAGS', 1)[0]
-        assert len(flags) == 8, flags
-        assert flags[0] == '$(', \
-               flags[0]
-        assert flags[1] == os.path.normpath('foo'), \
-               flags[1]
-        assert flags[2] == os.path.normpath('xx/foobar'), \
-               flags[2]
-        assert flags[3] == os.path.normpath('foo'), \
-               flags[3]
-        assert flags[4] == os.path.normpath('xx/baz/barbar'), \
-               flags[4]
-        assert flags[5] == os.path.normpath('foo'), \
-               flags[5]
-        assert flags[6] == os.path.normpath('blatbar'), \
-               flags[6]
-        assert flags[7] == '$)', \
-               flags[7]
+        # Ensure that lists and dictionaries are
+        # deep copied, but not instances.
+        class TestA:
+            pass
+        env1 = Environment(XXX=TestA(), YYY = [ 1, 2, 3 ],
+                           ZZZ = { 1:2, 3:4 })
+        env2=env1.Copy()
+        env2.Dictionary('YYY').append(4)
+        env2.Dictionary('ZZZ')[5] = 6
+        assert env1.Dictionary('XXX') is env2.Dictionary('XXX')
+        assert 4 in env2.Dictionary('YYY')
+        assert not 4 in env1.Dictionary('YYY')
+        assert env2.Dictionary('ZZZ').has_key(5)
+        assert not env1.Dictionary('ZZZ').has_key(5)
 
-        env = Environment(CPPPATH = '', F77PATH = '', LIBPATH = '',
-                          RDirs=RDirs)
-        assert len(env.subst_list('$_CPPINCFLAGS')[0]) == 0
-        assert len(env.subst_list('$_F77INCFLAGS')[0]) == 0
-        assert len(env.subst_list('$_LIBDIRFLAGS')[0]) == 0
+        #
+        env1 = Environment(BUILDERS = {'b1' : 1})
+        assert hasattr(env1, 'b1'), "env1.b1 was not set"
+        assert env1.b1.env == env1, "b1.env doesn't point to env1"
+        env2 = env1.Copy(BUILDERS = {'b2' : 2})
+        assert hasattr(env1, 'b1'), "b1 was mistakenly cleared from env1"
+        assert env1.b1.env == env1, "b1.env was changed"
+        assert not hasattr(env2, 'b1'), "b1 was not cleared from env2"
+        assert hasattr(env2, 'b2'), "env2.b2 was not set"
+        assert env2.b2.env == env2, "b2.env doesn't point to env2"
 
-        SCons.Node.FS.default_fs.Repository('/rep1')
-        SCons.Node.FS.default_fs.Repository('/rep2')
-        env = Environment(CPPPATH = [ 'foo', '/a/b', '$FOO/bar', blat],
-                          INCPREFIX = '-I ',
-                          INCSUFFIX = 'XXX',
-                          FOO = 'baz',
-                          RDirs=RDirs)
-        flags = env.subst_list('$_CPPINCFLAGS', 1)[0]
-        assert flags[0] == '$(', \
-               flags[0]
-        assert flags[1] == '-I', \
-               flags[1]
-        assert flags[2] == os.path.normpath('xx/fooXXX'), \
-               flags[2]
-        assert flags[3] == '-I', \
-               flags[3]
-        assert flags[4] == os.path.normpath('/rep1/xx/fooXXX'), \
-               flags[4]
-        assert flags[5] == '-I', \
-               flags[5]
-        assert flags[6] == os.path.normpath('/rep2/xx/fooXXX'), \
-               flags[6]
-        assert flags[7] == '-I', \
-               flags[7]
-        assert flags[8] == os.path.normpath('/a/bXXX'), \
-               flags[8]
-        assert flags[9] == '-I', \
-               flags[9]
-        assert flags[10] == os.path.normpath('xx/baz/barXXX'), \
-               flags[10]
-        assert flags[11] == '-I', \
-               flags[11]
-        assert flags[12] == os.path.normpath('/rep1/xx/baz/barXXX'), \
-               flags[12]
-        assert flags[13] == '-I', \
-               flags[13]
-        assert flags[14] == os.path.normpath('/rep2/xx/baz/barXXX'), \
-               flags[14]
-        assert flags[15] == '-I', \
-               flags[15]
-        assert flags[16] == os.path.normpath('blatXXX'), \
-               flags[16]
-        assert flags[17] == '$)', \
-               flags[17]
+        # Ensure that specifying new tools in a copied environment
+        # works.
+        def foo(env): env['FOO'] = 1
+        def bar(env): env['BAR'] = 2
+        def baz(env): env['BAZ'] = 3
+        env1 = Environment(tools=[foo])
+        env2 = env1.Copy()
+        env3 = env1.Copy(tools=[bar, baz])
+        
+        assert env1.get('FOO') is 1
+        assert env1.get('BAR') is None
+        assert env1.get('BAZ') is None
+        assert env2.get('FOO') is 1
+        assert env2.get('BAR') is None
+        assert env2.get('BAZ') is None
+        assert env3.get('FOO') is 1
+        assert env3.get('BAR') is 2
+        assert env3.get('BAZ') is 3
 
     def test_Detect(self):
         """Test Detect()ing tools"""
@@ -1077,132 +855,124 @@ class EnvironmentTestCase(unittest.TestCase):
         x = env.Detect('xxx.exe')
         assert x is None, x
 
-    def test_platform(self):
-        """Test specifying a platform callable when instantiating."""
-        class platform:
-            def __str__(self):        return "TestPlatform"
-            def __call__(self, env):  env['XYZZY'] = 777
+    def test_Dictionary(self):
+       """Test retrieval of known construction variables
 
-        def tool(env):
-            env['SET_TOOL'] = 'initialized'
-            assert env['PLATFORM'] == "TestPlatform"
+       Fetch them from the Dictionary and check for well-known
+       defaults that get inserted.
+       """
+       env = Environment(XXX = 'x', YYY = 'y', ZZZ = 'z')
+       assert env.Dictionary('XXX') == 'x'
+       assert env.Dictionary('YYY') == 'y'
+       assert env.Dictionary('XXX', 'ZZZ') == ['x', 'z']
+       xxx, zzz = env.Dictionary('XXX', 'ZZZ')
+       assert xxx == 'x'
+       assert zzz == 'z'
+       assert env.Dictionary().has_key('BUILDERS')
+       assert env.Dictionary().has_key('CC')
+       assert env.Dictionary().has_key('CCFLAGS')
+       assert env.Dictionary().has_key('ENV')
 
-        env = Environment(platform = platform(), tools = [tool])
-        assert env['XYZZY'] == 777, env
-        assert env['PLATFORM'] == "TestPlatform"
-        assert env['SET_TOOL'] == "initialized"
+       assert env['XXX'] == 'x'
+       env['XXX'] = 'foo'
+       assert env.Dictionary('XXX') == 'foo'
+       del env['XXX']
+       assert not env.Dictionary().has_key('XXX')
 
-    def test_Default_PLATFORM(self):
-        """Test overriding the default PLATFORM variable"""
-        class platform:
-            def __str__(self):        return "DefaultTestPlatform"
-            def __call__(self, env):  env['XYZZY'] = 888
+    def test_FindIxes(self):
+        "Test FindIxes()"
+        env = Environment(LIBPREFIX='lib', 
+                          LIBSUFFIX='.a',
+                          SHLIBPREFIX='lib', 
+                          SHLIBSUFFIX='.so',
+                          PREFIX='pre',
+                          SUFFIX='post')
 
-        def tool(env):
-            env['SET_TOOL'] = 'abcde'
-            assert env['PLATFORM'] == "DefaultTestPlatform"
+        paths = [os.path.join('dir', 'libfoo.a'),
+                 os.path.join('dir', 'libfoo.so')]
 
-        import SCons.Defaults
-        save = SCons.Defaults.ConstructionEnvironment.copy()
-        try:
-            import SCons.Defaults
-            SCons.Defaults.ConstructionEnvironment.update({
-                'PLATFORM' : platform(),
-            })
-            env = Environment(tools = [tool])
-            assert env['XYZZY'] == 888, env
-            assert env['PLATFORM'] == "DefaultTestPlatform"
-            assert env['SET_TOOL'] == "abcde"
-        finally:
-            SCons.Defaults.ConstructionEnvironment = save
+        assert paths[0] == env.FindIxes(paths, 'LIBPREFIX', 'LIBSUFFIX')
+        assert paths[1] == env.FindIxes(paths, 'SHLIBPREFIX', 'SHLIBSUFFIX')
+        assert None == env.FindIxes(paths, 'PREFIX', 'POST')
 
-    def test_tools(self):
-        """Test specifying a tool callable when instantiating."""
-        def t1(env):
-            env['TOOL1'] = 111
-        def t2(env):
-            env['TOOL2'] = 222
-        def t3(env):
-            env['AAA'] = env['XYZ']
-        def t4(env):
-            env['TOOL4'] = 444
-        env = Environment(tools = [t1, t2, t3], XYZ = 'aaa')
-        assert env['TOOL1'] == 111, env['TOOL1']
-        assert env['TOOL2'] == 222, env
-        assert env['AAA'] == 'aaa', env        
-        t4(env)
-        assert env['TOOL4'] == 444, env
-        
-    def test_Default_TOOLS(self):
-        """Test overriding the default TOOLS variable"""
-        def t5(env):
-            env['TOOL5'] = 555
-        def t6(env):
-            env['TOOL6'] = 666
-        def t7(env):
-            env['BBB'] = env['XYZ']
-        def t8(env):
-            env['TOOL8'] = 888
+        paths = ['libfoo.a', 'prefoopost']
 
-        import SCons.Defaults
-        save = SCons.Defaults.ConstructionEnvironment.copy()
-        try:
-            SCons.Defaults.ConstructionEnvironment.update({
-                'TOOLS' : [t5, t6, t7],
-            })
-            env = Environment(XYZ = 'bbb')
-            assert env['TOOL5'] == 555, env['TOOL5']
-            assert env['TOOL6'] == 666, env
-            assert env['BBB'] == 'bbb', env        
-            t8(env)
-            assert env['TOOL8'] == 888, env
-        finally:
-            SCons.Defaults.ConstructionEnvironment = save
+        assert paths[0] == env.FindIxes(paths, 'LIBPREFIX', 'LIBSUFFIX')
+        assert None == env.FindIxes(paths, 'SHLIBPREFIX', 'SHLIBSUFFIX')
+        assert paths[1] == env.FindIxes(paths, 'PREFIX', 'SUFFIX')
 
-    def test_get(self):
-        """Test the get() method."""
-        env = Environment(aaa = 'AAA')
+    def test_Override(self):
+        "Test overriding construction variables"
+        env = Environment(ONE=1, TWO=2)
+        assert env['ONE'] == 1
+        assert env['TWO'] == 2
+        env2 = env.Override({'TWO':'10'})
+        assert env2['ONE'] == 1
+        assert env2['TWO'] == '10'
+        assert env['TWO'] == 2
+        env2.Replace(ONE = "won")
+        assert env2['ONE'] == "won"
+        assert env['ONE'] == 1
 
-        x = env.get('aaa')
-        assert x == 'AAA', x
-        x = env.get('aaa', 'XXX')
-        assert x == 'AAA', x
-        x = env.get('bbb')
-        assert x is None, x
-        x = env.get('bbb', 'XXX')
-        assert x == 'XXX', x
+    def test_Prepend(self):
+        """Test prepending to construction variables in an Environment
+        """
+        import UserList
+        UL = UserList.UserList
+        env1 = Environment(AAA = 'a', BBB = 'b', CCC = 'c', DDD = 'd',
+                           EEE = ['e'], FFF = ['f'], GGG = ['g'], HHH = ['h'],
+                           III = UL(['i']), JJJ = UL(['j']),
+                           KKK = UL(['k']), LLL = UL(['l']))
+        env1.Prepend(BBB = 'B', CCC = ['C'], DDD = UL(['D']),
+                    FFF = 'F', GGG = ['G'], HHH = UL(['H']),
+                    JJJ = 'J', KKK = ['K'], LLL = UL(['L']))
+        env2 = Environment(AAA = 'a', BBB = 'Bb',
+                           CCC = ['C', 'c'], DDD = UL(['D', 'd']),
+                           EEE = ['e'], FFF = ['F', 'f'],
+                           GGG = ['G', 'g'], HHH = UL(['H', 'h']),
+                           III = UL(['i']), JJJ = UL(['J', 'j']),
+                           KKK = UL(['K', 'k']), LLL = UL(['L', 'l']))
+        assert env1 == env2, diff_env(env1, env2)
 
-    def test_concat(self):
-        "Test _concat()"
-        e1 = Environment(PRE='pre', SUF='suf', STR='a b', LIST=['a', 'b'])
-        s = e1.subst
-        assert s("${_concat('', '', '', __env__)}") == ''
-        assert s("${_concat('', [], '', __env__)}") == ''
-        assert s("${_concat(PRE, '', SUF, __env__)}") == ''
-        assert s("${_concat(PRE, STR, SUF, __env__)}") == 'prea bsuf'
-        assert s("${_concat(PRE, LIST, SUF, __env__)}") == 'preasuf prebsuf'
+        env3 = Environment(X = {'x1' : 7})
+        env3.Prepend(X = {'x1' : 8, 'x2' : 9}, Y = {'y1' : 10})
+        assert env3['X'] == {'x1': 8, 'x2' : 9}, env3['X']
+        assert env3['Y'] == {'y1': 10}, env3['Y']
+
+        env4 = Environment(BUILDERS = {'z1' : 11})
+        env4.Prepend(BUILDERS = {'z2' : 12})
+        assert env4['BUILDERS'] == {'z1' : 11, 'z2' : 12}, env4['BUILDERS']
+        assert hasattr(env4, 'z1')
+        assert hasattr(env4, 'z2')
 
-    def test_FindIxes(self):
-        "Test FindIxes()"
-        env = Environment(LIBPREFIX='lib', 
-                          LIBSUFFIX='.a',
-                          SHLIBPREFIX='lib', 
-                          SHLIBSUFFIX='.so',
-                          PREFIX='pre',
-                          SUFFIX='post')
+    def test_PrependENVPath(self):
+        """Test prepending to an ENV path."""
+        env1 = Environment(ENV = {'PATH': r'C:\dir\num\one;C:\dir\num\two'},
+                           MYENV = {'MYPATH': r'C:\mydir\num\one;C:\mydir\num\two'})
+        # have to include the pathsep here so that the test will work on UNIX too.
+        env1.PrependENVPath('PATH',r'C:\dir\num\two',sep = ';') 
+        env1.PrependENVPath('PATH',r'C:\dir\num\three',sep = ';')
+        env1.PrependENVPath('MYPATH',r'C:\mydir\num\three','MYENV',sep = ';')
+        env1.PrependENVPath('MYPATH',r'C:\mydir\num\one','MYENV',sep = ';')
+        assert(env1['ENV']['PATH'] == r'C:\dir\num\three;C:\dir\num\two;C:\dir\num\one')
+        assert(env1['MYENV']['MYPATH'] == r'C:\mydir\num\one;C:\mydir\num\three;C:\mydir\num\two')
 
-        paths = [os.path.join('dir', 'libfoo.a'),
-                 os.path.join('dir', 'libfoo.so')]
+    def test_Replace(self):
+        """Test replacing construction variables in an Environment
 
-        assert paths[0] == env.FindIxes(paths, 'LIBPREFIX', 'LIBSUFFIX')
-        assert paths[1] == env.FindIxes(paths, 'SHLIBPREFIX', 'SHLIBSUFFIX')
-        assert None == env.FindIxes(paths, 'PREFIX', 'POST')
+        After creation of the Environment, of course.
+        """
+        env1 = Environment(AAA = 'a', BBB = 'b')
+        env1.Replace(BBB = 'bbb', CCC = 'ccc')
 
-        paths = ['libfoo.a', 'prefoopost']
+        env2 = Environment(AAA = 'a', BBB = 'bbb', CCC = 'ccc')
+        assert env1 == env2, diff_env(env1, env2)
 
-        assert paths[0] == env.FindIxes(paths, 'LIBPREFIX', 'LIBSUFFIX')
-        assert None == env.FindIxes(paths, 'SHLIBPREFIX', 'SHLIBSUFFIX')
-        assert paths[1] == env.FindIxes(paths, 'PREFIX', 'SUFFIX')
+        env3 = Environment(BUILDERS = {'b1' : 1})
+        assert hasattr(env3, 'b1'), "b1 was not set"
+        env3.Replace(BUILDERS = {'b2' : 2})
+        assert not hasattr(env3, 'b1'), "b1 was not cleared"
+        assert hasattr(env3, 'b2'), "b2 was not set"
 
     def test_ReplaceIxes(self):
         "Test ReplaceIxes()"
@@ -1225,6 +995,218 @@ class EnvironmentTestCase(unittest.TestCase):
                                              'PREFIX', 'SUFFIX',
                                              'LIBPREFIX', 'LIBSUFFIX')
 
+
+
+    def test_AlwaysBuild(self):
+        """Test the AlwaysBuild() method"""
+        env = Environment(FOO='fff', BAR='bbb')
+        t = env.AlwaysBuild('a', 'b$FOO', ['c', 'd'], '$BAR')
+        assert t[0].__class__.__name__ == 'File'
+        assert t[0].path == 'a'
+        assert t[0].always_build
+        assert t[1].__class__.__name__ == 'File'
+        assert t[1].path == 'bfff'
+        assert t[1].always_build
+        assert t[2].__class__.__name__ == 'File'
+        assert t[2].path == 'c'
+        assert t[2].always_build
+        assert t[3].__class__.__name__ == 'File'
+        assert t[3].path == 'd'
+        assert t[3].always_build
+        assert t[4].__class__.__name__ == 'File'
+        assert t[4].path == 'bbb'
+        assert t[4].always_build
+
+    def test_Command(self):
+        """Test the Command() method."""
+        env = Environment()
+        t = env.Command(target='foo.out', source=['foo1.in', 'foo2.in'],
+                        action='buildfoo $target $source')
+        assert not t.builder is None
+        assert t.builder.action.__class__.__name__ == 'CommandAction'
+        assert t.builder.action.cmd_list == 'buildfoo $target $source'
+        assert 'foo1.in' in map(lambda x: x.path, t.sources)
+        assert 'foo2.in' in map(lambda x: x.path, t.sources)
+
+        sub = SCons.Node.FS.default_fs.Dir('sub')
+        t = env.Command(target='bar.out', source='sub',
+                        action='buildbar $target $source')
+        assert 'sub' in map(lambda x: x.path, t.sources)
+
+        def testFunc(env, target, source):
+            assert str(target[0]) == 'foo.out'
+            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)
+        assert not t.builder is None
+        assert t.builder.action.__class__.__name__ == 'FunctionAction'
+        t.build()
+        assert 'foo1.in' in map(lambda x: x.path, t.sources)
+        assert 'foo2.in' in map(lambda x: x.path, t.sources)
+
+    def test_Depends(self):
+       """Test the explicit Depends method."""
+       env = Environment(FOO = 'xxx', BAR='yyy')
+       t = env.Depends(target='EnvironmentTest.py', dependency='Environment.py')
+       assert t.__class__.__name__ == 'File'
+       assert t.path == 'EnvironmentTest.py'
+       assert len(t.depends) == 1
+       d = t.depends[0]
+       assert d.__class__.__name__ == 'File'
+       assert d.path == 'Environment.py'
+
+       t = env.Depends(target='${FOO}.py', dependency='${BAR}.py')
+       assert t.__class__.__name__ == 'File'
+       assert t.path == 'xxx.py'
+       assert len(t.depends) == 1
+       d = t.depends[0]
+       assert d.__class__.__name__ == 'File'
+       assert d.path == 'yyy.py'
+
+    def test_Ignore(self):
+        """Test the explicit Ignore method."""
+        env = Environment(FOO='yyy', BAR='zzz')
+        t = env.Ignore(target='targ.py', dependency='dep.py')
+        assert t.__class__.__name__ == 'File'
+        assert t.path == 'targ.py'
+        assert len(t.ignore) == 1
+        i = t.ignore[0]
+        assert i.__class__.__name__ == 'File'
+        assert i.path == 'dep.py'
+        t = env.Ignore(target='$FOO$BAR', dependency='$BAR$FOO')
+        assert t.__class__.__name__ == 'File'
+        assert t.path == 'yyyzzz'
+        assert len(t.ignore) == 1
+        i = t.ignore[0]
+        assert i.__class__.__name__ == 'File'
+        assert i.path == 'zzzyyy'
+
+    def test_Install(self):
+       """Test Install and InstallAs methods"""
+        env = Environment(FOO='iii', BAR='jjj')
+
+        tgt = env.Install('export', [ 'build/foo1', 'build/foo2' ])
+        paths = map(str, tgt)
+        paths.sort()
+        expect = map(os.path.normpath, [ 'export/foo1', 'export/foo2' ])
+        assert paths == expect, paths
+        for tnode in tgt:
+            assert tnode.builder == InstallBuilder
+
+        tgt = env.Install('$FOO', [ 'build/${BAR}1', 'build/${BAR}2' ])
+        paths = map(str, tgt)
+        paths.sort()
+        expect = map(os.path.normpath, [ 'iii/jjj1', 'iii/jjj2' ])
+        assert paths == expect, paths
+        for tnode in tgt:
+            assert tnode.builder == InstallBuilder
+
+        exc_caught = None
+        try:
+            tgt = env.Install('export', 'export')
+        except SCons.Errors.UserError, e:
+            exc_caught = 1
+        assert exc_caught, "UserError should be thrown when Install() target is not a file."
+        match = str(e) == "Source `export' of Install() is not a file.  Install() source must be one or more files."
+        assert match, e
+
+        exc_caught = None
+        try:
+            tgt = env.Install('export', ['export', 'build/foo1'])
+        except SCons.Errors.UserError, e:
+            exc_caught = 1
+        assert exc_caught, "UserError should be thrown when Install() target containins non-files."
+        match = str(e) == "Source `['export', 'build/foo1']' of Install() contains one or more non-files.  Install() source must be one or more files."
+        assert match, e
+
+        exc_caught = None
+        try:
+            tgt = env.Install('export/foo1', 'build/foo1')
+        except SCons.Errors.UserError, e:
+            exc_caught = 1
+        assert exc_caught, "UserError should be thrown reversing the order of Install() targets."
+        match = str(e) == "Target `export/foo1' of Install() is a file, but should be a directory.  Perhaps you have the Install() arguments backwards?"
+        assert match, e
+
+        tgt = env.InstallAs(target=string.split('foo1 foo2'),
+                            source=string.split('bar1 bar2'))
+        assert len(tgt) == 2, len(tgt)
+        paths = map(lambda x: str(x.sources[0]), tgt)
+        paths.sort()
+        expect = map(os.path.normpath, [ 'bar1', 'bar2' ])
+        assert paths == expect, paths
+        for tnode in tgt:
+            assert tnode.builder == InstallBuilder
+
+        tgt = env.InstallAs(target='${FOO}.t', source='${BAR}.s')
+        assert tgt.path == 'iii.t'
+        assert tgt.sources[0].path == 'jjj.s'
+        assert tgt.builder == InstallBuilder
+
+    def test_Precious(self):
+        """Test the Precious() method."""
+        env = Environment(FOO='ggg', BAR='hhh')
+        t = env.Precious('a', '${BAR}b', ['c', 'd'], '$FOO')
+        assert t[0].__class__.__name__ == 'File'
+        assert t[0].path == 'a'
+        assert t[0].precious
+        assert t[1].__class__.__name__ == 'File'
+        assert t[1].path == 'hhhb'
+        assert t[1].precious
+        assert t[2].__class__.__name__ == 'File'
+        assert t[2].path == 'c'
+        assert t[2].precious
+        assert t[3].__class__.__name__ == 'File'
+        assert t[3].path == 'd'
+        assert t[3].precious
+        assert t[4].__class__.__name__ == 'File'
+        assert t[4].path == 'ggg'
+        assert t[4].precious
+
+    def test_SideEffect(self):
+        """Test the SideEffect() method"""
+        env = Environment(LIB='lll', FOO='fff', BAR='bbb')
+
+        foo = env.Object('foo.obj', 'foo.cpp')
+        bar = env.Object('bar.obj', 'bar.cpp')
+        s = env.SideEffect('mylib.pdb', ['foo.obj', 'bar.obj'])
+        assert s.path == 'mylib.pdb'
+        assert s.side_effect
+        assert foo.side_effects == [s]
+        assert bar.side_effects == [s]
+        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'])
+        assert s.path == 'mylll.pdb'
+        assert s.side_effect
+        assert fff.side_effects == [s], fff.side_effects
+        assert bbb.side_effects == [s], bbb.side_effects
+        assert s.depends_on([bbb])
+        assert s.depends_on([fff])
+
+    def test_SourceCode(self):
+        """Test the SourceCode() method."""
+        env = Environment(FOO='mmm', BAR='nnn')
+        e = env.SourceCode('foo', None)
+        assert e.path == 'foo'
+        s = e.src_builder()
+        assert s is None, s
+
+        b = Builder()
+        e = env.SourceCode(e, b)
+        assert e.path == 'foo'
+        s = e.src_builder()
+        assert s is b, s
+
+        e = env.SourceCode('$BAR$FOO', None)
+        assert e.path == 'nnnmmm'
+        s = e.src_builder()
+        assert s is None, s
+
         
 if __name__ == "__main__":
     suite = unittest.makeSuite(EnvironmentTestCase, 'test_')