Index: scons-2.0.1/engine/SCons/CacheDir.py |
=================================================================== |
--- scons-2.0.1/engine/SCons/CacheDir.py (revision 0) |
+++ scons-2.0.1/engine/SCons/CacheDir.py (revision 0) |
@@ -0,0 +1,216 @@ |
+# |
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 The SCons Foundation |
+# |
+# Permission is hereby granted, free of charge, to any person obtaining |
+# a copy of this software and associated documentation files (the |
+# "Software"), to deal in the Software without restriction, including |
+# without limitation the rights to use, copy, modify, merge, publish, |
+# distribute, sublicense, and/or sell copies of the Software, and to |
+# permit persons to whom the Software is furnished to do so, subject to |
+# the following conditions: |
+# |
+# The above copyright notice and this permission notice shall be included |
+# in all copies or substantial portions of the Software. |
+# |
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY |
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE |
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE |
+# 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. |
+# |
+ |
+__revision__ = "src/engine/SCons/CacheDir.py 5134 2010/08/16 23:02:40 bdeegan" |
+ |
+__doc__ = """ |
+CacheDir support |
+""" |
+ |
+import os.path |
+import stat |
+import sys |
+ |
+import SCons.Action |
+ |
+cache_enabled = True |
+cache_debug = False |
+cache_force = False |
+cache_show = False |
+ |
+def CacheRetrieveFunc(target, source, env): |
+ t = target[0] |
+ fs = t.fs |
+ cd = env.get_CacheDir() |
+ cachedir, cachefile = cd.cachepath(t) |
+ if not fs.exists(cachefile): |
+ cd.CacheDebug('CacheRetrieve(%s): %s not in cache\n', t, cachefile) |
+ return 1 |
+ cd.CacheDebug('CacheRetrieve(%s): retrieving from %s\n', t, cachefile) |
+ if SCons.Action.execute_actions: |
+ if fs.islink(cachefile): |
+ fs.symlink(fs.readlink(cachefile), t.path) |
+ else: |
+ env.copy_from_cache(cachefile, t.path) |
+ st = fs.stat(cachefile) |
+ fs.chmod(t.path, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE) |
+ return 0 |
+ |
+def CacheRetrieveString(target, source, env): |
+ t = target[0] |
+ fs = t.fs |
+ cd = env.get_CacheDir() |
+ cachedir, cachefile = cd.cachepath(t) |
+ if t.fs.exists(cachefile): |
+ return "Retrieved `%s' from cache" % t.path |
+ return None |
+ |
+CacheRetrieve = SCons.Action.Action(CacheRetrieveFunc, CacheRetrieveString) |
+ |
+CacheRetrieveSilent = SCons.Action.Action(CacheRetrieveFunc, None) |
+ |
+def CachePushFunc(target, source, env): |
+ t = target[0] |
+ if t.nocache: |
+ return |
+ fs = t.fs |
+ cd = env.get_CacheDir() |
+ cachedir, cachefile = cd.cachepath(t) |
+ if fs.exists(cachefile): |
+ # Don't bother copying it if it's already there. Note that |
+ # usually this "shouldn't happen" because if the file already |
+ # existed in cache, we'd have retrieved the file from there, |
+ # not built it. This can happen, though, in a race, if some |
+ # other person running the same build pushes their copy to |
+ # the cache after we decide we need to build it but before our |
+ # build completes. |
+ cd.CacheDebug('CachePush(%s): %s already exists in cache\n', t, cachefile) |
+ return |
+ |
+ cd.CacheDebug('CachePush(%s): pushing to %s\n', t, cachefile) |
+ |
+ tempfile = cachefile+'.tmp'+str(os.getpid()) |
+ errfmt = "Unable to copy %s to cache. Cache file is %s" |
+ |
+ if not fs.isdir(cachedir): |
+ try: |
+ fs.makedirs(cachedir) |
+ except EnvironmentError: |
+ # We may have received an exception because another process |
+ # has beaten us creating the directory. |
+ if not fs.isdir(cachedir): |
+ msg = errfmt % (str(target), cachefile) |
+ raise SCons.Errors.EnvironmentError(msg) |
+ |
+ try: |
+ if fs.islink(t.path): |
+ fs.symlink(fs.readlink(t.path), tempfile) |
+ else: |
+ fs.copy2(t.path, tempfile) |
+ fs.rename(tempfile, cachefile) |
+ st = fs.stat(t.path) |
+ fs.chmod(cachefile, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE) |
+ except EnvironmentError: |
+ # It's possible someone else tried writing the file at the |
+ # same time we did, or else that there was some problem like |
+ # the CacheDir being on a separate file system that's full. |
+ # In any case, inability to push a file to cache doesn't affect |
+ # the correctness of the build, so just print a warning. |
+ msg = errfmt % (str(target), cachefile) |
+ SCons.Warnings.warn(SCons.Warnings.CacheWriteErrorWarning, msg) |
+ |
+CachePush = SCons.Action.Action(CachePushFunc, None) |
+ |
+class CacheDir(object): |
+ |
+ def __init__(self, path): |
+ try: |
+ import hashlib |
+ except ImportError: |
+ msg = "No hashlib or MD5 module available, CacheDir() not supported" |
+ SCons.Warnings.warn(SCons.Warnings.NoMD5ModuleWarning, msg) |
+ self.path = None |
+ else: |
+ self.path = path |
+ self.current_cache_debug = None |
+ self.debugFP = None |
+ |
+ def CacheDebug(self, fmt, target, cachefile): |
+ if cache_debug != self.current_cache_debug: |
+ if cache_debug == '-': |
+ self.debugFP = sys.stdout |
+ elif cache_debug: |
+ self.debugFP = open(cache_debug, 'w') |
+ else: |
+ self.debugFP = None |
+ self.current_cache_debug = cache_debug |
+ if self.debugFP: |
+ self.debugFP.write(fmt % (target, os.path.split(cachefile)[1])) |
+ |
+ def is_enabled(self): |
+ return (cache_enabled and not self.path is None) |
+ |
+ def cachepath(self, node): |
+ """ |
+ """ |
+ if not self.is_enabled(): |
+ return None, None |
+ |
+ sig = node.get_cachedir_bsig() |
+ subdir = sig[0].upper() |
+ dir = os.path.join(self.path, subdir) |
+ return dir, os.path.join(dir, sig) |
+ |
+ def retrieve(self, node): |
+ """ |
+ This method is called from multiple threads in a parallel build, |
+ so only do thread safe stuff here. Do thread unsafe stuff in |
+ built(). |
+ |
+ Note that there's a special trick here with the execute flag |
+ (one that's not normally done for other actions). Basically |
+ if the user requested a no_exec (-n) build, then |
+ SCons.Action.execute_actions is set to 0 and when any action |
+ is called, it does its showing but then just returns zero |
+ instead of actually calling the action execution operation. |
+ The problem for caching is that if the file does NOT exist in |
+ cache then the CacheRetrieveString won't return anything to |
+ show for the task, but the Action.__call__ won't call |
+ CacheRetrieveFunc; instead it just returns zero, which makes |
+ the code below think that the file *was* successfully |
+ retrieved from the cache, therefore it doesn't do any |
+ subsequent building. However, the CacheRetrieveString didn't |
+ print anything because it didn't actually exist in the cache, |
+ and no more build actions will be performed, so the user just |
+ sees nothing. The fix is to tell Action.__call__ to always |
+ execute the CacheRetrieveFunc and then have the latter |
+ explicitly check SCons.Action.execute_actions itself. |
+ """ |
+ if not self.is_enabled(): |
+ return False |
+ |
+ env = node.get_build_env() |
+ if cache_show: |
+ if CacheRetrieveSilent(node, [], env, execute=1) == 0: |
+ node.build(presub=0, execute=0) |
+ return True |
+ else: |
+ if CacheRetrieve(node, [], env, execute=1) == 0: |
+ return True |
+ |
+ return False |
+ |
+ def push(self, node): |
+ if not self.is_enabled(): |
+ return |
+ return CachePush(node, [], node.get_build_env()) |
+ |
+ def push_if_forced(self, node): |
+ if cache_force: |
+ return self.push(node) |
+ |
+# Local Variables: |
+# tab-width:4 |
+# indent-tabs-mode:nil |
+# End: |
+# vim: set expandtab tabstop=4 shiftwidth=4: |
Property changes on: scons-2.0.1/engine/SCons/CacheDir.py |
___________________________________________________________________ |
Added: svn:eol-style |
+ LF |