diff options
author | Steven Knight <knight@baldmt.com> | 2005-05-08 13:10:48 (GMT) |
---|---|---|
committer | Steven Knight <knight@baldmt.com> | 2005-05-08 13:10:48 (GMT) |
commit | e3d8cf40a448b1944e3f338d9c34e2611943ec81 (patch) | |
tree | 565ba7633504a68787a8436b4a44c97bce232631 /src | |
parent | fba12a94d3d00e58e82aab25865b5d2da7466e88 (diff) | |
download | SCons-e3d8cf40a448b1944e3f338d9c34e2611943ec81.zip SCons-e3d8cf40a448b1944e3f338d9c34e2611943ec81.tar.gz SCons-e3d8cf40a448b1944e3f338d9c34e2611943ec81.tar.bz2 |
Avoid rebuilds when otherwise unmodified Python function Actions move within a file and change line numbers.
Diffstat (limited to 'src')
-rw-r--r-- | src/CHANGES.txt | 4 | ||||
-rw-r--r-- | src/RELEASE.txt | 16 | ||||
-rw-r--r-- | src/engine/SCons/Action.py | 30 | ||||
-rw-r--r-- | src/engine/SCons/ActionTests.py | 31 |
4 files changed, 76 insertions, 5 deletions
diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 9f544d2..51574a5 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -271,6 +271,10 @@ RELEASE 0.97 - XXX 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 diff --git a/src/RELEASE.txt b/src/RELEASE.txt index 47b0031..bbf0a3a 100644 --- a/src/RELEASE.txt +++ b/src/RELEASE.txt @@ -88,7 +88,7 @@ RELEASE 0.97 - XXX 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 @@ -116,6 +116,20 @@ RELEASE 0.97 - XXX 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 diff --git a/src/engine/SCons/Action.py b/src/engine/SCons/Action.py index 51e9f03..f606bb2 100644 --- a/src/engine/SCons/Action.py +++ b/src/engine/SCons/Action.py @@ -97,6 +97,7 @@ way for wrapping up the functions. __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" +import dis import os import os.path import re @@ -127,6 +128,28 @@ def rfile(n): 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 @@ -626,6 +649,11 @@ class FunctionAction(_ActionAction): 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. @@ -643,6 +671,7 @@ class FunctionAction(_ActionAction): 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))) @@ -715,6 +744,7 @@ class ActionCaller: # 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 diff --git a/src/engine/SCons/ActionTests.py b/src/engine/SCons/ActionTests.py index f0905b6..f5aa67a 100644 --- a/src/engine/SCons/ActionTests.py +++ b/src/engine/SCons/ActionTests.py @@ -1430,13 +1430,19 @@ class FunctionActionTestCase(unittest.TestCase): """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) @@ -1602,8 +1608,11 @@ class ActionCallerTestCase(unittest.TestCase): def strfunc(): pass + def LocalFunc(): + pass + matches = [ - "\177\036\000\177\037\000d\000\000S", + "d\000\000S", "d\x00\x00S" ] @@ -1612,16 +1621,30 @@ class ActionCallerTestCase(unittest.TestCase): 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'>", |