summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorSteven Knight <knight@baldmt.com>2005-05-08 13:10:48 (GMT)
committerSteven Knight <knight@baldmt.com>2005-05-08 13:10:48 (GMT)
commite3d8cf40a448b1944e3f338d9c34e2611943ec81 (patch)
tree565ba7633504a68787a8436b4a44c97bce232631 /src
parentfba12a94d3d00e58e82aab25865b5d2da7466e88 (diff)
downloadSCons-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.txt4
-rw-r--r--src/RELEASE.txt16
-rw-r--r--src/engine/SCons/Action.py30
-rw-r--r--src/engine/SCons/ActionTests.py31
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'>",