diff options
Diffstat (limited to 'src/engine/SCons/Node')
-rw-r--r-- | src/engine/SCons/Node/FS.py | 174 | ||||
-rw-r--r-- | src/engine/SCons/Node/FSTests.py | 94 | ||||
-rw-r--r-- | src/engine/SCons/Node/__init__.py | 10 |
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 |