summaryrefslogtreecommitdiff
path: root/src/engine/SCons/Node
diff options
context:
space:
mode:
authorLuca Falavigna <dktrkranz@debian.org>2011-09-10 11:25:53 +0200
committerLuca Falavigna <dktrkranz@debian.org>2011-09-10 11:25:53 +0200
commitba4425ab5227fd9597fccd368bffff6bf1032149 (patch)
treeb286bf4e65900f6a7604c001dc2ad94fec12e768 /src/engine/SCons/Node
parent84c6f9729dbbc175431874957d0654310410bd6f (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.py4
-rw-r--r--src/engine/SCons/Node/AliasTests.py4
-rw-r--r--src/engine/SCons/Node/FS.py402
-rw-r--r--src/engine/SCons/Node/FSTests.py299
-rw-r--r--src/engine/SCons/Node/NodeTests.py4
-rw-r--r--src/engine/SCons/Node/Python.py4
-rw-r--r--src/engine/SCons/Node/PythonTests.py4
-rw-r--r--src/engine/SCons/Node/__init__.py11
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)