summaryrefslogtreecommitdiff
path: root/src/engine/SCons/Node/FS.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/engine/SCons/Node/FS.py')
-rw-r--r--src/engine/SCons/Node/FS.py160
1 files changed, 143 insertions, 17 deletions
diff --git a/src/engine/SCons/Node/FS.py b/src/engine/SCons/Node/FS.py
index a21561f..f54b0ab 100644
--- a/src/engine/SCons/Node/FS.py
+++ b/src/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, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -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 2013/03/03 09:48:35 garyo"
+__revision__ = "src/engine/SCons/Node/FS.py 2014/03/02 14:18:15 garyo"
import fnmatch
import os
@@ -44,6 +44,7 @@ import time
import codecs
import SCons.Action
+import SCons.Debug
from SCons.Debug import logInstanceCreation
import SCons.Errors
import SCons.Memoize
@@ -581,7 +582,7 @@ class Base(SCons.Node.Node):
our relative and absolute paths, identify our parent
directory, and indicate that this node should use
signatures."""
- if __debug__: logInstanceCreation(self, 'Node.FS.Base')
+ 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
@@ -1111,7 +1112,7 @@ class FS(LocalFS):
The path argument must be a valid absolute path.
"""
- if __debug__: logInstanceCreation(self, 'Node.FS')
+ if SCons.Debug.track_instances: logInstanceCreation(self, 'Node.FS')
self._memo = {}
@@ -1445,7 +1446,7 @@ class Dir(Base):
BuildInfo = DirBuildInfo
def __init__(self, name, directory, fs):
- if __debug__: logInstanceCreation(self, 'Node.FS.Dir')
+ if SCons.Debug.track_instances: logInstanceCreation(self, 'Node.FS.Dir')
Base.__init__(self, name, directory, fs)
self._morph()
@@ -1841,7 +1842,7 @@ class Dir(Base):
for entry in map(_my_normcase, entries):
d[entry] = True
self.on_disk_entries = d
- if sys.platform == 'win32':
+ if sys.platform == 'win32' or sys.platform == 'cygwin':
name = _my_normcase(name)
result = d.get(name)
if result is None:
@@ -2113,7 +2114,7 @@ class RootDir(Dir):
this directory.
"""
def __init__(self, drive, fs):
- if __debug__: logInstanceCreation(self, 'Node.FS.RootDir')
+ 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.
@@ -2361,7 +2362,7 @@ class File(Base):
"Directory %s found where file expected.")
def __init__(self, name, directory, fs):
- if __debug__: logInstanceCreation(self, 'Node.FS.File')
+ if SCons.Debug.track_instances: logInstanceCreation(self, 'Node.FS.File')
Base.__init__(self, name, directory, fs)
self._morph()
@@ -2397,6 +2398,8 @@ class File(Base):
self.scanner_paths = {}
if not hasattr(self, '_local'):
self._local = 0
+ if not hasattr(self, 'released_target_info'):
+ self.released_target_info = False
# If there was already a Builder set on this entry, then
# we need to make sure we call the target-decider function,
@@ -2724,7 +2727,7 @@ class File(Base):
return self.get_build_env().get_CacheDir().retrieve(self)
def visited(self):
- if self.exists():
+ if self.exists() and self.executor is not None:
self.get_build_env().get_CacheDir().push_if_forced(self)
ninfo = self.get_ninfo()
@@ -2746,6 +2749,58 @@ class File(Base):
self.store_info()
+ 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
+ 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.
+
+ 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...
+ self.changed(allowcache=True)
+ self.get_contents_sig()
+ self.get_build_env()
+ # Now purge unneeded stuff to free memory...
+ self.executor = None
+ self._memo.pop('rfile', None)
+ self.prerequisites = None
+ # Cleanup lists, but only if they're empty
+ if not len(self.ignore_set):
+ self.ignore_set = None
+ if not len(self.implicit_set):
+ self.implicit_set = None
+ if not len(self.depends_set):
+ self.depends_set = None
+ if not len(self.ignore):
+ self.ignore = None
+ if not len(self.depends):
+ self.depends = None
+ # Mark this node as done, we only have to release
+ # the memory once...
+ self.released_target_info = True
+
def find_src_builder(self):
if self.rexists():
return None
@@ -2956,6 +3011,52 @@ class File(Base):
SCons.Node.Node.builder_set(self, builder)
self.changed_since_last_build = self.decide_target
+ 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
+ not hasattr(self.attributes, 'keep_targetinfo')):
+ # Ensure that the build infos get computed and cached...
+ self.store_info()
+ # ... 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.
+
+ 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:
+ try:
+ return self._memo['changed']
+ except KeyError:
+ pass
+
+ has_changed = SCons.Node.Node.changed(self, node)
+ if allowcache:
+ self._memo['changed'] = has_changed
+ return has_changed
+
def changed_content(self, target, prev_ni):
cur_csig = self.get_csig()
try:
@@ -3089,25 +3190,50 @@ class File(Base):
self.cachedir_csig = self.get_csig()
return self.cachedir_csig
+ def get_contents_sig(self):
+ """
+ A helper method for get_cachedir_bsig.
+
+ 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())
+ return result
+
def get_cachedir_bsig(self):
+ """
+ Return the signature for a cached file, including
+ its children.
+
+ It adds the path of the cached file to the cache signature,
+ because multiple targets built by the same action will all
+ have the same build signature, and we have to differentiate
+ them somehow.
+ """
try:
return self.cachesig
except AttributeError:
pass
-
- # Add the path to the cache signature, because multiple
- # targets built by the same action will all have the same
- # build signature, and we have to differentiate them somehow.
+
+ # Collect signatures for all children
children = self.children()
- executor = self.get_executor()
- # sigs = [n.get_cachedir_csig() for n in children]
sigs = [n.get_cachedir_csig() for n in children]
- sigs.append(SCons.Util.MD5signature(executor.get_contents()))
+ # Append this node's signature...
+ sigs.append(self.get_contents_sig())
+ # ...and it's path
sigs.append(self.path)
+ # Merge this all into a single signature
result = self.cachesig = SCons.Util.MD5collect(sigs)
return result
-
default_fs = None
def get_default_fs():