Commits

Anonymous committed e7b83c3

When searching paths for Files or Dirs, match Entries,too.

Comments (0)

Files changed (2)

src/engine/SCons/Node/FS.py

     src = source[0].abspath
     dest = target[0].abspath
     dir, file = os.path.split(dest)
-    if dir and not os.path.isdir(dir):
+    if dir and not target[0].fs.isdir(dir):
         os.makedirs(dir)
     if not Link_Funcs:
         # Set a default order of link functions.
         import SCons.Builder
         # "env" will get filled in by Executor.get_build_env()
         # calling SCons.Defaults.DefaultEnvironment() when necessary.
-        DefaultSCCSBuilder = SCons.Builder.Builder(action = '$SCCSCOM',
+        act = SCons.Action.Action('$SCCSCOM', '$SCCSCOMSTR')
+        DefaultSCCSBuilder = SCons.Builder.Builder(action = act,
                                                    env = None,
                                                    name = "DefaultSCCSBuilder")
     return DefaultSCCSBuilder
         import SCons.Builder
         # "env" will get filled in by Executor.get_build_env()
         # calling SCons.Defaults.DefaultEnvironment() when necessary.
-        DefaultRCSBuilder = SCons.Builder.Builder(action = '$RCS_COCOM',
+        act = SCons.Action.Action('$RCS_COCOM', '$RCS_COCOMSTR')
+        DefaultRCSBuilder = SCons.Builder.Builder(action = act,
                                                   env = None,
                                                   name = "DefaultRCSBuilder")
     return DefaultRCSBuilder
             except AttributeError:
                 entry = self.get()
                 classname = string.split(str(entry.__class__), '.')[-1]
+                if classname[-2:] == "'>":
+                    # new-style classes report their name as:
+                    #   "<class 'something'>"
+                    # instead of the classic classes:
+                    #   "something"
+                    classname = classname[:-2]
                 raise AttributeError, "%s instance '%s' has no attribute '%s'" % (classname, entry.name, name)
             return attr
 
 
         self.name = name
         self.fs = fs
-        self.relpath = {self : '.'}
 
         assert directory, "A directory must be provided"
 
         """Completely clear a Node.FS.Base object of all its cached
         state (so that it can be re-evaluated by interfaces that do
         continuous integration builds).
+        __cache_reset__
         """
         SCons.Node.Node.clear(self)
-        try:
-            delattr(self, '_exists')
-        except AttributeError:
-            pass
-        try:
-            delattr(self, '_rexists')
-        except AttributeError:
-            pass
-        try:
-            delattr(self, '_str_val')
-        except AttributeError:
-            pass
-        self.relpath = {self : '.'}
 
     def get_dir(self):
         return self.dir
 
     def get_suffix(self):
+        "__cacheable__"
         return SCons.Util.splitext(self.name)[1]
 
     def rfile(self):
     def __str__(self):
         """A Node.FS.Base object's string representation is its path
         name."""
-        try:
-            return self._str_val
-        except AttributeError:
-            global Save_Strings
-            if self.duplicate or self.is_derived():
-                str_val = self.get_path()
-            else:
-                str_val = self.srcnode().get_path()
-            if Save_Strings:
-                self._str_val = str_val
-            return str_val
+        global Save_Strings
+        if Save_Strings:
+            return self._save_str()
+        return self._get_str()
+
+    def _save_str(self):
+        "__cacheable__"
+        return self._get_str()
+
+    def _get_str(self):
+        if self.duplicate or self.is_derived():
+            return self.get_path()
+        return self.srcnode().get_path()
 
     rstr = __str__
 
     def exists(self):
-        try:
-            return self._exists
-        except AttributeError:
-            self._exists = self.fs.exists(self.abspath)
-            return self._exists
+        "__cacheable__"
+        return self.fs.exists(self.abspath)
 
     def rexists(self):
-        try:
-            return self._rexists
-        except AttributeError:
-            self._rexists = self.rfile().exists()
-            return self._rexists
+        "__cacheable__"
+        return self.rfile().exists()
 
     def is_under(self, dir):
         if self is dir:
     def srcnode(self):
         """If this node is in a build path, return the node
         corresponding to its source file.  Otherwise, return
-        ourself."""
-        try:
-            return self._srcnode
-        except AttributeError:
-            dir=self.dir
-            name=self.name
-            while dir:
-                if dir.srcdir:
-                    self._srcnode = self.fs.Entry(name, dir.srcdir,
-                                                  klass=self.__class__)
-                    return self._srcnode
-                name = dir.name + os.sep + name
-                dir=dir.get_dir()
-            self._srcnode = self
-            return self._srcnode
+        ourself.
+        __cacheable__"""
+        dir=self.dir
+        name=self.name
+        while dir:
+            if dir.srcdir:
+                srcnode = self.fs.Entry(name, dir.srcdir,
+                                        klass=self.__class__)
+                return srcnode
+            name = dir.name + os.sep + name
+            dir=dir.get_dir()
+        return self
 
     def get_path(self, dir=None):
         """Return path relative to the current working directory of the
         Node.FS.Base object that owns us."""
         if not dir:
             dir = self.fs.getcwd()
-        try:
-            return self.relpath[dir]
-        except KeyError:
-            path_elems = []
-            d = self
+        path_elems = []
+        d = self
+        if d == dir:
+            path_elems.append('.')
+        else:
             while d != dir and not isinstance(d, ParentOfRoot):
                 path_elems.append(d.name)
                 d = d.dir
             path_elems.reverse()
-            ret = string.join(path_elems, os.sep)
-            self.relpath[dir] = ret
-            return ret
+        ret = string.join(path_elems, os.sep)
+        return ret
             
     def set_src_builder(self, builder):
         """Set the source code builder for this node."""
     time comes, and then call the same-named method in the transformed
     class."""
 
+    def disambiguate(self):
+        if self.fs.isdir(self.abspath):
+            self.__class__ = Dir
+            self._morph()
+        else:
+            self.__class__ = File
+            self._morph()
+            self.clear()
+        return self
+
     def rfile(self):
         """We're a generic Entry, but the caller is actually looking for
         a File at this point, so morph into one."""
         self.clear()
         return File.rfile(self)
 
-    def get_found_includes(self, env, scanner, target):
+    def get_found_includes(self, env, scanner, path):
         """If we're looking for included files, it's because this Entry
         is really supposed to be a File itself."""
-        node = self.rfile()
-        return node.get_found_includes(env, scanner, target)
+        return self.disambiguate().get_found_includes(env, scanner, path)
 
     def scanner_key(self):
         return self.get_suffix()
         if self.fs.isfile(self.abspath):
             self.__class__ = File
             self._morph()
-            return File.get_contents(self)
+            return self.get_contents()
         if self.fs.isdir(self.abspath):
             self.__class__ = Dir
             self._morph()
-            return Dir.get_contents(self)
+            return self.get_contents()
         if self.fs.islink(self.abspath):
             return ''             # avoid errors for dangling symlinks
         raise AttributeError
         """Return if the Entry exists.  Check the file system to see
         what we should turn into first.  Assume a file if there's no
         directory."""
-        if self.fs.isdir(self.abspath):
-            self.__class__ = Dir
-            self._morph()
-            return Dir.exists(self)
-        else:
-            self.__class__ = File
-            self._morph()
-            self.clear()
-            return File.exists(self)
+        return self.disambiguate().exists()
 
     def calc_signature(self, calc=None):
         """Return the Entry's calculated signature.  Check the file
         system to see what we should turn into first.  Assume a file if
         there's no directory."""
-        if self.fs.isdir(self.abspath):
-            self.__class__ = Dir
-            self._morph()
-            return Dir.calc_signature(self, calc)
-        else:
-            self.__class__ = File
-            self._morph()
-            self.clear()
-            return File.calc_signature(self, calc)
+        return self.disambiguate().calc_signature(calc)
 
     def must_be_a_Dir(self):
         """Called to make sure a Node is a Dir.  Since we're an
         Entry, we can morph into one."""
         self.__class__ = Dir
         self._morph()
+        return self
 
 # This is for later so we can differentiate between Entry the class and Entry
 # the method of the FS class.
 
 
 class LocalFS:
+
+    __metaclass__ = SCons.Memoize.Memoized_Metaclass
+    
     # This class implements an abstraction layer for operations involving
     # a local file system.  Essentially, this wraps any function in
     # the os, os.path or shutil modules that we use to actually go do
             return 0                    # no symlinks
         exists_or_islink = exists
 
+if not SCons.Memoize.has_metaclass:
+    _FSBase = LocalFS
+    class LocalFS(SCons.Memoize.Memoizer, _FSBase):
+        def __init__(self, *args, **kw):
+            apply(_FSBase.__init__, (self,)+args, kw)
+            SCons.Memoize.Memoizer.__init__(self)
+
+
 #class RemoteFS:
 #    # Skeleton for the obvious methods we might need from the
 #    # abstraction layer for a remote filesystem.
 
 
 class FS(LocalFS):
+
     def __init__(self, path = None):
         """Initialize the Node.FS subsystem.
 
 
         The path argument must be a valid absolute path.
         """
-        if __debug__: logInstanceCreation(self)
+        if __debug__: logInstanceCreation(self, 'Node.FS')
         self.Top = None
         if path == None:
             self.pathTop = os.getcwd()
         assert not self.Top, "You can only set the top-level path on an FS object that has not had its File, Dir, or Entry methods called yet."
         self.pathTop = path
 
+    def clear_cache(self):
+        "__cache_reset__"
+        pass
+    
     def set_SConstruct_dir(self, dir):
         self.SConstruct_dir = dir
         
     def __setTopLevelDir(self):
         if not self.Top:
-            self.Top = self.__doLookup(Dir, os.path.normpath(self.pathTop))
+            self.Top = self._doLookup(Dir, os.path.normpath(self.pathTop))
             self.Top.path = '.'
             self._cwd = self.Top
         
         raise TypeError, "Tried to lookup %s '%s' as a %s." % \
               (node.__class__.__name__, node.path, klass.__name__)
         
-    def __doLookup(self, fsclass, name, directory = None, create = 1):
+    def _doLookup(self, fsclass, name, directory = None, create = 1):
         """This method differs from the File and Dir factory methods in
         one important way: the meaning of the directory parameter.
         In this method, if directory is None or not supplied, the supplied
         name is expected to be an absolute path.  If you try to look up a
         relative path with directory=None, then an AssertionError will be
-        raised."""
+        raised.
+        __cacheable__"""
 
         if not name:
             # This is a stupid hack to compensate for the fact
                 directory.add_wkid(dir_temp)
                 directory = dir_temp
             else:
-                d.must_be_a_Dir()
-                directory = d
+                directory = d.must_be_a_Dir()
 
         entry_norm = _my_normcase(path_comp[-1])
         try:
             if directory and not isinstance(directory, Dir):
                 directory = self.Dir(directory)
             name, directory = self.__transformPath(name, directory)
-            return self.__doLookup(klass, name, directory, create)
+            return self._doLookup(klass, name, directory, create)
     
     def File(self, name, directory = None, create = 1):
         """Lookup or create a File node with the specified name.  If
             self.__setTopLevelDir()
             self.Top.addRepository(d)
 
+    def do_Rsearch(self, path, dir, func, clazz=_classEntry):
+        """Search for something in a Repository.  Returns the first
+        one found in the list, or None if there isn't one.
+        __cacheable__
+        """
+        d, name = os.path.split(path)
+        norm_name = _my_normcase(name)
+        if d:
+            dir = dir.Dir(d)
+        try:
+            node = dir.entries[norm_name]
+        except KeyError:
+            node = dir.node_on_disk(name, clazz)
+        else:
+            node = func(node)
+            if node:
+                dir = node.get_dir()
+        if node:
+            return node, dir
+        fname = '.'
+        while dir:
+            for rep in dir.getRepositories():
+                rdir = rep.Dir(fname)
+                try:
+                    node = rdir.entries[norm_name]
+                except KeyError:
+                    node = rdir.node_on_disk(name, clazz)
+                else:
+                    node = func(node)
+                if node:
+                    return node, dir
+            fname = dir.name + os.sep + fname
+            dir = dir.get_dir()
+        return None, None
+
     def Rsearch(self, path, clazz=_classEntry, cwd=None):
-        """Search for something in a Repository.  Returns the first
-        one found in the list, or None if there isn't one."""
         if isinstance(path, SCons.Node.Node):
             return path
-        else:
-            name, d = self.__transformPath(path, cwd)
-            n = self.__doLookup(clazz, name, d)
-            if n.exists():
-                return n
-            if isinstance(n, Dir):
-                # If n is a Directory that has Repositories directly
-                # attached to it, then any of those is a valid Repository
-                # path.  Return the first one that exists.
-                reps = filter(lambda x: x.exists(), n.getRepositories())
-                if len(reps):
-                    return reps[0]
-            d = n.get_dir()
-            name = n.name
-            # Search repositories of all directories that this file is under.
-            while d:
-                for rep in d.getRepositories():
-                    try:
-                        rnode = self.__doLookup(clazz, name, rep)
-                        # Only find the node if it exists and it is not
-			# a derived file.  If for some reason, we are
-			# explicitly building a file IN a Repository, we
-			# don't want it to show up in the build tree.
-			# This is usually the case with BuildDir().
-			# We only want to find pre-existing files.
-                        if rnode.exists() and \
-                           (isinstance(rnode, Dir) or not rnode.is_derived()):
-                            return rnode
-                    except TypeError:
-                        pass # Wrong type of node.
-                # Prepend directory name
-                name = d.name + os.sep + name
-                # Go up one directory
-                d = d.get_dir()
-        return None
+        def func(node, clazz=clazz):
+            if node.exists() and \
+               (isinstance(node, clazz) or isinstance(node, Entry) \
+                or not node.is_derived()):
+                   return node
+            return None
+        path, dir = self.__transformPath(path, cwd)
+        return self.do_Rsearch(path, dir, func, clazz)[0]
 
     def Rsearchall(self, pathlist, must_exist=1, clazz=_classEntry, cwd=None):
-        """Search for a list of somethings in the Repository list."""
-        ret = []
+        """Search for a list of somethings in the Repository list.
+        __cacheable__
+        """
+        result = []
         if SCons.Util.is_String(pathlist):
             pathlist = string.split(pathlist, os.pathsep)
         if not SCons.Util.is_List(pathlist):
             pathlist = [pathlist]
+
+        if must_exist:
+            select = lambda x, clazz=clazz: isinstance(x, clazz) and x.exists()
+        else:
+            select = lambda x, clazz=clazz: isinstance(x, clazz)
+
         for path in filter(None, pathlist):
             if isinstance(path, SCons.Node.Node):
-                ret.append(path)
+                result.append(path)
+                continue
+
+            path, dir = self.__transformPath(path, cwd)
+            d, name = os.path.split(path)
+            norm_name = _my_normcase(name)
+            if d:
+                dir = dir.Dir(d)
+            try:
+                node = dir.entries[norm_name]
+            except KeyError:
+                # If there's no Node on disk, we'll filter
+                # out the returned None below.
+                if must_exist:
+                    n = dir.node_on_disk(name, clazz)
+                else:
+                    n = self._doLookup(clazz, name, dir)
+                    dir.srcdir_duplicate(name, clazz)
+                result.append(n)
             else:
-                name, d = self.__transformPath(path, cwd)
-                n = self.__doLookup(clazz, name, d)
-                if not must_exist or n.exists():
-                    ret.append(n)
-                if isinstance(n, Dir):
-                    # If this node is a directory, then any repositories
-                    # attached to this node can be repository paths.
-                    ret.extend(filter(lambda x, me=must_exist, clazz=clazz: isinstance(x, clazz) and (not me or x.exists()),
-                                      n.getRepositories()))
-                    
-                d = n.get_dir()
-                name = n.name
-                # Search repositories of all directories that this file
-                # is under.
-                while d:
-                    for rep in d.getRepositories():
-                        try:
-                            rnode = self.__doLookup(clazz, name, rep)
-                            # Only find the node if it exists (or
-                            # must_exist is zero) and it is not a
-                            # derived file.  If for some reason, we
-                            # are explicitly building a file IN a
-                            # Repository, we don't want it to show up in
-                            # the build tree.  This is usually the case
-                            # with BuildDir().  We only want to find
-                            # pre-existing files.
-                            if (not must_exist or rnode.exists()) and \
-                               (not rnode.is_derived() or isinstance(rnode, Dir)):
-                                ret.append(rnode)
-                        except TypeError:
-                            pass # Wrong type of node.
-                    # Prepend directory name
-                    name = d.name + os.sep + name
-                    # Go up one directory
-                    d = d.get_dir()
-        return ret
+                if not must_exist or node.exists():
+                    result.append(node)
+            fname = '.'
+            while dir:
+                for rep in dir.getRepositories():
+                    rdir = rep.Dir(fname)
+                    try:
+                        node = rdir.entries[norm_name]
+                    except KeyError:
+                        # If there's no Node on disk, we'll filter
+                        # out the returned None below.
+                        if must_exist:
+                            n = rdir.node_on_disk(name, clazz)
+                        else:
+                            n = self._doLookup(clazz, name, rdir)
+                            rdir.srcdir_duplicate(name, clazz)
+                        result.append(n)
+                    else:
+                        if (not must_exist or node.exists()) and \
+                           (isinstance(node, Dir) or not node.is_derived()):
+                            result.append(node)
+                fname = dir.name + os.sep + fname
+                dir = dir.get_dir()
+
+        return filter(None, result)
 
     def CacheDir(self, path):
         self.CachePath = path
 
-    def build_dir_target_climb(self, dir, tail):
+    def build_dir_target_climb(self, orig, dir, tail):
         """Create targets in corresponding build directories
 
         Climb the directory tree, and look up path names
         relative to any linked build directories we find.
+        __cacheable__
         """
         targets = []
         message = None
+        fmt = "building associated BuildDir targets: %s"
+        start_dir = dir
         while dir:
             for bd in dir.build_dirs:
+                if start_dir.is_under(bd):
+                    # If already in the build-dir location, don't reflect
+                    return [orig], fmt % str(orig)
                 p = apply(os.path.join, [bd.path] + tail)
                 targets.append(self.Entry(p))
             tail = [dir.name] + tail
             dir = dir.up()
         if targets:
-            message = "building associated BuildDir targets: %s" % string.join(map(str, targets))
+            message = fmt % string.join(map(str, targets))
         return targets, message
 
-class DummyExecutor:
-    """Dummy executor class returned by Dir nodes to bamboozle SCons
-    into thinking we are an actual derived node, where our sources are
-    our directory entries."""
-    def cleanup(self):
-        pass
-    def get_raw_contents(self):
-        return ''
-    def get_contents(self):
-        return ''
-    def get_timestamp(self):
-        return 0
-    def get_build_env(self):
-        return None
 
 class Dir(Base):
     """A class for directories in a file system.
 
         Set up this directory's entries and hook it into the file
         system tree.  Specify that directories (this Node) don't use
-        signatures for calculating whether they're current."""
+        signatures for calculating whether they're current.
+        __cache_reset__"""
 
         self.repositories = []
         self.srcdir = None
         self._sconsign = None
         self.build_dirs = []
 
+    def disambiguate(self):
+        return self
+
     def __clearRepositoryCache(self, duplicate=None):
         """Called when we change the repository(ies) for a directory.
         This clears any cached information that is invalidated by changing
                 if node != self and isinstance(node, Dir):
                     node.__clearRepositoryCache(duplicate)
                 else:
+                    node.clear()
                     try:
                         del node._srcreps
                     except AttributeError:
                         pass
-                    try:
-                        del node._rfile
-                    except AttributeError:
-                        pass
-                    try:
-                        del node._rexists
-                    except AttributeError:
-                        pass
-                    try:
-                        del node._exists
-                    except AttributeError:
-                        pass
-                    try:
-                        del node._srcnode
-                    except AttributeError:
-                        pass
-                    try:
-                        del node._str_val
-                    except AttributeError:
-                        pass
                     if duplicate != None:
                         node.duplicate=duplicate
     
         self.implicit = []
         self.implicit_dict = {}
         self._children_reset()
-        try:
-            for filename in self.fs.listdir(self.abspath):
-                if filename != '.sconsign':
-                    self.Entry(filename)
-        except OSError:
-            # Directory does not exist.  No big deal
-            pass
-        keys = filter(lambda k: k != '.' and k != '..', self.entries.keys())
-        kids = map(lambda x, s=self: s.entries[x], keys)
-        def c(one, two):
-            return cmp(one.abspath, two.abspath)
-        kids.sort(c)
-        self._add_child(self.implicit, self.implicit_dict, kids)
+
+        dont_scan = lambda k: k not in ['.', '..', '.sconsign']
+        deps = filter(dont_scan, self.entries.keys())
+        # keys() is going to give back the entries in an internal,
+        # unsorted order.  Sort 'em so the order is deterministic.
+        deps.sort()
+        entries = map(lambda n, e=self.entries: e[n], deps)
+
+        self._add_child(self.implicit, self.implicit_dict, entries)
+
+    def get_found_includes(self, env, scanner, path):
+        """Return the included implicit dependencies in this file.
+        Cache results so we only scan the file once per path
+        regardless of how many times this information is requested.
+        __cacheable__"""
+        if not scanner:
+            return []
+        # Clear cached info for this Node.  If we already visited this
+        # directory on our walk down the tree (because we didn't know at
+        # that point it was being used as the source for another Node)
+        # then we may have calculated build signature before realizing
+        # we had to scan the disk.  Now that we have to, though, we need
+        # to invalidate the old calculated signature so that any node
+        # dependent on our directory structure gets one that includes
+        # info about everything on disk.
+        self.clear()
+        return scanner(self, env, path)
 
     def build(self, **kw):
         """A null "builder" for directories."""
         if not self.builder is MkdirBuilder:
             apply(SCons.Node.Node.build, [self,], kw)
 
+    def _create(self):
+        """Create this directory, silently and without worrying about
+        whether the builder is the default or not."""
+        listDirs = []
+        parent = self
+        while parent:
+            if parent.exists():
+                break
+            listDirs.append(parent)
+            p = parent.up()
+            if isinstance(p, ParentOfRoot):
+                raise SCons.Errors.StopError, parent.path
+            parent = p
+        listDirs.reverse()
+        for dirnode in listDirs:
+            try:
+                # Don't call dirnode.build(), call the base Node method
+                # directly because we definitely *must* create this
+                # directory.  The dirnode.build() method will suppress
+                # the build if it's the default builder.
+                SCons.Node.Node.build(dirnode)
+                dirnode.get_executor().nullify()
+                # The build() action may or may not have actually
+                # created the directory, depending on whether the -n
+                # option was used or not.  Delete the _exists and
+                # _rexists attributes so they can be reevaluated.
+                dirnode.clear()
+            except OSError:
+                pass
+
     def multiple_side_effect_has_builder(self):
         global MkdirBuilder
         return not self.builder is MkdirBuilder and self.has_builder()
     def alter_targets(self):
         """Return any corresponding targets in a build directory.
         """
-        return self.fs.build_dir_target_climb(self, [])
+        return self.fs.build_dir_target_climb(self, self, [])
 
     def scanner_key(self):
         """A directory does not get scanned."""
         for kid in self.children():
             contents.write(kid.get_contents())
         return contents.getvalue()
-    
+
     def prepare(self):
         pass
 
+    def do_duplicate(self, src):
+        pass
+
     def current(self, calc=None):
         """If all of our children were up-to-date, then this
         directory was up-to-date, too."""
             return 0
 
     def rdir(self):
-        try:
-            return self._rdir
-        except AttributeError:
-            self._rdir = self
-            if not self.exists():
-                n = self.fs.Rsearch(self.path, clazz=Dir, cwd=self.fs.Top)
-                if n:
-                    self._rdir = n
-            return self._rdir
+        "__cacheable__"
+        rdir = self
+        if not self.exists():
+            n = self.fs.Rsearch(self.path, clazz=Dir, cwd=self.fs.Top)
+            if n:
+                rdir = n
+        return rdir
 
     def sconsign(self):
         """Return the .sconsign file info for this directory,
     def must_be_a_Dir(self):
         """Called to make sure a Node is a Dir.  Since we're already
         one, this is a no-op for us."""
-        pass
+        return self
+
+    def entry_exists_on_disk(self, name):
+        """__cacheable__"""
+        return self.fs.exists(self.entry_abspath(name))
+
+    def rcs_on_disk(self, name):
+        rcspath = 'RCS' + os.sep + name+',v'
+        return self.entry_exists_on_disk(rcspath)
+
+    def sccs_on_disk(self, name):
+        sccspath = 'SCCS' + os.sep + 's.'+name
+        return self.entry_exists_on_disk(sccspath)
+
+    def srcdir_list(self):
+        """__cacheable__"""
+        result = []
+
+        dirname = '.'
+        dir = self
+        while dir:
+            if dir.srcdir:
+                d = dir.srcdir.Dir(dirname)
+                if d.is_under(dir):
+                    # Shouldn't source from something in the build path:
+                    # build_dir is probably under src_dir, in which case
+                    # we are reflecting.
+                    break
+                result.append(d)
+            dirname = dir.name + os.sep + dirname
+            dir = dir.get_dir()
+
+        return result
+
+    def srcdir_duplicate(self, name, clazz):
+        for dir in self.srcdir_list():
+            if dir.entry_exists_on_disk(name):
+                srcnode = self.fs._doLookup(clazz, name, dir)
+                if self.duplicate:
+                    node = self.fs._doLookup(clazz, name, self)
+                    node.do_duplicate(srcnode)
+                    return node
+                else:
+                    return srcnode
+        return None
+
+    def srcdir_find_file(self, filename):
+        """__cacheable__"""
+        fs = self.fs
+        do_Rsearch = fs.do_Rsearch
+
+        def func(node):
+            if (isinstance(node, File) or isinstance(node, Entry)) and \
+               (node.is_derived() or node.is_pseudo_derived() or node.exists()):
+                    return node
+            return None
+
+        node, d = do_Rsearch(filename, self, func, File)
+        if node:
+            return node, d
+
+        for dir in self.srcdir_list():
+            node, d = do_Rsearch(filename, dir, func, File)
+            if node:
+                return File(filename, self, fs), d
+        return None, None
+
+    def node_on_disk(self, name, clazz):
+        if self.entry_exists_on_disk(name) or \
+           self.sccs_on_disk(name) or \
+           self.rcs_on_disk(name):
+            try:
+                return self.fs._doLookup(clazz, name, self)
+            except TypeError:
+                pass
+        return self.srcdir_duplicate(name, clazz)
 
 class RootDir(Dir):
     """A class for the root directory of a file system.
         return self.fs.Rsearchall(pathlist, clazz=Dir, must_exist=0,
                                   cwd=self.cwd)
 
-    def generate_build_dict(self):
-        """Return an appropriate dictionary of values for building
-        this File."""
-        return {'Dir' : self.Dir,
-                'File' : self.File,
-                'RDirs' : self.RDirs}
-
     def _morph(self):
-        """Turn a file system node into a File object."""
+        """Turn a file system node into a File object.  __cache_reset__"""
         self.scanner_paths = {}
-        self.found_includes = {}
         if not hasattr(self, '_local'):
             self._local = 0
 
+    def disambiguate(self):
+        return self
+
     def root(self):
         return self.dir.root()
 
         self.dir.sconsign().set_entry(self.name, entry)
 
     def get_stored_info(self):
+        "__cacheable__"
         try:
             stored = self.dir.sconsign().get_entry(self.name)
+        except (KeyError, OSError):
+            return BuildInfo()
+        else:
             if isinstance(stored, BuildInfo):
                 return stored
             # The stored build information isn't a BuildInfo object.
             for key, val in stored.__dict__.items():
                 setattr(binfo, key, val)
             return binfo
-        except:
-            return BuildInfo()
 
     def get_stored_implicit(self):
         binfo = self.get_stored_info()
         except AttributeError:
             return None
 
-    def get_found_includes(self, env, scanner, target):
+    def get_found_includes(self, env, scanner, path):
         """Return the included implicit dependencies in this file.
-        Cache results so we only scan the file once regardless of
-        how many times this information is requested."""
+        Cache results so we only scan the file once per path
+        regardless of how many times this information is requested.
+        __cacheable__"""
         if not scanner:
             return []
-
-        try:
-            path = target.scanner_paths[scanner]
-        except AttributeError:
-            # The target had no scanner_paths attribute, which means
-            # it's an Alias or some other node that's not actually a
-            # file.  In that case, back off and use the path for this
-            # node itself.
-            try:
-                path = self.scanner_paths[scanner]
-            except KeyError:
-                path = scanner.path(env, self.cwd)
-                self.scanner_paths[scanner] = path
-        except KeyError:
-            path = scanner.path(env, target.cwd)
-            target.scanner_paths[scanner] = path
-
-        try:
-            includes = self.found_includes[path]
-        except KeyError:
-            includes = scanner(self, env, path)
-            self.found_includes[path] = includes
-
-        return includes
+        return scanner(self, env, path)
 
     def _createDir(self):
         # ensure that the directories for this node are
         # created.
-
-        listDirs = []
-        parent=self.dir
-        while parent:
-            if parent.exists():
-                break
-            listDirs.append(parent)
-            p = parent.up()
-            if isinstance(p, ParentOfRoot):
-                raise SCons.Errors.StopError, parent.path
-            parent = p
-        listDirs.reverse()
-        for dirnode in listDirs:
-            try:
-                # Don't call dirnode.build(), call the base Node method
-                # directly because we definitely *must* create this
-                # directory.  The dirnode.build() method will suppress
-                # the build if it's the default builder.
-                SCons.Node.Node.build(dirnode)
-                # The build() action may or may not have actually
-                # created the directory, depending on whether the -n
-                # option was used or not.  Delete the _exists and
-                # _rexists attributes so they can be reevaluated.
-                try:
-                    delattr(dirnode, '_exists')
-                except AttributeError:
-                    pass
-                try:
-                    delattr(dirnode, '_rexists')
-                except AttributeError:
-                    pass
-            except OSError:
-                pass
+        self.dir._create()
 
     def retrieve_from_cache(self):
         """Try to retrieve the node's content from a cache
         return None
 
     def built(self):
-        """Called just after this node is sucessfully built."""
+        """Called just after this node is successfully built.
+        __cache_reset__"""
         # Push this file out to cache before the superclass Node.built()
         # method has a chance to clear the build signature, which it
         # will do if this file has a source scanner.
         if self.fs.CachePath and self.fs.exists(self.path):
             CachePush(self, [], None)
+        self.fs.clear_cache()
         SCons.Node.Node.built(self)
-        self.found_includes = {}
-        try:
-            delattr(self, '_exists')
-        except AttributeError:
-            pass
-        try:
-            delattr(self, '_rexists')
-        except AttributeError:
-            pass
 
     def visited(self):
         if self.fs.CachePath and self.fs.cache_force and self.fs.exists(self.path):
             else:
                 scb = self.dir.src_builder()
                 if scb is _null:
-                    scb = None
-                    dir = self.dir.path
-                    sccspath = os.path.join('SCCS', 's.' + self.name)
-                    if dir != '.':
-                        sccspath = os.path.join(dir, sccspath)
-                    if self.fs.exists(sccspath):
+                    if self.dir.sccs_on_disk(self.name):
                         scb = get_DefaultSCCSBuilder()
+                    elif self.dir.rcs_on_disk(self.name):
+                        scb = get_DefaultRCSBuilder()
                     else:
-                        rcspath = os.path.join('RCS', self.name + ',v')
-                        if dir != '.':
-                            rcspath = os.path.join(dir, rcspath)
-                        if os.path.exists(rcspath):
-                            scb = get_DefaultRCSBuilder()
+                        scb = None
                 if scb is not None:
                     self.builder_set(scb)
             self.sbuilder = scb
         """
         if self.is_derived():
             return [], None
-        return self.fs.build_dir_target_climb(self.dir, [self.name])
+        return self.fs.build_dir_target_climb(self, self.dir, [self.name])
 
     def is_pseudo_derived(self):
+        "__cacheable__"
         return self.has_src_builder()
-    
+
+    def _rmv_existing(self):
+        '__cache_reset__'
+        Unlink(self, [], None)
+        
     def prepare(self):
         """Prepare for this file to be created."""
         SCons.Node.Node.prepare(self)
         if self.get_state() != SCons.Node.up_to_date:
             if self.exists():
                 if self.is_derived() and not self.precious:
-                    Unlink(self, [], None)
-                    try:
-                        delattr(self, '_exists')
-                    except AttributeError:
-                        pass
+                    self._rmv_existing()
             else:
                 try:
                     self._createDir()
             return 1
         return None
 
+    def do_duplicate(self, src):
+        self._createDir()
+        try:
+            Unlink(self, None, None)
+        except SCons.Errors.BuildError:
+            pass
+        try:
+            Link(self, src, None)
+        except SCons.Errors.BuildError, e:
+            desc = "Cannot duplicate `%s' in `%s': %s." % (src.path, self.dir.path, e.errstr)
+            raise SCons.Errors.StopError, desc
+        self.linked = 1
+        # The Link() action may or may not have actually
+        # created the file, depending on whether the -n
+        # option was used or not.  Delete the _exists and
+        # _rexists attributes so they can be reevaluated.
+        self.clear()
+
     def exists(self):
+        "__cacheable__"
         # Duplicate from source path if we are set up to do this.
         if self.duplicate and not self.is_derived() and not self.linked:
-            src=self.srcnode().rfile()
-            if src.exists() and src.abspath != self.abspath:
-                self._createDir()
-                try:
-                    Unlink(self, None, None)
-                except SCons.Errors.BuildError:
-                    pass
-                try:
-                    Link(self, src, None)
-                except SCons.Errors.BuildError, e:
-                    desc = "Cannot duplicate `%s' in `%s': %s." % (src.path, self.dir.path, e.errstr)
-                    raise SCons.Errors.StopError, desc
-                self.linked = 1
-                # The Link() action may or may not have actually
-                # created the file, depending on whether the -n
-                # option was used or not.  Delete the _exists and
-                # _rexists attributes so they can be reevaluated.
-                try:
-                    delattr(self, '_exists')
-                except AttributeError:
-                    pass
-                try:
-                    delattr(self, '_rexists')
-                except AttributeError:
-                    pass
+            src=self.srcnode()
+            if src is self:
+                return Base.exists(self)
+            src = src.rfile()
+            if src.abspath != self.abspath and src.exists():
+                self.do_duplicate(src)
         return Base.exists(self)
 
     def new_binfo(self):
 
         try:
             mtime = self.get_timestamp()
-        except:
+        except OSError:
             mtime = 0
             raise SCons.Errors.UserError, "no such %s" % self
 
 
         return csig
 
-    def current(self, calc=None, scan=1):
+    def current(self, calc=None):
         self.binfo = self.gen_binfo(calc)
+        return self._cur2()
+    def _cur2(self):
+        "__cacheable__"
         if self.always_build:
             return None
         if not self.exists():
                         LocalCopy(self, r, None)
                         self.store_info(self.binfo)
                     return 1
-            self._rfile = self
             return None
         else:
             old = self.get_stored_info()
             return (old == self.binfo)
 
     def rfile(self):
-        try:
-            return self._rfile
-        except AttributeError:
-            self._rfile = self
-            if not self.exists():
-                n = self.fs.Rsearch(self.path, clazz=File,
-                                    cwd=self.fs.Top)
-                if n:
-                    self._rfile = n
-            return self._rfile
+        "__cacheable__"
+        rfile = self
+        if not self.exists():
+            n = self.fs.Rsearch(self.path, clazz=File,
+                                cwd=self.fs.Top)
+            if n:
+                rfile = n
+        return rfile
 
     def rstr(self):
         return str(self.rfile())
 
 default_fs = FS()
 
-def find_file(filename, paths, node_factory=default_fs.File, verbose=None):
+def find_file(filename, paths, verbose=None):
     """
     find_file(str, [Dir()]) -> [nodes]
 
     filename - a filename to find
-    paths - a list of directory path *nodes* to search in
+    paths - a list of directory path *nodes* to search in.  Can be
+            represented as a list, a tuple, or a callable that is
+            called with no arguments and returns the list or tuple.
 
     returns - the node created from the found file.
 
 
     Only the first file found is returned, and none is returned
     if no file is found.
+    __cacheable__
     """
-    if verbose and not SCons.Util.is_String(verbose):
-        verbose = "find_file"
-    retval = None
+    if verbose:
+        if not SCons.Util.is_String(verbose):
+            verbose = "find_file"
+        if not callable(verbose):
+            verbose = '  %s: ' % verbose
+            verbose = lambda s, v=verbose: sys.stdout.write(v + s)
+    else:
+        verbose = lambda x: x
+
+    if callable(paths):
+        paths = paths()
+
+    # Give Entries a chance to morph into Dirs.
+    paths = map(lambda p: p.must_be_a_Dir(), paths)
+
+    filedir, filename = os.path.split(filename)
+    if filedir:
+        def filedir_lookup(p, fd=filedir):
+            try:
+                return p.Dir(fd)
+            except TypeError:
+                # We tried to look up a Dir, but it seems there's already
+                # a File (or something else) there.  No big.
+                return None
+        paths = filter(None, map(filedir_lookup, paths))
+
     for dir in paths:
-        if verbose:
-            sys.stdout.write("  %s: looking for '%s' in '%s' ...\n" % (verbose, filename, dir))
-        try:
-            node = node_factory(filename, dir)
-            # Return true if the node exists or is a derived node.
-            if node.is_derived() or \
-               node.is_pseudo_derived() or \
-               (isinstance(node, SCons.Node.FS.Base) and node.exists()):
-                retval = node
-                if verbose:
-                    sys.stdout.write("  %s: ... FOUND '%s' in '%s'\n" % (verbose, filename, dir))
-                break
-        except TypeError:
-            # If we find a directory instead of a file, we don't care
-            pass
+        verbose("looking for '%s' in '%s' ...\n" % (filename, dir))
+        node, d = dir.srcdir_find_file(filename)
+        if node:
+            verbose("... FOUND '%s' in '%s'\n" % (filename, d))
+            return node
+    return None
 
-    return retval
-
-def find_files(filenames, paths, node_factory = default_fs.File):
+def find_files(filenames, paths):
     """
     find_files([str], [Dir()]) -> [nodes]
 
     Only the first file found is returned for each filename,
     and any files that aren't found are ignored.
     """
-    nodes = map(lambda x, paths=paths, node_factory=node_factory:
-                       find_file(x, paths, node_factory),
-                filenames)
-    return filter(lambda x: x != None, nodes)
+    nodes = map(lambda x, paths=paths: find_file(x, paths), filenames)
+    return filter(None, nodes)

src/engine/SCons/Node/FSTests.py

         try:
             dir_made = []
             d9.builder = Builder(fs.Dir, action=MkdirAction(dir_made))
-            d9.reset_executor()
             f9.exists()
             expect = os.path.join('build', 'var2', 'new_dir')
             assert dir_made[0].path == expect, dir_made[0].path
         assert (stat.S_IMODE(st[stat.ST_MODE]) & stat.S_IWRITE), \
                stat.S_IMODE(st[stat.ST_MODE])
 
-        # This used to generate a UserError when we forbid the source
-        # directory from being outside the top-level SConstruct dir.
-        fs = SCons.Node.FS.FS()
-        fs.BuildDir('build', '/test/foo')
+        exc_caught = 0
+        try:
+            fs = SCons.Node.FS.FS()
+            fs.BuildDir('build', '/test/foo')
+        except SCons.Errors.UserError:
+            exc_caught = 1
+        assert exc_caught, "Should have caught a UserError."
 
         exc_caught = 0
         try:
 
         self.failIf(errors)
 
-class BaseTestCase(_tempdirTestCase):
-    def test_stat(self):
-        """Test the Base.stat() method"""
-        test = self.test
-        test.write("e1", "e1\n")
-        fs = SCons.Node.FS.FS()
-
-        e1 = fs.Entry('e1')
-        s = e1.stat()
-        assert not s is None, s
-
-        e2 = fs.Entry('e2')
-        s = e2.stat()
-        assert s is None, s
-
-    def test_getmtime(self):
-        """Test the Base.getmtime() method"""
-        test = self.test
-        test.write("file", "file\n")
-        fs = SCons.Node.FS.FS()
-
-        file = fs.Entry('file')
-        assert file.getmtime()
-
-        file = fs.Entry('nonexistent')
-        mtime = file.getmtime()
-        assert mtime is None, mtime
-
-    def test_getsize(self):
-        """Test the Base.getsize() method"""
-        test = self.test
-        test.write("file", "file\n")
-        fs = SCons.Node.FS.FS()
-
-        file = fs.Entry('file')
-        size = file.getsize()
-        assert size == 5, size
-
-        file = fs.Entry('nonexistent')
-        size = file.getsize()
-        assert size is None, size
-
-    def test_isdir(self):
-        """Test the Base.isdir() method"""
-        test = self.test
-        test.subdir('dir')
-        test.write("file", "file\n")
-        fs = SCons.Node.FS.FS()
-
-        dir = fs.Entry('dir')
-        assert dir.isdir()
-
-        file = fs.Entry('file')
-        assert not file.isdir()
-
-        nonexistent = fs.Entry('nonexistent')
-        assert not nonexistent.isdir()
-
-    def test_isfile(self):
-        """Test the Base.isfile() method"""
-        test = self.test
-        test.subdir('dir')
-        test.write("file", "file\n")
-        fs = SCons.Node.FS.FS()
-
-        dir = fs.Entry('dir')
-        assert not dir.isfile()
-
-        file = fs.Entry('file')
-        assert file.isfile()
-
-        nonexistent = fs.Entry('nonexistent')
-        assert not nonexistent.isfile()
-
-    if hasattr(os, 'symlink'):
-        def test_islink(self):
-            """Test the Base.islink() method"""
-            test = self.test
-            test.subdir('dir')
-            test.write("file", "file\n")
-            test.symlink("symlink", "symlink")
-            fs = SCons.Node.FS.FS()
-
-            dir = fs.Entry('dir')
-            assert not dir.islink()
-
-            file = fs.Entry('file')
-            assert not file.islink()
-
-            symlink = fs.Entry('symlink')
-            assert symlink.islink()
-
-            nonexistent = fs.Entry('nonexistent')
-            assert not nonexistent.islink()
-
-class NodeInfoTestCase(_tempdirTestCase):
-    def test___init__(self):
-        """Test NodeInfo initialization"""
-        ni = SCons.Node.FS.NodeInfo()
-        assert not hasattr(ni, 'bsig')
-
-    def test___cmp__(self):
-        """Test comparing NodeInfo objects"""
-        ni1 = SCons.Node.FS.NodeInfo()
-        ni2 = SCons.Node.FS.NodeInfo()
-
-        msg = "cmp(%s, %s) returned %s, not %s"
-
-        c = cmp(ni1, ni2)
-        assert c == 1, msg % (ni1, ni2, c, 1)
-
-        ni1.bsig = 777
-        c = cmp(ni1, ni2)
-        assert c == 1, msg % (ni1.bsig, ni2, c, 1)
-
-        ni2.bsig = 666
-        c = cmp(ni1, ni2)
-        assert c == 1, msg % (ni1.bsig, ni2.bsig, c, 1)
-
-        ni2.bsig = 777
-        c = cmp(ni1, ni2)
-        assert c == 0, msg % (ni1.bsig, ni2.bsig, c, 0)
-
-        ni2.bsig = 888
-        c = cmp(ni1, ni2)
-        assert c == -1, msg % (ni1.bsig, ni2.bsig, c, -1)
-
-    def test_update(self):
-        """Test updating a NodeInfo with on-disk information"""
-        test = self.test
-        test.write('fff', "fff\n")
-        fff = self.fs.File('fff')
-
-        ni = SCons.Node.FS.NodeInfo()
-        assert not hasattr(ni, 'timestamp')
-        assert not hasattr(ni, 'size')
-
-        ni.update(fff)
-        assert ni.timestamp == os.path.getmtime('fff'), ni.timestamp
-        assert ni.size == os.path.getsize('fff'), ni.size
-
-class BuildInfoTestCase(_tempdirTestCase):
-    def test___init__(self):
-        """Test BuildInfo initialization"""
-        fff = self.fs.File('fff')
-        bi = SCons.Node.FS.BuildInfo(fff)
-        assert bi.node is fff, bi.node
-
-    def test_convert_to_sconsign(self):
-        """Test converting to .sconsign file format"""
-
-    def test_convert_from_sconsign(self):
-        """Test converting from .sconsign file format"""
-
-class FSTestCase(_tempdirTestCase):
-    def test_runTest(self):
+class FSTestCase(unittest.TestCase):
+    def runTest(self):
         """Test FS (file system) Node operations
 
         This test case handles all of the file system node
         tests in one environment, so we don't have to set up a
         complicated directory structure for each test individually.
         """
-        test = self.test
-
+        test = TestCmd(workdir = '')
         test.subdir('sub', ['sub', 'dir'])
 
         wp = test.workpath('')
         assert not built_it
         d1.add_source([SCons.Node.Node()])    # XXX FAKE SUBCLASS ATTRIBUTE
         d1.builder_set(Builder(fs.File))
-        d1.reset_executor()
         d1.env_set(Environment())
         d1.build()
         assert built_it
         assert not built_it
         f1.add_source([SCons.Node.Node()])    # XXX FAKE SUBCLASS ATTRIBUTE
         f1.builder_set(Builder(fs.File))
-        f1.reset_executor()
         f1.env_set(Environment())
         f1.build()
         assert built_it
         # the reading of files in text mode.  This tests that
         # get_contents() returns the binary contents.
         test.write("binary_file", "Foo\x1aBar")
-        f1 = fs.File(test.workpath("binary_file"))
+        f1 = SCons.Node.FS.default_fs.File(test.workpath("binary_file"))
         assert f1.get_contents() == "Foo\x1aBar", f1.get_contents()
 
         def nonexistent(method, s):
         t = z.target_from_source('pre-', '-suf', lambda x: x[:-1])
         assert str(t) == 'pre-z-suf', str(t)
 
-    def test_same_name(self):
-        """Test that a local same-named file isn't found for # Dir lookup"""
-        test = self.test
-        fs = self.fs
-
-        test.subdir('subdir')
-        test.write(['subdir', 'build'], "subdir/build\n")
-
-        subdir = fs.Dir('subdir')
-        fs.chdir(subdir, change_os_dir=1)
-        path, dir = fs._transformPath('#build/file', subdir)
-        self.fs._doLookup(SCons.Node.FS.File, path, dir)
-
-    def test_above_root(self):
-        """Testing looking up a path above the root directory"""
-        test = self.test
-        fs = self.fs
-
-        d1 = fs.Dir('d1')
-        d2 = d1.Dir('d2')
-        dirs = string.split(os.path.normpath(d2.abspath), os.sep)
-        above_path = apply(os.path.join, ['..']*len(dirs) + ['above'])
-        above = d2.Dir(above_path)
-
-    def test_rel_path(self):
-        """Test the rel_path() method"""
-        test = self.test
-        fs = self.fs
-
-        d1 = fs.Dir('d1')
-        d1_f = d1.File('f')
-        d1_d2 = d1.Dir('d2')
-        d1_d2_f = d1_d2.File('f')
-
-        d3 = fs.Dir('d3')
-        d3_f = d3.File('f')
-        d3_d4 = d3.Dir('d4')
-        d3_d4_f = d3_d4.File('f')
-
-        cases = [
-                d1,             d1,             '.',
-                d1,             d1_f,           'f',
-                d1,             d1_d2,          'd2',
-                d1,             d1_d2_f,        'd2/f',
-                d1,             d3,             '../d3',
-                d1,             d3_f,           '../d3/f',
-                d1,             d3_d4,          '../d3/d4',
-                d1,             d3_d4_f,        '../d3/d4/f',
-
-                d1_f,           d1,             '.',
-                d1_f,           d1_f,           'f',
-                d1_f,           d1_d2,          'd2',
-                d1_f,           d1_d2_f,        'd2/f',
-                d1_f,           d3,             '../d3',
-                d1_f,           d3_f,           '../d3/f',
-                d1_f,           d3_d4,          '../d3/d4',
-                d1_f,           d3_d4_f,        '../d3/d4/f',
-
-                d1_d2,          d1,             '..',
-                d1_d2,          d1_f,           '../f',
-                d1_d2,          d1_d2,          '.',
-                d1_d2,          d1_d2_f,        'f',
-                d1_d2,          d3,             '../../d3',
-                d1_d2,          d3_f,           '../../d3/f',
-                d1_d2,          d3_d4,          '../../d3/d4',
-                d1_d2,          d3_d4_f,        '../../d3/d4/f',
-
-                d1_d2_f,        d1,             '..',
-                d1_d2_f,        d1_f,           '../f',
-                d1_d2_f,        d1_d2,          '.',
-                d1_d2_f,        d1_d2_f,        'f',
-                d1_d2_f,        d3,             '../../d3',
-                d1_d2_f,        d3_f,           '../../d3/f',
-                d1_d2_f,        d3_d4,          '../../d3/d4',
-                d1_d2_f,        d3_d4_f,        '../../d3/d4/f',
-        ]
-
-        d1.rel_path(d3)
-
-        failed = 0
-        while cases:
-            dir, other, expect = cases[:3]
-            expect = os.path.normpath(expect)
-            del cases[:3]
-            result = dir.rel_path(other)
-            if result != expect:
-                if failed == 0: print
-                fmt = "    dir_path(%(dir)s, %(other)s) => '%(result)s' did not match '%(expect)s'"
-                print fmt % locals()
-                failed = failed + 1
-        assert failed == 0, "%d rel_path() cases failed" % failed
-
 class DirTestCase(_tempdirTestCase):
 
-    def test__morph(self):
-        """Test handling of actions when morphing an Entry into a Dir"""
-        test = self.test
-        e = self.fs.Entry('eee')
-        x = e.get_executor()
-        x.add_pre_action('pre')
-        x.add_post_action('post')
-        e.must_be_a_Dir()
-        a = x.get_action_list()
-        assert a[0] == 'pre', a
-        assert a[2] == 'post', a
-
     def test_entry_exists_on_disk(self):
         """Test the Dir.entry_exists_on_disk() method
         """
 
         test.subdir('d')
         test.write(['d', 'exists'], "d/exists\n")
-        test.write(['d', 'Case-Insensitive'], "d/Case-Insensitive\n")
 
         d = self.fs.Dir('d')
         assert d.entry_exists_on_disk('exists')
         assert not d.entry_exists_on_disk('does_not_exist')
 
-        if os.path.normcase("TeSt") != os.path.normpath("TeSt") or sys.platform == "cygwin":
-            assert d.entry_exists_on_disk('case-insensitive')
-
     def test_srcdir_list(self):
         """Test the Dir.srcdir_list() method
         """
         src0 = self.fs.Dir('src0')
         self.fs.BuildDir(bld0, src0, duplicate=0)
 
-        n = bld0.srcdir_duplicate('does_not_exist')
+        n = bld0.srcdir_duplicate('does_not_exist', SCons.Node.FS.File)
         assert n is None, n
         assert not os.path.exists(test.workpath('bld0', 'does_not_exist'))
 
-        n = bld0.srcdir_duplicate('exists')
+        n = bld0.srcdir_duplicate('exists', SCons.Node.FS.File)
         assert str(n) == os.path.normpath('src0/exists'), str(n)
         assert not os.path.exists(test.workpath('bld0', 'exists'))
 
         src1 = self.fs.Dir('src1')
         self.fs.BuildDir(bld1, src1, duplicate=1)
 
-        n = bld1.srcdir_duplicate('does_not_exist')
+        n = bld1.srcdir_duplicate('does_not_exist', SCons.Node.FS.File)
         assert n is None, n
         assert not os.path.exists(test.workpath('bld1', 'does_not_exist'))
 
-        n = bld1.srcdir_duplicate('exists')
+        n = bld1.srcdir_duplicate('exists', SCons.Node.FS.File)
         assert str(n) == os.path.normpath('bld1/exists'), str(n)
         assert os.path.exists(test.workpath('bld1', 'exists'))
 
         exists_e.exists = return_true
 
         def check(result, expect):
-            result = map(str, result)
+	    result = map(str, result)
             expect = map(os.path.normpath, expect)
             assert result == expect, result
 
         n = bld1.srcdir_find_file('on-disk-e2')
         check(n, ['bld1/on-disk-e2', 'bld1'])
 
-    def test_dir_on_disk(self):
-        """Test the Dir.dir_on_disk() method"""
-        self.test.subdir('sub', ['sub', 'exists'])
-        self.test.write(['sub', 'file'], "self/file\n")
-        sub = self.fs.Dir('sub')
-
-        r = sub.dir_on_disk('does_not_exist')
-        assert not r, r
-
-        r = sub.dir_on_disk('exists')
-        assert r, r
-
-        r = sub.dir_on_disk('file')
-        assert not r, r
-
-    def test_file_on_disk(self):
-        """Test the Dir.file_on_disk() method"""
-        self.test.subdir('sub', ['sub', 'dir'])
-        self.test.write(['sub', 'exists'], "self/exists\n")
-        sub = self.fs.Dir('sub')
-
-        r = sub.file_on_disk('does_not_exist')
-        assert not r, r
-
-        r = sub.file_on_disk('exists')
-        assert r, r
-
-        r = sub.file_on_disk('dir')
-        assert not r, r
-
-class EntryTestCase(_tempdirTestCase):
-    def test_runTest(self):
+class EntryTestCase(unittest.TestCase):
+    def runTest(self):
         """Test methods specific to the Entry sub-class.
         """
         test = TestCmd(workdir='')
         # Doesn't exist, no sources, and no builder: no sig
         assert sig is None, sig
 
-    def test_Entry_Entry_lookup(self):
-        """Test looking up an Entry within another Entry"""
-        self.fs.Entry('#topdir')
-        self.fs.Entry('#topdir/a/b/c')
-
-
-
-class FileTestCase(_tempdirTestCase):
-
-    def test_Dirs(self):
-        """Test the File.Dirs() method"""
-        fff = self.fs.File('subdir/fff')
-        # This simulates that the SConscript file that defined
-        # fff is in subdir/.
-        fff.cwd = self.fs.Dir('subdir')
-        d1 = self.fs.Dir('subdir/d1')
-        d2 = self.fs.Dir('subdir/d2')
-        dirs = fff.Dirs(['d1', 'd2'])
-        assert dirs == [d1, d2], map(str, dirs)
-
-    def test_exists(self):
-        """Test the File.exists() method"""
-        fs = self.fs
-        test = self.test
-
-        src_f1 = fs.File('src/f1')
-        assert not src_f1.exists(), "%s apparently exists?" % src_f1
-
-        test.subdir('src')
-        test.write(['src', 'f1'], "src/f1\n")
-
-        assert not src_f1.exists(), "%s did not cache previous exists() value" % src_f1
-        src_f1.clear()
-        assert src_f1.exists(), "%s apparently does not exist?" % src_f1
-
-        test.subdir('build')
-        fs.BuildDir('build', 'src')
-        build_f1 = fs.File('build/f1')
-
-        assert build_f1.exists(), "%s did not realize that %s exists" % (build_f1, src_f1)
-        assert os.path.exists(build_f1.abspath), "%s did not get duplicated on disk" % build_f1.abspath
-
-        test.unlink(['src', 'f1'])
-        src_f1.clear()  # so the next exists() call will look on disk again
-
-        assert build_f1.exists(), "%s did not cache previous exists() value" % build_f1
-        build_f1.clear()
-        build_f1.linked = None
-        assert not build_f1.exists(), "%s did not realize that %s disappeared" % (build_f1, src_f1)
-        assert not os.path.exists(build_f1.abspath), "%s did not get removed after %s was removed" % (build_f1, src_f1)
-
 
 
 class RepositoryTestCase(_tempdirTestCase):
         r = map(lambda x, np=os.path.normpath: np(str(x)), rep)
         assert r == expect, r
 
-    def test_get_all_rdirs(self):
-        """Test the Dir.get_all_rdirs() method"""
-        self.fs.Repository('foo')
-        self.fs.Repository(os.path.join('foo', 'bar'))
-        self.fs.Repository('bar/foo')
-        self.fs.Repository('bar')
-
-        expect = [
-            '.',
-            self.rep1,
-            self.rep2,
-            self.rep3,
-            'foo',
-            os.path.join('foo', 'bar'),
-            os.path.join('bar', 'foo'),
-            'bar'
-        ]
-
-        rep = self.fs.Dir('#').get_all_rdirs()
-        r = map(lambda x, np=os.path.normpath: np(str(x)), rep)
-        assert r == expect, r
-
     def test_rdir(self):
         """Test the Dir.rdir() method"""
         return_true = lambda: 1
         r = e2.rfile()
         assert r is re2, r
 
-    def test_Rfindalldirs(self):
-        """Test the Rfindalldirs() methods"""
+    def test_Rsearches(self):
+        """Test the Rsearch() methods"""
         fs = self.fs
         test = self.test
 
-        d1 = fs.Dir('d1')
-        d2 = fs.Dir('d2')
-        rep1_d1 = fs.Dir(test.workpath('rep1', 'd1'))
-        rep2_d1 = fs.Dir(test.workpath('rep2', 'd1'))
-        rep3_d1 = fs.Dir(test.workpath('rep3', 'd1'))
-        sub = fs.Dir('sub')
-        sub_d1 = sub.Dir('d1')
-        rep1_sub_d1 = fs.Dir(test.workpath('rep1', 'sub', 'd1'))
-        rep2_sub_d1 = fs.Dir(test.workpath('rep2', 'sub', 'd1'))
-        rep3_sub_d1 = fs.Dir(test.workpath('rep3', 'sub', 'd1'))
-
-        r = fs.Rfindalldirs(d1, fs.Top)
-        assert r == [d1], map(str, r)
-
-        r = fs.Rfindalldirs([d1, d2], fs.Top)
-        assert r == [d1, d2], map(str, r)
-
-        r = fs.Rfindalldirs('d1', fs.Top)
-        assert r == [d1, rep1_d1, rep2_d1, rep3_d1], map(str, r)
-
-        r = fs.Rfindalldirs('#d1', fs.Top)
-        assert r == [d1, rep1_d1, rep2_d1, rep3_d1], map(str, r)
-
-        r = fs.Rfindalldirs('d1', sub)
-        assert r == [sub_d1, rep1_sub_d1, rep2_sub_d1, rep3_sub_d1], map(str, r)
-
-        r = fs.Rfindalldirs('#d1', sub)
-        assert r == [d1, rep1_d1, rep2_d1, rep3_d1], map(str, r)
-
-        r = fs.Rfindalldirs(['d1', d2], fs.Top)
-        assert r == [d1, rep1_d1, rep2_d1, rep3_d1, d2], map(str, r)
+        test.write([self.rep1, 'f2'], "")
+        test.subdir([self.rep2, 'f3'])
+        test.write([self.rep3, 'f3'], "")
+
+        r = fs.Rsearch('f1')
+        assert r is None, r
+
+        r = fs.Rsearch('f2')
+        assert r, r
+
+        f3 = fs.File('f3')
+        r = fs.Rsearch(f3)
+        assert r is f3, r
+
+    def test_Rsearchall(self):
+        """Test the Rsearchall() methods"""
+        fs = self.fs
+        test = self.test
+
+        list = fs.Rsearchall(fs.Dir('d1'))
+        assert len(list) == 1, list
+        assert list[0].path == 'd1', list[0].path
+
+        list = fs.Rsearchall([fs.Dir('d1')])
+        assert len(list) == 1, list
+        assert list[0].path == 'd1', list[0].path
+
+        list = fs.Rsearchall('d2')
+        assert list == [], list
+
+        list = fs.Rsearchall('#d2')
+        assert list == [], list
+
+        fs.File('d2').built() # Clear exists cache
+        test.subdir(['work', 'd2'])
+
+        list = fs.Rsearchall('d2')
+        assert map(str, list) == ['d2'], list
+
+        fs.File('../rep2/d2').built() # Clear exists cache
+        test.subdir(['rep2', 'd2'])
+
+        list = fs.Rsearchall('d2')
+        assert map(str, list) == ['d2', test.workpath('rep2', 'd2')], list
+
+        fs.File('../rep1/d2').built() # Clear exists cache
+        test.subdir(['rep1', 'd2'])
+
+        list = fs.Rsearchall('d2')
+        assert map(str, list) == ['d2',
+                                  test.workpath('rep1', 'd2'),
+                                  test.workpath('rep2', 'd2')], list
+
+        list = fs.Rsearchall(['d3', 'd4'])
+        assert list == [], list
+
+        fs.File('d3').built() # Clear exists cache
+        test.subdir(['work', 'd3'])
+
+        list = map(str, fs.Rsearchall(['d3', 'd4']))
+        assert list == ['d3'], list
+
+        fs.File('../rep3/d4').built() # Clear exists cache
+        test.subdir(['rep3', 'd4'])
+
+        list = map(str, fs.Rsearchall(['d3', 'd4']))
+        assert list == ['d3', test.workpath('rep3', 'd4')], list
+
+        list = map(str, fs.Rsearchall(string.join(['d3', 'd4'], os.pathsep)))
+        assert list == ['d3', test.workpath('rep3', 'd4')], list
+
+        work_d4 = fs.File(os.path.join('work', 'd4'))
+
+        list = map(str, fs.Rsearchall(['d3', work_d4]))
+        assert list == ['d3', str(work_d4)], list
+
+        list = fs.Rsearchall('')
+        assert list == [], list
+
+        list = fs.Rsearchall([None])
+        assert list == [], list
+
+        list = fs.Rsearchall([''])
+        assert list == [], list
 
     def test_rexists(self):
         """Test the Entry.rexists() method"""
         test = self.test
 
         test.write([self.rep1, 'f2'], "")
-        test.write([self.rep2, "i_exist"], "\n")
-        test.write(["work", "i_exist_too"], "\n")
 
         fs.BuildDir('build', '.')
 
         f = fs.File(test.workpath("work", "i_do_not_exist"))
         assert not f.rexists()
 
+        test.write([self.rep2, "i_exist"], "\n")
         f = fs.File(test.workpath("work", "i_exist"))
         assert f.rexists()
 
+        test.write(["work", "i_exist_too"], "\n")
         f = fs.File(test.workpath("work", "i_exist_too"))
         assert f.rexists()
 
 
 class stored_infoTestCase(unittest.TestCase):
     def runTest(self):
-        """Test how we store build information"""
+        """Test how storing build information"""
         test = TestCmd(workdir = '')
         test.subdir('sub')
         fs = SCons.Node.FS.FS(test.workpath(''))
         d = fs.Dir('sub')
         f = fs.File('file1', d)
         bi = f.get_stored_info()
-        assert bi.ninfo.timestamp == 0, bi.ninfo.timestamp
-        assert bi.ninfo.size == None, bi.ninfo.size
+        assert bi.bsig == None, bi.bsig
 
         class MySConsign:
             class Null:
         dir_made = []
         new_dir = fs.Dir("new_dir")
         new_dir.builder = Builder(fs.Dir, action=MkdirAction(dir_made))
-        new_dir.reset_executor()
         xyz = fs.File(os.path.join("new_dir", "xyz"))
 
         xyz.set_state(SCons.Node.up_to_date)
         try:
             f5 = fs.File("cd.f5")
             f5.binfo = f5.new_binfo()
-            f5.binfo.ninfo.bsig = 'a_fake_bsig'
+            f5.binfo.bsig = 'a_fake_bsig'
             cp = f5.cachepath()
             dirname = os.path.join('cache', 'A')
             filename = os.path.join(dirname, 'a_fake_bsig')
             test.write(cd_f7, "cd.f7\n")
             f7 = fs.File(cd_f7)
             f7.binfo = f7.new_binfo()
-            f7.binfo.ninfo.bsig = 'f7_bsig'
+            f7.binfo.bsig = 'f7_bsig'
 
             warn_caught = 0
             try:
 
 if __name__ == "__main__":
     suite = unittest.TestSuite()
+    suite.addTest(FSTestCase())
     suite.addTest(BuildDirTestCase())
+    suite.addTest(EntryTestCase())
     suite.addTest(find_fileTestCase())
     suite.addTest(StringDirTestCase())
     suite.addTest(stored_infoTestCase())
     suite.addTest(SpecialAttrTestCase())
     suite.addTest(SaveStringsTestCase())
     tclasses = [
-        BaseTestCase,
-        BuildInfoTestCase,
-        EntryTestCase,
-        FileTestCase,
-        NodeInfoTestCase,
-        FSTestCase,
         DirTestCase,
         RepositoryTestCase,
     ]