+ node = self.srcdir_duplicate(name)
+ if isinstance(node, Dir):
+ return None
+ return node
+
+ def walk(self, func, arg):
+ """
+ Walk this directory tree by calling the specified function
+ for each directory in the tree.
+
+ This behaves like the os.path.walk() function, but for in-memory
+ Node.FS.Dir objects. The function takes the same arguments as
+ the functions passed to os.path.walk():
+
+ func(arg, dirname, fnames)
+
+ Except that "dirname" will actually be the directory *Node*,
+ not the string. The '.' and '..' entries are excluded from
+ fnames. The fnames list may be modified in-place to filter the
+ subdirectories visited or otherwise impose a specific order.
+ The "arg" argument is always passed to func() and may be used
+ in any way (or ignored, passing None is common).
+ """
+ entries = self.entries
+ names = entries.keys()
+ names.remove('.')
+ names.remove('..')
+ func(arg, self, names)
+ for dirname in [n for n in names if isinstance(entries[n], Dir)]:
+ entries[dirname].walk(func, arg)
+
+ def glob(self, pathname, ondisk=True, source=False, strings=False):
+ """
+ Returns a list of Nodes (or strings) matching a specified
+ pathname pattern.
+
+ Pathname patterns follow UNIX shell semantics: * matches
+ any-length strings of any characters, ? matches any character,
+ and [] can enclose lists or ranges of characters. Matches do
+ not span directory separators.
+
+ The matches take into account Repositories, returning local
+ Nodes if a corresponding entry exists in a Repository (either
+ an in-memory Node or something on disk).
+
+ By defafult, the glob() function matches entries that exist
+ on-disk, in addition to in-memory Nodes. Setting the "ondisk"
+ argument to False (or some other non-true value) causes the glob()
+ function to only match in-memory Nodes. The default behavior is
+ to return both the on-disk and in-memory Nodes.
+
+ The "source" argument, when true, specifies that corresponding
+ source Nodes must be returned if you're globbing in a build
+ directory (initialized with VariantDir()). The default behavior
+ is to return Nodes local to the VariantDir().
+
+ The "strings" argument, when true, returns the matches as strings,
+ not Nodes. The strings are path names relative to this directory.
+
+ The underlying algorithm is adapted from the glob.glob() function
+ in the Python library (but heavily modified), and uses fnmatch()
+ under the covers.
+ """
+ dirname, basename = os.path.split(pathname)
+ if not dirname:
+ return sorted(self._glob1(basename, ondisk, source, strings),
+ key=lambda t: str(t))
+ if has_glob_magic(dirname):
+ list = self.glob(dirname, ondisk, source, strings=False)
+ else:
+ list = [self.Dir(dirname, create=True)]
+ result = []
+ for dir in list:
+ r = dir._glob1(basename, ondisk, source, strings)
+ if strings:
+ r = [os.path.join(str(dir), x) for x in r]
+ result.extend(r)
+ result.sort(lambda a, b: cmp(str(a), str(b)))
+ return result
+
+ def _glob1(self, pattern, ondisk=True, source=False, strings=False):
+ """
+ Globs for and returns a list of entry names matching a single
+ pattern in this directory.
+
+ This searches any repositories and source directories for
+ corresponding entries and returns a Node (or string) relative
+ to the current directory if an entry is found anywhere.
+
+ TODO: handle pattern with no wildcard
+ """
+ search_dir_list = self.get_all_rdirs()
+ for srcdir in self.srcdir_list():
+ search_dir_list.extend(srcdir.get_all_rdirs())
+
+ selfEntry = self.Entry
+ names = []
+ for dir in search_dir_list:
+ # We use the .name attribute from the Node because the keys of
+ # the dir.entries dictionary are normalized (that is, all upper
+ # case) on case-insensitive systems like Windows.
+ node_names = [ v.name for k, v in dir.entries.items()
+ if k not in ('.', '..') ]
+ names.extend(node_names)
+ if not strings:
+ # Make sure the working directory (self) actually has
+ # entries for all Nodes in repositories or variant dirs.
+ for name in node_names: selfEntry(name)
+ if ondisk:
+ try:
+ disk_names = os.listdir(dir.abspath)
+ except os.error:
+ continue
+ names.extend(disk_names)
+ if not strings:
+ # We're going to return corresponding Nodes in
+ # the local directory, so we need to make sure
+ # those Nodes exist. We only want to create
+ # Nodes for the entries that will match the
+ # specified pattern, though, which means we
+ # need to filter the list here, even though
+ # the overall list will also be filtered later,
+ # after we exit this loop.
+ if pattern[0] != '.':
+ #disk_names = [ d for d in disk_names if d[0] != '.' ]
+ disk_names = [x for x in disk_names if x[0] != '.']
+ disk_names = fnmatch.filter(disk_names, pattern)
+ dirEntry = dir.Entry
+ for name in disk_names:
+ # Add './' before disk filename so that '#' at
+ # beginning of filename isn't interpreted.
+ name = './' + name
+ node = dirEntry(name).disambiguate()
+ n = selfEntry(name)
+ if n.__class__ != node.__class__:
+ n.__class__ = node.__class__
+ n._morph()
+
+ names = set(names)
+ if pattern[0] != '.':
+ #names = [ n for n in names if n[0] != '.' ]
+ names = [x for x in names if x[0] != '.']
+ names = fnmatch.filter(names, pattern)
+
+ if strings:
+ return names
+
+ #return [ self.entries[_my_normcase(n)] for n in names ]
+ return [self.entries[_my_normcase(n)] for n in names]