summaryrefslogtreecommitdiffstats
path: root/src/engine/SCons/Node
diff options
context:
space:
mode:
authorRussel Winder <russel@winder.org.uk>2015-12-24 16:32:14 (GMT)
committerRussel Winder <russel@winder.org.uk>2015-12-24 16:32:14 (GMT)
commit2a270c8f314e959c78e9deda29c8f250bb934dd6 (patch)
tree7008e644357036404f0861c8ba1bbbf43b2b4123 /src/engine/SCons/Node
parenta7d764ed831fa3243aa0bd3307f641e1e1f9f8a8 (diff)
parent9d558dd65deacc958edc1f0da2dab1ec56ff4e4e (diff)
downloadSCons-2a270c8f314e959c78e9deda29c8f250bb934dd6.zip
SCons-2a270c8f314e959c78e9deda29c8f250bb934dd6.tar.gz
SCons-2a270c8f314e959c78e9deda29c8f250bb934dd6.tar.bz2
Post merge commit for safety. Building Fortran code works, but tests fail.
Diffstat (limited to 'src/engine/SCons/Node')
-rw-r--r--src/engine/SCons/Node/Alias.py49
-rw-r--r--src/engine/SCons/Node/AliasTests.py4
-rw-r--r--src/engine/SCons/Node/FS.py797
-rw-r--r--src/engine/SCons/Node/FSTests.py308
-rw-r--r--src/engine/SCons/Node/NodeTests.py152
-rw-r--r--src/engine/SCons/Node/Python.py40
-rw-r--r--src/engine/SCons/Node/PythonTests.py4
-rw-r--r--src/engine/SCons/Node/__init__.py534
8 files changed, 1267 insertions, 621 deletions
diff --git a/src/engine/SCons/Node/Alias.py b/src/engine/SCons/Node/Alias.py
index f817356..a035816 100644
--- a/src/engine/SCons/Node/Alias.py
+++ b/src/engine/SCons/Node/Alias.py
@@ -56,13 +56,47 @@ class AliasNameSpace(collections.UserDict):
return None
class AliasNodeInfo(SCons.Node.NodeInfoBase):
- current_version_id = 1
+ __slots__ = ('csig',)
+ current_version_id = 2
field_list = ['csig']
def str_to_node(self, s):
return default_ans.Alias(s)
+ def __getstate__(self):
+ """
+ Return all fields that shall be pickled. Walk the slots in the class
+ hierarchy and add those to the state dictionary. If a '__dict__' slot is
+ available, copy all entries to the dictionary. Also include the version
+ id, which is fixed for all instances of a class.
+ """
+ state = getattr(self, '__dict__', {}).copy()
+ for obj in type(self).mro():
+ for name in getattr(obj,'__slots__',()):
+ if hasattr(self, name):
+ state[name] = getattr(self, name)
+
+ state['_version_id'] = self.current_version_id
+ try:
+ del state['__weakref__']
+ except KeyError:
+ pass
+
+ return state
+
+ def __setstate__(self, state):
+ """
+ Restore the attributes from a pickled state.
+ """
+ # TODO check or discard version
+ del state['_version_id']
+ for key, value in state.items():
+ if key not in ('__weakref__',):
+ setattr(self, key, value)
+
+
class AliasBuildInfo(SCons.Node.BuildInfoBase):
- current_version_id = 1
+ __slots__ = ()
+ current_version_id = 2
class Alias(SCons.Node.Node):
@@ -72,7 +106,9 @@ class Alias(SCons.Node.Node):
def __init__(self, name):
SCons.Node.Node.__init__(self)
self.name = name
-
+ self.changed_since_last_build = 1
+ self.store_info = 0
+
def str_for_display(self):
return '"' + self.__str__() + '"'
@@ -105,13 +141,6 @@ class Alias(SCons.Node.Node):
#
#
- def changed_since_last_build(self, target, prev_ni):
- cur_csig = self.get_csig()
- try:
- return cur_csig != prev_ni.csig
- except AttributeError:
- return 1
-
def build(self):
"""A "builder" for aliases."""
pass
diff --git a/src/engine/SCons/Node/AliasTests.py b/src/engine/SCons/Node/AliasTests.py
index 2d11bdf..8e31875 100644
--- a/src/engine/SCons/Node/AliasTests.py
+++ b/src/engine/SCons/Node/AliasTests.py
@@ -103,14 +103,14 @@ class AliasNodeInfoTestCase(unittest.TestCase):
"""Test AliasNodeInfo initialization"""
ans = SCons.Node.Alias.AliasNameSpace()
aaa = ans.Alias('aaa')
- ni = SCons.Node.Alias.AliasNodeInfo(aaa)
+ ni = SCons.Node.Alias.AliasNodeInfo()
class AliasBuildInfoTestCase(unittest.TestCase):
def test___init__(self):
"""Test AliasBuildInfo initialization"""
ans = SCons.Node.Alias.AliasNameSpace()
aaa = ans.Alias('aaa')
- bi = SCons.Node.Alias.AliasBuildInfo(aaa)
+ bi = SCons.Node.Alias.AliasBuildInfo()
if __name__ == "__main__":
suite = unittest.TestSuite()
diff --git a/src/engine/SCons/Node/FS.py b/src/engine/SCons/Node/FS.py
index efca0c7..a4dd5d9 100644
--- a/src/engine/SCons/Node/FS.py
+++ b/src/engine/SCons/Node/FS.py
@@ -57,10 +57,23 @@ import SCons.Warnings
from SCons.Debug import Trace
-do_store_info = True
print_duplicate = 0
+def sconsign_none(node):
+ raise NotImplementedError
+
+def sconsign_dir(node):
+ """Return the .sconsign file info for this directory,
+ creating it first if necessary."""
+ if not node._sconsign:
+ import SCons.SConsign
+ node._sconsign = SCons.SConsign.ForDirectory(node)
+ return node._sconsign
+
+_sconsign_map = {0 : sconsign_none,
+ 1 : sconsign_dir}
+
class EntryProxyAttributeError(AttributeError):
"""
An AttributeError subclass for recording and displaying the name
@@ -110,7 +123,7 @@ def save_strings(val):
# tells us whether or not os.path.splitdrive() actually does anything
# on this system, and therefore whether we need to bother calling it
# when looking up path names in various methods below.
-#
+#
do_splitdrive = None
_my_splitdrive =None
@@ -145,7 +158,7 @@ def initialize_do_splitdrive():
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 == '/'
@@ -166,7 +179,7 @@ needs_normpath_check = re.compile(
# 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.
+ # d) The path contains a '..' in the middle.
# E.g. dirs/../moredirs
(.*/)?\.\.(?:/|$) |
@@ -174,7 +187,7 @@ needs_normpath_check = re.compile(
# 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.
+ # for explicitly since this is common enough case.
#
# Note that we check for all the following cases:
#
@@ -188,7 +201,7 @@ needs_normpath_check = re.compile(
\./|.*/\.(?:/|$)
- ''',
+ ''',
re.VERBOSE
)
needs_normpath_match = needs_normpath_check.match
@@ -269,8 +282,8 @@ def LinkFunc(target, source, env):
# who want to move their soft-linked src-trees around. Those
# people should use the 'hard-copy' mode, softlinks cannot be
# used for that; at least I have no idea how ...
- src = source[0].abspath
- dest = target[0].abspath
+ src = source[0].get_abspath()
+ dest = target[0].get_abspath()
dir, file = os.path.split(dest)
if dir and not target[0].fs.isdir(dir):
os.makedirs(dir)
@@ -303,7 +316,7 @@ LocalCopy = SCons.Action.Action(LinkFunc, LocalString)
def UnlinkFunc(target, source, env):
t = target[0]
- t.fs.unlink(t.abspath)
+ t.fs.unlink(t.get_abspath())
return 0
Unlink = SCons.Action.Action(UnlinkFunc, None)
@@ -311,7 +324,7 @@ Unlink = SCons.Action.Action(UnlinkFunc, None)
def MkdirFunc(target, source, env):
t = target[0]
if not t.exists():
- t.fs.mkdir(t.abspath)
+ t.fs.mkdir(t.get_abspath())
return 0
Mkdir = SCons.Action.Action(MkdirFunc, None, presub=None)
@@ -404,7 +417,7 @@ def do_diskcheck_match(node, predicate, errorfmt):
except (AttributeError, KeyError):
pass
if result:
- raise TypeError(errorfmt % node.abspath)
+ raise TypeError(errorfmt % node.get_abspath())
def ignore_diskcheck_match(node, predicate, errorfmt):
pass
@@ -574,7 +587,20 @@ class Base(SCons.Node.Node):
object identity comparisons.
"""
- memoizer_counters = []
+ __slots__ = ['name',
+ 'fs',
+ '_abspath',
+ '_labspath',
+ '_path',
+ '_tpath',
+ '_path_elements',
+ 'dir',
+ 'cwd',
+ 'duplicate',
+ '_local',
+ 'sbuilder',
+ '_proxy',
+ '_func_sconsign']
def __init__(self, name, directory, fs):
"""Initialize a generic Node.FS.Base object.
@@ -592,27 +618,26 @@ class Base(SCons.Node.Node):
#: 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 #: Reference to parent Node.FS object
assert directory, "A directory must be provided"
- self.abspath = SCons.Util.silent_intern(directory.entry_abspath(name))
- self.labspath = SCons.Util.silent_intern(directory.entry_labspath(name))
- if directory.path == '.':
- self.path = SCons.Util.silent_intern(name)
- else:
- self.path = SCons.Util.silent_intern(directory.entry_path(name))
- if directory.tpath == '.':
- self.tpath = SCons.Util.silent_intern(name)
- else:
- self.tpath = SCons.Util.silent_intern(directory.entry_tpath(name))
- self.path_elements = directory.path_elements + [self]
+ self._abspath = None
+ self._labspath = None
+ self._path = None
+ self._tpath = None
+ self._path_elements = None
self.dir = directory
self.cwd = None # will hold the SConscript directory for target nodes
self.duplicate = directory.duplicate
+ self.changed_since_last_build = 2
+ self._func_sconsign = 0
+ self._func_exists = 2
+ self._func_rexists = 2
+ self._func_get_contents = 0
+ self._func_target_from_source = 1
+ self.store_info = 1
def str_for_display(self):
return '"' + self.__str__() + '"'
@@ -625,17 +650,38 @@ class Base(SCons.Node.Node):
if isinstance(self, klass) or klass is Entry:
return
raise TypeError("Tried to lookup %s '%s' as a %s." %\
- (self.__class__.__name__, self.path, klass.__name__))
+ (self.__class__.__name__, self.get_internal_path(), klass.__name__))
def get_dir(self):
return self.dir
def get_suffix(self):
- return self.suffix
+ return SCons.Util.splitext(self.name)[1]
def rfile(self):
return self
+ def __getattr__(self, attr):
+ """ Together with the node_bwcomp dict defined below,
+ this method provides a simple backward compatibility
+ layer for the Node attributes 'abspath', 'labspath',
+ 'path', 'tpath', 'suffix' and 'path_elements'. These Node
+ attributes used to be directly available in v2.3 and earlier, but
+ have been replaced by getter methods that initialize the
+ single variables lazily when required, in order to save memory.
+ The redirection to the getters lets older Tools and
+ SConstruct continue to work without any additional changes,
+ fully transparent to the user.
+ Note, that __getattr__ is only called as fallback when the
+ requested attribute can't be found, so there should be no
+ speed performance penalty involved for standard builds.
+ """
+ if attr in node_bwcomp:
+ return node_bwcomp[attr](self)
+
+ raise AttributeError("%r object has no attribute %r" %
+ (self.__class__, attr))
+
def __str__(self):
"""A Node.FS.Base object's string representation is its path
name."""
@@ -644,8 +690,7 @@ class Base(SCons.Node.Node):
return self._save_str()
return self._get_str()
- memoizer_counters.append(SCons.Memoize.CountValue('_save_str'))
-
+ @SCons.Memoize.CountMethodCall
def _save_str(self):
try:
return self._memo['_save_str']
@@ -682,21 +727,20 @@ class Base(SCons.Node.Node):
rstr = __str__
- memoizer_counters.append(SCons.Memoize.CountValue('stat'))
-
+ @SCons.Memoize.CountMethodCall
def stat(self):
try: return self._memo['stat']
except KeyError: pass
- try: result = self.fs.stat(self.abspath)
+ try: result = self.fs.stat(self.get_abspath())
except os.error: result = None
self._memo['stat'] = result
return result
def exists(self):
- return self.stat() is not None
+ return SCons.Node._exists_map[self._func_exists](self)
def rexists(self):
- return self.rfile().exists()
+ return SCons.Node._rexists_map[self._func_rexists](self)
def getmtime(self):
st = self.stat()
@@ -718,7 +762,7 @@ class Base(SCons.Node.Node):
if hasattr(os, 'symlink'):
def islink(self):
- try: st = self.fs.lstat(self.abspath)
+ try: st = self.fs.lstat(self.get_abspath())
except os.error: return 0
return stat.S_ISLNK(st[stat.ST_MODE])
else:
@@ -753,10 +797,10 @@ class Base(SCons.Node.Node):
dir = self.fs.getcwd()
if self == dir:
return '.'
- path_elems = self.path_elements
+ path_elems = self.get_path_elements()
pathname = ''
try: i = path_elems.index(dir)
- except ValueError:
+ except ValueError:
for p in path_elems[:-1]:
pathname += p.dirname
else:
@@ -786,7 +830,26 @@ class Base(SCons.Node.Node):
def get_abspath(self):
"""Get the absolute path of the file."""
- return self.abspath
+ return self.dir.entry_abspath(self.name)
+
+ def get_labspath(self):
+ """Get the absolute path of the file."""
+ return self.dir.entry_labspath(self.name)
+
+ def get_internal_path(self):
+ if self.dir._path == '.':
+ return self.name
+ else:
+ return self.dir.entry_path(self.name)
+
+ def get_tpath(self):
+ if self.dir._tpath == '.':
+ return self.name
+ else:
+ return self.dir.entry_tpath(self.name)
+
+ def get_path_elements(self):
+ return self.dir._path_elements + [self]
def for_signature(self):
# Return just our name. Even an absolute path would not work,
@@ -812,13 +875,12 @@ class Base(SCons.Node.Node):
files that need different behavior. See Tool/swig.py for
an example.
"""
- return self.dir.Entry(prefix + splitext(self.name)[0] + suffix)
+ return SCons.Node._target_from_source_map[self._func_target_from_source](self, prefix, suffix, splitext)
def _Rfindalldirs_key(self, pathlist):
return pathlist
- memoizer_counters.append(SCons.Memoize.CountDict('Rfindalldirs', _Rfindalldirs_key))
-
+ @SCons.Memoize.CountDictCall(_Rfindalldirs_key)
def Rfindalldirs(self, pathlist):
"""
Return all of the directories for a given path list, including
@@ -857,8 +919,7 @@ class Base(SCons.Node.Node):
cwd = self.cwd or self.fs._cwd
return cwd.Rfindalldirs(pathlist)
- memoizer_counters.append(SCons.Memoize.CountValue('rentry'))
-
+ @SCons.Memoize.CountMethodCall
def rentry(self):
try:
return self._memo['rentry']
@@ -880,6 +941,17 @@ class Base(SCons.Node.Node):
def _glob1(self, pattern, ondisk=True, source=False, strings=False):
return []
+# Dict that provides a simple backward compatibility
+# layer for the Node attributes 'abspath', 'labspath',
+# 'path', 'tpath' and 'path_elements'.
+# @see Base.__getattr__ above
+node_bwcomp = {'abspath' : Base.get_abspath,
+ 'labspath' : Base.get_labspath,
+ 'path' : Base.get_internal_path,
+ 'tpath' : Base.get_tpath,
+ 'path_elements' : Base.get_path_elements,
+ 'suffix' : Base.get_suffix}
+
class Entry(Base):
"""This is the class for generic Node.FS entries--that is, things
that could be a File or a Dir, but we're just not sure yet.
@@ -888,6 +960,28 @@ class Entry(Base):
time comes, and then call the same-named method in the transformed
class."""
+ __slots__ = ['scanner_paths',
+ 'cachedir_csig',
+ 'cachesig',
+ 'repositories',
+ 'srcdir',
+ 'entries',
+ 'searched',
+ '_sconsign',
+ 'variant_dirs',
+ 'root',
+ 'dirname',
+ 'on_disk_entries',
+ 'sccs_dir',
+ 'rcs_dir',
+ 'released_target_info',
+ 'contentsig']
+
+ def __init__(self, name, directory, fs):
+ Base.__init__(self, name, directory, fs)
+ self._func_exists = 3
+ self._func_get_contents = 1
+
def diskcheck_match(self):
pass
@@ -918,7 +1012,7 @@ class Entry(Base):
self.__class__ = Dir
self._morph()
elif must_exist:
- msg = "No such file or directory: '%s'" % self.abspath
+ msg = "No such file or directory: '%s'" % self.get_abspath()
raise SCons.Errors.UserError(msg)
else:
self.__class__ = File
@@ -940,17 +1034,7 @@ class Entry(Base):
def get_contents(self):
"""Fetch the contents of the entry. Returns the exact binary
contents of the file."""
- try:
- self = self.disambiguate(must_exist=1)
- except SCons.Errors.UserError:
- # There was nothing on disk with which to disambiguate
- # this entry. Leave it as an Entry, but return a null
- # string so calls to get_contents() in emitters and the
- # like (e.g. in qt.py) don't have to disambiguate by hand
- # or catch the exception.
- return ''
- else:
- return self.get_contents()
+ return SCons.Node._get_contents_map[self._func_get_contents](self)
def get_text_contents(self):
"""Fetch the decoded text contents of a Unicode encoded Entry.
@@ -990,10 +1074,7 @@ class Entry(Base):
# to make various tests pass.
def exists(self):
- """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."""
- return self.disambiguate().exists()
+ return SCons.Node._exists_map[self._func_exists](self)
def rel_path(self, other):
d = self.disambiguate()
@@ -1004,9 +1085,6 @@ class Entry(Base):
def new_ninfo(self):
return self.disambiguate().new_ninfo()
- def changed_since_last_build(self, target, prev_ni):
- return self.disambiguate().changed_since_last_build(target, prev_ni)
-
def _glob1(self, pattern, ondisk=True, source=False, strings=False):
return self.disambiguate()._glob1(pattern, ondisk, source, strings)
@@ -1020,9 +1098,6 @@ _classEntry = Entry
class LocalFS(object):
- if SCons.Memoize.use_memoizer:
- __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
@@ -1091,19 +1166,8 @@ class LocalFS(object):
return ''
-#class RemoteFS:
-# # Skeleton for the obvious methods we might need from the
-# # abstraction layer for a remote filesystem.
-# def upload(self, local_src, remote_dst):
-# pass
-# def download(self, remote_src, local_dst):
-# pass
-
-
class FS(LocalFS):
- memoizer_counters = []
-
def __init__(self, path = None):
"""Initialize the Node.FS subsystem.
@@ -1129,13 +1193,13 @@ class FS(LocalFS):
self.defaultDrive = _my_normcase(_my_splitdrive(self.pathTop)[0])
self.Top = self.Dir(self.pathTop)
- self.Top.path = '.'
- self.Top.tpath = '.'
+ self.Top._path = '.'
+ self.Top._tpath = '.'
self._cwd = self.Top
DirNodeInfo.fs = self
FileNodeInfo.fs = self
-
+
def set_SConstruct_dir(self, dir):
self.SConstruct_dir = dir
@@ -1161,7 +1225,7 @@ class FS(LocalFS):
if dir is not None:
self._cwd = dir
if change_os_dir:
- os.chdir(dir.abspath)
+ os.chdir(dir.get_abspath())
except OSError:
self._cwd = curr
raise
@@ -1247,12 +1311,12 @@ class FS(LocalFS):
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
+ p = directory.get_labspath()
else:
- p = directory.labspath + '/' + p
+ p = directory.get_labspath() + '/' + p
else:
if do_splitdrive:
drive, p = _my_splitdrive(p)
@@ -1286,9 +1350,9 @@ class FS(LocalFS):
directory = self._cwd
if p in ('', '.'):
- p = directory.labspath
+ p = directory.get_labspath()
else:
- p = directory.labspath + '/' + p
+ p = directory.get_labspath() + '/' + p
if drive:
root = self.get_root(drive)
@@ -1394,7 +1458,7 @@ class FS(LocalFS):
if start_dir.is_under(bd):
# If already in the build-dir location, don't reflect
return [orig], fmt % str(orig)
- p = os.path.join(bd.path, *tail)
+ p = os.path.join(bd._path, *tail)
targets.append(self.Entry(p))
tail = [dir.name] + tail
dir = dir.up()
@@ -1402,19 +1466,20 @@ class FS(LocalFS):
message = fmt % ' '.join(map(str, targets))
return targets, message
- def Glob(self, pathname, ondisk=True, source=True, strings=False, cwd=None):
+ def Glob(self, pathname, ondisk=True, source=True, strings=False, exclude=None, cwd=None):
"""
Globs
- This is mainly a shim layer
+ This is mainly a shim layer
"""
if cwd is None:
cwd = self.getcwd()
- return cwd.glob(pathname, ondisk, source, strings)
+ return cwd.glob(pathname, ondisk, source, strings, exclude)
class DirNodeInfo(SCons.Node.NodeInfoBase):
+ __slots__ = ()
# This should get reset by the FS initialization.
- current_version_id = 1
+ current_version_id = 2
fs = None
@@ -1426,11 +1491,12 @@ class DirNodeInfo(SCons.Node.NodeInfoBase):
if drive:
root = self.fs.get_root(drive)
if not os.path.isabs(s):
- s = top.labspath + '/' + s
+ s = top.get_labspath() + '/' + s
return root._lookup_abs(s, Entry)
class DirBuildInfo(SCons.Node.BuildInfoBase):
- current_version_id = 1
+ __slots__ = ()
+ current_version_id = 2
glob_magic_check = re.compile('[*?[]')
@@ -1441,7 +1507,22 @@ class Dir(Base):
"""A class for directories in a file system.
"""
- memoizer_counters = []
+ __slots__ = ['scanner_paths',
+ 'cachedir_csig',
+ 'cachesig',
+ 'repositories',
+ 'srcdir',
+ 'entries',
+ 'searched',
+ '_sconsign',
+ 'variant_dirs',
+ 'root',
+ 'dirname',
+ 'on_disk_entries',
+ 'sccs_dir',
+ 'rcs_dir',
+ 'released_target_info',
+ 'contentsig']
NodeInfo = DirNodeInfo
BuildInfo = DirBuildInfo
@@ -1471,6 +1552,22 @@ class Dir(Base):
self._sconsign = None
self.variant_dirs = []
self.root = self.dir.root
+ self.changed_since_last_build = 3
+ self._func_sconsign = 1
+ self._func_exists = 2
+ self._func_get_contents = 2
+
+ self._abspath = SCons.Util.silent_intern(self.dir.entry_abspath(self.name))
+ self._labspath = SCons.Util.silent_intern(self.dir.entry_labspath(self.name))
+ if self.dir._path == '.':
+ self._path = SCons.Util.silent_intern(self.name)
+ else:
+ self._path = SCons.Util.silent_intern(self.dir.entry_path(self.name))
+ if self.dir._tpath == '.':
+ self._tpath = SCons.Util.silent_intern(self.name)
+ else:
+ self._tpath = SCons.Util.silent_intern(self.dir.entry_tpath(self.name))
+ self._path_elements = self.dir._path_elements + [self]
# For directories, we make a difference between the directory
# 'name' and the directory 'dirname'. The 'name' attribute is
@@ -1498,7 +1595,7 @@ class Dir(Base):
# Prepend MkdirBuilder action to existing action list
l = self.get_executor().action_list
a = get_MkdirBuilder().action
- l.insert(0, a)
+ l.insert(0, a)
self.get_executor().set_action_list(l)
def diskcheck_match(self):
@@ -1563,8 +1660,7 @@ class Dir(Base):
return self.srcdir.get_all_rdirs() + self.repositories
return self.repositories
- memoizer_counters.append(SCons.Memoize.CountValue('get_all_rdirs'))
-
+ @SCons.Memoize.CountMethodCall
def get_all_rdirs(self):
try:
return list(self._memo['get_all_rdirs'])
@@ -1590,7 +1686,7 @@ class Dir(Base):
def addRepository(self, dir):
if dir != self and not dir in self.repositories:
self.repositories.append(dir)
- dir.tpath = '.'
+ dir._tpath = '.'
self.__clearRepositoryCache()
def up(self):
@@ -1599,8 +1695,7 @@ class Dir(Base):
def _rel_path_key(self, other):
return str(other)
- memoizer_counters.append(SCons.Memoize.CountDict('rel_path', _rel_path_key))
-
+ @SCons.Memoize.CountDictCall(_rel_path_key)
def rel_path(self, other):
"""Return a path to "other" relative to this directory.
"""
@@ -1629,7 +1724,7 @@ class Dir(Base):
if self is other:
result = '.'
- elif not other in self.path_elements:
+ elif not other in self._path_elements:
try:
other_dir = other.get_dir()
except AttributeError:
@@ -1644,11 +1739,11 @@ class Dir(Base):
else:
result = dir_rel_path + OS_SEP + other.name
else:
- i = self.path_elements.index(other) + 1
+ i = self._path_elements.index(other) + 1
+
+ path_elems = ['..'] * (len(self._path_elements) - i) \
+ + [n.name for n in other._path_elements[i:]]
- path_elems = ['..'] * (len(self.path_elements) - i) \
- + [n.name for n in other.path_elements[i:]]
-
result = OS_SEP.join(path_elems)
memo_dict[other] = result
@@ -1714,7 +1809,7 @@ class Dir(Base):
if p is None:
# Don't use while: - else: for this condition because
# if so, then parent is None and has no .path attribute.
- raise SCons.Errors.StopError(parent.path)
+ raise SCons.Errors.StopError(parent._path)
parent = p
listDirs.reverse()
for dirnode in listDirs:
@@ -1754,10 +1849,7 @@ class Dir(Base):
def get_contents(self):
"""Return content signatures and names of all our children
separated by new-lines. Ensure that the nodes are sorted."""
- contents = []
- for node in sorted(self.children(), key=lambda t: t.name):
- contents.append('%s %s\n' % (node.get_csig(), node.name))
- return ''.join(contents)
+ return SCons.Node._get_contents_map[self._func_get_contents](self)
def get_csig(self):
"""Compute the content signature for Directory nodes. In
@@ -1771,8 +1863,6 @@ class Dir(Base):
def do_duplicate(self, src):
pass
- changed_since_last_build = SCons.Node.Node.state_has_changed
-
def is_up_to_date(self):
"""If any child is not up-to-date, then this directory isn't,
either."""
@@ -1796,12 +1886,8 @@ class Dir(Base):
return self
def sconsign(self):
- """Return the .sconsign file info for this directory,
- creating it first if necessary."""
- if not self._sconsign:
- import SCons.SConsign
- self._sconsign = SCons.SConsign.ForDirectory(self)
- return self._sconsign
+ """Return the .sconsign file info for this directory. """
+ return _sconsign_map[self._func_sconsign](self)
def srcnode(self):
"""Dir has a special need for srcnode()...if we
@@ -1818,25 +1904,48 @@ class Dir(Base):
stamp = kid.get_timestamp()
return stamp
+ def get_abspath(self):
+ """Get the absolute path of the file."""
+ return self._abspath
+
+ def get_labspath(self):
+ """Get the absolute path of the file."""
+ return self._labspath
+
+ def get_internal_path(self):
+ return self._path
+
+ def get_tpath(self):
+ return self._tpath
+
+ def get_path_elements(self):
+ return self._path_elements
+
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
+ 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):
+ """ Searches through the file/dir entries of the current
+ directory, and returns True if a physical entry with the given
+ name could be found.
+
+ @see rentry_exists_on_disk
+ """
try:
d = self.on_disk_entries
except AttributeError:
d = {}
try:
- entries = os.listdir(self.abspath)
+ entries = os.listdir(self._abspath)
except OSError:
pass
else:
@@ -1849,14 +1958,40 @@ 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:
return name in d
- memoizer_counters.append(SCons.Memoize.CountValue('srcdir_list'))
+ def rentry_exists_on_disk(self, name):
+ """ Searches through the file/dir entries of the current
+ *and* all its remote directories (repos), and returns
+ True if a physical entry with the given name could be found.
+ The local directory (self) gets searched first, so
+ repositories take a lower precedence regarding the
+ searching order.
+
+ @see entry_exists_on_disk
+ """
+ rentry_exists = self.entry_exists_on_disk(name)
+ if not rentry_exists:
+ # Search through the repository folders
+ norm_name = _my_normcase(name)
+ for rdir in self.get_all_rdirs():
+ try:
+ node = rdir.entries[norm_name]
+ if node:
+ rentry_exists = True
+ break
+ except KeyError:
+ if rdir.entry_exists_on_disk(name):
+ rentry_exists = True
+ break
+ return rentry_exists
+
+ @SCons.Memoize.CountMethodCall
def srcdir_list(self):
try:
return self._memo['srcdir_list']
@@ -1897,8 +2032,7 @@ class Dir(Base):
def _srcdir_find_file_key(self, filename):
return filename
- memoizer_counters.append(SCons.Memoize.CountDict('srcdir_find_file', _srcdir_find_file_key))
-
+ @SCons.Memoize.CountDictCall(_srcdir_find_file_key)
def srcdir_find_file(self, filename):
try:
memo_dict = self._memo['srcdir_find_file']
@@ -1988,7 +2122,7 @@ class Dir(Base):
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):
+ def glob(self, pathname, ondisk=True, source=False, strings=False, exclude=None):
"""
Returns a list of Nodes (or strings) matching a specified
pathname pattern.
@@ -2016,24 +2150,36 @@ class Dir(Base):
The "strings" argument, when true, returns the matches as strings,
not Nodes. The strings are path names relative to this directory.
+ The "exclude" argument, if not None, must be a pattern or a list
+ of patterns following the same UNIX shell semantics.
+ Elements matching a least one pattern of this list will be excluded
+ from the result.
+
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)
+ result = self._glob1(basename, ondisk, source, strings)
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)
+ if has_glob_magic(dirname):
+ list = self.glob(dirname, ondisk, source, False, exclude)
+ 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)
+ if exclude:
+ excludes = []
+ excludeList = SCons.Util.flatten(exclude)
+ for x in excludeList:
+ r = self.glob(x, ondisk, source, strings)
+ excludes.extend(r)
+ result = filter(lambda x: not any(fnmatch.fnmatch(str(x), str(e)) for e in SCons.Util.flatten(excludes)), result)
return sorted(result, key=lambda a: str(a))
def _glob1(self, pattern, ondisk=True, source=False, strings=False):
@@ -2066,7 +2212,7 @@ class Dir(Base):
for name in node_names: selfEntry(name)
if ondisk:
try:
- disk_names = os.listdir(dir.abspath)
+ disk_names = os.listdir(dir._abspath)
except os.error:
continue
names.extend(disk_names)
@@ -2080,7 +2226,6 @@ class Dir(Base):
# 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
@@ -2096,14 +2241,12 @@ class Dir(Base):
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]
class RootDir(Dir):
@@ -2114,23 +2257,17 @@ class RootDir(Dir):
add a separator when creating the path names of entries within
this directory.
"""
+
+ __slots__ = ['_lookupDict']
+
def __init__(self, drive, fs):
if SCons.Debug.track_instances: 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__()
- # won't gag won't it calls some of our methods.
- self.abspath = ''
- self.labspath = ''
- self.path = ''
- self.tpath = ''
- self.path_elements = []
- self.duplicate = 0
- self.root = self
+ SCons.Node.Node.__init__(self)
# Handle all the types of drives:
if drive == '':
# No drive, regular UNIX root or Windows default drive.
- name = OS_SEP
+ name = OS_SEP
dirname = OS_SEP
elif drive == '//':
# UNC path
@@ -2141,33 +2278,85 @@ class RootDir(Dir):
name = drive
dirname = drive + OS_SEP
- Base.__init__(self, name, self, fs)
+ #: 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)
+ self.fs = fs #: Reference to parent Node.FS object
+
+ self._path_elements = [self]
+ self.dir = self
+ self._func_rexists = 2
+ self._func_target_from_source = 1
+ self.store_info = 1
# 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 = dirname
- self.tpath = dirname
- self._morph()
-
- # Must be reset after Dir._morph() is invoked...
+ self._abspath = dirname
+ self._labspath = ''
+ self._path = dirname
+ self._tpath = dirname
self.dirname = dirname
+ self._morph()
+
+ self.duplicate = 0
self._lookupDict = {}
self._lookupDict[''] = self
self._lookupDict['/'] = self
-
+ self.root = 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 _morph(self):
+ """Turn a file system Node (either a freshly initialized directory
+ object or a separate Entry object) into a proper directory object.
+
+ 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.
+ """
+
+ self.repositories = []
+ self.srcdir = None
+
+ self.entries = {}
+ self.entries['.'] = self
+ self.entries['..'] = self.dir
+ self.cwd = self
+ self.searched = 0
+ self._sconsign = None
+ self.variant_dirs = []
+ self.changed_since_last_build = 3
+ self._func_sconsign = 1
+ self._func_exists = 2
+ self._func_get_contents = 2
+
+ # Don't just reset the executor, replace its action list,
+ # because it might have some pre-or post-actions that need to
+ # be preserved.
+ #
+ # 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 must_be_same(self, klass):
if klass is Dir:
return
@@ -2198,17 +2387,7 @@ class RootDir(Dir):
raise SCons.Errors.UserError(msg)
# There is no Node for this path name, and we're allowed
# to create it.
- # (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_name, file_name = p.rsplit('/',1)
dir_node = self._lookup_abs(dir_name, Dir)
result = klass(file_name, dir_node, self.fs)
@@ -2226,19 +2405,19 @@ class RootDir(Dir):
return result
def __str__(self):
- return self.abspath
+ return self._abspath
def entry_abspath(self, name):
- return self.abspath + name
+ return self._abspath + name
def entry_labspath(self, name):
return '/' + name
def entry_path(self, name):
- return self.path + name
+ return self._path + name
def entry_tpath(self, name):
- return self.tpath + name
+ return self._tpath + name
def is_under(self, dir):
if self is dir:
@@ -2256,7 +2435,8 @@ class RootDir(Dir):
return _null
class FileNodeInfo(SCons.Node.NodeInfoBase):
- current_version_id = 1
+ __slots__ = ('csig', 'timestamp', 'size')
+ current_version_id = 2
field_list = ['csig', 'timestamp', 'size']
@@ -2271,11 +2451,43 @@ class FileNodeInfo(SCons.Node.NodeInfoBase):
if drive:
root = self.fs.get_root(drive)
if not os.path.isabs(s):
- s = top.labspath + '/' + s
+ s = top.get_labspath() + '/' + s
return root._lookup_abs(s, Entry)
+ def __getstate__(self):
+ """
+ Return all fields that shall be pickled. Walk the slots in the class
+ hierarchy and add those to the state dictionary. If a '__dict__' slot is
+ available, copy all entries to the dictionary. Also include the version
+ id, which is fixed for all instances of a class.
+ """
+ state = getattr(self, '__dict__', {}).copy()
+ for obj in type(self).mro():
+ for name in getattr(obj,'__slots__',()):
+ if hasattr(self, name):
+ state[name] = getattr(self, name)
+
+ state['_version_id'] = self.current_version_id
+ try:
+ del state['__weakref__']
+ except KeyError:
+ pass
+
+ return state
+
+ def __setstate__(self, state):
+ """
+ Restore the attributes from a pickled state.
+ """
+ # TODO check or discard version
+ del state['_version_id']
+ for key, value in state.items():
+ if key not in ('__weakref__',):
+ setattr(self, key, value)
+
class FileBuildInfo(SCons.Node.BuildInfoBase):
- current_version_id = 1
+ __slots__ = ()
+ current_version_id = 2
def convert_to_sconsign(self):
"""
@@ -2290,7 +2502,7 @@ class FileBuildInfo(SCons.Node.BuildInfoBase):
else:
def node_to_str(n):
try:
- s = n.path
+ s = n.get_internal_path()
except AttributeError:
s = str(n)
else:
@@ -2331,6 +2543,8 @@ class FileBuildInfo(SCons.Node.BuildInfoBase):
nodeinfos = getattr(self, sattr)
except AttributeError:
continue
+ if strings is None or nodeinfos is None:
+ continue
nodes = []
for s, ni in zip(strings, nodeinfos):
if not isinstance(s, SCons.Node.Node):
@@ -2344,6 +2558,8 @@ class FileBuildInfo(SCons.Node.BuildInfoBase):
for bkid, bkidsig in zip(bkids, bkidsigs):
result.append(str(bkid) + ': ' +
' '.join(bkidsig.format(names=names)))
+ if not hasattr(self,'bact'):
+ self.bact = "none"
result.append('%s [%s]' % (self.bactsig, self.bact))
return '\n'.join(result)
@@ -2351,7 +2567,22 @@ class File(Base):
"""A class for files in a file system.
"""
- memoizer_counters = []
+ __slots__ = ['scanner_paths',
+ 'cachedir_csig',
+ 'cachesig',
+ 'repositories',
+ 'srcdir',
+ 'entries',
+ 'searched',
+ '_sconsign',
+ 'variant_dirs',
+ 'root',
+ 'dirname',
+ 'on_disk_entries',
+ 'sccs_dir',
+ 'rcs_dir',
+ 'released_target_info',
+ 'contentsig']
NodeInfo = FileNodeInfo
BuildInfo = FileBuildInfo
@@ -2387,13 +2618,6 @@ class File(Base):
the directory of this file."""
return self.dir.File(name)
- #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."""
self.scanner_paths = {}
@@ -2402,6 +2626,14 @@ class File(Base):
if not hasattr(self, 'released_target_info'):
self.released_target_info = False
+ self.store_info = 1
+ self._func_exists = 4
+ self._func_get_contents = 3
+
+ # Initialize this Node's decider function to decide_source() because
+ # every file is a source file until it has a Builder attached...
+ self.changed_since_last_build = 4
+
# If there was already a Builder set on this entry, then
# we need to make sure we call the target-decider function,
# not the source-decider. Reaching in and doing this by hand
@@ -2413,22 +2645,13 @@ class File(Base):
# not clear right now how to fix that, stick with what works
# until it becomes clear...
if self.has_builder():
- self.changed_since_last_build = self.decide_target
+ self.changed_since_last_build = 5
def scanner_key(self):
return self.get_suffix()
def get_contents(self):
- if not self.rexists():
- return b'' # Should always be bytes
- fname = self.rfile().abspath
- try:
- contents = open(fname, "rb").read()
- except EnvironmentError as e:
- if not e.filename:
- e.filename = fname
- raise
- return contents
+ return SCons.Node._get_contents_map[self._func_get_contents](self)
# This attempts to figure out what the encoding of the text is
# based upon the BOM bytes, and then decodes the contents so that
@@ -2455,7 +2678,7 @@ class File(Base):
"""
if not self.rexists():
return SCons.Util.MD5signature('')
- fname = self.rfile().abspath
+ fname = self.rfile().get_abspath()
try:
cs = SCons.Util.MD5filesignature(fname,
chunksize=SCons.Node.FS.File.md5_chunksize*1024)
@@ -2464,10 +2687,8 @@ class File(Base):
e.filename = fname
raise
return cs
-
-
- memoizer_counters.append(SCons.Memoize.CountValue('get_size'))
+ @SCons.Memoize.CountMethodCall
def get_size(self):
try:
return self._memo['get_size']
@@ -2483,8 +2704,7 @@ class File(Base):
return size
- memoizer_counters.append(SCons.Memoize.CountValue('get_timestamp'))
-
+ @SCons.Memoize.CountMethodCall
def get_timestamp(self):
try:
return self._memo['get_timestamp']
@@ -2500,14 +2720,6 @@ class File(Base):
return timestamp
- def store_info(self):
- # Merge our build information into the already-stored entry.
- # This accomodates "chained builds" where a file that's a target
- # in one build (SConstruct file) is a source in a different build.
- # See test/chained-build.py for the use case.
- if do_store_info:
- self.dir.sconsign().store_info(self.name, self)
-
convert_copy_attrs = [
'bsources',
'bimplicit',
@@ -2620,8 +2832,7 @@ class File(Base):
delattr(old_entry, attr)
return new_entry
- memoizer_counters.append(SCons.Memoize.CountValue('get_stored_info'))
-
+ @SCons.Memoize.CountMethodCall
def get_stored_info(self):
try:
return self._memo['get_stored_info']
@@ -2661,8 +2872,7 @@ class File(Base):
def _get_found_includes_key(self, env, scanner, path):
return (id(env), id(scanner), path)
- memoizer_counters.append(SCons.Memoize.CountDict('get_found_includes', _get_found_includes_key))
-
+ @SCons.Memoize.CountDictCall(_get_found_includes_key)
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
@@ -2681,9 +2891,7 @@ class File(Base):
pass
if scanner:
- # result = [n.disambiguate() for n in scanner(self, env, path)]
- result = scanner(self, env, path)
- result = [N.disambiguate() for N in result]
+ result = [n.disambiguate() for n in scanner(self, env, path)]
else:
result = []
@@ -2746,18 +2954,18 @@ class File(Base):
# any build information that's stored in the .sconsign file
# into our binfo object so it doesn't get lost.
old = self.get_stored_info()
- self.get_binfo().__dict__.update(old.binfo.__dict__)
+ self.get_binfo().merge(old.binfo)
- self.store_info()
+ SCons.Node.store_info_map[self.store_info](self)
def release_target_info(self):
"""Called just after this node has been marked
up-to-date or was built completely.
-
+
This is where we try to release as many target node infos
as possible for clean builds and update runs, in order
to minimize the overall memory consumption.
-
+
We'd like to remove a lot more attributes like self.sources
and self.sources_set, but they might get used
in a next build step. For example, during configuration
@@ -2765,18 +2973,18 @@ class File(Base):
which linker to use for the resulting Program (gcc vs. g++)!
That's why we check for the 'keep_targetinfo' attribute,
config Nodes and the Interactive mode just don't allow
- an early release of most variables.
+ an early release of most variables.
In the same manner, we can't simply remove the self.attributes
here. The smart linking relies on the shared flag, and some
parts of the java Tool use it to transport information
about nodes...
-
+
@see: built() and Node.release_target_info()
"""
if (self.released_target_info or SCons.Node.interactive):
return
-
+
if not hasattr(self.attributes, 'keep_targetinfo'):
# Cache some required values, before releasing
# stuff like env, executor and builder...
@@ -2848,8 +3056,8 @@ class File(Base):
def _rmv_existing(self):
self.clear_memoized_values()
- if print_duplicate:
- print("dup: removing existing target %s"%self)
+ if SCons.Node.print_duplicate:
+ print("dup: removing existing target {}".format(self))
e = Unlink(self, [], None)
if isinstance(e, SCons.Errors.BuildError):
raise e
@@ -2874,8 +3082,7 @@ class File(Base):
try:
self._createDir()
except SCons.Errors.StopError as drive:
- desc = "No drive `%s' for target `%s'." % (drive, self)
- raise SCons.Errors.StopError(desc)
+ raise SCons.Errors.StopError("No drive `%s' for target `{}'.".format(drive, self))
#
#
@@ -2884,19 +3091,18 @@ class File(Base):
def remove(self):
"""Remove this file."""
if self.exists() or self.islink():
- self.fs.unlink(self.path)
+ self.fs.unlink(self.get_internal_path())
return 1
return None
def do_duplicate(self, src):
self._createDir()
- if print_duplicate:
- print("dup: relinking variant '%s' from '%s'"%(self, src))
+ if SCons.Node.print_duplicate:
+ print("dup: relinking variant '{}' from '{}'".format(self, src))
Unlink(self, None, None)
e = Link(self, src, None)
if isinstance(e, SCons.Errors.BuildError):
- desc = "Cannot duplicate `%s' in `%s': %s." % (src.path, self.dir.path, e.errstr)
- raise SCons.Errors.StopError(desc)
+ raise SCons.Errors.StopError("Cannot duplicate `{}' in `{}': {}.".format(src.get_internal_path(), self.dir._path, e.errstr))
self.linked = 1
# The Link() action may or may not have actually
# created the file, depending on whether the -n
@@ -2904,36 +3110,13 @@ class File(Base):
# _rexists attributes so they can be reevaluated.
self.clear()
- memoizer_counters.append(SCons.Memoize.CountValue('exists'))
-
+ @SCons.Memoize.CountMethodCall
def exists(self):
try:
return self._memo['exists']
except KeyError:
pass
- # 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()
- if src is not self:
- # At this point, src is meant to be copied in a variant directory.
- src = src.rfile()
- if src.abspath != self.abspath:
- if src.exists():
- self.do_duplicate(src)
- # Can't return 1 here because the duplication might
- # not actually occur if the -n option is being used.
- 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
- # above will have cached its value if the file existed.
- self._memo['exists'] = None
- return None
- result = Base.exists(self)
+ result = SCons.Node._exists_map[self._func_exists](self)
self._memo['exists'] = result
return result
@@ -3010,41 +3193,41 @@ class File(Base):
def builder_set(self, builder):
SCons.Node.Node.builder_set(self, builder)
- self.changed_since_last_build = self.decide_target
+ self.changed_since_last_build = 5
def built(self):
"""Called just after this File node is successfully built.
-
+
Just like for 'release_target_info' we try to release
some more target node attributes in order to minimize the
overall memory consumption.
-
+
@see: release_target_info
"""
SCons.Node.Node.built(self)
- if (not SCons.Node.interactive and
+ if (not SCons.Node.interactive and
not hasattr(self.attributes, 'keep_targetinfo')):
- # Ensure that the build infos get computed and cached...
- self.store_info()
+ # Ensure that the build infos get computed and cached...
+ SCons.Node.store_info_map[self.store_info](self)
# ... then release some more variables.
self._specific_sources = False
- self.labspath = None
+ self._labspath = None
self._save_str()
self.cwd = None
-
+
self.scanner_paths = None
def changed(self, node=None, allowcache=False):
"""
Returns if the node is up-to-date with respect to the BuildInfo
- stored last time it was built.
-
+ stored last time it was built.
+
For File nodes this is basically a wrapper around Node.changed(),
but we allow the return value to get cached after the reference
to the Executor got released in release_target_info().
-
+
@see: Node.changed()
"""
if node is None:
@@ -3052,7 +3235,7 @@ class File(Base):
return self._memo['changed']
except KeyError:
pass
-
+
has_changed = SCons.Node.Node.changed(self, node)
if allowcache:
self._memo['changed'] = has_changed
@@ -3089,16 +3272,6 @@ class File(Base):
except AttributeError:
return 1
- def decide_source(self, target, prev_ni):
- return target.get_build_env().decide_source(self, target, prev_ni)
-
- def decide_target(self, target, prev_ni):
- return target.get_build_env().decide_target(self, target, prev_ni)
-
- # Initialize this Node's decider function to decide_source() because
- # every file is a source file until it has a Builder attached...
- changed_since_last_build = decide_source
-
def is_up_to_date(self):
T = 0
if T: Trace('is_up_to_date(%s):' % self)
@@ -3115,8 +3288,8 @@ class File(Base):
# ...and they'd like a local copy.
e = LocalCopy(self, r, None)
if isinstance(e, SCons.Errors.BuildError):
- raise
- self.store_info()
+ raise
+ SCons.Node.store_info_map[self.store_info](self)
if T: Trace(' 1\n')
return 1
self.changed()
@@ -3127,8 +3300,7 @@ class File(Base):
if T: Trace(' self.exists(): %s\n' % r)
return not r
- memoizer_counters.append(SCons.Memoize.CountValue('rfile'))
-
+ @SCons.Memoize.CountMethodCall
def rfile(self):
try:
return self._memo['rfile']
@@ -3198,12 +3370,12 @@ class File(Base):
It computes and returns the signature for this
node's contents.
"""
-
+
try:
return self.contentsig
except AttributeError:
pass
-
+
executor = self.get_executor()
result = self.contentsig = SCons.Util.MD5signature(executor.get_contents())
@@ -3223,14 +3395,14 @@ class File(Base):
return self.cachesig
except AttributeError:
pass
-
+
# Collect signatures for all children
children = self.children()
sigs = [n.get_cachedir_csig() for n in children]
# Append this node's signature...
sigs.append(self.get_contents_sig())
# ...and it's path
- sigs.append(self.path)
+ sigs.append(self.get_internal_path())
# Merge this all into a single signature
result = self.cachesig = SCons.Util.MD5collect(sigs)
return result
@@ -3246,10 +3418,6 @@ def get_default_fs():
class FileFinder(object):
"""
"""
- if SCons.Memoize.use_memoizer:
- __metaclass__ = SCons.Memoize.Memoized_Metaclass
-
- memoizer_counters = []
def __init__(self):
self._memo = {}
@@ -3291,9 +3459,8 @@ class FileFinder(object):
def _find_file_key(self, filename, paths, verbose=None):
return (filename, paths)
-
- memoizer_counters.append(SCons.Memoize.CountDict('find_file', _find_file_key))
+ @SCons.Memoize.CountDictCall(_find_file_key)
def find_file(self, filename, paths, verbose=None):
"""
find_file(str, [Dir()]) -> [nodes]
@@ -3331,36 +3498,6 @@ class FileFinder(object):
filedir, filename = os.path.split(filename)
if filedir:
- # More compact code that we can't use until we drop
- # support for Python 1.5.2:
- #
- #def filedir_lookup(p, fd=filedir):
- # """
- # A helper function that looks up a directory for a file
- # we're trying to find. This only creates the Dir Node
- # if it exists on-disk, since if the directory doesn't
- # exist we know we won't find any files in it... :-)
- # """
- # dir, name = os.path.split(fd)
- # if dir:
- # p = filedir_lookup(p, dir)
- # if not p:
- # return None
- # norm_name = _my_normcase(name)
- # try:
- # node = p.entries[norm_name]
- # except KeyError:
- # return p.dir_on_disk(name)
- # if isinstance(node, Dir):
- # return node
- # if isinstance(node, Entry):
- # node.must_be_same(Dir)
- # return node
- # if isinstance(node, Dir) or isinstance(node, Entry):
- # return node
- # return None
- #paths = [_f for _f in map(filedir_lookup, paths) if _f]
-
self.default_filedir = filedir
paths = [_f for _f in map(self.filedir_lookup, paths) if _f]
@@ -3408,7 +3545,7 @@ def invalidate_node_memos(targets):
if not SCons.Util.is_List(targets):
targets = [targets]
-
+
for entry in targets:
# If the target is a Node object, clear the cache. If it is a
# filename, look up potentially existing Node object first.
@@ -3420,7 +3557,7 @@ def invalidate_node_memos(targets):
# do not correspond to an existing Node object.
node = get_default_fs().Entry(entry)
if node:
- node.clear_memoized_values()
+ node.clear_memoized_values()
# Local Variables:
# tab-width:4
diff --git a/src/engine/SCons/Node/FSTests.py b/src/engine/SCons/Node/FSTests.py
index 9a8763a..b9c19bc 100644
--- a/src/engine/SCons/Node/FSTests.py
+++ b/src/engine/SCons/Node/FSTests.py
@@ -130,34 +130,34 @@ class VariantDirTestCase(unittest.TestCase):
fs.VariantDir('build', 'src')
f2 = fs.File('build/test2')
d1 = fs.Dir('build')
- assert f1.srcnode().path == os.path.normpath('src/test1'), f1.srcnode().path
- assert f2.srcnode().path == os.path.normpath('src/test2'), f2.srcnode().path
- assert d1.srcnode().path == 'src', d1.srcnode().path
+ assert f1.srcnode().get_internal_path() == os.path.normpath('src/test1'), f1.srcnode().get_internal_path()
+ assert f2.srcnode().get_internal_path() == os.path.normpath('src/test2'), f2.srcnode().get_internal_path()
+ assert d1.srcnode().get_internal_path() == 'src', d1.srcnode().get_internal_path()
fs = SCons.Node.FS.FS()
f1 = fs.File('build/test1')
fs.VariantDir('build', '.')
f2 = fs.File('build/test2')
d1 = fs.Dir('build')
- assert f1.srcnode().path == 'test1', f1.srcnode().path
- assert f2.srcnode().path == 'test2', f2.srcnode().path
- assert d1.srcnode().path == '.', d1.srcnode().path
+ assert f1.srcnode().get_internal_path() == 'test1', f1.srcnode().get_internal_path()
+ assert f2.srcnode().get_internal_path() == 'test2', f2.srcnode().get_internal_path()
+ assert d1.srcnode().get_internal_path() == '.', d1.srcnode().get_internal_path()
fs = SCons.Node.FS.FS()
fs.VariantDir('build/var1', 'src')
fs.VariantDir('build/var2', 'src')
f1 = fs.File('build/var1/test1')
f2 = fs.File('build/var2/test1')
- assert f1.srcnode().path == os.path.normpath('src/test1'), f1.srcnode().path
- assert f2.srcnode().path == os.path.normpath('src/test1'), f2.srcnode().path
+ assert f1.srcnode().get_internal_path() == os.path.normpath('src/test1'), f1.srcnode().get_internal_path()
+ assert f2.srcnode().get_internal_path() == os.path.normpath('src/test1'), f2.srcnode().get_internal_path()
fs = SCons.Node.FS.FS()
fs.VariantDir('../var1', 'src')
fs.VariantDir('../var2', 'src')
f1 = fs.File('../var1/test1')
f2 = fs.File('../var2/test1')
- assert f1.srcnode().path == os.path.normpath('src/test1'), f1.srcnode().path
- assert f2.srcnode().path == os.path.normpath('src/test1'), f2.srcnode().path
+ assert f1.srcnode().get_internal_path() == os.path.normpath('src/test1'), f1.srcnode().get_internal_path()
+ assert f2.srcnode().get_internal_path() == os.path.normpath('src/test1'), f2.srcnode().get_internal_path()
# Set up some files
test.subdir('work', ['work', 'src'])
@@ -210,8 +210,8 @@ class VariantDirTestCase(unittest.TestCase):
f2out_2.builder = 1
fs.Repository(test.workpath('rep1'))
- assert f1.srcnode().path == os.path.normpath('src/test.in'),\
- f1.srcnode().path
+ assert f1.srcnode().get_internal_path() == os.path.normpath('src/test.in'),\
+ f1.srcnode().get_internal_path()
# str(node) returns source path for duplicate = 0
assert str(f1) == os.path.normpath('src/test.in'), str(f1)
# Build path does not exist
@@ -221,11 +221,11 @@ class VariantDirTestCase(unittest.TestCase):
# And duplicate=0 should also work just like a Repository
assert f1.rexists()
# rfile() should point to the source path
- assert f1.rfile().path == os.path.normpath('src/test.in'),\
- f1.rfile().path
+ assert f1.rfile().get_internal_path() == os.path.normpath('src/test.in'),\
+ f1.rfile().get_internal_path()
- assert f2.srcnode().path == os.path.normpath('src/test.in'),\
- f2.srcnode().path
+ assert f2.srcnode().get_internal_path() == os.path.normpath('src/test.in'),\
+ f2.srcnode().get_internal_path()
# str(node) returns build path for duplicate = 1
assert str(f2) == os.path.normpath('build/var2/test.in'), str(f2)
# Build path exists
@@ -239,8 +239,8 @@ class VariantDirTestCase(unittest.TestCase):
f3 = fs.File('build/var1/test2.in')
f4 = fs.File('build/var2/test2.in')
- assert f3.srcnode().path == os.path.normpath('src/test2.in'),\
- f3.srcnode().path
+ assert f3.srcnode().get_internal_path() == os.path.normpath('src/test2.in'),\
+ f3.srcnode().get_internal_path()
# str(node) returns source path for duplicate = 0
assert str(f3) == os.path.normpath('src/test2.in'), str(f3)
# Build path does not exist
@@ -250,11 +250,11 @@ class VariantDirTestCase(unittest.TestCase):
# But we do have a file in the Repository
assert f3.rexists()
# rfile() should point to the source path
- assert f3.rfile().path == os.path.normpath(test.workpath('rep1/src/test2.in')),\
- f3.rfile().path
+ assert f3.rfile().get_internal_path() == os.path.normpath(test.workpath('rep1/src/test2.in')),\
+ f3.rfile().get_internal_path()
- assert f4.srcnode().path == os.path.normpath('src/test2.in'),\
- f4.srcnode().path
+ assert f4.srcnode().get_internal_path() == os.path.normpath('src/test2.in'),\
+ f4.srcnode().get_internal_path()
# str(node) returns build path for duplicate = 1
assert str(f4) == os.path.normpath('build/var2/test2.in'), str(f4)
# Build path should exist
@@ -264,8 +264,8 @@ class VariantDirTestCase(unittest.TestCase):
# should exist in repository, since exists() is true
assert f4.rexists()
# rfile() should point to ourselves
- assert f4.rfile().path == os.path.normpath('build/var2/test2.in'),\
- f4.rfile().path
+ assert f4.rfile().get_internal_path() == os.path.normpath('build/var2/test2.in'),\
+ f4.rfile().get_internal_path()
f5 = fs.File('build/var1/test.out')
f6 = fs.File('build/var2/test.out')
@@ -285,14 +285,14 @@ class VariantDirTestCase(unittest.TestCase):
assert not f7.exists()
assert f7.rexists()
- r = f7.rfile().path
+ r = f7.rfile().get_internal_path()
expect = os.path.normpath(test.workpath('rep1/build/var1/test2.out'))
assert r == expect, (repr(r), repr(expect))
assert not f8.exists()
assert f8.rexists()
- assert f8.rfile().path == os.path.normpath(test.workpath('rep1/build/var2/test2.out')),\
- f8.rfile().path
+ assert f8.rfile().get_internal_path() == os.path.normpath(test.workpath('rep1/build/var2/test2.out')),\
+ f8.rfile().get_internal_path()
# Verify the Mkdir and Link actions are called
d9 = fs.Dir('build/var2/new_dir')
@@ -319,9 +319,9 @@ class VariantDirTestCase(unittest.TestCase):
d9.reset_executor()
f9.exists()
expect = os.path.join('build', 'var2', 'new_dir')
- assert dir_made[0].path == expect, dir_made[0].path
+ assert dir_made[0].get_internal_path() == expect, dir_made[0].get_internal_path()
expect = os.path.join('build', 'var2', 'new_dir', 'test9.out')
- assert link_made[0].path == expect, link_made[0].path
+ assert link_made[0].get_internal_path() == expect, link_made[0].get_internal_path()
assert f9.linked
finally:
SCons.Node.FS.Link = save_Link
@@ -338,7 +338,7 @@ class VariantDirTestCase(unittest.TestCase):
f11 = fs.File('src/file11')
t, m = f11.alter_targets()
- bdt = [n.path for n in t]
+ bdt = [n.get_internal_path() for n in t]
var1_file11 = os.path.normpath('build/var1/file11')
var2_file11 = os.path.normpath('build/var2/file11')
assert bdt == [var1_file11, var2_file11], bdt
@@ -346,11 +346,11 @@ class VariantDirTestCase(unittest.TestCase):
f12 = fs.File('src/file12')
f12.builder = 1
bdt, m = f12.alter_targets()
- assert bdt == [], [n.path for n in bdt]
+ assert bdt == [], [n.get_internal_path() for n in bdt]
d13 = fs.Dir('src/new_dir')
t, m = d13.alter_targets()
- bdt = [n.path for n in t]
+ bdt = [n.get_internal_path() for n in t]
var1_new_dir = os.path.normpath('build/var1/new_dir')
var2_new_dir = os.path.normpath('build/var2/new_dir')
assert bdt == [var1_new_dir, var2_new_dir], bdt
@@ -566,13 +566,13 @@ class VariantDirTestCase(unittest.TestCase):
f = dir + '/f'
fnode = fs.File(dir + '/f')
- dp = dnode.srcnode().path
+ dp = dnode.srcnode().get_internal_path()
expect = os.path.normpath(srcnode_map.get(dir, dir))
if dp != expect:
print("Dir `%s' srcnode() `%s' != expected `%s'" % (dir, dp, expect))
errors = errors + 1
- fp = fnode.srcnode().path
+ fp = fnode.srcnode().get_internal_path()
expect = os.path.normpath(srcnode_map.get(f, f))
if fp != expect:
print("File `%s' srcnode() `%s' != expected `%s'" % (f, fp, expect))
@@ -584,14 +584,14 @@ class VariantDirTestCase(unittest.TestCase):
fnode = fs.File(dir + '/f')
t, m = dnode.alter_targets()
- tp = t[0].path
+ tp = t[0].get_internal_path()
expect = os.path.normpath(alter_map.get(dir, dir))
if tp != expect:
print("Dir `%s' alter_targets() `%s' != expected `%s'" % (dir, tp, expect))
errors = errors + 1
t, m = fnode.alter_targets()
- tp = t[0].path
+ tp = t[0].get_internal_path()
expect = os.path.normpath(alter_map.get(f, f))
if tp != expect:
print("File `%s' alter_targets() `%s' != expected `%s'" % (f, tp, expect))
@@ -698,19 +698,19 @@ class DirNodeInfoTestCase(_tempdirTestCase):
def test___init__(self):
"""Test DirNodeInfo initialization"""
ddd = self.fs.Dir('ddd')
- ni = SCons.Node.FS.DirNodeInfo(ddd)
+ ni = SCons.Node.FS.DirNodeInfo()
class DirBuildInfoTestCase(_tempdirTestCase):
def test___init__(self):
"""Test DirBuildInfo initialization"""
ddd = self.fs.Dir('ddd')
- bi = SCons.Node.FS.DirBuildInfo(ddd)
+ bi = SCons.Node.FS.DirBuildInfo()
class FileNodeInfoTestCase(_tempdirTestCase):
def test___init__(self):
"""Test FileNodeInfo initialization"""
fff = self.fs.File('fff')
- ni = SCons.Node.FS.FileNodeInfo(fff)
+ ni = SCons.Node.FS.FileNodeInfo()
assert isinstance(ni, SCons.Node.FS.FileNodeInfo)
def test_update(self):
@@ -718,7 +718,7 @@ class FileNodeInfoTestCase(_tempdirTestCase):
test = self.test
fff = self.fs.File('fff')
- ni = SCons.Node.FS.FileNodeInfo(fff)
+ ni = SCons.Node.FS.FileNodeInfo()
test.write('fff', "fff\n")
@@ -751,51 +751,45 @@ class FileNodeInfoTestCase(_tempdirTestCase):
size = st[stat.ST_SIZE]
assert ni.size != size, (ni.size, size)
- #fff.clear()
- #ni.update(fff)
-
- #st = os.stat('fff')
-
- #mtime = st[stat.ST_MTIME]
- #assert ni.timestamp == mtime, (ni.timestamp, mtime)
- #size = st[stat.ST_SIZE]
- #assert ni.size == size, (ni.size, size)
-
class FileBuildInfoTestCase(_tempdirTestCase):
def test___init__(self):
"""Test File.BuildInfo initialization"""
fff = self.fs.File('fff')
- bi = SCons.Node.FS.FileBuildInfo(fff)
+ bi = SCons.Node.FS.FileBuildInfo()
assert bi, bi
def test_convert_to_sconsign(self):
"""Test converting to .sconsign file format"""
fff = self.fs.File('fff')
- bi = SCons.Node.FS.FileBuildInfo(fff)
+ bi = SCons.Node.FS.FileBuildInfo()
assert hasattr(bi, 'convert_to_sconsign')
def test_convert_from_sconsign(self):
"""Test converting from .sconsign file format"""
fff = self.fs.File('fff')
- bi = SCons.Node.FS.FileBuildInfo(fff)
+ bi = SCons.Node.FS.FileBuildInfo()
assert hasattr(bi, 'convert_from_sconsign')
def test_prepare_dependencies(self):
"""Test that we have a prepare_dependencies() method"""
fff = self.fs.File('fff')
- bi = SCons.Node.FS.FileBuildInfo(fff)
+ bi = SCons.Node.FS.FileBuildInfo()
bi.prepare_dependencies()
def test_format(self):
"""Test the format() method"""
f1 = self.fs.File('f1')
- bi1 = SCons.Node.FS.FileBuildInfo(f1)
+ bi1 = SCons.Node.FS.FileBuildInfo()
+
+ self.fs.File('n1')
+ self.fs.File('n2')
+ self.fs.File('n3')
- s1sig = SCons.Node.FS.FileNodeInfo(self.fs.File('n1'))
+ s1sig = SCons.Node.FS.FileNodeInfo()
s1sig.csig = 1
- d1sig = SCons.Node.FS.FileNodeInfo(self.fs.File('n2'))
+ d1sig = SCons.Node.FS.FileNodeInfo()
d1sig.timestamp = 2
- i1sig = SCons.Node.FS.FileNodeInfo(self.fs.File('n3'))
+ i1sig = SCons.Node.FS.FileNodeInfo()
i1sig.size = 3
bi1.bsources = [self.fs.File('s1')]
@@ -953,7 +947,7 @@ class FSTestCase(_tempdirTestCase):
assert isinstance(f1, SCons.Node.FS.File)
d1_f1 = os.path.join('d1', 'f1')
- assert f1.path == d1_f1, "f1.path %s != %s" % (f1.path, d1_f1)
+ assert f1.get_internal_path() == d1_f1, "f1.path %s != %s" % (f1.get_internal_path(), d1_f1)
assert str(f1) == d1_f1, "str(f1) %s != %s" % (str(f1), d1_f1)
x1 = d1.File('x1')
@@ -1045,16 +1039,16 @@ class FSTestCase(_tempdirTestCase):
name = os.sep
if dir.up() is None:
- dir_up_path = dir.path
+ dir_up_path = dir.get_internal_path()
else:
- dir_up_path = dir.up().path
+ dir_up_path = dir.up().get_internal_path()
assert dir.name == name, \
"dir.name %s != expected name %s" % \
(dir.name, name)
- assert dir.path == path, \
+ assert dir.get_internal_path() == path, \
"dir.path %s != expected path %s" % \
- (dir.path, path)
+ (dir.get_internal_path(), path)
assert str(dir) == path, \
"str(dir) %s != expected path %s" % \
(str(dir), path)
@@ -1136,9 +1130,9 @@ class FSTestCase(_tempdirTestCase):
# Test for a bug in 0.04 that did not like looking up
# dirs with a trailing slash on Windows.
d=fs.Dir('./')
- assert d.path == '.', d.abspath
+ assert d.get_internal_path() == '.', d.get_abspath()
d=fs.Dir('foo/')
- assert d.path == 'foo', d.abspath
+ assert d.get_internal_path() == 'foo', d.get_abspath()
# Test for sub-classing of node building.
global built_it
@@ -1167,50 +1161,50 @@ class FSTestCase(_tempdirTestCase):
e1 = fs.Entry("d1")
assert e1.__class__.__name__ == 'Dir'
- match(e1.path, "d1")
- match(e1.dir.path, ".")
+ match(e1.get_internal_path(), "d1")
+ match(e1.dir.get_internal_path(), ".")
e2 = fs.Entry("d1/f1")
assert e2.__class__.__name__ == 'File'
- match(e2.path, "d1/f1")
- match(e2.dir.path, "d1")
+ match(e2.get_internal_path(), "d1/f1")
+ match(e2.dir.get_internal_path(), "d1")
e3 = fs.Entry("e3")
assert e3.__class__.__name__ == 'Entry'
- match(e3.path, "e3")
- match(e3.dir.path, ".")
+ match(e3.get_internal_path(), "e3")
+ match(e3.dir.get_internal_path(), ".")
e4 = fs.Entry("d1/e4")
assert e4.__class__.__name__ == 'Entry'
- match(e4.path, "d1/e4")
- match(e4.dir.path, "d1")
+ match(e4.get_internal_path(), "d1/e4")
+ match(e4.dir.get_internal_path(), "d1")
e5 = fs.Entry("e3/e5")
assert e3.__class__.__name__ == 'Dir'
- match(e3.path, "e3")
- match(e3.dir.path, ".")
+ match(e3.get_internal_path(), "e3")
+ match(e3.dir.get_internal_path(), ".")
assert e5.__class__.__name__ == 'Entry'
- match(e5.path, "e3/e5")
- match(e5.dir.path, "e3")
+ match(e5.get_internal_path(), "e3/e5")
+ match(e5.dir.get_internal_path(), "e3")
e6 = fs.Dir("d1/e4")
assert e6 is e4
assert e4.__class__.__name__ == 'Dir'
- match(e4.path, "d1/e4")
- match(e4.dir.path, "d1")
+ match(e4.get_internal_path(), "d1/e4")
+ match(e4.dir.get_internal_path(), "d1")
e7 = fs.File("e3/e5")
assert e7 is e5
assert e5.__class__.__name__ == 'File'
- match(e5.path, "e3/e5")
- match(e5.dir.path, "e3")
+ match(e5.get_internal_path(), "e3/e5")
+ match(e5.dir.get_internal_path(), "e3")
fs.chdir(fs.Dir('subdir'))
f11 = fs.File("f11")
- match(f11.path, "subdir/f11")
+ match(f11.get_internal_path(), "subdir/f11")
d12 = fs.Dir("d12")
e13 = fs.Entry("subdir/e13")
- match(e13.path, "subdir/subdir/e13")
+ match(e13.get_internal_path(), "subdir/subdir/e13")
fs.chdir(fs.Dir('..'))
# Test scanning
@@ -1220,13 +1214,13 @@ class FSTestCase(_tempdirTestCase):
f1.builder.target_scanner = Scanner(xyz)
f1.scan()
- assert f1.implicit[0].path == "xyz"
+ assert f1.implicit[0].get_internal_path() == "xyz"
f1.implicit = []
f1.scan()
assert f1.implicit == []
f1.implicit = None
f1.scan()
- assert f1.implicit[0].path == "xyz"
+ assert f1.implicit[0].get_internal_path() == "xyz"
# Test underlying scanning functionality in get_found_includes()
env = Environment()
@@ -1284,9 +1278,9 @@ class FSTestCase(_tempdirTestCase):
fs.chdir(fs.Dir('subdir'))
# The cwd's path is always "."
assert str(fs.getcwd()) == ".", str(fs.getcwd())
- assert fs.getcwd().path == 'subdir', fs.getcwd().path
+ assert fs.getcwd().get_internal_path() == 'subdir', fs.getcwd().get_internal_path()
fs.chdir(fs.Dir('../..'))
- assert fs.getcwd().path == test.workdir, fs.getcwd().path
+ assert fs.getcwd().get_internal_path() == test.workdir, fs.getcwd().get_internal_path()
f1 = fs.File(test.workpath("do_i_exist"))
assert not f1.exists()
@@ -1589,15 +1583,15 @@ class FSTestCase(_tempdirTestCase):
assert dir.name == name, \
"dir.name %s != expected name %s" % \
(dir.name, name)
- assert dir.path == path, \
+ assert dir.get_internal_path() == path, \
"dir.path %s != expected path %s" % \
- (dir.path, path)
+ (dir.get_internal_path(), path)
assert str(dir) == path, \
"str(dir) %s != expected path %s" % \
(str(dir), path)
- assert dir.up().path == up_path, \
+ assert dir.up().get_internal_path() == up_path, \
"dir.up().path %s != expected parent path %s" % \
- (dir.up().path, up_path)
+ (dir.up().get_internal_path(), up_path)
save_os_path = os.path
save_os_sep = os.sep
@@ -1691,22 +1685,22 @@ class FSTestCase(_tempdirTestCase):
name = path.split(os.sep)[-1]
if dir.up() is None:
- dir_up_path = dir.path
+ dir_up_path = dir.get_internal_path()
else:
- dir_up_path = dir.up().path
+ dir_up_path = dir.up().get_internal_path()
assert dir.name == name, \
"dir.name %s != expected name %s" % \
(dir.name, name)
- assert dir.path == path, \
+ assert dir.get_internal_path() == path, \
"dir.path %s != expected path %s" % \
- (dir.path, path)
+ (dir.get_internal_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)
+ (dir.up().get_internal_path(), up_path)
save_os_path = os.path
save_os_sep = os.sep
@@ -1792,7 +1786,7 @@ class FSTestCase(_tempdirTestCase):
d1 = fs.Dir('d1')
d2 = d1.Dir('d2')
- dirs = os.path.normpath(d2.abspath).split(os.sep)
+ dirs = os.path.normpath(d2.get_abspath()).split(os.sep)
above_path = os.path.join(*['..']*len(dirs) + ['above'])
above = d2.Dir(above_path)
@@ -1831,9 +1825,9 @@ class FSTestCase(_tempdirTestCase):
fs = self.fs
if sys.platform not in ('win32',):
return
- p = fs.Dir(r"\\computername\sharename").abspath
+ p = fs.Dir(r"\\computername\sharename").get_abspath()
assert p == r"\\computername\sharename", p
- p = fs.Dir(r"\\\computername\sharename").abspath
+ p = fs.Dir(r"\\\computername\sharename").get_abspath()
assert p == r"\\computername\sharename", p
def test_rel_path(self):
@@ -1980,7 +1974,7 @@ class DirTestCase(_tempdirTestCase):
fs.Dir(os.path.join('ddd', 'd1', 'f4'))
fs.Dir(os.path.join('ddd', 'd1', 'f5'))
dir.scan()
- kids = sorted([x.path for x in dir.children(None)])
+ kids = sorted([x.get_internal_path() for x in dir.children(None)])
assert kids == [os.path.join('ddd', 'd1'),
os.path.join('ddd', 'f1'),
os.path.join('ddd', 'f2'),
@@ -2024,12 +2018,12 @@ class DirTestCase(_tempdirTestCase):
fs.File(os.path.join('ddd', 'f1'))
dir.scan()
- kids = sorted([x.path for x in dir.children()])
+ kids = sorted([x.get_internal_path() for x in dir.children()])
assert kids == [os.path.join('ddd', 'f1')], kids
fs.File(os.path.join('ddd', 'f2'))
dir.scan()
- kids = sorted([x.path for x in dir.children()])
+ kids = sorted([x.get_internal_path() for x in dir.children()])
assert kids == [os.path.join('ddd', 'f1'),
os.path.join('ddd', 'f2')], kids
@@ -2052,6 +2046,32 @@ class DirTestCase(_tempdirTestCase):
if os.path.normcase("TeSt") != os.path.normpath("TeSt") or sys.platform == "cygwin":
assert d.entry_exists_on_disk('case-insensitive')
+ def test_rentry_exists_on_disk(self):
+ """Test the Dir.rentry_exists_on_disk() method
+ """
+ test = self.test
+
+ does_not_exist = self.fs.Dir('does_not_exist')
+ assert not does_not_exist.rentry_exists_on_disk('foo')
+
+ test.subdir('d')
+ test.write(['d', 'exists'], "d/exists\n")
+ test.write(['d', 'Case-Insensitive'], "d/Case-Insensitive\n")
+
+ test.subdir('r')
+ test.write(['r', 'rexists'], "r/rexists\n")
+
+ d = self.fs.Dir('d')
+ r = self.fs.Dir('r')
+ d.addRepository(r)
+
+ assert d.rentry_exists_on_disk('exists')
+ assert d.rentry_exists_on_disk('rexists')
+ assert not d.rentry_exists_on_disk('does_not_exist')
+
+ if os.path.normcase("TeSt") != os.path.normpath("TeSt") or sys.platform == "cygwin":
+ assert d.rentry_exists_on_disk('case-insensitive')
+
def test_srcdir_list(self):
"""Test the Dir.srcdir_list() method
"""
@@ -2145,8 +2165,12 @@ class DirTestCase(_tempdirTestCase):
"""
test = self.test
- return_true = lambda: 1
+ def return_true(node):
+ return 1
+ SCons.Node._is_derived_map[2] = return_true
+ SCons.Node._exists_map[5] = return_true
+
test.subdir('src0')
test.write(['src0', 'on-disk-f1'], "src0/on-disk-f1\n")
test.write(['src0', 'on-disk-f2'], "src0/on-disk-f2\n")
@@ -2158,14 +2182,14 @@ class DirTestCase(_tempdirTestCase):
self.fs.VariantDir(bld0, src0, duplicate=0)
derived_f = src0.File('derived-f')
- derived_f.is_derived = return_true
+ derived_f._func_is_derived = 2
exists_f = src0.File('exists-f')
- exists_f.exists = return_true
+ exists_f._func_exists = 5
derived_e = src0.Entry('derived-e')
- derived_e.is_derived = return_true
+ derived_e._func_is_derived = 2
exists_e = src0.Entry('exists-e')
- exists_e.exists = return_true
+ exists_e._func_exists = 5
def check(result, expect):
result = list(map(str, result))
@@ -2219,14 +2243,14 @@ class DirTestCase(_tempdirTestCase):
self.fs.VariantDir(bld1, src1, duplicate=1)
derived_f = src1.File('derived-f')
- derived_f.is_derived = return_true
+ derived_f._func_is_derived = 2
exists_f = src1.File('exists-f')
- exists_f.exists = return_true
+ exists_f._func_exists = 5
derived_e = src1.Entry('derived-e')
- derived_e.is_derived = return_true
+ derived_e._func_is_derived = 2
exists_e = src1.Entry('exists-e')
- exists_e.exists = return_true
+ exists_e._func_exists = 5
# First check from the source directory.
n = src1.srcdir_find_file('does_not_exist')
@@ -2406,7 +2430,7 @@ class FileTestCase(_tempdirTestCase):
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
+ assert os.path.exists(build_f1.get_abspath()), "%s did not get duplicated on disk" % build_f1.get_abspath()
test.unlink(['src', 'f1'])
src_f1.clear() # so the next exists() call will look on disk again
@@ -2415,7 +2439,7 @@ class FileTestCase(_tempdirTestCase):
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)
+ assert not os.path.exists(build_f1.get_abspath()), "%s did not get removed after %s was removed" % (build_f1, src_f1)
@@ -2509,7 +2533,7 @@ class GlobTestCase(_tempdirTestCase):
for input, string_expect, node_expect in cases:
r = self.fs.Glob(input, **kwargs)
if node_expect:
- r = sorted(r, key=lambda a: a.path)
+ r = sorted(r, key=lambda a: a.get_internal_path())
result = []
for n in node_expect:
if isinstance(n, str):
@@ -2880,8 +2904,14 @@ class RepositoryTestCase(_tempdirTestCase):
def test_rdir(self):
"""Test the Dir.rdir() method"""
- return_true = lambda: 1
- return_false = lambda: 0
+ def return_true(obj):
+ return 1
+ def return_false(obj):
+ return 0
+ SCons.Node._exists_map[5] = return_true
+ SCons.Node._exists_map[6] = return_false
+ SCons.Node._is_derived_map[2] = return_true
+ SCons.Node._is_derived_map[3] = return_false
d1 = self.fs.Dir('d1')
d2 = self.fs.Dir('d2')
@@ -2905,19 +2935,19 @@ class RepositoryTestCase(_tempdirTestCase):
assert r == os.path.join(self.rep3, 'd3'), r
e1 = self.fs.Dir('e1')
- e1.exists = return_false
+ e1._func_exists = 6
e2 = self.fs.Dir('e2')
- e2.exists = return_false
+ e2._func_exists = 6
# Make sure we match entries in repositories,
# regardless of whether they're derived or not.
re1 = self.fs.Entry(os.path.join(self.rep1, 'e1'))
- re1.exists = return_true
- re1.is_derived = return_true
+ re1._func_exists = 5
+ re1._func_is_derived = 2
re2 = self.fs.Entry(os.path.join(self.rep2, 'e2'))
- re2.exists = return_true
- re2.is_derived = return_false
+ re2._func_exists = 5
+ re2._func_is_derived = 3
r = e1.rdir()
assert r is re1, r
@@ -2927,8 +2957,14 @@ class RepositoryTestCase(_tempdirTestCase):
def test_rfile(self):
"""Test the File.rfile() method"""
- return_true = lambda: 1
- return_false = lambda: 0
+ def return_true(obj):
+ return 1
+ def return_false(obj):
+ return 0
+ SCons.Node._exists_map[5] = return_true
+ SCons.Node._exists_map[6] = return_false
+ SCons.Node._is_derived_map[2] = return_true
+ SCons.Node._is_derived_map[3] = return_false
f1 = self.fs.File('f1')
f2 = self.fs.File('f2')
@@ -2952,19 +2988,19 @@ class RepositoryTestCase(_tempdirTestCase):
assert r == os.path.join(self.rep3, 'f3'), r
e1 = self.fs.File('e1')
- e1.exists = return_false
+ e1._func_exists = 6
e2 = self.fs.File('e2')
- e2.exists = return_false
+ e2._func_exists = 6
# Make sure we match entries in repositories,
# regardless of whether they're derived or not.
re1 = self.fs.Entry(os.path.join(self.rep1, 'e1'))
- re1.exists = return_true
- re1.is_derived = return_true
+ re1._func_exists = 5
+ re1._func_is_derived = 2
re2 = self.fs.Entry(os.path.join(self.rep2, 'e2'))
- re2.exists = return_true
- re2.is_derived = return_false
+ re2._func_exists = 5
+ re2._func_is_derived = 3
r = e1.rfile()
assert r is re1, r
@@ -3218,9 +3254,13 @@ class stored_infoTestCase(unittest.TestCase):
self.xyzzy = 7
def get_entry(self, name):
return self.Null()
+
+ def test_sconsign(node):
+ return MySConsign()
f = fs.File('file2', d)
- f.dir.sconsign = MySConsign
+ SCons.Node.FS._sconsign_map[2] = test_sconsign
+ f.dir._func_sconsign = 2
bi = f.get_stored_info()
assert bi.xyzzy == 7, bi
@@ -3334,7 +3374,7 @@ class prepareTestCase(unittest.TestCase):
xyz.set_state(0)
xyz.prepare()
- assert dir_made[0].path == "new_dir", dir_made[0]
+ assert dir_made[0].get_internal_path() == "new_dir", dir_made[0]
dir = fs.Dir("dir")
dir.prepare()
@@ -3347,7 +3387,7 @@ class SConstruct_dirTestCase(unittest.TestCase):
fs = SCons.Node.FS.FS()
fs.set_SConstruct_dir(fs.Dir('xxx'))
- assert fs.SConstruct_dir.path == 'xxx'
+ assert fs.SConstruct_dir.get_internal_path() == 'xxx'
@@ -3526,7 +3566,7 @@ class SpecialAttrTestCase(unittest.TestCase):
for_sig = f.suffix.for_signature()
assert for_sig == 'baz.blat_suffix', for_sig
- s = str(f.abspath)
+ s = str(f.get_abspath())
assert s == test.workpath('work', 'foo', 'bar', 'baz.blat'), s
assert f.abspath.is_literal(), f.abspath
for_sig = f.abspath.for_signature()
diff --git a/src/engine/SCons/Node/NodeTests.py b/src/engine/SCons/Node/NodeTests.py
index da502b0..3802f8c 100644
--- a/src/engine/SCons/Node/NodeTests.py
+++ b/src/engine/SCons/Node/NodeTests.py
@@ -121,6 +121,8 @@ class Environment(object):
self._dict.update(kw)
def __getitem__(self, key):
return self._dict[key]
+ def get(self, key, default = None):
+ return self._dict.get(key, default)
def Dictionary(self, *args):
return {}
def Override(self, overrides):
@@ -132,7 +134,12 @@ class Environment(object):
def get_factory(self, factory):
return factory or MyNode
def get_scanner(self, scanner_key):
- return self._dict['SCANNERS'][0]
+ try:
+ return self._dict['SCANNERS'][0]
+ except:
+ pass
+
+ return []
class Builder(object):
def __init__(self, env=None, is_explicit=1):
@@ -184,8 +191,8 @@ class Scanner(object):
called = None
def __call__(self, node):
self.called = 1
- return node.found_includes
- def path(self, env, dir, target=None, source=None):
+ return node.GetTag('found_includes')
+ def path(self, env, dir=None, target=None, source=None, kw={}):
return ()
def select(self, node):
return self
@@ -200,7 +207,7 @@ class MyNode(SCons.Node.Node):
def __init__(self, name):
SCons.Node.Node.__init__(self)
self.name = name
- self.found_includes = []
+ self.Tag('found_includes', [])
def __str__(self):
return self.name
def get_found_includes(self, env, scanner, target):
@@ -224,11 +231,18 @@ class Calculator(object):
class NodeInfoBaseTestCase(unittest.TestCase):
+ # The abstract class NodeInfoBase has not enough default slots to perform
+ # the merge and format test (arbitrary attributes do not work). Do it with a
+ # derived class that does provide the slots.
def test_merge(self):
"""Test merging NodeInfoBase attributes"""
- ni1 = SCons.Node.NodeInfoBase(SCons.Node.Node())
- ni2 = SCons.Node.NodeInfoBase(SCons.Node.Node())
+
+ class TestNodeInfo(SCons.Node.NodeInfoBase):
+ __slots__ = ('a1', 'a2', 'a3')
+
+ ni1 = TestNodeInfo()
+ ni2 = TestNodeInfo()
ni1.a1 = 1
ni1.a2 = 2
@@ -237,27 +251,32 @@ class NodeInfoBaseTestCase(unittest.TestCase):
ni2.a3 = 333
ni1.merge(ni2)
- expect = {'a1':1, 'a2':222, 'a3':333, '_version_id':1}
- assert ni1.__dict__ == expect, ni1.__dict__
+ assert ni1.a1 == 1, ni1.a1
+ assert ni1.a2 == 222, ni1.a2
+ assert ni1.a3 == 333, ni1.a3
def test_update(self):
"""Test the update() method"""
- ni = SCons.Node.NodeInfoBase(SCons.Node.Node())
+ ni = SCons.Node.NodeInfoBase()
ni.update(SCons.Node.Node())
def test_format(self):
"""Test the NodeInfoBase.format() method"""
- ni1 = SCons.Node.NodeInfoBase(SCons.Node.Node())
+
+ class TestNodeInfo(SCons.Node.NodeInfoBase):
+ __slots__ = ('xxx', 'yyy', 'zzz')
+
+ ni1 = TestNodeInfo()
ni1.xxx = 'x'
ni1.yyy = 'y'
ni1.zzz = 'z'
f = ni1.format()
- assert f == ['1', 'x', 'y', 'z'], f
+ assert f == ['x', 'y', 'z'], f
+
+ field_list = ['xxx', 'zzz', 'aaa']
- ni1.field_list = ['xxx', 'zzz', 'aaa']
-
- f = ni1.format()
+ f = ni1.format(field_list)
assert f == ['x', 'z', 'None'], f
@@ -267,26 +286,26 @@ class BuildInfoBaseTestCase(unittest.TestCase):
def test___init__(self):
"""Test BuildInfoBase initialization"""
n = SCons.Node.Node()
- bi = SCons.Node.BuildInfoBase(n)
+ bi = SCons.Node.BuildInfoBase()
assert bi
def test_merge(self):
"""Test merging BuildInfoBase attributes"""
n1 = SCons.Node.Node()
- bi1 = SCons.Node.BuildInfoBase(n1)
+ bi1 = SCons.Node.BuildInfoBase()
n2 = SCons.Node.Node()
- bi2 = SCons.Node.BuildInfoBase(n2)
+ bi2 = SCons.Node.BuildInfoBase()
- bi1.a1 = 1
- bi1.a2 = 2
+ bi1.bsources = 1
+ bi1.bdepends = 2
- bi2.a2 = 222
- bi2.a3 = 333
+ bi2.bdepends = 222
+ bi2.bact = 333
bi1.merge(bi2)
- assert bi1.a1 == 1, bi1.a1
- assert bi1.a2 == 222, bi1.a2
- assert bi1.a3 == 333, bi1.a3
+ assert bi1.bsources == 1, bi1.bsources
+ assert bi1.bdepends == 222, bi1.bdepends
+ assert bi1.bact == 333, bi1.bact
class NodeTestCase(unittest.TestCase):
@@ -339,8 +358,6 @@ class NodeTestCase(unittest.TestCase):
ggg.path = "ggg"
fff.sources = ["hhh", "iii"]
ggg.sources = ["hhh", "iii"]
- # [Charles C. 1/7/2002] Uhhh, why are there no asserts here?
- # [SK, 15 May 2003] I dunno, let's add some...
built_it = None
fff.build()
assert built_it
@@ -427,6 +444,7 @@ class NodeTestCase(unittest.TestCase):
def test_built(self):
"""Test the built() method"""
class SubNodeInfo(SCons.Node.NodeInfoBase):
+ __slots__ = ('updated',)
def update(self, node):
self.updated = 1
class SubNode(SCons.Node.Node):
@@ -434,7 +452,7 @@ class NodeTestCase(unittest.TestCase):
self.cleared = 1
n = SubNode()
- n.ninfo = SubNodeInfo(n)
+ n.ninfo = SubNodeInfo()
n.built()
assert n.cleared, n.cleared
assert n.ninfo.updated, n.ninfo.cleared
@@ -568,32 +586,56 @@ class NodeTestCase(unittest.TestCase):
def test_get_csig(self):
"""Test generic content signature calculation
"""
- node = SCons.Node.Node()
- node.get_contents = lambda: 444
- result = node.get_csig()
- assert result == '550a141f12de6341fba65b0ad0433500', result
+
+ class TestNodeInfo(SCons.Node.NodeInfoBase):
+ __slots__ = ('csig',)
+ try:
+ SCons.Node.Node.NodeInfo = TestNodeInfo
+ def my_contents(obj):
+ return 444
+ SCons.Node._get_contents_map[4] = my_contents
+ node = SCons.Node.Node()
+ node._func_get_contents = 4
+ result = node.get_csig()
+ assert result == '550a141f12de6341fba65b0ad0433500', result
+ finally:
+ SCons.Node.Node.NodeInfo = SCons.Node.NodeInfoBase
def test_get_cachedir_csig(self):
"""Test content signature calculation for CacheDir
"""
- node = SCons.Node.Node()
- node.get_contents = lambda: 555
- result = node.get_cachedir_csig()
- assert result == '15de21c670ae7c3f6f3f1f37029303c9', result
+ class TestNodeInfo(SCons.Node.NodeInfoBase):
+ __slots__ = ('csig',)
+ try:
+ SCons.Node.Node.NodeInfo = TestNodeInfo
+ def my_contents(obj):
+ return 555
+ SCons.Node._get_contents_map[4] = my_contents
+ node = SCons.Node.Node()
+ node._func_get_contents = 4
+ result = node.get_cachedir_csig()
+ assert result == '15de21c670ae7c3f6f3f1f37029303c9', result
+ finally:
+ SCons.Node.Node.NodeInfo = SCons.Node.NodeInfoBase
def test_get_binfo(self):
"""Test fetching/creating a build information structure
"""
+ class TestNodeInfo(SCons.Node.NodeInfoBase):
+ __slots__ = ('csig',)
+ SCons.Node.Node.NodeInfo = TestNodeInfo
node = SCons.Node.Node()
-
+
binfo = node.get_binfo()
assert isinstance(binfo, SCons.Node.BuildInfoBase), binfo
node = SCons.Node.Node()
d = SCons.Node.Node()
- d.get_ninfo().csig = 777
+ ninfo = d.get_ninfo()
+ assert isinstance(ninfo, SCons.Node.NodeInfoBase), ninfo
i = SCons.Node.Node()
- i.get_ninfo().csig = 888
+ ninfo = i.get_ninfo()
+ assert isinstance(ninfo, SCons.Node.NodeInfoBase), ninfo
node.depends = [d]
node.implicit = [i]
@@ -655,7 +697,7 @@ class NodeTestCase(unittest.TestCase):
"""Test calling the method to store build information
"""
node = SCons.Node.Node()
- node.store_info()
+ SCons.Node.store_info_map[node.store_info](node)
def test_get_stored_info(self):
"""Test calling the method to fetch stored build information
@@ -888,37 +930,37 @@ class NodeTestCase(unittest.TestCase):
s = Scanner()
d1 = MyNode("d1")
d2 = MyNode("d2")
- node.found_includes = [d1, d2]
+ node.Tag('found_includes', [d1, d2])
# Simple return of the found includes
- deps = node.get_implicit_deps(env, s, target)
+ deps = node.get_implicit_deps(env, s, s.path)
assert deps == [d1, d2], deps
# By default, our fake scanner recurses
e = MyNode("eee")
f = MyNode("fff")
g = MyNode("ggg")
- d1.found_includes = [e, f]
- d2.found_includes = [e, f]
- f.found_includes = [g]
- deps = node.get_implicit_deps(env, s, target)
+ d1.Tag('found_includes', [e, f])
+ d2.Tag('found_includes', [e, f])
+ f.Tag('found_includes', [g])
+ deps = node.get_implicit_deps(env, s, s.path)
assert deps == [d1, d2, e, f, g], list(map(str, deps))
# Recursive scanning eliminates duplicates
- e.found_includes = [f]
- deps = node.get_implicit_deps(env, s, target)
+ e.Tag('found_includes', [f])
+ deps = node.get_implicit_deps(env, s, s.path)
assert deps == [d1, d2, e, f, g], list(map(str, deps))
# Scanner method can select specific nodes to recurse
def no_fff(nodes):
return [n for n in nodes if str(n)[0] != 'f']
s.recurse_nodes = no_fff
- deps = node.get_implicit_deps(env, s, target)
+ deps = node.get_implicit_deps(env, s, s.path)
assert deps == [d1, d2, e, f], list(map(str, deps))
# Scanner method can short-circuit recursing entirely
s.recurse_nodes = lambda nodes: []
- deps = node.get_implicit_deps(env, s, target)
+ deps = node.get_implicit_deps(env, s, s.path)
assert deps == [d1, d2], list(map(str, deps))
def test_get_env_scanner(self):
@@ -994,7 +1036,7 @@ class NodeTestCase(unittest.TestCase):
s = Scanner()
d = MyNode("ddd")
- node.found_includes = [d]
+ node.Tag('found_includes', [d])
node.builder.target_scanner = s
assert node.implicit is None
@@ -1207,7 +1249,7 @@ class NodeTestCase(unittest.TestCase):
def test_Annotate(self):
"""Test using an interface-specific Annotate function."""
def my_annotate(node, self=self):
- node.annotation = self.node_string
+ node.Tag('annotation', self.node_string)
save_Annotate = SCons.Node.Annotate
SCons.Node.Annotate = my_annotate
@@ -1215,11 +1257,13 @@ class NodeTestCase(unittest.TestCase):
try:
self.node_string = '#1'
n = SCons.Node.Node()
- assert n.annotation == '#1', n.annotation
+ a = n.GetTag('annotation')
+ assert a == '#1', a
self.node_string = '#2'
n = SCons.Node.Node()
- assert n.annotation == '#2', n.annotation
+ a = n.GetTag('annotation')
+ assert a == '#2', a
finally:
SCons.Node.Annotate = save_Annotate
@@ -1230,7 +1274,7 @@ class NodeTestCase(unittest.TestCase):
n.set_state(3)
n.binfo = 'xyz'
n.includes = 'testincludes'
- n.found_include = {'testkey':'testvalue'}
+ n.Tag('found_includes', {'testkey':'testvalue'})
n.implicit = 'testimplicit'
x = MyExecutor()
diff --git a/src/engine/SCons/Node/Python.py b/src/engine/SCons/Node/Python.py
index 3d8bdaa..f151fc5 100644
--- a/src/engine/SCons/Node/Python.py
+++ b/src/engine/SCons/Node/Python.py
@@ -32,15 +32,49 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import SCons.Node
class ValueNodeInfo(SCons.Node.NodeInfoBase):
- current_version_id = 1
+ __slots__ = ('csig',)
+ current_version_id = 2
field_list = ['csig']
def str_to_node(self, s):
return Value(s)
+ def __getstate__(self):
+ """
+ Return all fields that shall be pickled. Walk the slots in the class
+ hierarchy and add those to the state dictionary. If a '__dict__' slot is
+ available, copy all entries to the dictionary. Also include the version
+ id, which is fixed for all instances of a class.
+ """
+ state = getattr(self, '__dict__', {}).copy()
+ for obj in type(self).mro():
+ for name in getattr(obj,'__slots__',()):
+ if hasattr(self, name):
+ state[name] = getattr(self, name)
+
+ state['_version_id'] = self.current_version_id
+ try:
+ del state['__weakref__']
+ except KeyError:
+ pass
+
+ return state
+
+ def __setstate__(self, state):
+ """
+ Restore the attributes from a pickled state.
+ """
+ # TODO check or discard version
+ del state['_version_id']
+ for key, value in state.items():
+ if key not in ('__weakref__',):
+ setattr(self, key, value)
+
+
class ValueBuildInfo(SCons.Node.BuildInfoBase):
- current_version_id = 1
+ __slots__ = ()
+ current_version_id = 2
class Value(SCons.Node.Node):
"""A class for Python variables, typically passed on the command line
@@ -53,6 +87,8 @@ class Value(SCons.Node.Node):
def __init__(self, value, built_value=None):
SCons.Node.Node.__init__(self)
self.value = value
+ self.changed_since_last_build = 6
+ self.store_info = 0
if built_value is not None:
self.built_value = built_value
diff --git a/src/engine/SCons/Node/PythonTests.py b/src/engine/SCons/Node/PythonTests.py
index fcdfe77..e2e36bf 100644
--- a/src/engine/SCons/Node/PythonTests.py
+++ b/src/engine/SCons/Node/PythonTests.py
@@ -104,13 +104,13 @@ class ValueNodeInfoTestCase(unittest.TestCase):
def test___init__(self):
"""Test ValueNodeInfo initialization"""
vvv = SCons.Node.Python.Value('vvv')
- ni = SCons.Node.Python.ValueNodeInfo(vvv)
+ ni = SCons.Node.Python.ValueNodeInfo()
class ValueBuildInfoTestCase(unittest.TestCase):
def test___init__(self):
"""Test ValueBuildInfo initialization"""
vvv = SCons.Node.Python.Value('vvv')
- bi = SCons.Node.Python.ValueBuildInfo(vvv)
+ bi = SCons.Node.Python.ValueBuildInfo()
if __name__ == "__main__":
suite = unittest.TestSuite()
diff --git a/src/engine/SCons/Node/__init__.py b/src/engine/SCons/Node/__init__.py
index f13fd03..85e30c2 100644
--- a/src/engine/SCons/Node/__init__.py
+++ b/src/engine/SCons/Node/__init__.py
@@ -55,6 +55,8 @@ import SCons.Util
from SCons.Debug import Trace
+print_duplicate = 0
+
def classname(obj):
return str(obj.__class__).split('.')[-1]
@@ -105,6 +107,233 @@ Annotate = do_nothing
# clean builds and update runs (see release_target_info).
interactive = False
+def is_derived_none(node):
+ raise NotImplementedError
+
+def is_derived_node(node):
+ """
+ Returns true if this node is derived (i.e. built).
+ """
+ return node.has_builder() or node.side_effect
+
+_is_derived_map = {0 : is_derived_none,
+ 1 : is_derived_node}
+
+def exists_none(node):
+ raise NotImplementedError
+
+def exists_always(node):
+ return 1
+
+def exists_base(node):
+ return node.stat() is not None
+
+def exists_entry(node):
+ """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."""
+ node.disambiguate()
+ return _exists_map[node._func_exists](node)
+
+def exists_file(node):
+ # Duplicate from source path if we are set up to do this.
+ if node.duplicate and not node.is_derived() and not node.linked:
+ src = node.srcnode()
+ if src is not node:
+ # At this point, src is meant to be copied in a variant directory.
+ src = src.rfile()
+ if src.get_abspath() != node.get_abspath():
+ if src.exists():
+ node.do_duplicate(src)
+ # Can't return 1 here because the duplication might
+ # not actually occur if the -n option is being used.
+ 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 exists_base(node) or node.islink():
+ node.fs.unlink(node.get_internal_path())
+ # Return None explicitly because the Base.exists() call
+ # above will have cached its value if the file existed.
+ return None
+ return exists_base(node)
+
+_exists_map = {0 : exists_none,
+ 1 : exists_always,
+ 2 : exists_base,
+ 3 : exists_entry,
+ 4 : exists_file}
+
+
+def rexists_none(node):
+ raise NotImplementedError
+
+def rexists_node(node):
+ return node.exists()
+
+def rexists_base(node):
+ return node.rfile().exists()
+
+_rexists_map = {0 : rexists_none,
+ 1 : rexists_node,
+ 2 : rexists_base}
+
+def get_contents_none(node):
+ raise NotImplementedError
+
+def get_contents_entry(node):
+ """Fetch the contents of the entry. Returns the exact binary
+ contents of the file."""
+ try:
+ node = node.disambiguate(must_exist=1)
+ except SCons.Errors.UserError:
+ # There was nothing on disk with which to disambiguate
+ # this entry. Leave it as an Entry, but return a null
+ # string so calls to get_contents() in emitters and the
+ # like (e.g. in qt.py) don't have to disambiguate by hand
+ # or catch the exception.
+ return ''
+ else:
+ return _get_contents_map[node._func_get_contents](node)
+
+def get_contents_dir(node):
+ """Return content signatures and names of all our children
+ separated by new-lines. Ensure that the nodes are sorted."""
+ contents = []
+ for n in sorted(node.children(), key=lambda t: t.name):
+ contents.append('%s %s\n' % (n.get_csig(), n.name))
+ return ''.join(contents)
+
+def get_contents_file(node):
+ if not node.rexists():
+ return ''
+ fname = node.rfile().get_abspath()
+ try:
+ contents = open(fname, "rb").read()
+ except EnvironmentError, e:
+ if not e.filename:
+ e.filename = fname
+ raise
+ return contents
+
+_get_contents_map = {0 : get_contents_none,
+ 1 : get_contents_entry,
+ 2 : get_contents_dir,
+ 3 : get_contents_file}
+
+def target_from_source_none(node, prefix, suffix, splitext):
+ raise NotImplementedError
+
+def target_from_source_base(node, prefix, suffix, splitext):
+ return node.dir.Entry(prefix + splitext(node.name)[0] + suffix)
+
+_target_from_source_map = {0 : target_from_source_none,
+ 1 : target_from_source_base}
+
+#
+# The new decider subsystem for Nodes
+#
+# We would set and overwrite the changed_since_last_build function
+# before, but for being able to use slots (less memory!) we now have
+# a dictionary of the different decider functions. Then in the Node
+# subclasses we simply store the index to the decider that should be
+# used by it.
+#
+
+#
+# First, the single decider functions
+#
+def changed_since_last_build_node(node, target, prev_ni):
+ """
+
+ Must be overridden in a specific subclass to return True if this
+ Node (a dependency) has changed since the last time it was used
+ to build the specified target. prev_ni is this Node's state (for
+ example, its file timestamp, length, maybe content signature)
+ as of the last time the target was built.
+
+ Note that this method is called through the dependency, not the
+ target, because a dependency Node must be able to use its own
+ logic to decide if it changed. For example, File Nodes need to
+ obey if we're configured to use timestamps, but Python Value Nodes
+ never use timestamps and always use the content. If this method
+ were called through the target, then each Node's implementation
+ of this method would have to have more complicated logic to
+ handle all the different Node types on which it might depend.
+ """
+ raise NotImplementedError
+
+def changed_since_last_build_alias(node, target, prev_ni):
+ cur_csig = node.get_csig()
+ try:
+ return cur_csig != prev_ni.csig
+ except AttributeError:
+ return 1
+
+def changed_since_last_build_entry(node, target, prev_ni):
+ node.disambiguate()
+ return _decider_map[node.changed_since_last_build](node, target, prev_ni)
+
+def changed_since_last_build_state_changed(node, target, prev_ni):
+ return (node.state != SCons.Node.up_to_date)
+
+def decide_source(node, target, prev_ni):
+ return target.get_build_env().decide_source(node, target, prev_ni)
+
+def decide_target(node, target, prev_ni):
+ return target.get_build_env().decide_target(node, target, prev_ni)
+
+def changed_since_last_build_python(node, target, prev_ni):
+ cur_csig = node.get_csig()
+ try:
+ return cur_csig != prev_ni.csig
+ except AttributeError:
+ return 1
+
+
+#
+# Now, the mapping from indices to decider functions
+#
+_decider_map = {0 : changed_since_last_build_node,
+ 1 : changed_since_last_build_alias,
+ 2 : changed_since_last_build_entry,
+ 3 : changed_since_last_build_state_changed,
+ 4 : decide_source,
+ 5 : decide_target,
+ 6 : changed_since_last_build_python}
+
+do_store_info = True
+
+#
+# The new store_info subsystem for Nodes
+#
+# We would set and overwrite the store_info function
+# before, but for being able to use slots (less memory!) we now have
+# a dictionary of the different functions. Then in the Node
+# subclasses we simply store the index to the info method that should be
+# used by it.
+#
+
+#
+# First, the single info functions
+#
+
+def store_info_pass(node):
+ pass
+
+def store_info_file(node):
+ # Merge our build information into the already-stored entry.
+ # This accommodates "chained builds" where a file that's a target
+ # in one build (SConstruct file) is a source in a different build.
+ # See test/chained-build.py for the use case.
+ if do_store_info:
+ node.dir.sconsign().store_info(node.name, node)
+
+
+store_info_map = {0 : store_info_pass,
+ 1 : store_info_file}
+
# Classes for signature info for Nodes.
class NodeInfoBase(object):
@@ -114,11 +343,8 @@ class NodeInfoBase(object):
Node subclasses should subclass NodeInfoBase to provide their own
logic for dealing with their own Node-specific signature information.
"""
- current_version_id = 1
- def __init__(self, node=None):
- # Create an object attribute from the class attribute so it ends up
- # in the pickled data in the .sconsign file.
- self._version_id = self.current_version_id
+ __slots__ = ('__weakref__',)
+ current_version_id = 2
def update(self, node):
try:
field_list = self.field_list
@@ -138,13 +364,25 @@ class NodeInfoBase(object):
def convert(self, node, val):
pass
def merge(self, other):
- self.__dict__.update(other.__dict__)
+ """
+ Merge the fields of another object into this object. Already existing
+ information is overwritten by the other instance's data.
+ WARNING: If a '__dict__' slot is added, it should be updated instead of
+ replaced.
+ """
+ state = other.__getstate__()
+ self.__setstate__(state)
def format(self, field_list=None, names=0):
if field_list is None:
try:
field_list = self.field_list
except AttributeError:
- field_list = sorted(self.__dict__.keys())
+ field_list = getattr(self, '__dict__', {}).keys()
+ for obj in type(self).mro():
+ for slot in getattr(obj, '__slots__', ()):
+ if slot not in ('__weakref__', '__dict__'):
+ field_list.append(slot)
+ field_list.sort()
fields = []
for field in field_list:
try:
@@ -157,6 +395,38 @@ class NodeInfoBase(object):
fields.append(f)
return fields
+ def __getstate__(self):
+ """
+ Return all fields that shall be pickled. Walk the slots in the class
+ hierarchy and add those to the state dictionary. If a '__dict__' slot is
+ available, copy all entries to the dictionary. Also include the version
+ id, which is fixed for all instances of a class.
+ """
+ state = getattr(self, '__dict__', {}).copy()
+ for obj in type(self).mro():
+ for name in getattr(obj,'__slots__',()):
+ if hasattr(self, name):
+ state[name] = getattr(self, name)
+
+ state['_version_id'] = self.current_version_id
+ try:
+ del state['__weakref__']
+ except KeyError:
+ pass
+ return state
+
+ def __setstate__(self, state):
+ """
+ Restore the attributes from a pickled state. The version is discarded.
+ """
+ # TODO check or discard version
+ del state['_version_id']
+
+ for key, value in state.items():
+ if key not in ('__weakref__',):
+ setattr(self, key, value)
+
+
class BuildInfoBase(object):
"""
The generic base class for build information for a Node.
@@ -167,30 +437,106 @@ class BuildInfoBase(object):
generic build stuff we have to track: sources, explicit dependencies,
implicit dependencies, and action information.
"""
- current_version_id = 1
- def __init__(self, node=None):
+ __slots__ = ("bsourcesigs", "bdependsigs", "bimplicitsigs", "bactsig",
+ "bsources", "bdepends", "bact", "bimplicit", "__weakref__")
+ current_version_id = 2
+ def __init__(self):
# Create an object attribute from the class attribute so it ends up
# in the pickled data in the .sconsign file.
- self._version_id = self.current_version_id
self.bsourcesigs = []
self.bdependsigs = []
self.bimplicitsigs = []
self.bactsig = None
def merge(self, other):
- self.__dict__.update(other.__dict__)
+ """
+ Merge the fields of another object into this object. Already existing
+ information is overwritten by the other instance's data.
+ WARNING: If a '__dict__' slot is added, it should be updated instead of
+ replaced.
+ """
+ state = other.__getstate__()
+ self.__setstate__(state)
+
+ def __getstate__(self):
+ """
+ Return all fields that shall be pickled. Walk the slots in the class
+ hierarchy and add those to the state dictionary. If a '__dict__' slot is
+ available, copy all entries to the dictionary. Also include the version
+ id, which is fixed for all instances of a class.
+ """
+ state = getattr(self, '__dict__', {}).copy()
+ for obj in type(self).mro():
+ for name in getattr(obj,'__slots__',()):
+ if hasattr(self, name):
+ state[name] = getattr(self, name)
+
+ state['_version_id'] = self.current_version_id
+ try:
+ del state['__weakref__']
+ except KeyError:
+ pass
+ return state
+
+ def __setstate__(self, state):
+ """
+ Restore the attributes from a pickled state.
+ """
+ # TODO check or discard version
+ del state['_version_id']
+ for key, value in state.items():
+ if key not in ('__weakref__',):
+ setattr(self, key, value)
class Node(object):
"""The base Node class, for entities that we know how to
build, or use to build other Nodes.
"""
- if SCons.Memoize.use_memoizer:
- __metaclass__ = SCons.Memoize.Memoized_Metaclass
-
- memoizer_counters = []
+ __slots__ = ['sources',
+ 'sources_set',
+ '_specific_sources',
+ 'depends',
+ 'depends_set',
+ 'ignore',
+ 'ignore_set',
+ 'prerequisites',
+ 'implicit',
+ 'waiting_parents',
+ 'waiting_s_e',
+ 'ref_count',
+ 'wkids',
+ 'env',
+ 'state',
+ 'precious',
+ 'noclean',
+ 'nocache',
+ 'cached',
+ 'always_build',
+ 'includes',
+ 'attributes',
+ 'side_effect',
+ 'side_effects',
+ 'linked',
+ '_memo',
+ 'executor',
+ 'binfo',
+ 'ninfo',
+ 'builder',
+ 'is_explicit',
+ 'implicit_set',
+ 'changed_since_last_build',
+ 'store_info',
+ 'pseudo',
+ '_tags',
+ '_func_is_derived',
+ '_func_exists',
+ '_func_rexists',
+ '_func_get_contents',
+ '_func_target_from_source']
class Attrs(object):
- pass
+ __slots__ = ('shared', '__dict__')
+
def __init__(self):
if SCons.Debug.track_instances: logInstanceCreation(self, 'Node.Node')
@@ -234,7 +580,15 @@ class Node(object):
self.side_effect = 0 # true iff this node is a side effect
self.side_effects = [] # the side effects of building this target
self.linked = 0 # is this node linked to the variant directory?
-
+ self.changed_since_last_build = 0
+ self.store_info = 0
+ self._tags = None
+ self._func_is_derived = 1
+ self._func_exists = 1
+ self._func_rexists = 1
+ self._func_get_contents = 0
+ self._func_target_from_source = 0
+
self.clear_memoized_values()
# Let the interface in which the build engine is embedded
@@ -248,8 +602,7 @@ class Node(object):
def get_suffix(self):
return ''
- memoizer_counters.append(SCons.Memoize.CountValue('get_build_env'))
-
+ @SCons.Memoize.CountMethodCall
def get_build_env(self):
"""Fetch the appropriate Environment to build this node.
"""
@@ -418,7 +771,7 @@ class Node(object):
pass
else:
self.ninfo.update(self)
- self.store_info()
+ SCons.Node.store_info_map[self.store_info](self)
def release_target_info(self):
"""Called just after this node has been marked
@@ -546,7 +899,7 @@ class Node(object):
example: source with source builders are not derived in this sense,
and hence should not return true.
"""
- return self.has_builder() or self.side_effect
+ return _is_derived_map[self._func_is_derived](self)
def alter_targets(self):
"""Return a list of alternate targets for this Node.
@@ -563,35 +916,56 @@ class Node(object):
"""
return []
- def get_implicit_deps(self, env, scanner, path):
+ def get_implicit_deps(self, env, initial_scanner, path_func, kw = {}):
"""Return a list of implicit dependencies for this node.
This method exists to handle recursive invocation of the scanner
on the implicit dependencies returned by the scanner, if the
scanner's recursive flag says that we should.
"""
- if not scanner:
- return []
-
- # Give the scanner a chance to select a more specific scanner
- # for this Node.
- #scanner = scanner.select(self)
-
nodes = [self]
seen = {}
seen[self] = 1
- deps = []
- while nodes:
- n = nodes.pop(0)
- d = [x for x in n.get_found_includes(env, scanner, path) if x not in seen]
- if d:
- deps.extend(d)
- for n in d:
- seen[n] = 1
- nodes.extend(scanner.recurse_nodes(d))
+ dependencies = []
- return deps
+ root_node_scanner = self._get_scanner(env, initial_scanner, None, kw)
+ while nodes:
+ node = nodes.pop(0)
+
+ scanner = node._get_scanner(env, initial_scanner, root_node_scanner, kw)
+
+ if not scanner:
+ continue
+
+ path = path_func(scanner)
+
+ included_deps = [x for x in node.get_found_includes(env, scanner, path) if x not in seen]
+ if included_deps:
+ dependencies.extend(included_deps)
+ for dep in included_deps:
+ seen[dep] = 1
+ nodes.extend(scanner.recurse_nodes(included_deps))
+
+ return dependencies
+
+ def _get_scanner(self, env, initial_scanner, root_node_scanner, kw):
+ if not initial_scanner:
+ # handle implicit scanner case
+ scanner = self.get_env_scanner(env, kw)
+ if scanner:
+ scanner = scanner.select(self)
+ else:
+ # handle explicit scanner case
+ scanner = initial_scanner.select(self)
+
+ if not scanner:
+ # no scanner could be found for the given node's scanner key;
+ # thus, make an attempt at using a default.
+ scanner = root_node_scanner
+
+ return scanner
+
def get_env_scanner(self, env, kw={}):
return env.get_scanner(self.scanner_key())
@@ -706,7 +1080,7 @@ class Node(object):
BuildInfo = BuildInfoBase
def new_ninfo(self):
- ninfo = self.NodeInfo(self)
+ ninfo = self.NodeInfo()
return ninfo
def get_ninfo(self):
@@ -717,7 +1091,7 @@ class Node(object):
return self.ninfo
def new_binfo(self):
- binfo = self.BuildInfo(self)
+ binfo = self.BuildInfo()
return binfo
def get_binfo(self):
@@ -802,14 +1176,6 @@ class Node(object):
def get_cachedir_csig(self):
return self.get_csig()
- def store_info(self):
- """Make the build signature permanent (that is, store it in the
- .sconsign file or equivalent)."""
- pass
-
- def do_not_store_info(self):
- pass
-
def get_stored_info(self):
return None
@@ -847,13 +1213,16 @@ class Node(object):
def exists(self):
"""Does this node exists?"""
- # All node exist by default:
- return 1
+ return _exists_map[self._func_exists](self)
def rexists(self):
"""Does this node exist locally or in a repositiory?"""
# There are no repositories by default:
- return self.exists()
+ return _rexists_map[self._func_rexists](self)
+
+ def get_contents(self):
+ """Fetch the contents of the entry."""
+ return _get_contents_map[self._func_get_contents](self)
def missing(self):
return not self.is_derived() and \
@@ -912,11 +1281,6 @@ class Node(object):
def _add_child(self, collection, set, child):
"""Adds 'child' to 'collection', first checking 'set' to see if it's
already present."""
- #if type(child) is not type([]):
- # child = [child]
- #for c in child:
- # if not isinstance(c, Node):
- # raise TypeError, c
added = None
for c in child:
if c not in set:
@@ -941,11 +1305,10 @@ class Node(object):
# build info that it's cached so we can re-calculate it.
self.executor_cleanup()
- memoizer_counters.append(SCons.Memoize.CountValue('_children_get'))
-
+ @SCons.Memoize.CountMethodCall
def _children_get(self):
try:
- return self._memo['children_get']
+ return self._memo['_children_get']
except KeyError:
pass
@@ -976,7 +1339,7 @@ class Node(object):
else:
children = self.all_children(scan=0)
- self._memo['children_get'] = children
+ self._memo['_children_get'] = children
return children
def all_children(self, scan=1):
@@ -1016,9 +1379,6 @@ class Node(object):
def get_state(self):
return self.state
- def state_has_changed(self, target, prev_ni):
- return (self.state != SCons.Node.up_to_date)
-
def get_env(self):
env = self.env
if not env:
@@ -1026,28 +1386,28 @@ class Node(object):
env = SCons.Defaults.DefaultEnvironment()
return env
- def changed_since_last_build(self, target, prev_ni):
- """
-
- Must be overridden in a specific subclass to return True if this
- Node (a dependency) has changed since the last time it was used
- to build the specified target. prev_ni is this Node's state (for
- example, its file timestamp, length, maybe content signature)
- as of the last time the target was built.
-
- Note that this method is called through the dependency, not the
- target, because a dependency Node must be able to use its own
- logic to decide if it changed. For example, File Nodes need to
- obey if we're configured to use timestamps, but Python Value Nodes
- never use timestamps and always use the content. If this method
- were called through the target, then each Node's implementation
- of this method would have to have more complicated logic to
- handle all the different Node types on which it might depend.
- """
- raise NotImplementedError
-
def Decider(self, function):
- SCons.Util.AddMethod(self, function, 'changed_since_last_build')
+ foundkey = None
+ for k, v in _decider_map.iteritems():
+ if v == function:
+ foundkey = k
+ break
+ if not foundkey:
+ foundkey = len(_decider_map)
+ _decider_map[foundkey] = function
+ self.changed_since_last_build = foundkey
+
+ def Tag(self, key, value):
+ """ Add a user-defined tag. """
+ if not self._tags:
+ self._tags = {}
+ self._tags[key] = value
+
+ def GetTag(self, key):
+ """ Return a user-defined tag. """
+ if not self._tags:
+ return None
+ return self._tags.get(key, None)
def changed(self, node=None, allowcache=False):
"""
@@ -1095,7 +1455,7 @@ class Node(object):
result = True
for child, prev_ni in zip(children, then):
- if child.changed_since_last_build(self, prev_ni):
+ if _decider_map[child.changed_since_last_build](child, self, prev_ni):
if t: Trace(': %s changed' % child)
result = True
@@ -1266,7 +1626,7 @@ class Node(object):
for k in new_bkids:
if not k in old_bkids:
lines.append("`%s' is a new dependency\n" % stringify(k))
- elif k.changed_since_last_build(self, osig[k]):
+ elif _decider_map[k.changed_since_last_build](k, self, osig[k]):
lines.append("`%s' changed\n" % stringify(k))
if len(lines) == 0 and old_bkids != new_bkids: