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
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:
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
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:
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
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):
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):
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
"""
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
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):
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 ''
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())
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.
def prepare(self):
"""Prepare for this file to be created."""
-
SCons.Node.Node.prepare(self)
if self.get_state() != SCons.Node.up_to_date:
delattr(self, '_rexists')
except AttributeError:
pass
- return Entry.exists(self)
+ return Base.exists(self)
def current(self, calc):
bsig = calc.bsig(self)
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:
#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')
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):
suite = unittest.TestSuite()
suite.addTest(FSTestCase())
suite.addTest(BuildDirTestCase())
+ suite.addTest(EntryTestCase())
suite.addTest(RepositoryTestCase())
suite.addTest(find_fileTestCase())
suite.addTest(StringDirTestCase())
test = TestSCons.TestSCons()
+test.subdir('sub')
+
test.write('build.py', r"""
import sys
contents = open(sys.argv[2], 'rb').read()
""")
test.write('SConstruct', """
+import os
+
def buildIt(env, target, source):
contents = open(str(source[0]), 'rb').read()
file = open(str(target[0]), 'wb')
file.close()
return 0
+def sub(env, target, source):
+ target = str(target[0])
+ source = str(source[0])
+ t = open(target, 'wb')
+ files = os.listdir(source)
+ files.sort()
+ for f in files:
+ t.write(open(os.path.join(source, f), 'rb').read())
+ t.close()
+ return 0
+
env = Environment()
env.Command(target = 'f1.out', source = 'f1.in',
action = buildIt)
env.Command(target = 'f2.out', source = 'f2.in',
action = r'%s' + " build.py temp2 $SOURCES\\n" + r'%s' + " build.py $TARGET temp2")
+
env.Command(target = 'f3.out', source = 'f3.in',
action = [ [ r'%s', 'build.py', 'temp3', '$SOURCES' ],
[ r'%s', 'build.py', '$TARGET', 'temp3'] ])
+env.Command(target = 'f4.out', source = 'sub', action = sub)
""" % (python, python, python, python))
test.write('f1.in', "f1.in\n")
-
test.write('f2.in', "f2.in\n")
-
test.write('f3.in', "f3.in\n")
+test.write(['sub', 'f4a'], "sub/f4a\n")
+test.write(['sub', 'f4b'], "sub/f4b\n")
+test.write(['sub', 'f4c'], "sub/f4c\n")
test.run(arguments = '.')
test.fail_test(test.read('f1.out') != "f1.in\n")
test.fail_test(test.read('f2.out') != "f2.in\n")
test.fail_test(test.read('f3.out') != "f3.in\n")
+test.fail_test(test.read('f4.out') != "sub/f4a\nsub/f4b\nsub/f4c\n")
test.pass_test()