NodeInfo = FileNodeInfo
BuildInfo = FileBuildInfo
+ md5_chunksize = 64
+
def diskcheck_match(self):
diskcheck_match(self, self.isdir,
"Directory %s found where file expected.")
raise
return r
+ def get_content_hash(self):
+ """
+ Compute and return the MD5 hash for this file.
+ """
+ if not self.rexists():
+ return SCons.Util.MD5signature('')
+ fname = self.rfile().abspath
+ try:
+ cs = SCons.Util.MD5filesignature(fname,
+ chunksize=SCons.Node.FS.File.md5_chunksize*1024)
+ except EnvironmentError, e:
+ if not e.filename:
+ e.filename = fname
+ raise
+ return cs
+
+
memoizer_counters.append(SCons.Memoize.CountValue('get_size'))
def get_size(self):
if csig is None:
try:
- contents = self.get_contents()
+ if self.get_size() < SCons.Node.FS.File.md5_chunksize:
+ contents = self.get_contents()
+ else:
+ csig = self.get_content_hash()
except IOError:
# This can happen if there's actually a directory on-disk,
# which can be the case if they've disabled disk checks,
# create a same-named directory by mistake.
csig = ''
else:
- csig = SCons.Util.MD5signature(contents)
+ if not csig:
+ csig = SCons.Util.MD5signature(contents)
ninfo.csig = csig
cachedir, cachefile = self.get_build_env().get_CacheDir().cachepath(self)
if not self.exists() and cachefile and os.path.exists(cachefile):
- contents = open(cachefile, 'rb').read()
- self.cachedir_csig = SCons.Util.MD5signature(contents)
+ self.cachedir_csig = SCons.Util.MD5filesignature(cachefile, \
+ SCons.Node.FS.File.md5_chunksize * 1024)
else:
self.cachedir_csig = self.get_csig()
return self.cachedir_csig
self.cachesig = SCons.Util.MD5collect(sigs)
return self.cachesig
+
default_fs = None
def get_default_fs():
--- /dev/null
+#!/usr/bin/env python
+#
+# __COPYRIGHT__
+#
+# 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__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+import string
+import sys
+
+import TestSCons
+
+_python_ = TestSCons._python_
+
+test = TestSCons.TestSCons()
+
+test.write('build.py', r"""
+import sys
+contents = open(sys.argv[2], 'rb').read()
+file = open(sys.argv[1], 'wb')
+file.write(contents)
+file.close()
+""")
+
+test.write('SConstruct', """
+SetOption('md5_chunksize', 128)
+B = Builder(action = r'%(_python_)s build.py $TARGETS $SOURCES')
+env = Environment(BUILDERS = { 'B' : B })
+f1 = env.B(target = 'f1.out', source = 'f1.in')
+f2 = env.B(target = 'f2.out', source = 'f2.in')
+Requires(f2, f1)
+""" % locals())
+
+test.write('f1.in', str(range(10)))
+test.write('f2.in', str(range(100000)))
+
+expected_stdout = test.wrap_stdout("""\
+%(_python_)s build.py f1.out f1.in
+%(_python_)s build.py f2.out f2.in
+""" % locals())
+
+#
+# Test with SetOption('md5_chunksize')
+#
+test.run(arguments = '.',
+ stdout=expected_stdout,
+ stderr='')
+test.must_exist('f1.out')
+test.must_exist('f2.out')
+
+test.run(arguments = '-c .')
+test.must_not_exist('f1.out')
+test.must_not_exist('f2.out')
+
+#
+# Test with --md5-chunksize
+#
+test.run(arguments = '--md5-chunksize=128 .',
+ stdout=expected_stdout,
+ stderr='')
+test.must_exist('f1.out')
+test.must_exist('f2.out')
+
+test.run(arguments = '--md5-chunksize=128 -c .')
+test.must_not_exist('f1.out')
+test.must_not_exist('f2.out')
+
+test.pass_test()
+
+#
+# Big-file test
+#
+test2 = TestSCons.TestSCons()
+
+if string.find(sys.platform, 'linux') == -1:
+ test2.skip_test("skipping test on non-Linux platform '%s'\n" % sys.platform)
+
+dd = test2.where_is('dd')
+
+if not dd:
+ test2.skip_test('dd not found; skipping test\n')
+
+expected_stdout = test2.wrap_stdout("""\
+dd if=/dev/zero of=test.big seek=100 bs=1M count=0 2>/dev/null
+get_stat(["test.stat"], ["test.big"])
+""")
+
+test2.write('SConstruct', """
+import os
+def get_stat(target, source, env):
+ stat = os.stat(source[0].abspath)
+ dest = open(target[0].abspath,'w')
+ dest.write(str(stat))
+ dest.close()
+env = Environment()
+env.Command('test.big', 'SConstruct', 'dd if=/dev/zero of=test.big seek=100 bs=1M count=0 2>/dev/null')
+env.AlwaysBuild('test.big')
+env.Command('test.stat', 'test.big', Action(get_stat))
+""")
+
+test2.run(arguments='--md5-chunksize=128', stdout=expected_stdout, stderr='')
+test2.pass_test()