summaryrefslogtreecommitdiffstats
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/FS.py174
-rw-r--r--src/engine/SCons/Node/FSTests.py94
-rw-r--r--src/engine/SCons/Node/__init__.py10
3 files changed, 195 insertions, 83 deletions
diff --git a/src/engine/SCons/Node/FS.py b/src/engine/SCons/Node/FS.py
index 12b8869..0951935 100644
--- a/src/engine/SCons/Node/FS.py
+++ b/src/engine/SCons/Node/FS.py
@@ -291,20 +291,21 @@ class EntryProxy(SCons.Util.Proxy):
except KeyError:
return SCons.Util.Proxy.__getattr__(self, name)
-class Entry(SCons.Node.Node):
+class Base(SCons.Node.Node):
"""A generic class for file system entries. This class is for
when we don't know yet whether the entry being looked up is a file
or a directory. Instances of this class can morph into either
Dir or File objects by a later, more precise lookup.
- Note: this class does not define __cmp__ and __hash__ for efficiency
- reasons. SCons does a lot of comparing of Entry objects, and so that
- operation must be as fast as possible, which means we want to use
- Python's built-in object identity comparison.
+ Note: this class does not define __cmp__ and __hash__ for
+ efficiency reasons. SCons does a lot of comparing of
+ Node.FS.{Base,Entry,File,Dir} objects, so those operations must be
+ as fast as possible, which means we want to use Python's built-in
+ object identity comparisons.
"""
def __init__(self, name, directory, fs):
- """Initialize a generic file system Entry.
+ """Initialize a generic Node.FS.Base object.
Call the superclass initialization, take care of setting up
our relative and absolute paths, identify our parent
@@ -331,9 +332,9 @@ class Entry(SCons.Node.Node):
self.duplicate = directory.duplicate
def clear(self):
- """Completely clear an Entry of all its cached state (so that it
- can be re-evaluated by interfaces that do continuous integration
- builds).
+ """Completely clear a Node.FS.Base object of all its cached
+ state (so that it can be re-evaluated by interfaces that do
+ continuous integration builds).
"""
SCons.Node.Node.clear(self)
try:
@@ -349,27 +350,12 @@ class Entry(SCons.Node.Node):
return self.dir
def __str__(self):
- """A FS node's string representation is its path name."""
+ """A Node.FS.Base object's string representation is its path
+ name."""
if self.duplicate or self.is_derived():
return self.get_path()
return self.srcnode().get_path()
- def get_contents(self):
- """Fetch the contents of the entry.
-
- Since this should return the real contents from the file
- system, we check to see into what sort of subclass we should
- morph this Entry."""
- if os.path.isfile(self.abspath):
- self.__class__ = File
- self._morph()
- return File.get_contents(self)
- if os.path.isdir(self.abspath):
- self.__class__ = Dir
- self._morph()
- return Dir.get_contents(self)
- raise AttributeError
-
def exists(self):
try:
return self._exists
@@ -438,7 +424,7 @@ class Entry(SCons.Node.Node):
def get_path(self, dir=None):
"""Return path relative to the current working directory of the
- FS object that owns us."""
+ Node.FS.Base object that owns us."""
if not dir:
dir = self.fs.getcwd()
try:
@@ -490,6 +476,75 @@ class Entry(SCons.Node.Node):
self._proxy = ret
return ret
+class Entry(Base):
+ """This is the class for generic Node.FS entries--that is, things
+ that could be a File or a Dir, but we're just not sure yet.
+ Consequently, the methods in this class really exist just to
+ transform their associated object into the right class when the
+ time comes, and then call the same-named method in the transformed
+ class."""
+
+ def rfile(self):
+ """We're a generic Entry, but the caller is actually looking for
+ a File at this point, so morph into one."""
+ self.__class__ = File
+ self._morph()
+ self.clear()
+ return File.rfile(self)
+
+ def get_found_includes(self, env, scanner, target):
+ """If we're looking for included files, it's because this Entry
+ is really supposed to be a File itself."""
+ node = self.rfile()
+ return node.get_found_includes(env, scanner, target)
+
+ def scanner_key(self):
+ return os.path.splitext(self.name)[1]
+
+ def get_contents(self):
+ """Fetch the contents of the entry.
+
+ Since this should return the real contents from the file
+ system, we check to see into what sort of subclass we should
+ morph this Entry."""
+ if os.path.isfile(self.abspath):
+ self.__class__ = File
+ self._morph()
+ return File.get_contents(self)
+ if os.path.isdir(self.abspath):
+ self.__class__ = Dir
+ self._morph()
+ return Dir.get_contents(self)
+ raise AttributeError
+
+ 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."""
+ if os.path.isdir(self.abspath):
+ self.__class__ = Dir
+ self._morph()
+ return Dir.exists(self)
+ else:
+ self.__class__ = File
+ self._morph()
+ self.clear()
+ return File.exists(self)
+
+ def calc_signature(self, calc):
+ """Return the Entry's calculated signature. Check the file
+ system to see what we should turn into first. Assume a file if
+ there's no directory."""
+ if os.path.isdir(self.abspath):
+ self.__class__ = Dir
+ self._morph()
+ return Dir.calc_signature(self, calc)
+ else:
+ self.__class__ = File
+ self._morph()
+ self.clear()
+ return File.calc_signature(self, calc)
+
# This is for later so we can differentiate between Entry the class and Entry
# the method of the FS class.
_classEntry = Entry
@@ -680,7 +735,7 @@ class FS:
if not klass:
klass = Entry
- if isinstance(name, Entry):
+ if isinstance(name, Base):
return self.__checkClass(name, klass)
else:
if directory and not isinstance(directory, Dir):
@@ -847,18 +902,12 @@ class FS:
message = "building associated BuildDir targets: %s" % string.join(map(str, targets))
return targets, message
-# XXX TODO?
-# Annotate with the creator
-# rel_path
-# linked_targets
-# is_accessible
-
-class Dir(Entry):
+class Dir(Base):
"""A class for directories in a file system.
"""
def __init__(self, name, directory, fs):
- Entry.__init__(self, name, directory, fs)
+ Base.__init__(self, name, directory, fs)
self._morph()
def _morph(self):
@@ -883,7 +932,7 @@ class Dir(Entry):
self.builder = 1
self._sconsign = None
self.build_dirs = []
-
+
def __clearRepositoryCache(self, duplicate=None):
"""Called when we change the repository(ies) for a directory.
This clears any cached information that is invalidated by changing
@@ -998,6 +1047,10 @@ class Dir(Entry):
"""
return self.fs.build_dir_target_climb(self, [])
+ def scanner_key(self):
+ """A directory does not get scanned."""
+ return None
+
def calc_signature(self, calc):
"""A directory has no signature."""
return None
@@ -1055,20 +1108,13 @@ class Dir(Entry):
have a srcdir attribute set, then that *is* our srcnode."""
if self.srcdir:
return self.srcdir
- return Entry.srcnode(self)
+ return Base.srcnode(self)
-# XXX TODO?
-# base_suf
-# suffix
-# addsuffix
-# accessible
-# relpath
-
-class File(Entry):
+class File(Base):
"""A class for files in a file system.
"""
def __init__(self, name, directory, fs):
- Entry.__init__(self, name, directory, fs)
+ Base.__init__(self, name, directory, fs)
self._morph()
def Entry(self, name):
@@ -1107,6 +1153,9 @@ class File(Entry):
def root(self):
return self.dir.root()
+ def scanner_key(self):
+ return os.path.splitext(self.name)[1]
+
def get_contents(self):
if not self.rexists():
return ''
@@ -1118,29 +1167,6 @@ class File(Entry):
else:
return 0
- def calc_signature(self, calc):
- """
- Select and calculate the appropriate build signature for a File.
-
- self - the File node
- calc - the signature calculation module
- returns - the signature
- """
- try:
- return self._calculated_sig
- except AttributeError:
- if self.is_derived():
- if SCons.Sig.build_signature:
- sig = self.rfile().calc_bsig(calc, self)
- else:
- sig = self.rfile().calc_csig(calc, self)
- elif not self.rexists():
- sig = None
- else:
- sig = self.rfile().calc_csig(calc, self)
- self._calculated_sig = sig
- return sig
-
def store_csig(self):
self.dir.sconsign().set_csig(self.name, self.get_csig())
@@ -1192,9 +1218,6 @@ class File(Entry):
return includes
- def scanner_key(self):
- return os.path.splitext(self.name)[1]
-
def _createDir(self):
# ensure that the directories for this node are
# created.
@@ -1328,7 +1351,6 @@ class File(Entry):
def prepare(self):
"""Prepare for this file to be created."""
-
SCons.Node.Node.prepare(self)
if self.get_state() != SCons.Node.up_to_date:
@@ -1385,7 +1407,7 @@ class File(Entry):
delattr(self, '_rexists')
except AttributeError:
pass
- return Entry.exists(self)
+ return Base.exists(self)
def current(self, calc):
bsig = calc.bsig(self)
@@ -1457,7 +1479,7 @@ def find_file(filename, paths, node_factory = default_fs.File):
node = node_factory(filename, dir)
# Return true of the node exists or is a derived node.
if node.is_derived() or \
- (isinstance(node, SCons.Node.FS.Entry) and node.exists()):
+ (isinstance(node, SCons.Node.FS.Base) and node.exists()):
retval = node
break
except TypeError:
diff --git a/src/engine/SCons/Node/FSTests.py b/src/engine/SCons/Node/FSTests.py
index a80c8f9..d651e1c 100644
--- a/src/engine/SCons/Node/FSTests.py
+++ b/src/engine/SCons/Node/FSTests.py
@@ -987,8 +987,18 @@ class FSTestCase(unittest.TestCase):
#XXX test get_prevsiginfo()
- assert fs.File('foo.x').scanner_key() == '.x'
- assert fs.File('foo.xyz').scanner_key() == '.xyz'
+ skey = fs.Entry('eee.x').scanner_key()
+ assert skey == '.x', skey
+ skey = fs.Entry('eee.xyz').scanner_key()
+ assert skey == '.xyz', skey
+
+ skey = fs.File('fff.x').scanner_key()
+ assert skey == '.x', skey
+ skey = fs.File('fff.xyz').scanner_key()
+ assert skey == '.xyz', skey
+
+ skey = fs.Dir('ddd.x').scanner_key()
+ assert skey is None, skey
d1 = fs.Dir('dir')
f1 = fs.File('dir/file')
@@ -1064,6 +1074,85 @@ class FSTestCase(unittest.TestCase):
f.get_string(0)
assert f.get_string(1) == 'baz', f.get_string(1)
+class EntryTestCase(unittest.TestCase):
+ def runTest(self):
+ """Test methods specific to the Entry sub-class.
+ """
+ test = TestCmd(workdir='')
+ # FS doesn't like the cwd to be something other than its root.
+ os.chdir(test.workpath(""))
+
+ fs = SCons.Node.FS.FS()
+
+ e1 = fs.Entry('e1')
+ e1.rfile()
+ assert e1.__class__ is SCons.Node.FS.File, e1.__class__
+
+ e2 = fs.Entry('e2')
+ e2.get_found_includes(None, None, None)
+ assert e2.__class__ is SCons.Node.FS.File, e2.__class__
+
+ test.subdir('e3d')
+ test.write('e3f', "e3f\n")
+
+ e3d = fs.Entry('e3d')
+ e3d.get_contents()
+ assert e3d.__class__ is SCons.Node.FS.Dir, e3d.__class__
+
+ e3f = fs.Entry('e3f')
+ e3f.get_contents()
+ assert e3f.__class__ is SCons.Node.FS.File, e3f.__class__
+
+ e3n = fs.Entry('e3n')
+ exc_caught = None
+ try:
+ e3n.get_contents()
+ except AttributeError:
+ exc_caught = 1
+ assert exc_caught, "did not catch expected AttributeError"
+
+ test.subdir('e4d')
+ test.write('e4f', "e4f\n")
+
+ e4d = fs.Entry('e4d')
+ exists = e4d.exists()
+ assert e4d.__class__ is SCons.Node.FS.Dir, e4d.__class__
+ assert exists, "e4d does not exist?"
+
+ e4f = fs.Entry('e4f')
+ exists = e4f.exists()
+ assert e4f.__class__ is SCons.Node.FS.File, e4f.__class__
+ assert exists, "e4f does not exist?"
+
+ e4n = fs.Entry('e4n')
+ exists = e4n.exists()
+ assert e4n.__class__ is SCons.Node.FS.File, e4n.__class__
+ assert not exists, "e4n exists?"
+
+ class MyCalc:
+ def __init__(self, val):
+ self.val = val
+ def csig(self, node, cache):
+ return self.val
+ test.subdir('e5d')
+ test.write('e5f', "e5f\n")
+
+ e5d = fs.Entry('e5d')
+ sig = e5d.calc_signature(MyCalc(555))
+ assert e5d.__class__ is SCons.Node.FS.Dir, e5d.__class__
+ assert sig is None, sig
+
+ e5f = fs.Entry('e5f')
+ sig = e5f.calc_signature(MyCalc(666))
+ assert e5f.__class__ is SCons.Node.FS.File, e5f.__class__
+ assert sig == 666, sig
+
+ e5n = fs.Entry('e5n')
+ sig = e5n.calc_signature(MyCalc(777))
+ assert e5n.__class__ is SCons.Node.FS.File, e5n.__class__
+ assert sig is None, sig
+
+
class RepositoryTestCase(unittest.TestCase):
def runTest(self):
@@ -1613,6 +1702,7 @@ if __name__ == "__main__":
suite = unittest.TestSuite()
suite.addTest(FSTestCase())
suite.addTest(BuildDirTestCase())
+ suite.addTest(EntryTestCase())
suite.addTest(RepositoryTestCase())
suite.addTest(find_fileTestCase())
suite.addTest(StringDirTestCase())
diff --git a/src/engine/SCons/Node/__init__.py b/src/engine/SCons/Node/__init__.py
index 66ffe64..ea61de6 100644
--- a/src/engine/SCons/Node/__init__.py
+++ b/src/engine/SCons/Node/__init__.py
@@ -419,15 +419,15 @@ class Node:
try:
return self._calculated_sig
except AttributeError:
- if self.has_builder():
+ if self.is_derived():
if SCons.Sig.build_signature:
- sig = self.calc_bsig(calc)
+ sig = self.rfile().calc_bsig(calc, self)
else:
- sig = self.calc_csig(calc)
- elif not self.exists():
+ sig = self.rfile().calc_csig(calc, self)
+ elif not self.rexists():
sig = None
else:
- sig = self.calc_csig(calc)
+ sig = self.rfile().calc_csig(calc, self)
self._calculated_sig = sig
return sig