diff options
Diffstat (limited to 'engine/SCons/Node/FS.py')
-rw-r--r-- | engine/SCons/Node/FS.py | 321 |
1 files changed, 149 insertions, 172 deletions
diff --git a/engine/SCons/Node/FS.py b/engine/SCons/Node/FS.py index 6935384..a953890 100644 --- a/engine/SCons/Node/FS.py +++ b/engine/SCons/Node/FS.py @@ -11,7 +11,7 @@ that can be used by scripts or modules looking for the canonical default. """ # -# Copyright (c) 2001 - 2016 The SCons Foundation +# Copyright (c) 2001 - 2017 The SCons Foundation # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -31,8 +31,9 @@ that can be used by scripts or modules looking for the canonical default. # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +from __future__ import print_function -__revision__ = "src/engine/SCons/Node/FS.py rel_2.5.1:3735:9dc6cee5c168 2016/11/03 14:02:02 bdbaddog" +__revision__ = "src/engine/SCons/Node/FS.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" import fnmatch import os @@ -122,7 +123,7 @@ def save_strings(val): # tells us whether or not os.path.splitdrive() actually does anything # on this system, and therefore whether we need to bother calling it # when looking up path names in various methods below. -# +# do_splitdrive = None _my_splitdrive =None @@ -157,7 +158,7 @@ def initialize_do_splitdrive(): global OS_SEP global UNC_PREFIX global os_sep_is_slash - + OS_SEP = os.sep UNC_PREFIX = OS_SEP + OS_SEP os_sep_is_slash = OS_SEP == '/' @@ -178,7 +179,7 @@ needs_normpath_check = re.compile( # b) The path starts with '..'. E.g. '../' or '../moredirs' # but we not match '..abc/'. # c) The path ends with '..'. E.g. '/..' or 'dirs/..' - # d) The path contains a '..' in the middle. + # d) The path contains a '..' in the middle. # E.g. dirs/../moredirs (.*/)?\.\.(?:/|$) | @@ -200,7 +201,7 @@ needs_normpath_check = re.compile( \./|.*/\.(?:/|$) - ''', + ''', re.VERBOSE ) needs_normpath_match = needs_normpath_check.match @@ -219,7 +220,12 @@ needs_normpath_match = needs_normpath_check.match # there should be *no* changes to the external file system(s)... # -if hasattr(os, 'link'): +# For Now disable hard & softlinks for win32 +# PY3 supports them, but the rest of SCons is not ready for this +# in some cases user permissions may be required. +# TODO: See if theres a reasonable way to enable using links on win32/64 + +if hasattr(os, 'link') and sys.platform != 'win32': def _hardlink_func(fs, src, dst): # If the source is a symlink, we can't just hard-link to it # because a relative symlink may point somewhere completely @@ -235,7 +241,7 @@ if hasattr(os, 'link'): else: _hardlink_func = None -if hasattr(os, 'symlink'): +if hasattr(os, 'symlink') and sys.platform != 'win32': def _softlink_func(fs, src, dst): fs.symlink(src, dst) else: @@ -350,33 +356,6 @@ class _Null(object): _null = _Null() -DefaultSCCSBuilder = None -DefaultRCSBuilder = None - -def get_DefaultSCCSBuilder(): - global DefaultSCCSBuilder - if DefaultSCCSBuilder is None: - import SCons.Builder - # "env" will get filled in by Executor.get_build_env() - # calling SCons.Defaults.DefaultEnvironment() when necessary. - act = SCons.Action.Action('$SCCSCOM', '$SCCSCOMSTR') - DefaultSCCSBuilder = SCons.Builder.Builder(action = act, - env = None, - name = "DefaultSCCSBuilder") - return DefaultSCCSBuilder - -def get_DefaultRCSBuilder(): - global DefaultRCSBuilder - if DefaultRCSBuilder is None: - import SCons.Builder - # "env" will get filled in by Executor.get_build_env() - # calling SCons.Defaults.DefaultEnvironment() when necessary. - act = SCons.Action.Action('$RCS_COCOM', '$RCS_COCOMSTR') - DefaultRCSBuilder = SCons.Builder.Builder(action = act, - env = None, - name = "DefaultRCSBuilder") - return DefaultRCSBuilder - # Cygwin's os.path.normcase pretends it's on a case-sensitive filesystem. _is_cygwin = sys.platform == "cygwin" if os.path.normcase("TeSt") == os.path.normpath("TeSt") and not _is_cygwin: @@ -421,46 +400,12 @@ def do_diskcheck_match(node, predicate, errorfmt): def ignore_diskcheck_match(node, predicate, errorfmt): pass -def do_diskcheck_rcs(node, name): - try: - rcs_dir = node.rcs_dir - except AttributeError: - if node.entry_exists_on_disk('RCS'): - rcs_dir = node.Dir('RCS') - else: - rcs_dir = None - node.rcs_dir = rcs_dir - if rcs_dir: - return rcs_dir.entry_exists_on_disk(name+',v') - return None - -def ignore_diskcheck_rcs(node, name): - return None -def do_diskcheck_sccs(node, name): - try: - sccs_dir = node.sccs_dir - except AttributeError: - if node.entry_exists_on_disk('SCCS'): - sccs_dir = node.Dir('SCCS') - else: - sccs_dir = None - node.sccs_dir = sccs_dir - if sccs_dir: - return sccs_dir.entry_exists_on_disk('s.'+name) - return None - -def ignore_diskcheck_sccs(node, name): - return None diskcheck_match = DiskChecker('match', do_diskcheck_match, ignore_diskcheck_match) -diskcheck_rcs = DiskChecker('rcs', do_diskcheck_rcs, ignore_diskcheck_rcs) -diskcheck_sccs = DiskChecker('sccs', do_diskcheck_sccs, ignore_diskcheck_sccs) diskcheckers = [ diskcheck_match, - diskcheck_rcs, - diskcheck_sccs, ] def set_diskcheck(list): @@ -476,6 +421,11 @@ class EntryProxy(SCons.Util.Proxy): __str__ = SCons.Util.Delegate('__str__') + # In PY3 if a class defines __eq__, then it must explicitly provide + # __hash__. Since SCons.Util.Proxy provides __eq__ we need the following + # see: https://docs.python.org/3.1/reference/datamodel.html#object.__hash__ + __hash__ = SCons.Util.Delegate('__hash__') + def __get_abspath(self): entry = self.get() return SCons.Subst.SpecialAttrWrapper(entry.get_abspath(), @@ -564,7 +514,7 @@ class EntryProxy(SCons.Util.Proxy): except KeyError: try: attr = SCons.Util.Proxy.__getattr__(self, name) - except AttributeError, e: + except AttributeError as e: # Raise our own AttributeError subclass with an # overridden __str__() method that identifies the # name of the entry that caused the exception. @@ -573,6 +523,7 @@ class EntryProxy(SCons.Util.Proxy): else: return attr_function(self) + 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 @@ -608,14 +559,13 @@ class Base(SCons.Node.Node): our relative and absolute paths, identify our parent directory, and indicate that this node should use signatures.""" + if SCons.Debug.track_instances: logInstanceCreation(self, 'Node.FS.Base') SCons.Node.Node.__init__(self) - # Filenames and paths are probably reused and are intern'ed to - # save some memory. - - #: Filename with extension as it was specified when the object was - #: created; to obtain filesystem path, use Python str() function + # Filenames and paths are probably reused and are intern'ed to save some memory. + # 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 @@ -670,14 +620,14 @@ class Base(SCons.Node.Node): 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. + 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)) @@ -689,13 +639,17 @@ class Base(SCons.Node.Node): return self._save_str() return self._get_str() + def __lt__(self, other): + """ less than operator used by sorting on py3""" + return str(self) < str(other) + @SCons.Memoize.CountMethodCall def _save_str(self): try: return self._memo['_save_str'] except KeyError: pass - result = sys.intern(self._get_str()) + result = SCons.Util.silent_intern(self._get_str()) self._memo['_save_str'] = result return result @@ -799,7 +753,7 @@ class Base(SCons.Node.Node): path_elems = self.get_path_elements() pathname = '' try: i = path_elems.index(dir) - except ValueError: + except ValueError: for p in path_elems[:-1]: pathname += p.dirname else: @@ -840,13 +794,13 @@ class Base(SCons.Node.Node): 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] @@ -939,7 +893,7 @@ 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'. @@ -971,15 +925,13 @@ class Entry(Base): '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 + self._func_get_contents = 1 def diskcheck_match(self): pass @@ -1198,7 +1150,7 @@ class FS(LocalFS): DirNodeInfo.fs = self FileNodeInfo.fs = self - + def set_SConstruct_dir(self, dir): self.SConstruct_dir = dir @@ -1210,9 +1162,9 @@ class FS(LocalFS): def getcwd(self): if hasattr(self, "_cwd"): - return self._cwd + return self._cwd else: - return "<no cwd>" + return "<no cwd>" def chdir(self, dir, change_os_dir=0): """Change the current working directory for lookups. @@ -1310,7 +1262,7 @@ class FS(LocalFS): p = p.strip('/') needs_normpath = needs_normpath_match(p) - + # The path is relative to the top-level SCons directory. if p in ('', '.'): p = directory.get_labspath() @@ -1437,6 +1389,35 @@ class FS(LocalFS): if not isinstance(d, SCons.Node.Node): d = self.Dir(d) self.Top.addRepository(d) + + def PyPackageDir(self, modulename): + """Locate the directory of a given python module name + + For example scons might resolve to + Windows: C:\Python27\Lib\site-packages\scons-2.5.1 + Linux: /usr/lib/scons + + This can be useful when we want to determine a toolpath based on a python module name""" + + dirpath = '' + if sys.version_info[0] < 3 or (sys.version_info[0] == 3 and sys.version_info[1] in (0,1,2,3,4)): + # Python2 Code + import imp + splitname = modulename.split('.') + srchpths = sys.path + for item in splitname: + file, path, desc = imp.find_module(item, srchpths) + if file is not None: + path = os.path.dirname(path) + srchpths = [path] + dirpath = path + else: + # Python3 Code + import importlib.util + modspec = importlib.util.find_spec(modulename) + dirpath = os.path.dirname(modspec.origin) + return self._lookup(dirpath, None, Dir, True) + def variant_dir_target_climb(self, orig, dir, tail): """Create targets in corresponding variant directories @@ -1469,7 +1450,7 @@ class FS(LocalFS): """ Globs - This is mainly a shim layer + This is mainly a shim layer """ if cwd is None: cwd = self.getcwd() @@ -1518,8 +1499,6 @@ class Dir(Base): 'root', 'dirname', 'on_disk_entries', - 'sccs_dir', - 'rcs_dir', 'released_target_info', 'contentsig'] @@ -1555,7 +1534,7 @@ class Dir(Base): 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 == '.': @@ -1594,7 +1573,7 @@ class Dir(Base): # Prepend MkdirBuilder action to existing action list l = self.get_executor().action_list a = get_MkdirBuilder().action - l.insert(0, a) + l.insert(0, a) self.get_executor().set_action_list(l) def diskcheck_match(self): @@ -1606,7 +1585,7 @@ class Dir(Base): This clears any cached information that is invalidated by changing the repository.""" - for node in self.entries.values(): + for node in list(self.entries.values()): if node != self.dir: if node != self and isinstance(node, Dir): node.__clearRepositoryCache(duplicate) @@ -1742,7 +1721,7 @@ class Dir(Base): path_elems = ['..'] * (len(self._path_elements) - i) \ + [n.name for n in other._path_elements[i:]] - + result = OS_SEP.join(path_elems) memo_dict[other] = result @@ -1913,10 +1892,10 @@ class Dir(Base): def get_internal_path(self): return self._path - + def get_tpath(self): return self._tpath - + def get_path_elements(self): return self._path_elements @@ -1936,7 +1915,7 @@ class Dir(Base): """ Searches through the file/dir entries of the current directory, and returns True if a physical entry with the given name could be found. - + @see rentry_exists_on_disk """ try: @@ -1970,10 +1949,10 @@ class Dir(Base): The local directory (self) gets searched first, so repositories take a lower precedence regarding the searching order. - + @see entry_exists_on_disk """ - + rentry_exists = self.entry_exists_on_disk(name) if not rentry_exists: # Search through the repository folders @@ -2085,9 +2064,7 @@ class Dir(Base): return node def file_on_disk(self, name): - if self.entry_exists_on_disk(name) or \ - diskcheck_rcs(self, name) or \ - diskcheck_sccs(self, name): + if self.entry_exists_on_disk(name): try: return self.File(name) except TypeError: pass node = self.srcdir_duplicate(name) @@ -2178,7 +2155,7 @@ class Dir(Base): for x in excludeList: r = self.glob(x, ondisk, source, strings) excludes.extend(r) - result = filter(lambda x: not any(fnmatch.fnmatch(str(x), str(e)) for e in SCons.Util.flatten(excludes)), result) + result = [x for x in result if not any(fnmatch.fnmatch(str(x), str(e)) for e in SCons.Util.flatten(excludes))] return sorted(result, key=lambda a: str(a)) def _glob1(self, pattern, ondisk=True, source=False, strings=False): @@ -2256,9 +2233,9 @@ 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') SCons.Node.Node.__init__(self) @@ -2266,7 +2243,7 @@ class RootDir(Dir): # Handle all the types of drives: if drive == '': # No drive, regular UNIX root or Windows default drive. - name = OS_SEP + name = OS_SEP dirname = OS_SEP elif drive == '//': # UNC path @@ -2277,8 +2254,8 @@ class RootDir(Dir): name = drive dirname = drive + OS_SEP - #: Filename with extension as it was specified when the object was - #: created; to obtain filesystem path, use Python str() function + # 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 @@ -2336,7 +2313,7 @@ class RootDir(Dir): 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. @@ -2352,9 +2329,9 @@ class RootDir(Dir): # Prepend MkdirBuilder action to existing action list l = self.get_executor().action_list a = get_MkdirBuilder().action - l.insert(0, a) + l.insert(0, a) self.get_executor().set_action_list(l) - + def must_be_same(self, klass): if klass is Dir: @@ -2433,6 +2410,7 @@ class RootDir(Dir): def src_builder(self): return _null + class FileNodeInfo(SCons.Node.NodeInfoBase): __slots__ = ('csig', 'timestamp', 'size') current_version_id = 2 @@ -2484,6 +2462,7 @@ class FileNodeInfo(SCons.Node.NodeInfoBase): if key not in ('__weakref__',): setattr(self, key, value) + class FileBuildInfo(SCons.Node.BuildInfoBase): __slots__ = () current_version_id = 2 @@ -2514,6 +2493,7 @@ class FileBuildInfo(SCons.Node.BuildInfoBase): pass else: setattr(self, attr, list(map(node_to_str, val))) + def convert_from_sconsign(self, dir, name): """ Converts a newly-read FileBuildInfo object for in-SCons use @@ -2522,6 +2502,7 @@ class FileBuildInfo(SCons.Node.BuildInfoBase): perform--but we're leaving this method here to make that clear. """ pass + def prepare_dependencies(self): """ Prepares a FileBuildInfo object for explaining what changed @@ -2550,6 +2531,7 @@ class FileBuildInfo(SCons.Node.BuildInfoBase): s = ni.str_to_node(s) nodes.append(s) setattr(self, nattr, nodes) + def format(self, names=0): result = [] bkids = self.bsources + self.bdepends + self.bimplicit @@ -2562,6 +2544,7 @@ class FileBuildInfo(SCons.Node.BuildInfoBase): result.append('%s [%s]' % (self.bactsig, self.bact)) return '\n'.join(result) + class File(Base): """A class for files in a file system. """ @@ -2578,8 +2561,6 @@ class File(Base): 'root', 'dirname', 'on_disk_entries', - 'sccs_dir', - 'rcs_dir', 'released_target_info', 'contentsig'] @@ -2628,11 +2609,11 @@ class File(Base): 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 @@ -2652,10 +2633,12 @@ class File(Base): def get_contents(self): 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 - # it's a valid python string. def get_text_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 + it's a valid python string. + """ contents = self.get_contents() # The behavior of various decode() methods and functions # w.r.t. the initial BOM bytes is different for different @@ -2663,13 +2646,20 @@ class File(Base): # them, but has a 'utf-8-sig' which does; 'utf-16' seems to # strip them; etc.) Just sidestep all the complication by # explicitly stripping the BOM before we decode(). - if contents.startswith(codecs.BOM_UTF8): + if contents[:len(codecs.BOM_UTF8)] == codecs.BOM_UTF8: return contents[len(codecs.BOM_UTF8):].decode('utf-8') - if contents.startswith(codecs.BOM_UTF16_LE): + if contents[:len(codecs.BOM_UTF16_LE)] == codecs.BOM_UTF16_LE: return contents[len(codecs.BOM_UTF16_LE):].decode('utf-16-le') - if contents.startswith(codecs.BOM_UTF16_BE): + if contents[:len(codecs.BOM_UTF16_BE)] == codecs.BOM_UTF16_BE: return contents[len(codecs.BOM_UTF16_BE):].decode('utf-16-be') - return contents + try: + return contents.decode('utf-8') + except UnicodeDecodeError as e: + try: + return contents.decode('latin-1') + except UnicodeDecodeError as e: + return contents.decode('utf-8', error='backslashreplace') + def get_content_hash(self): """ @@ -2681,12 +2671,12 @@ class File(Base): try: cs = SCons.Util.MD5filesignature(fname, chunksize=SCons.Node.FS.File.md5_chunksize*1024) - except EnvironmentError, e: + except EnvironmentError as e: if not e.filename: e.filename = fname raise return cs - + @SCons.Memoize.CountMethodCall def get_size(self): try: @@ -2960,30 +2950,30 @@ class File(Base): def release_target_info(self): """Called just after this node has been marked up-to-date or was built completely. - + This is where we try to release as many target node infos as possible for clean builds and update runs, in order to minimize the overall memory consumption. - + We'd like to remove a lot more attributes like self.sources and self.sources_set, but they might get used in a next build step. For example, during configuration - the source files for a built *.o file are used to figure out + the source files for a built E{*}.o file are used to figure out which linker to use for the resulting Program (gcc vs. g++)! That's why we check for the 'keep_targetinfo' attribute, config Nodes and the Interactive mode just don't allow - an early release of most variables. + an early release of most variables. In the same manner, we can't simply remove the self.attributes here. The smart linking relies on the shared flag, and some parts of the java Tool use it to transport information about nodes... - + @see: built() and Node.release_target_info() """ if (self.released_target_info or SCons.Node.interactive): return - + if not hasattr(self.attributes, 'keep_targetinfo'): # Cache some required values, before releasing # stuff like env, executor and builder... @@ -3014,12 +3004,7 @@ class File(Base): return None scb = self.dir.src_builder() if scb is _null: - if diskcheck_sccs(self.dir, self.name): - scb = get_DefaultSCCSBuilder() - elif diskcheck_rcs(self.dir, self.name): - scb = get_DefaultRCSBuilder() - else: - scb = None + scb = None if scb is not None: try: b = self.builder @@ -3056,7 +3041,7 @@ class File(Base): def _rmv_existing(self): self.clear_memoized_values() if SCons.Node.print_duplicate: - print "dup: removing existing target %s"%self + print("dup: removing existing target {}".format(self)) e = Unlink(self, [], None) if isinstance(e, SCons.Errors.BuildError): raise e @@ -3080,9 +3065,8 @@ class File(Base): else: try: self._createDir() - except SCons.Errors.StopError, drive: - desc = "No drive `%s' for target `%s'." % (drive, self) - raise SCons.Errors.StopError(desc) + except SCons.Errors.StopError as drive: + raise SCons.Errors.StopError("No drive `{}' for target `{}'.".format(drive, self)) # # @@ -3098,12 +3082,11 @@ class File(Base): def do_duplicate(self, src): self._createDir() if SCons.Node.print_duplicate: - print "dup: relinking variant '%s' from '%s'"%(self, src) + print("dup: relinking variant '{}' from '{}'".format(self, src)) Unlink(self, None, None) e = Link(self, src, None) if isinstance(e, SCons.Errors.BuildError): - desc = "Cannot duplicate `%s' in `%s': %s." % (src.get_internal_path(), self.dir._path, e.errstr) - raise SCons.Errors.StopError(desc) + raise SCons.Errors.StopError("Cannot duplicate `{}' in `{}': {}.".format(src.get_internal_path(), self.dir._path, e.errstr)) self.linked = 1 # The Link() action may or may not have actually # created the file, depending on whether the -n @@ -3117,7 +3100,6 @@ class File(Base): return self._memo['exists'] except KeyError: pass - result = SCons.Node._exists_map[self._func_exists](self) self._memo['exists'] = result return result @@ -3199,37 +3181,37 @@ class File(Base): def built(self): """Called just after this File node is successfully built. - + Just like for 'release_target_info' we try to release some more target node attributes in order to minimize the overall memory consumption. - + @see: release_target_info """ SCons.Node.Node.built(self) - if (not SCons.Node.interactive and + if (not SCons.Node.interactive and not hasattr(self.attributes, 'keep_targetinfo')): - # Ensure that the build infos get computed and cached... + # Ensure that the build infos get computed and cached... SCons.Node.store_info_map[self.store_info](self) # ... then release some more variables. self._specific_sources = False self._labspath = None self._save_str() self.cwd = None - + self.scanner_paths = None def changed(self, node=None, allowcache=False): """ Returns if the node is up-to-date with respect to the BuildInfo - stored last time it was built. - + stored last time it was built. + For File nodes this is basically a wrapper around Node.changed(), but we allow the return value to get cached after the reference to the Executor got released in release_target_info(). - + @see: Node.changed() """ if node is None: @@ -3237,7 +3219,7 @@ class File(Base): return self._memo['changed'] except KeyError: pass - + has_changed = SCons.Node.Node.changed(self, node) if allowcache: self._memo['changed'] = has_changed @@ -3290,7 +3272,7 @@ class File(Base): # ...and they'd like a local copy. e = LocalCopy(self, r, None) if isinstance(e, SCons.Errors.BuildError): - raise + raise SCons.Node.store_info_map[self.store_info](self) if T: Trace(' 1\n') return 1 @@ -3372,12 +3354,12 @@ class File(Base): It computes and returns the signature for this node's contents. """ - + try: return self.contentsig except AttributeError: pass - + executor = self.get_executor() result = self.contentsig = SCons.Util.MD5signature(executor.get_contents()) @@ -3397,7 +3379,7 @@ class File(Base): return self.cachesig except AttributeError: pass - + # Collect signatures for all children children = self.children() sigs = [n.get_cachedir_csig() for n in children] @@ -3461,24 +3443,19 @@ class FileFinder(object): def _find_file_key(self, filename, paths, verbose=None): return (filename, paths) - + @SCons.Memoize.CountDictCall(_find_file_key) def find_file(self, filename, paths, verbose=None): """ - find_file(str, [Dir()]) -> [nodes] + Find a node corresponding to either a derived file or a file that exists already. - filename - a filename to find - paths - a list of directory path *nodes* to search in. Can be - represented as a list, a tuple, or a callable that is - called with no arguments and returns the list or tuple. + Only the first file found is returned, and none is returned if no file is found. - returns - the node created from the found file. + filename: A filename to find + paths: A list of directory path *nodes* to search in. Can be represented as a list, a tuple, or a callable that is called with no arguments and returns the list or tuple. - Find a node corresponding to either a derived file or a file - that exists already. + returns The node created from the found file. - Only the first file found is returned, and none is returned - if no file is found. """ memo_key = self._find_file_key(filename, paths) try: @@ -3547,7 +3524,7 @@ def invalidate_node_memos(targets): if not SCons.Util.is_List(targets): targets = [targets] - + for entry in targets: # If the target is a Node object, clear the cache. If it is a # filename, look up potentially existing Node object first. @@ -3559,7 +3536,7 @@ def invalidate_node_memos(targets): # do not correspond to an existing Node object. node = get_default_fs().Entry(entry) if node: - node.clear_memoized_values() + node.clear_memoized_values() # Local Variables: # tab-width:4 |