'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
.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
'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
.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.
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".
'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
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(),
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
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
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
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.
"""
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
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
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_')
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.
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)
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):
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
'Precious',
'SideEffect',
'SourceCode',
+ 'SourceSignatures',
+ 'TargetSignatures',
]
for name in EnvironmentMethods:
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
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):
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:
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:
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()
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()
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', """
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', """
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")
"""))