Add SourceSignatures() and TargetSignatures() environment methods.
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Fri, 12 Sep 2003 13:07:20 +0000 (13:07 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Fri, 12 Sep 2003 13:07:20 +0000 (13:07 +0000)
git-svn-id: http://scons.tigris.org/svn/scons/trunk@793 fdb21ef1-2011-0410-befe-b5e4ea1792b1

12 files changed:
doc/man/scons.1
src/CHANGES.txt
src/engine/SCons/Defaults.py
src/engine/SCons/Environment.py
src/engine/SCons/EnvironmentTests.py
src/engine/SCons/Node/__init__.py
src/engine/SCons/Script/SConscript.py
src/engine/SCons/Script/__init__.py
src/engine/SCons/Sig/__init__.py
src/engine/SCons/Taskmaster.py
test/SourceSignatures.py
test/TargetSignatures.py

index 26e55952f108ce3b5e6e39532f308553f0b84285..696f38b905e874f2b2a04dd60b0442b63e61ed00 100644 (file)
@@ -3141,10 +3141,21 @@ env.SourceCode('no_source.c', None)
 '\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
 .TP
 .RI SourceSignatures( type )
+.TP
+.RI env.SourceSignatures( type )
 This function tells SCons what type of signature to use for source files:
 .B "MD5"
 or
 .BR "timestamp" .
+If the environment method is used,
+the specified type of source signature
+is only used when deciding whether targets
+built with that environment are up-to-date or must be rebuilt.
+If the global function is used,
+the specified type of source signature becomes the default
+used for all decisions
+about whether targets are up-to-date.
+
 "MD5" means the signature of a source file
 is the MD5 checksum of its contents.
 "timestamp" means the signature of a source file
@@ -3181,11 +3192,22 @@ files = Split("""
 '\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
 .TP
 .RI TargetSignatures( type )
+.TP
+.RI env.TargetSignatures( type )
 This function tells SCons what type of signatures to use
 for target files:
 .B "build"
 or
 .BR "content" .
+If the environment method is used,
+the specified type of signature is only used
+for targets built with that environment.
+If the global function is used,
+the specified type of signature becomes the default
+used for all target files that
+don't have an explicit target signature type
+specified for their environments.
+
 "build" means the signature of a target file
 is made by concatenating all of the
 signatures of all its source files.
@@ -3193,7 +3215,8 @@ signatures of all its source files.
 file is an MD5 checksum of its contents.
 "build" signatures are usually faster to compute,
 but "content" signatures can prevent unnecessary rebuilds
-when a target file is rebuilt to the exact same contents as last time.
+when a target file is rebuilt to the exact same contents
+as the previous build.
 The default is "build".
 
 '\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
index 259e199c395aeb3fab436c41f87e9bee7b1a4995..7e6b70fad579330237eb5dc4dc92110cc3b4dd92 100644 (file)
@@ -44,8 +44,8 @@ RELEASE X.XX - XXX
     file and directory arguments to Builder calls and Environment methods.
 
   - Add Environment-method versions of the following global function:
-    AddPreAction(), AddPostAction(), Clean(), Default(), FindFile()
-    and Local().
+    AddPreAction(), AddPostAction(), Clean(), Default(), FindFile(),
+    Local(), SourceSignatures(), TargetSignatures().
 
   - Add the following global functions that correspond to the same-named
     Environment methods:  AlwaysBuild(), Command(), Depends(), Ignore(),
index 66e8b008f70977bbc7d8eab3dfe3e0dda14baa9a..798abb377553462f77b6fcca658c3abf841f2072 100644 (file)
@@ -51,6 +51,7 @@ import SCons.Node.FS
 import SCons.Scanner.C
 import SCons.Scanner.Fortran
 import SCons.Scanner.Prog
+import SCons.Sig
 
 # A placeholder for a default Environment (for fetching source files
 # from source code management systems and the like).  This must be
@@ -64,6 +65,8 @@ def DefaultEnvironment(*args, **kw):
     global _default_env
     if not _default_env:
         _default_env = apply(SCons.Environment.Environment, args, kw)
+        _default_env._build_signature = 1
+        _default_env._calc_module = SCons.Sig.default_module
     return _default_env
 
 
index f2cc03fca9c0c4becf1adab948649a3983310b32..3d00837c6d5e974c1e499c167768d953b61c1e9f 100644 (file)
@@ -64,7 +64,12 @@ _null = _Null
 
 DefaultTargets = None
 CleanTargets = {}
-SigModule = None
+CalculatorArgs = {}
+
+# Pull UserError into the global name space for the benefit of
+# Environment().SourceSignatures(), which has some import statements
+# which seem to mess up its ability to reference SCons directly.
+UserError = SCons.Errors.UserError
 
 def installFunc(target, source, env):
     """Install a source file into a target using the function specified
@@ -312,6 +317,21 @@ class Environment:
     
         return nodes
 
+    def get_calculator(self):
+        try:
+            return self._calculator
+        except AttributeError:
+            try:
+                module = self._calc_module
+                c = apply(SCons.Sig.Calculator, (module,), CalculatorArgs)
+            except AttributeError:
+                # Note that we're calling get_calculator() here, so the
+                # DefaultEnvironment() must have a _calc_module attribute
+                # to avoid infinite recursion.
+                c = SCons.Defaults.DefaultEnvironment().get_calculator()
+            self._calculator = c
+            return c
+
     def get_builder(self, name):
         """Fetch the builder with the specified name from the environment.
         """
@@ -358,6 +378,14 @@ class Environment:
         return SCons.Util.scons_subst_list(string, self, mode,
                                            target, source)
 
+    def use_build_signature(self):
+        try:
+            return self._build_signature
+        except AttributeError:
+            b = SCons.Defaults.DefaultEnvironment()._build_signature
+            self._build_signature = b
+            return b
+
     #######################################################################
     # Public methods for manipulating an Environment.  These begin with
     # upper-case letters.  The essential characteristic of methods in
@@ -765,3 +793,23 @@ class Environment:
         if len(entries) == 1:
             return entries[0]
         return entries
+
+    def SourceSignatures(self, type):
+        type = self.subst(type)
+        if type == 'MD5':
+            import SCons.Sig.MD5
+            self._calc_module = SCons.Sig.MD5
+        elif type == 'timestamp':
+            import SCons.Sig.TimeStamp
+            self._calc_module = SCons.Sig.TimeStamp
+        else:
+            raise UserError, "Unknown source signature type '%s'"%type
+
+    def TargetSignatures(self, type):
+        type = self.subst(type)
+        if type == 'build':
+            self._build_signature = 1
+        elif type == 'content':
+            self._build_signature = 0
+        else:
+            raise SCons.Errors.UserError, "Unknown target signature type '%s'"%type
index 1dc47e221b7c47ae690d056c3f46eb2883be66af..baf501efb2a3e76152b550a496e221752fa494a2 100644 (file)
@@ -1310,6 +1310,54 @@ class EnvironmentTestCase(unittest.TestCase):
         s = e.src_builder()
         assert s is None, s
 
+    def test_SourceSignatures(type):
+        """Test the SourceSignatures() method"""
+        env = Environment(M = 'MD5', T = 'timestamp')
+
+        exc_caught = None
+        try:
+            env.SourceSignatures('invalid_type')
+        except SCons.Errors.UserError:
+            exc_caught = 1
+        assert exc_caught, "did not catch expected UserError"
+        assert not hasattr(env, '_calc_module')
+
+        env.SourceSignatures('MD5')
+        m = env._calc_module
+
+        env.SourceSignatures('$M')
+        assert env._calc_module is m
+
+        env.SourceSignatures('timestamp')
+        t = env._calc_module
+
+        env.SourceSignatures('$T')
+        assert env._calc_module is t
+
+    def test_TargetSignatures(type):
+        """Test the TargetSignatures() method"""
+        env = Environment(B = 'build', C = 'content')
+
+        exc_caught = None
+        try:
+            env.TargetSignatures('invalid_type')
+        except SCons.Errors.UserError:
+            exc_caught = 1
+        assert exc_caught, "did not catch expected UserError"
+        assert not hasattr(env, '_build_signature')
+
+        env.TargetSignatures('build')
+        assert env._build_signature == 1, env._build_signature
+
+        env.TargetSignatures('content')
+        assert env._build_signature == 0, env._build_signature
+
+        env.TargetSignatures('$B')
+        assert env._build_signature == 1, env._build_signature
+
+        env.TargetSignatures('$C')
+        assert env._build_signature == 0, env._build_signature
+
 
 if __name__ == "__main__":
     suite = unittest.makeSuite(EnvironmentTestCase, 'test_')
index ea61de68e6ec0ef5e85f5b2ff03d6638f24aa3f7..3802aad4fe5a48c3466aa7be2f0c2b87e352ee72 100644 (file)
@@ -408,6 +408,10 @@ class Node:
             return
         self.env = env
 
+    def calculator(self):
+        env = self.env or SCons.Defaults.DefaultEnvironment()
+        return env.get_calculator()
+
     def calc_signature(self, calc):
         """
         Select and calculate the appropriate build signature for a node.
@@ -420,7 +424,8 @@ class Node:
             return self._calculated_sig
         except AttributeError:
             if self.is_derived():
-                if SCons.Sig.build_signature:
+                env = self.env or SCons.Defaults.DefaultEnvironment()
+                if env.use_build_signature():
                     sig = self.rfile().calc_bsig(calc, self)
                 else:
                     sig = self.rfile().calc_csig(calc, self)
index b79c34a07546a9449cf9b696c28bb612bfb4e87b..4f29f26b46c281fe4f36b67d158b0dd3e427ab7c 100644 (file)
@@ -394,33 +394,13 @@ def SetBuildSignatureType(type):
     SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning,
                         "The SetBuildSignatureType() function has been deprecated;\n" +\
                         "\tuse the TargetSignatures() function instead.")
-    TargetSignatures(type)
-
-def TargetSignatures(type):
-    import SCons.Sig
-    if type == 'build':
-        SCons.Sig.build_signature = 1
-    elif type == 'content':
-        SCons.Sig.build_signature = 0
-    else:
-        raise SCons.Errors.UserError, "Unknown build signature type '%s'"%type
+    SCons.Defaults.DefaultEnvironment().TargetSignatures(type)
 
 def SetContentSignatureType(type):
     SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning,
                         "The SetContentSignatureType() function has been deprecated;\n" +\
                         "\tuse the SourceSignatures() function instead.")
-    SourceSignatures(type)
-
-def SourceSignatures(type):
-    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
-
+    SCons.Defaults.DefaultEnvironment().SourceSignatures(type)
 
 class Options(SCons.Options.Options):
     def __init__(self, files=None, args=arguments):
@@ -534,9 +514,7 @@ def BuildDefaultGlobals():
     globals['SetContentSignatureType'] = SetContentSignatureType
     globals['SetJobs']           = SetJobs
     globals['SetOption']         = SetOption
-    globals['SourceSignatures']  = SourceSignatures
     globals['Split']             = SCons.Util.Split
-    globals['TargetSignatures']  = TargetSignatures
     globals['Tool']              = SCons.Tool.Tool
     globals['Value']             = SCons.Node.Python.Value
     globals['WhereIs']           = SCons.Util.WhereIs
@@ -566,6 +544,8 @@ def BuildDefaultGlobals():
         'Precious',
         'SideEffect',
         'SourceCode',
+        'SourceSignatures',
+        'TargetSignatures',
     ]
 
     for name in EnvironmentMethods:
index 34b112fe2c7eba30d8ee071bd9e5faecd93a9978..61e75611ce1c641e3ffdc2037e841765d8c8be74 100644 (file)
@@ -233,7 +233,6 @@ command_time = 0
 exit_status = 0 # exit status, assume success by default
 profiling = 0
 repositories = []
-sig_module = SCons.Sig.default_module
 num_jobs = 1 # this is modifed by SConscript.SetJobs()
 
 # Exceptions for this module
@@ -942,10 +941,7 @@ def _main(args, parser):
     except AttributeError:
         pass
 
-    if not calc:
-        SCons.Sig.default_calc = SCons.Sig.Calculator(module=sig_module,
-                                                      max_drift=ssoptions.get('max_drift'))
-        calc = SCons.Sig.default_calc
+    SCons.Environment.CalculatorArgs['max_drift'] = ssoptions.get('max_drift')
 
     if options.random:
         def order(dependencies):
index 0245a2c2886499d1a4989288445edca2f73d49df..4898f29ed8ab40bbbf00a8238cd2a2e0bce45434 100644 (file)
@@ -51,10 +51,6 @@ sig_files = []
 
 SConsign_db = None
 
-# 1 means use build signature for derived source files
-# 0 means use content signature for derived source files
-build_signature = 1
-
 def write():
     global sig_files
     for sig_file in sig_files:
index 79375699d5b0bdd1e930bfbe282b45ca7ecbd6f4..9b13b60d624d25c87d6bbb2311d2bf0b121444ca 100644 (file)
@@ -161,8 +161,10 @@ class Task:
     def make_ready(self):
         """Make a task ready for execution."""
         state = SCons.Node.up_to_date
+        calc = self.tm.calc
         for t in self.targets:
-            if not t.current(self.tm.calc):
+            c = calc or t.calculator()
+            if not t.current(c):
                 state = SCons.Node.executing
         for t in self.targets:
             if state == SCons.Node.executing:
index 26a389e2eafbe2b378de604cecb219b5f7b4a464..e982fbe8f7d25602f687d4a4453bbb2984a78513 100644 (file)
@@ -125,20 +125,51 @@ env.B(target = 'f4.out', source = 'f4.in')
 
 test.up_to_date(arguments = 'f1.out f2.out f3.out f4.out')
 
-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')
+env2 = env.Copy()
+env2.SourceSignatures('MD5')
+env.B(target = 'f5.out', source = 'f5.in')
+env.B(target = 'f6.out', source = 'f6.in')
+env2.B(target = 'f7.out', source = 'f7.in')
+env2.B(target = 'f8.out', source = 'f8.in')
 
 SourceSignatures('timestamp')
 """)
 
-test.run(arguments = 'f1.out f2.out f3.out f4.out',
-         stdout = test.wrap_stdout(''))
+test.write('f5.in', "f5.in\n")
+test.write('f6.in', "f6.in\n")
+test.write('f7.in', "f7.in\n")
+test.write('f8.in', "f8.in\n")
+
+test.run(arguments = 'f5.out f7.out')
+
+test.run(arguments = 'f5.out f6.out f7.out f8.out',
+         stdout = test.wrap_stdout("""\
+scons: `f5.out' is up to date.
+build("f6.out", "f6.in")
+scons: `f7.out' is up to date.
+build("f8.out", "f8.in")
+"""))
+
+os.utime(test.workpath('f5.in'), 
+         (os.path.getatime(test.workpath('f5.in')),
+          os.path.getmtime(test.workpath('f5.in'))+10))
+os.utime(test.workpath('f7.in'), 
+         (os.path.getatime(test.workpath('f7.in')),
+          os.path.getmtime(test.workpath('f7.in'))+10))
+
+test.run(arguments = 'f5.out f6.out f7.out f8.out',
+         stdout = test.wrap_stdout("""\
+build("f5.out", "f5.in")
+scons: `f6.out' is up to date.
+scons: `f7.out' is up to date.
+scons: `f8.out' is up to date.
+"""))
+
+test.up_to_date(arguments = 'f5.out f6.out f7.out f8.out')
+
+test.pass_test()
index 3c7ed0f2cdf365de3b0bbca2045eb514e15475a5..5060157f3003cb598cd2d4cf607368584c0f8796 100644 (file)
@@ -40,21 +40,29 @@ def copy2(env, source, target):
 env['BUILDERS']['Copy1'] = Builder(action=copy1)
 env['BUILDERS']['Copy2'] = Builder(action=copy2)
 
-env.Copy2('foo.out', 'foo.in')
-env.Copy1('foo.out.out', 'foo.out')
+env.Copy2('foo.mid', 'foo.in')
+env.Copy1('foo.out', 'foo.mid')
+
+env2 = env.Copy()
+env2.TargetSignatures('build')
+env2.Copy2('bar.mid', 'bar.in')
+env2.Copy1('bar.out', 'bar.mid')
 
 TargetSignatures('content')
 """)
 
 test.write('foo.in', 'foo.in')
+test.write('bar.in', 'bar.in')
 
-test.run(arguments='foo.out.out',
+test.run(arguments="bar.out foo.out",
          stdout=test.wrap_stdout("""\
-copy2("foo.out", "foo.in")
-copy1("foo.out.out", "foo.out")
+copy2("bar.mid", "bar.in")
+copy1("bar.out", "bar.mid")
+copy2("foo.mid", "foo.in")
+copy1("foo.out", "foo.mid")
 """))
 
-test.up_to_date(arguments='foo.out.out')
+test.up_to_date(arguments='bar.out foo.out')
 
 test.write('SConstruct', """
 env = Environment()
@@ -69,16 +77,23 @@ def copy2(env, source, target):
 env['BUILDERS']['Copy1'] = Builder(action=copy1)
 env['BUILDERS']['Copy2'] = Builder(action=copy2)
 
-env.Copy2('foo.out', 'foo.in')
-env.Copy1('foo.out.out', 'foo.out')
+env.Copy2('foo.mid', 'foo.in')
+env.Copy1('foo.out', 'foo.mid')
+
+env2 = env.Copy()
+env2.TargetSignatures('build')
+env2.Copy2('bar.mid', 'bar.in')
+env2.Copy1('bar.out', 'bar.mid')
 
 TargetSignatures('content')
 """)
 
-test.run(arguments='foo.out.out',
+test.run(arguments="bar.out foo.out",
          stdout=test.wrap_stdout("""\
-copy2("foo.out", "foo.in")
-scons: `foo.out.out' is up to date.
+copy2("bar.mid", "bar.in")
+copy1("bar.out", "bar.mid")
+copy2("foo.mid", "foo.in")
+scons: `foo.out' is up to date.
 """))
 
 test.write('SConstruct', """
@@ -94,15 +109,21 @@ def copy2(env, source, target):
 env['BUILDERS']['Copy1'] = Builder(action=copy1)
 env['BUILDERS']['Copy2'] = Builder(action=copy2)
 
-env.Copy2('foo.out', 'foo.in')
-env.Copy1('foo.out.out', 'foo.out')
+env.Copy2('foo.mid', 'foo.in')
+env.Copy1('foo.out', 'foo.mid')
+
+env2 = env.Copy()
+env2.TargetSignatures('content')
+env2.Copy2('bar.mid', 'bar.in')
+env2.Copy1('bar.out', 'bar.mid')
 
 TargetSignatures('build')
 """)
 
-test.run(arguments='foo.out.out',
+test.run(arguments="bar.out foo.out",
          stdout=test.wrap_stdout("""\
-copy1("foo.out.out", "foo.out")
+copy1("bar.out", "bar.mid")
+copy1("foo.out", "foo.mid")
 """))
 
 test.write('SConstruct', """
@@ -117,16 +138,23 @@ def copy2(env, source, target):
 env['BUILDERS']['Copy1'] = Builder(action=copy1)
 env['BUILDERS']['Copy2'] = Builder(action=copy2)
 
-env.Copy2('foo.out', 'foo.in')
-env.Copy1('foo.out.out', 'foo.out')
+env.Copy2('foo.mid', 'foo.in')
+env.Copy1('foo.out', 'foo.mid')
+
+env2 = env.Copy()
+env2.TargetSignatures('content')
+env2.Copy2('bar.mid', 'bar.in')
+env2.Copy1('bar.out', 'bar.mid')
 
 TargetSignatures('build')
 """)
 
-test.run(arguments='foo.out.out',
+test.run(arguments='bar.out foo.out',
          stdout=test.wrap_stdout("""\
-copy2("foo.out", "foo.in")
-copy1("foo.out.out", "foo.out")
+copy2("bar.mid", "bar.in")
+scons: `bar.out' is up to date.
+copy2("foo.mid", "foo.in")
+copy1("foo.out", "foo.mid")
 """))