The old behavior of a separate .sconsign file in each directory can
be specified by calling SConsignFile(None).
+ - Remove line number byte codes within the signature calculation
+ of Python function actions, so that changing the location of an
+ otherwise unmodified Python function doesn't cause rebuilds.
+
From Wayne Lee:
- Avoid "maximum recursion limit" errors when removing $(-$) pairs
This release adds several changes to the signature mechanism that
will cause SCons to rebuild most configurations after upgrading
- (and if switching back from 0.97 to an earlier release).
+ (and when switching back to an earlier release from 0.97).
These changes are:
-- NORMALIZED PATHS IN SConsignFile() DATABASES ON WINDOWS
the top of each SConstruct file, but we hope to make this
an easier/more naturally supported thing in the future.)
+ -- PYTHON FUNCTION ACTION SIGNATURES HAVE CHANGED TO AVOID
+ FUTURE REBUILDS AND REBUILDS BETWEEN PYTHON VERSIONS
+
+ SCons Actions for Python functions use the functions byte
+ code to generate their signature. The byte code in older
+ versions of Python includes indications of the line numbers
+ at which the function's code appeared in its original
+ source file, which means that changes in the location of
+ an otherwise unmodified Python function would trigger
+ rebuilds. The line number byte codes are now removed
+ from the signature, which will cause any targets built by
+ Python function Actions (including various pre-supplied
+ SCons Actions) be rebuilt.
+
-- CACHED Configure() RESULTS ARE STORED IN A DIFFERENT FILE
The Configure() subsystem now stores its cached results in a
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+import dis
import os
import os.path
import re
def default_exitstatfunc(s):
return s
+try:
+ SET_LINENO = dis.SET_LINENO
+ HAVE_ARGUMENT = dis.HAVE_ARGUMENT
+except AttributeError:
+ remove_set_lineno_codes = lambda x: x
+else:
+ def remove_set_lineno_codes(code):
+ result = []
+ n = len(code)
+ i = 0
+ while i < n:
+ c = code[i]
+ op = ord(c)
+ if op >= HAVE_ARGUMENT:
+ if op != SET_LINENO:
+ result.append(code[i:i+3])
+ i = i+3
+ else:
+ result.append(c)
+ i = i+1
+ return string.join(result, '')
+
def _actionAppend(act1, act2):
# This function knows how to slap two actions together.
# Mainly, it handles ListActions by concatenating into
By providing direct access to the code object of the
function, Python makes this extremely easy. Hooray!
+
+ Unfortunately, older versions of Python include line
+ number indications in the compiled byte code. Boo!
+ So we remove the line number byte codes to prevent
+ recompilations from moving a Python function.
"""
try:
# "self.execfunction" is a function.
contents = str(self.execfunction)
else:
contents = gc(target, source, env)
+ contents = remove_set_lineno_codes(contents)
return contents + env.subst(string.join(map(lambda v: '${'+v+'}',
self.varlist)))
# No __call__() method, so it might be a builtin
# or something like that. Do the best we can.
contents = str(actfunc)
+ contents = remove_set_lineno_codes(contents)
return contents
def subst(self, s, target, source, env):
# Special-case hack: Let a custom function wrapped in an
"""Test fetching the contents of a function Action
"""
- a = SCons.Action.FunctionAction(GlobalFunc)
+ def LocalFunc():
+ pass
matches = [
- "\177\036\000\177\037\000d\000\000S",
+ "d\000\000S",
"d\x00\x00S",
]
+ a = SCons.Action.FunctionAction(GlobalFunc)
+ c = a.get_contents(target=[], source=[], env=Environment())
+ assert c in matches, repr(c)
+
+ a = SCons.Action.FunctionAction(LocalFunc)
c = a.get_contents(target=[], source=[], env=Environment())
assert c in matches, repr(c)
def strfunc():
pass
+ def LocalFunc():
+ pass
+
matches = [
- "\177\036\000\177\037\000d\000\000S",
+ "d\000\000S",
"d\x00\x00S"
]
c = ac.get_contents([], [], Environment())
assert c in matches, repr(c)
+ af = SCons.Action.ActionFactory(LocalFunc, strfunc)
+ ac = SCons.Action.ActionCaller(af, [], {})
+ c = ac.get_contents([], [], Environment())
+ assert c in matches, repr(c)
+
matches = [
- '\177"\000\177#\000d\000\000S',
+ 'd\000\000S',
"d\x00\x00S"
]
+ class LocalActFunc:
+ def __call__(self):
+ pass
+
af = SCons.Action.ActionFactory(GlobalActFunc(), strfunc)
ac = SCons.Action.ActionCaller(af, [], {})
c = ac.get_contents([], [], Environment())
assert c in matches, repr(c)
+ af = SCons.Action.ActionFactory(LocalActFunc(), strfunc)
+ ac = SCons.Action.ActionCaller(af, [], {})
+ c = ac.get_contents([], [], Environment())
+ assert c in matches, repr(c)
+
matches = [
"<built-in function str>",
"<type 'str'>",