summaryrefslogtreecommitdiff
path: root/src/engine/SCons/Node
diff options
context:
space:
mode:
Diffstat (limited to 'src/engine/SCons/Node')
-rw-r--r--src/engine/SCons/Node/Alias.py51
-rw-r--r--src/engine/SCons/Node/AliasTests.py6
-rw-r--r--src/engine/SCons/Node/FS.py592
-rw-r--r--src/engine/SCons/Node/FSTests.py274
-rw-r--r--src/engine/SCons/Node/NodeTests.py131
-rw-r--r--src/engine/SCons/Node/Python.py42
-rw-r--r--src/engine/SCons/Node/PythonTests.py6
-rw-r--r--src/engine/SCons/Node/__init__.py474
8 files changed, 1102 insertions, 474 deletions
diff --git a/src/engine/SCons/Node/Alias.py b/src/engine/SCons/Node/Alias.py
index 0bd6422..7e13b00 100644
--- a/src/engine/SCons/Node/Alias.py
+++ b/src/engine/SCons/Node/Alias.py
@@ -30,7 +30,7 @@ This creates a hash of global Aliases (dummy targets).
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
-__revision__ = "src/engine/SCons/Node/Alias.py rel_2.3.5:3347:d31d5a4e74b6 2015/07/31 14:36:10 bdbaddog"
+__revision__ = "src/engine/SCons/Node/Alias.py rel_2.4.0:3365:9259ea1c13d7 2015/09/21 14:03:43 bdbaddog"
import collections
@@ -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 6b26f9b..0553c6f 100644
--- a/src/engine/SCons/Node/AliasTests.py
+++ b/src/engine/SCons/Node/AliasTests.py
@@ -21,7 +21,7 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
-__revision__ = "src/engine/SCons/Node/AliasTests.py rel_2.3.5:3347:d31d5a4e74b6 2015/07/31 14:36:10 bdbaddog"
+__revision__ = "src/engine/SCons/Node/AliasTests.py rel_2.4.0:3365:9259ea1c13d7 2015/09/21 14:03:43 bdbaddog"
import sys
import unittest
@@ -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 8d71f7e..b6a1bb5 100644
--- a/src/engine/SCons/Node/FS.py
+++ b/src/engine/SCons/Node/FS.py
@@ -32,7 +32,7 @@ that can be used by scripts or modules looking for the canonical default.
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-__revision__ = "src/engine/SCons/Node/FS.py rel_2.3.5:3347:d31d5a4e74b6 2015/07/31 14:36:10 bdbaddog"
+__revision__ = "src/engine/SCons/Node/FS.py rel_2.4.0:3365:9259ea1c13d7 2015/09/21 14:03:43 bdbaddog"
import fnmatch
import os
@@ -56,10 +56,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
@@ -268,8 +281,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)
@@ -302,7 +315,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)
@@ -310,7 +323,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)
@@ -403,7 +416,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
@@ -573,7 +586,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.
@@ -591,27 +617,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__() + '"'
@@ -624,17 +649,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."""
@@ -643,8 +689,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']
@@ -681,21 +726,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()
@@ -717,7 +761,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:
@@ -752,7 +796,7 @@ 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:
@@ -785,7 +829,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,
@@ -811,13 +874,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
@@ -856,8 +918,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']
@@ -878,6 +939,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
@@ -887,6 +959,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
@@ -917,7 +1011,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
@@ -939,17 +1033,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.
@@ -989,10 +1073,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()
@@ -1003,9 +1084,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)
@@ -1019,9 +1097,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
@@ -1101,8 +1176,6 @@ class LocalFS(object):
class FS(LocalFS):
- memoizer_counters = []
-
def __init__(self, path = None):
"""Initialize the Node.FS subsystem.
@@ -1128,8 +1201,8 @@ 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
@@ -1160,7 +1233,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
@@ -1249,9 +1322,9 @@ class FS(LocalFS):
# 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)
@@ -1285,9 +1358,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)
@@ -1393,7 +1466,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()
@@ -1412,8 +1485,9 @@ class FS(LocalFS):
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
@@ -1425,11 +1499,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('[*?[]')
@@ -1440,7 +1515,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
@@ -1470,6 +1560,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
@@ -1562,8 +1668,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'])
@@ -1589,7 +1694,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):
@@ -1598,8 +1703,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.
"""
@@ -1628,7 +1732,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:
@@ -1643,10 +1747,10 @@ 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)
@@ -1713,7 +1817,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:
@@ -1753,10 +1857,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
@@ -1770,8 +1871,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."""
@@ -1795,12 +1894,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
@@ -1817,17 +1912,34 @@ 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
@@ -1841,7 +1953,7 @@ class Dir(Base):
except AttributeError:
d = {}
try:
- entries = os.listdir(self.abspath)
+ entries = os.listdir(self._abspath)
except OSError:
pass
else:
@@ -1854,7 +1966,7 @@ class Dir(Base):
if result is None:
# Belt-and-suspenders for Windows: check directly for
# 8.3 file names that don't show up in os.listdir().
- result = os.path.exists(self.abspath + OS_SEP + name)
+ result = os.path.exists(self._abspath + OS_SEP + name)
d[name] = result
return result
else:
@@ -1887,8 +1999,7 @@ class Dir(Base):
break
return rentry_exists
- memoizer_counters.append(SCons.Memoize.CountValue('srcdir_list'))
-
+ @SCons.Memoize.CountMethodCall
def srcdir_list(self):
try:
return self._memo['srcdir_list']
@@ -1929,8 +2040,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']
@@ -2105,7 +2215,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)
@@ -2151,18 +2261,12 @@ 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 == '':
@@ -2178,33 +2282,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
@@ -2253,19 +2409,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:
@@ -2283,7 +2439,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']
@@ -2298,11 +2455,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):
"""
@@ -2317,7 +2506,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:
@@ -2358,6 +2547,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):
@@ -2371,6 +2562,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)
@@ -2378,7 +2571,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
@@ -2429,6 +2637,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
@@ -2440,22 +2656,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 ''
- fname = self.rfile().abspath
- try:
- contents = open(fname, "rb").read()
- except EnvironmentError, 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
@@ -2482,7 +2689,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)
@@ -2492,9 +2699,7 @@ class File(Base):
raise
return cs
-
- memoizer_counters.append(SCons.Memoize.CountValue('get_size'))
-
+ @SCons.Memoize.CountMethodCall
def get_size(self):
try:
return self._memo['get_size']
@@ -2510,8 +2715,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']
@@ -2527,14 +2731,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',
@@ -2647,8 +2843,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']
@@ -2688,8 +2883,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
@@ -2773,9 +2967,9 @@ 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
@@ -2875,7 +3069,7 @@ class File(Base):
def _rmv_existing(self):
self.clear_memoized_values()
- if print_duplicate:
+ if SCons.Node.print_duplicate:
print "dup: removing existing target %s"%self
e = Unlink(self, [], None)
if isinstance(e, SCons.Errors.BuildError):
@@ -2911,18 +3105,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:
+ if SCons.Node.print_duplicate:
print "dup: relinking variant '%s' from '%s'"%(self, src)
Unlink(self, None, None)
e = Link(self, src, None)
if isinstance(e, SCons.Errors.BuildError):
- desc = "Cannot duplicate `%s' in `%s': %s." % (src.path, self.dir.path, e.errstr)
+ desc = "Cannot duplicate `%s' in `%s': %s." % (src.get_internal_path(), self.dir._path, e.errstr)
raise SCons.Errors.StopError(desc)
self.linked = 1
# The Link() action may or may not have actually
@@ -2931,36 +3125,14 @@ 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
@@ -3037,7 +3209,7 @@ 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.
@@ -3054,10 +3226,10 @@ class File(Base):
if (not SCons.Node.interactive and
not hasattr(self.attributes, 'keep_targetinfo')):
# Ensure that the build infos get computed and cached...
- self.store_info()
+ 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
@@ -3116,16 +3288,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)
@@ -3143,7 +3305,7 @@ class File(Base):
e = LocalCopy(self, r, None)
if isinstance(e, SCons.Errors.BuildError):
raise
- self.store_info()
+ SCons.Node.store_info_map[self.store_info](self)
if T: Trace(' 1\n')
return 1
self.changed()
@@ -3154,8 +3316,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']
@@ -3257,7 +3418,7 @@ class File(Base):
# 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
@@ -3273,10 +3434,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 = {}
@@ -3319,8 +3476,7 @@ 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]
diff --git a/src/engine/SCons/Node/FSTests.py b/src/engine/SCons/Node/FSTests.py
index fde9550..7cda32d 100644
--- a/src/engine/SCons/Node/FSTests.py
+++ b/src/engine/SCons/Node/FSTests.py
@@ -22,7 +22,7 @@
#
from __future__ import division
-__revision__ = "src/engine/SCons/Node/FSTests.py rel_2.3.5:3347:d31d5a4e74b6 2015/07/31 14:36:10 bdbaddog"
+__revision__ = "src/engine/SCons/Node/FSTests.py rel_2.4.0:3365:9259ea1c13d7 2015/09/21 14:03:43 bdbaddog"
import SCons.compat
@@ -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")
@@ -765,37 +765,41 @@ 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()
- s1sig = SCons.Node.FS.FileNodeInfo(self.fs.File('n1'))
+ self.fs.File('n1')
+ self.fs.File('n2')
+ self.fs.File('n3')
+
+ 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 +957,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 +1049,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 +1140,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 +1171,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 +1224,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 +1288,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 +1593,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 +1695,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 +1796,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 +1835,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 +1984,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 +2028,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
@@ -2171,8 +2175,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")
@@ -2184,14 +2192,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))
@@ -2245,14 +2253,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')
@@ -2432,7 +2440,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
@@ -2441,7 +2449,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)
@@ -2535,7 +2543,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):
@@ -2906,8 +2914,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')
@@ -2931,19 +2945,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
@@ -2953,8 +2967,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')
@@ -2978,19 +2998,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
@@ -3244,9 +3264,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
@@ -3360,7 +3384,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()
@@ -3373,7 +3397,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'
@@ -3552,7 +3576,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 422d905..3a896ce 100644
--- a/src/engine/SCons/Node/NodeTests.py
+++ b/src/engine/SCons/Node/NodeTests.py
@@ -20,7 +20,7 @@
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-__revision__ = "src/engine/SCons/Node/NodeTests.py rel_2.3.5:3347:d31d5a4e74b6 2015/07/31 14:36:10 bdbaddog"
+__revision__ = "src/engine/SCons/Node/NodeTests.py rel_2.4.0:3365:9259ea1c13d7 2015/09/21 14:03:43 bdbaddog"
import SCons.compat
@@ -184,7 +184,7 @@ class Scanner(object):
called = None
def __call__(self, node):
self.called = 1
- return node.found_includes
+ return node.GetTag('found_includes')
def path(self, env, dir, target=None, source=None):
return ()
def select(self, node):
@@ -200,7 +200,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 +224,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 +244,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 +279,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):
@@ -427,6 +439,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 +447,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 +581,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 +692,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,7 +925,7 @@ 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)
@@ -898,14 +935,14 @@ class NodeTestCase(unittest.TestCase):
e = MyNode("eee")
f = MyNode("fff")
g = MyNode("ggg")
- d1.found_includes = [e, f]
- d2.found_includes = [e, f]
- f.found_includes = [g]
+ d1.Tag('found_includes', [e, f])
+ d2.Tag('found_includes', [e, f])
+ f.Tag('found_includes', [g])
deps = node.get_implicit_deps(env, s, target)
assert deps == [d1, d2, e, f, g], list(map(str, deps))
# Recursive scanning eliminates duplicates
- e.found_includes = [f]
+ e.Tag('found_includes', [f])
deps = node.get_implicit_deps(env, s, target)
assert deps == [d1, d2, e, f, g], list(map(str, deps))
@@ -994,7 +1031,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 +1244,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 +1252,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 +1269,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 99773ab..b247ff6 100644
--- a/src/engine/SCons/Node/Python.py
+++ b/src/engine/SCons/Node/Python.py
@@ -27,20 +27,54 @@ Python nodes.
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
-__revision__ = "src/engine/SCons/Node/Python.py rel_2.3.5:3347:d31d5a4e74b6 2015/07/31 14:36:10 bdbaddog"
+__revision__ = "src/engine/SCons/Node/Python.py rel_2.4.0:3365:9259ea1c13d7 2015/09/21 14:03:43 bdbaddog"
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 9544382..7163b91 100644
--- a/src/engine/SCons/Node/PythonTests.py
+++ b/src/engine/SCons/Node/PythonTests.py
@@ -21,7 +21,7 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
-__revision__ = "src/engine/SCons/Node/PythonTests.py rel_2.3.5:3347:d31d5a4e74b6 2015/07/31 14:36:10 bdbaddog"
+__revision__ = "src/engine/SCons/Node/PythonTests.py rel_2.4.0:3365:9259ea1c13d7 2015/09/21 14:03:43 bdbaddog"
import sys
import unittest
@@ -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 a261ee7..79db894 100644
--- a/src/engine/SCons/Node/__init__.py
+++ b/src/engine/SCons/Node/__init__.py
@@ -41,7 +41,7 @@ be able to depend on any other type of "thing."
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-__revision__ = "src/engine/SCons/Node/__init__.py rel_2.3.5:3347:d31d5a4e74b6 2015/07/31 14:36:10 bdbaddog"
+__revision__ = "src/engine/SCons/Node/__init__.py rel_2.4.0:3365:9259ea1c13d7 2015/09/21 14:03:43 bdbaddog"
import collections
import copy
@@ -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.
@@ -706,7 +1059,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 +1070,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 +1155,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 +1192,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 \
@@ -941,11 +1289,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 +1323,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 +1363,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 +1370,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 +1439,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 +1610,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: