Add a function to choose content signature type. (Anthony Roach)
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Mon, 4 Nov 2002 23:01:27 +0000 (23:01 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Mon, 4 Nov 2002 23:01:27 +0000 (23:01 +0000)
git-svn-id: http://scons.tigris.org/svn/scons/trunk@489 fdb21ef1-2011-0410-befe-b5e4ea1792b1

config
doc/man/scons.1
runtest.py
src/CHANGES.txt
src/engine/SCons/Script/SConscript.py
src/engine/SCons/Script/__init__.py
src/engine/SCons/Sig/TimeStamp.py
src/engine/SCons/Sig/__init__.py
test/SetContentSignatureType.py [new file with mode: 0644]

diff --git a/config b/config
index 28215e75867c9430771563198aca8ecaf80ce64d..fc04c4b3800351eedbb5be5e070c6513944e3e2f 100644 (file)
--- a/config
+++ b/config
@@ -234,14 +234,20 @@ diff_command =
        test $? -le 1";
 
 /*
- * We use an intermediary test.pl script to execute tests.
- * This serves as glue between the tests themselves (which are
- * written to conform to Perl conventions) and Aegis' expectations.
- * See the comments in the test.pl script itself for details.
+ * We use a runtest.pl script to execute tests.  This takes care of
+ * massaging environment variables and the like to test agains the
+ * unpacked package in the current directory.
+ *
+ * Note that we must include $spe in the batch_test_command line (so
+ * that Aegis thinks we're smart about testing ourselves against the
+ * baseline) but we don't actually need it.  Our tests always run
+ * relative to the package built under the current directory, which
+ * is set appropriately during a baseline test.  So we just use the
+ * proper aesub variable to comment out the expanded $spe.
  */
 test_command = "python ${Source runtest.py Absolute} -p tar-gz -q ${File_Name}";
 
-batch_test_command = "python ${Source runtest.py Absolute} -p tar-gz -o ${Output} ${File_Names}";
+batch_test_command = "python ${Source runtest.py Absolute} -p tar-gz -o ${Output} ${File_Names} ${COMment $spe}";
 
 new_test_filename = "test/CHANGETHIS.py";
 
index 6307f34fc07b07a1ebf251dbcdc4bb856fc5647d..3d7d0341eb5e08e7adbe728e0318159954f09362 100644 (file)
@@ -2420,6 +2420,16 @@ SConscriptChdir(0)
 SConscript('bar/SConscript')   # will not chdir to bar
 .EE
 
+.TP 
+.RI SetBuildSignatureType( type )
+
+This function tells SCons what type of build signature to use: "build" or
+"content". "build" means to concatenate the signatures of all source files
+of a derived file to make its signature, and "content" means to use
+the derived files content signature as its signature. "build" signatures
+are usually faster to compute, but "content" signatures can prevent
+redundant rebuilds. The default is "build".
+
 .TP 
 .RI SetCommandHandler( function )
 
@@ -2441,6 +2451,17 @@ is that arguments to the command.
 is a dictionary of the environment variables
 in which the command should be executed.
 
+.TP
+.RI SetContentSignatureType( type )
+
+This function tells SCons what type of content signature to use: "MD5" or
+"timestamp". "MD5" means to use the MD5 checksum of a files contents as
+its signature, and "timestamp" means to use a files timestamp as its
+signature. When using "timestamp" signatures, changes in the
+command line will not cause files to be rebuilt. "MD5" signatures take
+longer to compute, but "timestamp" signatures are less accurate. The
+default is "MD5".
+
 .TP
 .RI Split( arg )
 Returns a list of file names or other objects.
index fddcd64f7d6f3912cddc963874ff42b8590c8a91..98a24ebfc35fd84c420a028f1370b615dd465cb9 100644 (file)
@@ -246,5 +246,11 @@ if output:
         f.write('      exit_status = %d; },\n' % t.status)
     f.write("];\n")
     f.close()
-
-sys.exit(len(fail) + len(no_result))
+    sys.exit(0)
+else:
+    if len(fail):
+        sys.exit(1)
+    elif len(no_result):
+        sys.exit(2)
+    else:
+        sys.exit(0)
index 0b1b027e7156c7349fc9f922cea3bb5d9e586c5c..c91a59edb6ac3a9eaedc8eaaaed814839379a02f 100644 (file)
@@ -96,6 +96,9 @@ RELEASE 0.09 -
 
   - Fix the overly-verbose stack trace on ListBuilder build errors.
 
+  - Add a SetContentSignatureType() function, allowing use of file
+    timestamps instead of MD5 signatures.
+
   From sam th:
 
   - Dynamically check for the existence of utilities with which to
index cb20ae9fafd496c129ae4195054ae2e7d236dcf4..2802e8fd89ec4296745f92bdc621002dd01ebe81 100644 (file)
@@ -39,7 +39,6 @@ import SCons.Node.FS
 import SCons.Platform
 import SCons.Tool
 import SCons.Util
-import SCons.Sig
 import SCons.Options
 import SCons
 
@@ -258,6 +257,7 @@ def GetLaunchDir():
     return launch_dir
 
 def SetBuildSignatureType(type):
+    import SCons.Sig
     if type == 'build':
         SCons.Sig.build_signature = 1
     elif type == 'content':
@@ -265,6 +265,18 @@ def SetBuildSignatureType(type):
     else:
         raise SCons.Errors.UserError, "Unknown build signature type '%s'"%type
 
+def SetContentSignatureType(type):
+    import SCons.Script
+    if type == 'MD5':
+        import SCons.Sig.MD5
+        SCons.Script.sig_module = SCons.Sig.MD5
+    elif type == 'timestamp':
+        import SCons.Sig.TimeStamp
+        SCons.Script.sig_module = SCons.Sig.TimeStamp
+    else:
+        raise SCons.Errors.UserError, "Unknown content signature type '%s'"%type
+
+
 class Options(SCons.Options.Options):
     def Update(self, env):
         return SCons.Options.Options.Update(self, env, arguments)
@@ -321,6 +333,7 @@ def BuildDefaultGlobals():
     globals['Object']            = SCons.Defaults.StaticObject
     globals['Repository']        = SCons.Node.FS.default_fs.Repository
     globals['SetBuildSignatureType'] = SetBuildSignatureType
+    globals['SetContentSignatureType'] = SetContentSignatureType
     globals['StaticLibrary']     = SCons.Defaults.StaticLibrary
     globals['StaticObject']      = SCons.Defaults.StaticObject
     globals['SharedLibrary']     = SCons.Defaults.SharedLibrary
index fd43b9a24b96803bed02b30917f604e3ca482e2d..1403724f3fcf1e21fd593e5c705d3ff94545303e 100644 (file)
@@ -61,7 +61,6 @@ import SCons.Node.FS
 import SCons.Job
 from SCons.Errors import *
 import SCons.Sig
-import SCons.Sig.MD5
 from SCons.Taskmaster import Taskmaster
 import SCons.Builder
 import SCons.Script.SConscript
@@ -178,6 +177,7 @@ exit_status = 0 # exit status, assume success by default
 profiling = 0
 max_drift = None
 repositories = []
+sig_module = None
 
 #
 def print_it(text):
@@ -1040,9 +1040,14 @@ def _main():
 
     if not calc:
         if max_drift is not None:
-            SCons.Sig.default_calc = SCons.Sig.Calculator(SCons.Sig.MD5,
-                                                          max_drift)
-
+            if sig_module is not None:
+                SCons.Sig.default_calc = SCons.Sig.Calculator(module=sig_module,
+                                                              max_drift=max_drift)
+            else:
+                SCons.Sig.default_calc = SCons.Sig.Calculator(max_drift=max_drift)
+        elif sig_module is not None:
+            SCons.Sig.default_calc = SCons.Sig.Calculator(module=sig_module)
+            
         calc = SCons.Sig.default_calc
 
     display("scons: Building targets ...")
index de0600d91e367ffb02e06919c5369aa3727df7d1..997742d524534e58782977f90e4622edb06ebf09 100644 (file)
@@ -63,6 +63,13 @@ def to_string(signature):
 
 def from_string(string):
     """Convert a string to a timestamp"""
-    return int(string)
+    try:
+        return int(string)
+    except ValueError:
+        # if the signature isn't an int, then
+        # the user probably just switched from
+        # MD5 signatures to timestamp signatures,
+        # so ignore the error:
+        return None
 
 
index b6a5c25fe42958ceb389a81b48411fd43057da52..9a4c84691ee058834c82ad6e138e1a799919a2a9 100644 (file)
@@ -36,6 +36,13 @@ import SCons.Node
 import time
 import SCons.Warnings
 
+try:
+    import MD5
+    default_module = MD5
+except ImportError:
+    import TimeStamp
+    default_module = TimeStamp
+
 #XXX Get rid of the global array so this becomes re-entrant.
 sig_files = []
 
@@ -265,14 +272,13 @@ class SConsignFile:
             except:
                 pass
 
-
 class Calculator:
     """
     Encapsulates signature calculations and .sconsign file generating
     for the build engine.
     """
 
-    def __init__(self, module=None, max_drift=2*24*60*60):
+    def __init__(self, module=default_module, max_drift=2*24*60*60):
         """
         Initialize the calculator.
 
@@ -281,17 +287,7 @@ class Calculator:
           cache content signatures. A negative value means to never cache
           content signatures. (defaults to 2 days)
         """
-        if module is None:
-            try:
-                import MD5
-                self.module = MD5
-            except ImportError:
-                # fallback on timestamp signatures if MD5 is not available
-                # XXX add a warning message here
-                import TimeStamp
-                self.module = TimeStamp
-        else:
-            self.module = module
+        self.module = module
         self.max_drift = max_drift
 
     def bsig(self, node):
diff --git a/test/SetContentSignatureType.py b/test/SetContentSignatureType.py
new file mode 100644 (file)
index 0000000..f9915ad
--- /dev/null
@@ -0,0 +1,131 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2001, 2002 Steven Knight
+#
+# 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 imp
+import os
+import os.path
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.write('SConstruct', """
+def build(env, target, source):
+    open(str(target[0]), 'wt').write(open(str(source[0]), 'rt').read())
+B = Builder(action = build)
+env = Environment(BUILDERS = { 'B' : B })
+env.B(target = 'f1.out', source = 'f1.in')
+env.B(target = 'f2.out', source = 'f2.in')
+env.B(target = 'f3.out', source = 'f3.in')
+env.B(target = 'f4.out', source = 'f4.in')
+
+SetContentSignatureType('timestamp')
+""")
+
+test.write('f1.in', "f1.in\n")
+test.write('f2.in', "f2.in\n")
+test.write('f3.in', "f3.in\n")
+test.write('f4.in', "f4.in\n")
+
+test.run(arguments = 'f1.out f3.out')
+
+test.run(arguments = 'f1.out f2.out f3.out f4.out',
+         stdout = test.wrap_stdout('scons: "f1.out" is up to date.\nscons: "f3.out" is up to date.\n'))
+
+os.utime(test.workpath('f1.in'), 
+         (os.path.getatime(test.workpath('f1.in')),
+          os.path.getmtime(test.workpath('f1.in'))+10))
+os.utime(test.workpath('f3.in'), 
+         (os.path.getatime(test.workpath('f3.in')),
+          os.path.getmtime(test.workpath('f3.in'))+10))
+
+test.run(arguments = 'f1.out f2.out f3.out f4.out',
+         stdout = test.wrap_stdout('scons: "f2.out" is up to date.\nscons: "f4.out" is up to date.\n'))
+
+test.write('SConstruct', """
+def build(env, target, source):
+    open(str(target[0]), 'wt').write(open(str(source[0]), 'rt').read())
+B = Builder(action = build)
+env = Environment(BUILDERS = { 'B' : B })
+env.B(target = 'f1.out', source = 'f1.in')
+env.B(target = 'f2.out', source = 'f2.in')
+env.B(target = 'f3.out', source = 'f3.in')
+env.B(target = 'f4.out', source = 'f4.in')
+
+SetContentSignatureType('MD5')
+""")
+
+test.write('f1.in', "f1.in\n")
+test.write('f2.in', "f2.in\n")
+test.write('f3.in', "f3.in\n")
+test.write('f4.in', "f4.in\n")
+
+test.run(arguments = 'f1.out f3.out')
+
+test.run(arguments = 'f1.out f2.out f3.out f4.out',
+         stdout = test.wrap_stdout('scons: "f1.out" is up to date.\nscons: "f3.out" is up to date.\n'))
+
+os.utime(test.workpath('f1.in'), 
+         (os.path.getatime(test.workpath('f1.in')),
+          os.path.getmtime(test.workpath('f1.in'))+10))
+os.utime(test.workpath('f3.in'), 
+         (os.path.getatime(test.workpath('f3.in')),
+          os.path.getmtime(test.workpath('f3.in'))+10))
+
+test.run(arguments = 'f1.out f2.out f3.out f4.out',
+         stdout = test.wrap_stdout('scons: "f1.out" is up to date.\nscons: "f2.out" is up to date.\nscons: "f3.out" is up to date.\nscons: "f4.out" is up to date.\n'))
+
+test.write('SConstruct', """
+def build(env, target, source):
+    open(str(target[0]), 'wt').write(open(str(source[0]), 'rt').read())
+B = Builder(action = build)
+env = Environment(BUILDERS = { 'B' : B })
+env.B(target = 'f1.out', source = 'f1.in')
+env.B(target = 'f2.out', source = 'f2.in')
+env.B(target = 'f3.out', source = 'f3.in')
+env.B(target = 'f4.out', source = 'f4.in')
+""")
+
+test.run(arguments = 'f1.out f2.out f3.out f4.out',
+         stdout = test.wrap_stdout('scons: "f1.out" is up to date.\nscons: "f2.out" is up to date.\nscons: "f3.out" is up to date.\nscons: "f4.out" is up to date.\n'))
+
+test.pass_test()
+
+test.write('SConstruct', """
+def build(env, target, source):
+    open(str(target[0]), 'wt').write(open(str(source[0]), 'rt').read())
+B = Builder(action = build)
+env = Environment(BUILDERS = { 'B' : B })
+env.B(target = 'f1.out', source = 'f1.in')
+env.B(target = 'f2.out', source = 'f2.in')
+env.B(target = 'f3.out', source = 'f3.in')
+env.B(target = 'f4.out', source = 'f4.in')
+
+SetContentSignatureType('timestamp')
+""")
+
+test.run(arguments = 'f1.out f2.out f3.out f4.out',
+         stdout = test.wrap_stdout(''))