diff options
author | Luca Falavigna <dktrkranz@debian.org> | 2011-09-10 11:25:53 +0200 |
---|---|---|
committer | Luca Falavigna <dktrkranz@debian.org> | 2011-09-10 11:25:53 +0200 |
commit | ba4425ab5227fd9597fccd368bffff6bf1032149 (patch) | |
tree | b286bf4e65900f6a7604c001dc2ad94fec12e768 /src/engine/SCons/Node | |
parent | 84c6f9729dbbc175431874957d0654310410bd6f (diff) |
Imported Upstream version 2.1.0upstream/2.1.0
Diffstat (limited to 'src/engine/SCons/Node')
-rw-r--r-- | src/engine/SCons/Node/Alias.py | 4 | ||||
-rw-r--r-- | src/engine/SCons/Node/AliasTests.py | 4 | ||||
-rw-r--r-- | src/engine/SCons/Node/FS.py | 402 | ||||
-rw-r--r-- | src/engine/SCons/Node/FSTests.py | 299 | ||||
-rw-r--r-- | src/engine/SCons/Node/NodeTests.py | 4 | ||||
-rw-r--r-- | src/engine/SCons/Node/Python.py | 4 | ||||
-rw-r--r-- | src/engine/SCons/Node/PythonTests.py | 4 | ||||
-rw-r--r-- | src/engine/SCons/Node/__init__.py | 11 |
8 files changed, 578 insertions, 154 deletions
diff --git a/src/engine/SCons/Node/Alias.py b/src/engine/SCons/Node/Alias.py index c0f3ece..9996e64 100644 --- a/src/engine/SCons/Node/Alias.py +++ b/src/engine/SCons/Node/Alias.py @@ -8,7 +8,7 @@ This creates a hash of global Aliases (dummy targets). """ # -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 The SCons Foundation +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -30,7 +30,7 @@ This creates a hash of global Aliases (dummy targets). # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Node/Alias.py 5134 2010/08/16 23:02:40 bdeegan" +__revision__ = "src/engine/SCons/Node/Alias.py 5357 2011/09/09 21:31:03 bdeegan" import collections diff --git a/src/engine/SCons/Node/AliasTests.py b/src/engine/SCons/Node/AliasTests.py index 6551ea4..49debc5 100644 --- a/src/engine/SCons/Node/AliasTests.py +++ b/src/engine/SCons/Node/AliasTests.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 The SCons Foundation +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -21,7 +21,7 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Node/AliasTests.py 5134 2010/08/16 23:02:40 bdeegan" +__revision__ = "src/engine/SCons/Node/AliasTests.py 5357 2011/09/09 21:31:03 bdeegan" import sys import unittest diff --git a/src/engine/SCons/Node/FS.py b/src/engine/SCons/Node/FS.py index 0997fc0..9c1b05a 100644 --- a/src/engine/SCons/Node/FS.py +++ b/src/engine/SCons/Node/FS.py @@ -11,7 +11,7 @@ that can be used by scripts or modules looking for the canonical default. """ # -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 The SCons Foundation +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -32,7 +32,7 @@ that can be used by scripts or modules looking for the canonical default. # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -__revision__ = "src/engine/SCons/Node/FS.py 5134 2010/08/16 23:02:40 bdeegan" +__revision__ = "src/engine/SCons/Node/FS.py 5357 2011/09/09 21:31:03 bdeegan" import fnmatch import os @@ -56,6 +56,7 @@ import SCons.Warnings from SCons.Debug import Trace do_store_info = True +print_duplicate = 0 class EntryProxyAttributeError(AttributeError): @@ -110,33 +111,85 @@ def save_strings(val): # do_splitdrive = None +_my_splitdrive =None def initialize_do_splitdrive(): global do_splitdrive + global has_unc drive, path = os.path.splitdrive('X:/foo') - do_splitdrive = not not drive + has_unc = hasattr(os.path, 'splitunc') + + do_splitdrive = not not drive or has_unc + + global _my_splitdrive + if has_unc: + def splitdrive(p): + if p[1:2] == ':': + return p[:2], p[2:] + if p[0:2] == '//': + # Note that we leave a leading slash in the path + # because UNC paths are always absolute. + return '//', p[1:] + return '', p + else: + def splitdrive(p): + if p[1:2] == ':': + return p[:2], p[2:] + return '', p + _my_splitdrive = splitdrive + + # Keep some commonly used values in global variables to skip to + # module look-up costs. + global OS_SEP + global UNC_PREFIX + global os_sep_is_slash + + OS_SEP = os.sep + UNC_PREFIX = OS_SEP + OS_SEP + os_sep_is_slash = OS_SEP == '/' initialize_do_splitdrive() -# - -needs_normpath_check = None - -def initialize_normpath_check(): - """ - Initialize the normpath_check regular expression. - - This function is used by the unit tests to re-initialize the pattern - when testing for behavior with different values of os.sep. - """ - global needs_normpath_check - if os.sep == '/': - pattern = r'.*/|\.$|\.\.$' - else: - pattern = r'.*[/%s]|\.$|\.\.$' % re.escape(os.sep) - needs_normpath_check = re.compile(pattern) - -initialize_normpath_check() +# Used to avoid invoking os.path.normpath if not necessary. +needs_normpath_check = re.compile( + r''' + # We need to renormalize the path if it contains any consecutive + # '/' characters. + .*// | + + # We need to renormalize the path if it contains a '..' directory. + # Note that we check for all the following cases: + # + # a) The path is a single '..' + # b) The path starts with '..'. E.g. '../' or '../moredirs' + # but we not match '..abc/'. + # c) The path ends with '..'. E.g. '/..' or 'dirs/..' + # d) The path contains a '..' in the middle. + # E.g. dirs/../moredirs + + (.*/)?\.\.(?:/|$) | + + # We need to renormalize the path if it contains a '.' + # directory, but NOT if it is a single '.' '/' characters. We + # do not want to match a single '.' because this case is checked + # for explicitely since this is common enough case. + # + # Note that we check for all the following cases: + # + # a) We don't match a single '.' + # b) We match if the path starts with '.'. E.g. './' or + # './moredirs' but we not match '.abc/'. + # c) We match if the path ends with '.'. E.g. '/.' or + # 'dirs/.' + # d) We match if the path contains a '.' in the middle. + # E.g. dirs/./moredirs + + \./|.*/\.(?:/|$) + + ''', + re.VERBOSE + ) +needs_normpath_match = needs_normpath_check.match # # SCons.Action objects for interacting with the outside world. @@ -438,21 +491,21 @@ class EntryProxy(SCons.Util.Proxy): def __get_posix_path(self): """Return the path with / as the path separator, regardless of platform.""" - if os.sep == '/': + if os_sep_is_slash: return self else: entry = self.get() - r = entry.get_path().replace(os.sep, '/') + r = entry.get_path().replace(OS_SEP, '/') return SCons.Subst.SpecialAttrWrapper(r, entry.name + "_posix") def __get_windows_path(self): """Return the path with \ as the path separator, regardless of platform.""" - if os.sep == '\\': + if OS_SEP == '\\': return self else: entry = self.get() - r = entry.get_path().replace(os.sep, '\\') + r = entry.get_path().replace(OS_SEP, '\\') return SCons.Subst.SpecialAttrWrapper(r, entry.name + "_windows") def __get_srcnode(self): @@ -533,9 +586,13 @@ class Base(SCons.Node.Node): # Filenames and paths are probably reused and are intern'ed to # save some memory. + + #: Filename with extension as it was specified when the object was + #: created; to obtain filesystem path, use Python str() function self.name = SCons.Util.silent_intern(name) + #: Cached filename extension self.suffix = SCons.Util.silent_intern(SCons.Util.splitext(name)[1]) - self.fs = fs + self.fs = fs #: Reference to parent Node.FS object assert directory, "A directory must be provided" @@ -606,7 +663,7 @@ class Base(SCons.Node.Node): else: result = srcnode.get_path() if not Save_Strings: - # We're not at the point where we're saving the string string + # We're not at the point where we're saving the string # representations of FS Nodes (because we haven't finished # reading the SConscript files and need to have str() return # things relative to them). That also means we can't yet @@ -695,11 +752,15 @@ class Base(SCons.Node.Node): if self == dir: return '.' path_elems = self.path_elements + pathname = '' try: i = path_elems.index(dir) - except ValueError: pass - else: path_elems = path_elems[i+1:] - path_elems = [n.name for n in path_elems] - return os.sep.join(path_elems) + except ValueError: + for p in path_elems[:-1]: + pathname += p.dirname + else: + for p in path_elems[i+1:-1]: + pathname += p.dirname + return pathname + path_elems[-1].name def set_src_builder(self, builder): """Set the source code builder for this node.""" @@ -1063,7 +1124,7 @@ class FS(LocalFS): self.pathTop = os.getcwd() else: self.pathTop = path - self.defaultDrive = _my_normcase(os.path.splitdrive(self.pathTop)[0]) + self.defaultDrive = _my_normcase(_my_splitdrive(self.pathTop)[0]) self.Top = self.Dir(self.pathTop) self.Top.path = '.' @@ -1083,7 +1144,10 @@ class FS(LocalFS): self.max_drift = max_drift def getcwd(self): - return self._cwd + if hasattr(self, "_cwd"): + return self._cwd + else: + return "<no cwd>" def chdir(self, dir, change_os_dir=0): """Change the current working directory for lookups. @@ -1147,54 +1211,110 @@ class FS(LocalFS): # str(p) in case it's something like a proxy object p = str(p) - initial_hash = (p[0:1] == '#') - if initial_hash: + if not os_sep_is_slash: + p = p.replace(OS_SEP, '/') + + if p[0:1] == '#': # There was an initial '#', so we strip it and override # whatever directory they may have specified with the # top-level SConstruct directory. p = p[1:] directory = self.Top - if directory and not isinstance(directory, Dir): - directory = self.Dir(directory) + # There might be a drive letter following the + # '#'. Although it is not described in the SCons man page, + # the regression test suite explicitly tests for that + # syntax. It seems to mean the following thing: + # + # Assuming the the SCons top dir is in C:/xxx/yyy, + # '#X:/toto' means X:/xxx/yyy/toto. + # + # i.e. it assumes that the X: drive has a directory + # structure similar to the one found on drive C:. + if do_splitdrive: + drive, p = _my_splitdrive(p) + if drive: + root = self.get_root(drive) + else: + root = directory.root + else: + root = directory.root - if do_splitdrive: - drive, p = os.path.splitdrive(p) - else: - drive = '' - if drive and not p: - # This causes a naked drive letter to be treated as a synonym - # for the root directory on that drive. - p = os.sep - absolute = os.path.isabs(p) - - needs_normpath = needs_normpath_check.match(p) - - if initial_hash or not absolute: - # This is a relative lookup, either to the top-level - # SConstruct directory (because of the initial '#') or to - # the current directory (the path name is not absolute). - # Add the string to the appropriate directory lookup path, - # after which the whole thing gets normalized. - if not directory: - directory = self._cwd - if p: + # We can only strip trailing after splitting the drive + # since the drive might the UNC '//' prefix. + p = p.strip('/') + + needs_normpath = needs_normpath_match(p) + + # The path is relative to the top-level SCons directory. + if p in ('', '.'): + p = directory.labspath + else: p = directory.labspath + '/' + p + else: + if do_splitdrive: + drive, p = _my_splitdrive(p) + if drive and not p: + # This causes a naked drive letter to be treated + # as a synonym for the root directory on that + # drive. + p = '/' else: - p = directory.labspath + drive = '' - if needs_normpath: - p = os.path.normpath(p) + # We can only strip trailing '/' since the drive might the + # UNC '//' prefix. + if p != '/': + p = p.rstrip('/') - if drive or absolute: - root = self.get_root(drive) - else: - if not directory: - directory = self._cwd - root = directory.root + needs_normpath = needs_normpath_match(p) + + if p[0:1] == '/': + # Absolute path + root = self.get_root(drive) + else: + # This is a relative lookup or to the current directory + # (the path name is not absolute). Add the string to the + # appropriate directory lookup path, after which the whole + # thing gets normalized. + if directory: + if not isinstance(directory, Dir): + directory = self.Dir(directory) + else: + directory = self._cwd + + if p in ('', '.'): + p = directory.labspath + else: + p = directory.labspath + '/' + p + + if drive: + root = self.get_root(drive) + else: + root = directory.root + + if needs_normpath is not None: + # Normalize a pathname. Will return the same result for + # equivalent paths. + # + # We take advantage of the fact that we have an absolute + # path here for sure. In addition, we know that the + # components of lookup path are separated by slashes at + # this point. Because of this, this code is about 2X + # faster than calling os.path.normpath() followed by + # replacing os.sep with '/' again. + ins = p.split('/')[1:] + outs = [] + for d in ins: + if d == '..': + try: + outs.pop() + except IndexError: + pass + elif d not in ('', '.'): + outs.append(d) + p = '/' + '/'.join(outs) - if os.sep != '/': - p = p.replace(os.sep, '/') return root._lookup_abs(p, fsclass, create) def Entry(self, name, directory = None, create = 1): @@ -1300,7 +1420,7 @@ class DirNodeInfo(SCons.Node.NodeInfoBase): top = self.fs.Top root = top.root if do_splitdrive: - drive, s = os.path.splitdrive(s) + drive, s = _my_splitdrive(s) if drive: root = self.fs.get_root(drive) if not os.path.isabs(s): @@ -1350,11 +1470,34 @@ class Dir(Base): self.variant_dirs = [] self.root = self.dir.root + # For directories, we make a difference between the directory + # 'name' and the directory 'dirname'. The 'name' attribute is + # used when we need to print the 'name' of the directory or + # when we it is used as the last part of a path. The 'dirname' + # is used when the directory is not the last element of the + # path. The main reason for making that distinction is that + # for RoorDir's the dirname can not be easily inferred from + # the name. For example, we have to add a '/' after a drive + # letter but not after a UNC path prefix ('//'). + self.dirname = self.name + OS_SEP + # Don't just reset the executor, replace its action list, # because it might have some pre-or post-actions that need to # be preserved. - self.builder = get_MkdirBuilder() - self.get_executor().set_action_list(self.builder.action) + # + # But don't reset the executor if there is a non-null executor + # attached already. The existing executor might have other + # targets, in which case replacing the action list with a + # Mkdir action is a big mistake. + if not hasattr(self, 'executor'): + self.builder = get_MkdirBuilder() + self.get_executor().set_action_list(self.builder.action) + else: + # Prepend MkdirBuilder action to existing action list + l = self.get_executor().action_list + a = get_MkdirBuilder().action + l.insert(0, a) + self.get_executor().set_action_list(l) def diskcheck_match(self): diskcheck_match(self, self.isfile, @@ -1403,23 +1546,6 @@ class Dir(Base): """ return self.fs.File(name, self) - def _lookup_rel(self, name, klass, create=1): - """ - Looks up a *normalized* relative path name, relative to this - directory. - - This method is intended for use by internal lookups with - already-normalized path data. For general-purpose lookups, - use the Entry(), Dir() and File() methods above. - - This method does *no* input checking and will die or give - incorrect results if it's passed a non-normalized path name (e.g., - a path containing '..'), an absolute path name, a top-relative - ('#foo') path name, or any kind of object. - """ - name = self.entry_labspath(name) - return self.root._lookup_abs(name, klass, create) - def link(self, srcdir, duplicate): """Set this directory as the variant directory for the supplied source directory.""" @@ -1452,7 +1578,7 @@ class Dir(Base): if fname == '.': fname = dir.name else: - fname = dir.name + os.sep + fname + fname = dir.name + OS_SEP + fname dir = dir.up() self._memo['get_all_rdirs'] = list(result) @@ -1466,7 +1592,7 @@ class Dir(Base): self.__clearRepositoryCache() def up(self): - return self.entries['..'] + return self.dir def _rel_path_key(self, other): return str(other) @@ -1514,14 +1640,14 @@ class Dir(Base): if dir_rel_path == '.': result = other.name else: - result = dir_rel_path + os.sep + other.name + result = dir_rel_path + OS_SEP + other.name else: i = self.path_elements.index(other) + 1 path_elems = ['..'] * (len(self.path_elements) - i) \ + [n.name for n in other.path_elements[i:]] - result = os.sep.join(path_elems) + result = OS_SEP.join(path_elems) memo_dict[other] = result @@ -1691,16 +1817,16 @@ class Dir(Base): return stamp def entry_abspath(self, name): - return self.abspath + os.sep + name + return self.abspath + OS_SEP + name def entry_labspath(self, name): return self.labspath + '/' + name def entry_path(self, name): - return self.path + os.sep + name + return self.path + OS_SEP + name def entry_tpath(self, name): - return self.tpath + os.sep + name + return self.tpath + OS_SEP + name def entry_exists_on_disk(self, name): try: @@ -1721,7 +1847,7 @@ class Dir(Base): if result is None: # Belt-and-suspenders for Windows: check directly for # 8.3 file names that don't show up in os.listdir(). - result = os.path.exists(self.abspath + os.sep + name) + result = os.path.exists(self.abspath + OS_SEP + name) d[name] = result return result else: @@ -1742,7 +1868,7 @@ class Dir(Base): while dir: if dir.srcdir: result.append(dir.srcdir.Dir(dirname)) - dirname = dir.name + os.sep + dirname + dirname = dir.name + OS_SEP + dirname dir = dir.up() self._memo['srcdir_list'] = result @@ -1986,7 +2112,7 @@ class RootDir(Dir): add a separator when creating the path names of entries within this directory. """ - def __init__(self, name, fs): + def __init__(self, drive, fs): if __debug__: logInstanceCreation(self, 'Node.FS.RootDir') # We're going to be our own parent directory (".." entry and .dir # attribute) so we have to set up some values so Base.__init__() @@ -1998,29 +2124,47 @@ class RootDir(Dir): self.path_elements = [] self.duplicate = 0 self.root = self + + # Handle all the types of drives: + if drive == '': + # No drive, regular UNIX root or Windows default drive. + name = OS_SEP + dirname = OS_SEP + elif drive == '//': + # UNC path + name = UNC_PREFIX + dirname = UNC_PREFIX + else: + # Windows drive letter + name = drive + dirname = drive + OS_SEP + Base.__init__(self, name, self, fs) - # Now set our paths to what we really want them to be: the - # initial drive letter (the name) plus the directory separator, - # except for the "lookup abspath," which does not have the - # drive letter. - self.abspath = name + os.sep + # Now set our paths to what we really want them to be. The + # name should already contain any necessary separators, such + # as the initial drive letter (the name) plus the directory + # separator, except for the "lookup abspath," which does not + # have the drive letter. + self.abspath = dirname self.labspath = '' - self.path = name + os.sep - self.tpath = name + os.sep + self.path = dirname + self.tpath = dirname self._morph() + # Must be reset after Dir._morph() is invoked... + self.dirname = dirname + self._lookupDict = {} - # The // and os.sep + os.sep entries are necessary because - # os.path.normpath() seems to preserve double slashes at the - # beginning of a path (presumably for UNC path names), but - # collapses triple slashes to a single slash. self._lookupDict[''] = self self._lookupDict['/'] = self - self._lookupDict['//'] = self - self._lookupDict[os.sep] = self - self._lookupDict[os.sep + os.sep] = self + + # The // entry is necessary because os.path.normpath() + # preserves double slashes at the beginning of a path on Posix + # platforms. + if not has_unc: + self._lookupDict['//'] = self def must_be_same(self, klass): if klass is Dir: @@ -2039,7 +2183,7 @@ class RootDir(Dir): normalized absolute path; we merely let Python's dictionary look up and return the One True Node.FS object for the path. - If no Node for the specified "p" doesn't already exist, and + If a Node for the specified "p" doesn't already exist, and "create" is specified, the Node may be created after recursive invocation to find or create the parent directory or directories. """ @@ -2052,7 +2196,17 @@ class RootDir(Dir): raise SCons.Errors.UserError(msg) # There is no Node for this path name, and we're allowed # to create it. - dir_name, file_name = os.path.split(p) + # (note: would like to use p.rsplit('/',1) here but + # that's not in python 2.3) + # e.g.: dir_name, file_name = p.rsplit('/',1) + last_slash = p.rindex('/') + if (last_slash >= 0): + dir_name = p[:last_slash] + file_name = p[last_slash+1:] + else: + dir_name = p # shouldn't happen, just in case + file_name = '' + dir_node = self._lookup_abs(dir_name, Dir) result = klass(file_name, dir_node, self.fs) @@ -2111,7 +2265,7 @@ class FileNodeInfo(SCons.Node.NodeInfoBase): top = self.fs.Top root = top.root if do_splitdrive: - drive, s = os.path.splitdrive(s) + drive, s = _my_splitdrive(s) if drive: root = self.fs.get_root(drive) if not os.path.isabs(s): @@ -2129,7 +2283,7 @@ class FileBuildInfo(SCons.Node.BuildInfoBase): usual string representation: relative to the top-level SConstruct directory, or an absolute path if it's outside. """ - if os.sep == '/': + if os_sep_is_slash: node_to_str = str else: def node_to_str(n): @@ -2138,7 +2292,7 @@ class FileBuildInfo(SCons.Node.BuildInfoBase): except AttributeError: s = str(n) else: - s = s.replace(os.sep, '/') + s = s.replace(OS_SEP, '/') return s for attr in ['bsources', 'bdepends', 'bimplicit']: try: @@ -2638,6 +2792,8 @@ class File(Base): def _rmv_existing(self): self.clear_memoized_values() + if print_duplicate: + print "dup: removing existing target %s"%self e = Unlink(self, [], None) if isinstance(e, SCons.Errors.BuildError): raise e @@ -2678,6 +2834,8 @@ class File(Base): def do_duplicate(self, src): self._createDir() + if print_duplicate: + print "dup: relinking variant '%s' from '%s'"%(self, src) Unlink(self, None, None) e = Link(self, src, None) if isinstance(e, SCons.Errors.BuildError): @@ -2711,6 +2869,8 @@ class File(Base): else: # The source file does not exist. Make sure no old # copy remains in the variant directory. + if print_duplicate: + print "dup: no src for %s, unlinking old variant copy"%self if Base.exists(self) or self.islink(): self.fs.unlink(self.path) # Return None explicitly because the Base.exists() call @@ -2982,8 +3142,8 @@ class FileFinder(object): if fd is None: fd = self.default_filedir dir, name = os.path.split(fd) - drive, d = os.path.splitdrive(dir) - if not name and d[:1] in ('/', os.sep): + drive, d = _my_splitdrive(dir) + if not name and d[:1] in ('/', OS_SEP): #return p.fs.get_root(drive).dir_on_disk(name) return p.fs.get_root(drive) if dir: diff --git a/src/engine/SCons/Node/FSTests.py b/src/engine/SCons/Node/FSTests.py index 63b50ac..e471497 100644 --- a/src/engine/SCons/Node/FSTests.py +++ b/src/engine/SCons/Node/FSTests.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 The SCons Foundation +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -22,7 +22,7 @@ # from __future__ import division -__revision__ = "src/engine/SCons/Node/FSTests.py 5134 2010/08/16 23:02:40 bdeegan" +__revision__ = "src/engine/SCons/Node/FSTests.py 5357 2011/09/09 21:31:03 bdeegan" import SCons.compat @@ -817,6 +817,107 @@ class FileBuildInfoTestCase(_tempdirTestCase): assert format == expect, (repr(expect), repr(format)) class FSTestCase(_tempdirTestCase): + def test_needs_normpath(self): + """Test the needs_normpath Regular expression + + This test case verifies that the regular expression used to + determine whether a path needs normalization works as + expected. + """ + needs_normpath_match = SCons.Node.FS.needs_normpath_match + + do_not_need_normpath = [ + ".", + "/", + "/a", + "/aa", + "/a/", + "/aa/", + "/a/b", + "/aa/bb", + "/a/b/", + "/aa/bb/", + + "", + "a", + "aa", + "a/", + "aa/", + "a/b", + "aa/bb", + "a/b/", + "aa/bb/", + + "a.", + "a..", + "/a.", + "/a..", + "a./", + "a../", + "/a./", + "/a../", + + + ".a", + "..a", + "/.a", + "/..a", + ".a/", + "..a/", + "/.a/", + "/..a/", + ] + for p in do_not_need_normpath: + assert needs_normpath_match(p) is None, p + + needs_normpath = [ + "//", + "//a", + "//aa", + "//a/", + "//a/", + "/aa//", + + "//a/b", + "//aa/bb", + "//a/b/", + "//aa/bb/", + + "/a//b", + "/aa//bb", + "/a/b//", + "/aa/bb//", + + "/a/b//", + "/aa/bb//", + + "a//", + "aa//", + "a//b", + "aa//bb", + "a//b/", + "aa//bb/", + "a/b//", + "aa/bb//", + + "..", + "/.", + "/..", + "./", + "../", + "/./", + "/../", + + "a/.", + "a/..", + "./a", + "../a", + "a/./a", + "a/../a", + ] + for p in needs_normpath: + assert needs_normpath_match(p) is not None, p + def test_runTest(self): """Test FS (file system) Node operations @@ -932,8 +1033,20 @@ class FSTestCase(_tempdirTestCase): path = strip_slash(path_) abspath = strip_slash(abspath_) up_path = strip_slash(up_path_) + name = abspath.split(os.sep)[-1] + if not name: + if drive: + name = drive + else: + name = os.sep + + if dir.up() is None: + dir_up_path = dir.path + else: + dir_up_path = dir.up().path + assert dir.name == name, \ "dir.name %s != expected name %s" % \ (dir.name, name) @@ -946,15 +1059,16 @@ class FSTestCase(_tempdirTestCase): assert dir.get_abspath() == abspath, \ "dir.abspath %s != expected absolute path %s" % \ (dir.get_abspath(), abspath) - assert dir.up().path == up_path, \ + assert dir_up_path == up_path, \ "dir.up().path %s != expected parent path %s" % \ - (dir.up().path, up_path) + (dir_up_path, up_path) for sep in seps: def Dir_test(lpath, path_, abspath_, up_path_, sep=sep, func=_do_Dir_test): return func(lpath, path_, abspath_, up_path_, sep) - + + Dir_test('/', '/', '/', '/') Dir_test('', './', sub_dir, sub) Dir_test('foo', 'foo/', sub_dir_foo, './') Dir_test('foo/bar', 'foo/bar/', sub_dir_foo_bar, 'foo/') @@ -1423,12 +1537,12 @@ class FSTestCase(_tempdirTestCase): test.subdir('sub', ['sub', 'dir']) - def drive_workpath(drive, dirs, test=test): + def drive_workpath(dirs, test=test): x = test.workpath(*dirs) drive, path = os.path.splitdrive(x) return 'X:' + path - wp = drive_workpath('X:', ['']) + wp = drive_workpath(['']) if wp[-1] in (os.sep, '/'): tmp = os.path.split(wp[:-1])[0] @@ -1441,13 +1555,13 @@ class FSTestCase(_tempdirTestCase): tmp_foo = os.path.join(tmp, 'foo') - foo = drive_workpath('X:', ['foo']) - foo_bar = drive_workpath('X:', ['foo', 'bar']) - sub = drive_workpath('X:', ['sub', '']) - sub_dir = drive_workpath('X:', ['sub', 'dir', '']) - sub_dir_foo = drive_workpath('X:', ['sub', 'dir', 'foo', '']) - sub_dir_foo_bar = drive_workpath('X:', ['sub', 'dir', 'foo', 'bar', '']) - sub_foo = drive_workpath('X:', ['sub', 'foo', '']) + foo = drive_workpath(['foo']) + foo_bar = drive_workpath(['foo', 'bar']) + sub = drive_workpath(['sub', '']) + sub_dir = drive_workpath(['sub', 'dir', '']) + sub_dir_foo = drive_workpath(['sub', 'dir', 'foo', '']) + sub_dir_foo_bar = drive_workpath(['sub', 'dir', 'foo', 'bar', '']) + sub_foo = drive_workpath(['sub', 'foo', '']) fs = SCons.Node.FS.FS() @@ -1490,7 +1604,6 @@ class FSTestCase(_tempdirTestCase): os.path = ntpath os.sep = '\\' SCons.Node.FS.initialize_do_splitdrive() - SCons.Node.FS.initialize_normpath_check() for sep in seps: @@ -1517,7 +1630,117 @@ class FSTestCase(_tempdirTestCase): os.path = save_os_path os.sep = save_os_sep SCons.Node.FS.initialize_do_splitdrive() - SCons.Node.FS.initialize_normpath_check() + + def test_unc_path(self): + """Test UNC path look-ups""" + + test = self.test + + test.subdir('sub', ['sub', 'dir']) + + def strip_slash(p): + if p[-1] == os.sep and len(p) > 3: + p = p[:-1] + return p + + def unc_workpath(dirs, test=test): + import ntpath + x = apply(test.workpath, dirs) + drive, path = ntpath.splitdrive(x) + unc, path = ntpath.splitunc(path) + path = strip_slash(path) + return '//' + path[1:] + + wp = unc_workpath(['']) + + if wp[-1] in (os.sep, '/'): + tmp = os.path.split(wp[:-1])[0] + else: + tmp = os.path.split(wp)[0] + + parent_tmp = os.path.split(tmp)[0] + + tmp_foo = os.path.join(tmp, 'foo') + + foo = unc_workpath(['foo']) + foo_bar = unc_workpath(['foo', 'bar']) + sub = unc_workpath(['sub', '']) + sub_dir = unc_workpath(['sub', 'dir', '']) + sub_dir_foo = unc_workpath(['sub', 'dir', 'foo', '']) + sub_dir_foo_bar = unc_workpath(['sub', 'dir', 'foo', 'bar', '']) + sub_foo = unc_workpath(['sub', 'foo', '']) + + fs = SCons.Node.FS.FS() + + seps = [os.sep] + if os.sep != '/': + seps = seps + ['/'] + + def _do_Dir_test(lpath, path, up_path, sep, fileSys=fs): + dir = fileSys.Dir(lpath.replace('/', sep)) + + if os.sep != '/': + path = path.replace('/', os.sep) + up_path = up_path.replace('/', os.sep) + + if path == os.sep + os.sep: + name = os.sep + os.sep + else: + name = path.split(os.sep)[-1] + + if dir.up() is None: + dir_up_path = dir.path + else: + dir_up_path = dir.up().path + + assert dir.name == name, \ + "dir.name %s != expected name %s" % \ + (dir.name, name) + assert dir.path == path, \ + "dir.path %s != expected path %s" % \ + (dir.path, path) + assert str(dir) == path, \ + "str(dir) %s != expected path %s" % \ + (str(dir), path) + assert dir_up_path == up_path, \ + "dir.up().path %s != expected parent path %s" % \ + (dir.up().path, up_path) + + save_os_path = os.path + save_os_sep = os.sep + try: + import ntpath + os.path = ntpath + os.sep = '\\' + SCons.Node.FS.initialize_do_splitdrive() + + for sep in seps: + + def Dir_test(lpath, path_, up_path_, sep=sep, func=_do_Dir_test): + return func(lpath, path_, up_path_, sep) + + Dir_test('//foo', '//foo', '//') + Dir_test('//foo/bar', '//foo/bar', '//foo') + Dir_test('//', '//', '//') + Dir_test('//..', '//', '//') + Dir_test('//foo/..', '//', '//') + Dir_test('//../foo', '//foo', '//') + Dir_test('//.', '//', '//') + Dir_test('//./.', '//', '//') + Dir_test('//foo/./bar', '//foo/bar', '//foo') + Dir_test('//foo/../bar', '//bar', '//') + Dir_test('//foo/../../bar', '//bar', '//') + Dir_test('//foo/bar/../..', '//', '//') + Dir_test('#//', wp, tmp) + Dir_test('#//../foo', tmp_foo, tmp) + Dir_test('#//../foo', tmp_foo, tmp) + Dir_test('#//foo/bar', foo_bar, foo) + Dir_test('#//foo/bar', foo_bar, foo) + Dir_test('#//', wp, tmp) + finally: + os.path = save_os_path + os.sep = save_os_sep + SCons.Node.FS.initialize_do_splitdrive() def test_target_from_source(self): """Test the method for generating target nodes from sources""" @@ -1571,6 +1794,46 @@ class FSTestCase(_tempdirTestCase): above_path = os.path.join(*['..']*len(dirs) + ['above']) above = d2.Dir(above_path) + def test_lookup_abs(self): + """Exercise the _lookup_abs function""" + test = self.test + fs = self.fs + + root = fs.Dir('/') + d = root._lookup_abs('/tmp/foo-nonexistent/nonexistent-dir', SCons.Node.FS.Dir) + assert d.__class__ == SCons.Node.FS.Dir, str(d.__class__) + + def test_lookup_uncpath(self): + """Testing looking up a UNC path on Windows""" + if sys.platform not in ('win32',): + return + test = self.test + fs = self.fs + path='//servername/C$/foo' + f = self.fs._lookup('//servername/C$/foo', fs.Dir('#'), SCons.Node.FS.File) + # before the fix in this commit, this returned 'C:\servername\C$\foo' + # Should be a normalized Windows UNC path as below. + assert str(f) == r'\\servername\C$\foo', \ + 'UNC path %s got looked up as %s'%(path, f) + + def test_unc_drive_letter(self): + """Test drive-letter lookup for windows UNC-style directories""" + if sys.platform not in ('win32',): + return + share = self.fs.Dir(r'\\SERVER\SHARE\Directory') + assert str(share) == r'\\SERVER\SHARE\Directory', str(share) + + def test_UNC_dirs_2689(self): + """Test some UNC dirs that printed incorrectly and/or caused + infinite recursion errors prior to r5180 (SCons 2.1).""" + fs = self.fs + if sys.platform not in ('win32',): + return + p = fs.Dir(r"\\computername\sharename").abspath + assert p == r"\\computername\sharename", p + p = fs.Dir(r"\\\computername\sharename").abspath + assert p == r"\\computername\sharename", p + def test_rel_path(self): """Test the rel_path() method""" test = self.test @@ -1676,8 +1939,8 @@ class DirTestCase(_tempdirTestCase): x.add_post_action('post') e.must_be_same(SCons.Node.FS.Dir) a = x.get_action_list() - assert a[0] == 'pre', a - assert a[2] == 'post', a + assert 'pre' in a, a + assert 'post' in a, a def test_subclass(self): """Test looking up subclass of Dir nodes""" diff --git a/src/engine/SCons/Node/NodeTests.py b/src/engine/SCons/Node/NodeTests.py index 39e7fd8..416c134 100644 --- a/src/engine/SCons/Node/NodeTests.py +++ b/src/engine/SCons/Node/NodeTests.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 The SCons Foundation +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -20,7 +20,7 @@ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -__revision__ = "src/engine/SCons/Node/NodeTests.py 5134 2010/08/16 23:02:40 bdeegan" +__revision__ = "src/engine/SCons/Node/NodeTests.py 5357 2011/09/09 21:31:03 bdeegan" import SCons.compat diff --git a/src/engine/SCons/Node/Python.py b/src/engine/SCons/Node/Python.py index 3245005..b97f9d4 100644 --- a/src/engine/SCons/Node/Python.py +++ b/src/engine/SCons/Node/Python.py @@ -5,7 +5,7 @@ Python nodes. """ # -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 The SCons Foundation +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -27,7 +27,7 @@ Python nodes. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Node/Python.py 5134 2010/08/16 23:02:40 bdeegan" +__revision__ = "src/engine/SCons/Node/Python.py 5357 2011/09/09 21:31:03 bdeegan" import SCons.Node diff --git a/src/engine/SCons/Node/PythonTests.py b/src/engine/SCons/Node/PythonTests.py index 3e9e687..b91c2d3 100644 --- a/src/engine/SCons/Node/PythonTests.py +++ b/src/engine/SCons/Node/PythonTests.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 The SCons Foundation +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -21,7 +21,7 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Node/PythonTests.py 5134 2010/08/16 23:02:40 bdeegan" +__revision__ = "src/engine/SCons/Node/PythonTests.py 5357 2011/09/09 21:31:03 bdeegan" import sys import unittest diff --git a/src/engine/SCons/Node/__init__.py b/src/engine/SCons/Node/__init__.py index b737798..a832a5f 100644 --- a/src/engine/SCons/Node/__init__.py +++ b/src/engine/SCons/Node/__init__.py @@ -20,7 +20,7 @@ be able to depend on any other type of "thing." """ # -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 The SCons Foundation +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -41,7 +41,7 @@ be able to depend on any other type of "thing." # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -__revision__ = "src/engine/SCons/Node/__init__.py 5134 2010/08/16 23:02:40 bdeegan" +__revision__ = "src/engine/SCons/Node/__init__.py 5357 2011/09/09 21:31:03 bdeegan" import collections import copy @@ -628,9 +628,10 @@ class Node(object): if implicit_deps_unchanged or self.is_up_to_date(): return # one of this node's sources has changed, - # so we must recalculate the implicit deps: - self.implicit = [] - self.implicit_set = set() + # so we must recalculate the implicit deps for all targets + for tgt in executor.get_all_targets(): + tgt.implicit = [] + tgt.implicit_set = set() # Have the executor scan the sources. executor.scan_sources(self.builder.source_scanner) |