diff options
author | Steven Knight <knight@baldmt.com> | 2001-11-02 03:12:07 (GMT) |
---|---|---|
committer | Steven Knight <knight@baldmt.com> | 2001-11-02 03:12:07 (GMT) |
commit | 2f51f69cf055ec44e37eaac22b1992bd5218278f (patch) | |
tree | 60165bd248d020974a0be3f5477825876a682445 /src | |
parent | 00c3ada748b164ecf909727fa81f521dde72fda9 (diff) | |
download | SCons-2f51f69cf055ec44e37eaac22b1992bd5218278f.zip SCons-2f51f69cf055ec44e37eaac22b1992bd5218278f.tar.gz SCons-2f51f69cf055ec44e37eaac22b1992bd5218278f.tar.bz2 |
Rebuild in response to a changed build command.
Diffstat (limited to 'src')
-rw-r--r-- | src/engine/SCons/Builder.py | 113 | ||||
-rw-r--r-- | src/engine/SCons/BuilderTests.py | 31 | ||||
-rw-r--r-- | src/engine/SCons/Node/NodeTests.py | 11 | ||||
-rw-r--r-- | src/engine/SCons/Node/__init__.py | 17 | ||||
-rw-r--r-- | src/engine/SCons/Sig/MD5.py | 7 | ||||
-rw-r--r-- | src/engine/SCons/Sig/MD5Tests.py | 9 | ||||
-rw-r--r-- | src/engine/SCons/Sig/SigTests.py | 23 | ||||
-rw-r--r-- | src/engine/SCons/Sig/__init__.py | 6 |
8 files changed, 183 insertions, 34 deletions
diff --git a/src/engine/SCons/Builder.py b/src/engine/SCons/Builder.py index 4023530..3bb17da 100644 --- a/src/engine/SCons/Builder.py +++ b/src/engine/SCons/Builder.py @@ -188,6 +188,12 @@ class BuilderBase: """ return apply(self.action.execute, (), kw) + def get_contents(self, **kw): + """Fetch the "contents" of the builder's action + (for signature calculation). + """ + return apply(self.action.get_contents, (), kw) + class MultiStepBuilder(BuilderBase): """This is a builder subclass that can build targets in multiple steps. The src_builder parameter to the constructor @@ -301,47 +307,85 @@ class ActionBase: def show(self, string): print string + def subst_dict(self, **kw): + """Create a dictionary for substitution of construction + variables. + + This translates the following special arguments: + + env - the construction environment itself, + the values of which (CC, CCFLAGS, etc.) + are copied straight into the dictionary + + target - the target (object or array of objects), + used to generate the TARGET and TARGETS + construction variables + + source - the source (object or array of objects), + used to generate the SOURCES construction + variable + + Any other keyword arguments are copied into the + dictionary.""" + + dict = {} + if kw.has_key('env'): + dict.update(kw['env']) + del kw['env'] + + if kw.has_key('target'): + t = kw['target'] + del kw['target'] + if type(t) is type(""): + t = [t] + dict['TARGETS'] = PathList(map(os.path.normpath, t)) + dict['TARGET'] = dict['TARGETS'][0] + if kw.has_key('source'): + s = kw['source'] + del kw['source'] + if type(s) is type(""): + s = [s] + dict['SOURCES'] = PathList(map(os.path.normpath, s)) + + dict.update(kw) + + return dict + class CommandAction(ActionBase): """Class for command-execution actions.""" def __init__(self, string): - self.command = string + self.command = string def execute(self, **kw): - loc = {} - if kw.has_key('target'): - t = kw['target'] - if type(t) is type(""): - t = [t] - loc['TARGETS'] = PathList(map(os.path.normpath, t)) - loc['TARGET'] = loc['TARGETS'][0] - if kw.has_key('source'): - s = kw['source'] - if type(s) is type(""): - s = [s] - loc['SOURCES'] = PathList(map(os.path.normpath, s)) - - glob = {} - if kw.has_key('env'): - glob = kw['env'] - - cmd_str = scons_subst(self.command, loc, glob) + dict = apply(self.subst_dict, (), kw) + cmd_str = scons_subst(self.command, dict, {}) for cmd in string.split(cmd_str, '\n'): if print_actions: self.show(cmd) if execute_actions: args = string.split(cmd) try: - ENV = glob['ENV'] + ENV = kw['env']['ENV'] except: import SCons.Defaults ENV = SCons.Defaults.ConstructionEnvironment['ENV'] ret = spawn(args[0], args, ENV) if ret: - #XXX This doesn't account for ignoring errors (-i) return ret return 0 + def get_contents(self, **kw): + """Return the signature contents of this action's command line. + For signature purposes, it doesn't matter what targets or + sources we use, so long as we use the same ones every time + so the signature stays the same. We supply an array of two + of each to allow for distinction between TARGET and TARGETS. + """ + kw['target'] = ['__t1__', '__t2__'] + kw['source'] = ['__s1__', '__s2__'] + dict = apply(self.subst_dict, (), kw) + return scons_subst(self.command, dict, {}) class FunctionAction(ActionBase): """Class for Python function actions.""" @@ -352,7 +396,24 @@ class FunctionAction(ActionBase): # if print_actions: # XXX: WHAT SHOULD WE PRINT HERE? if execute_actions: - return self.function(kw) + dict = apply(self.subst_dict, (), kw) + return apply(self.function, (), dict) + + def get_contents(self, **kw): + """Return the signature contents of this callable action. + + By providing direct access to the code object of the + function, Python makes this extremely easy. Hooray! + """ + #XXX DOES NOT ACCOUNT FOR CHANGES IN ENVIRONMENT VARIABLES + #THE FUNCTION MAY USE + try: + # "self.function" is a function. + code = self.function.func_code.co_code + except: + # "self.function" is a callable object. + code = self.function.__call__.im_func.func_code.co_code + return str(code) class ListAction(ActionBase): """Class for lists of other actions.""" @@ -365,3 +426,11 @@ class ListAction(ActionBase): if r != 0: return r return 0 + + def get_contents(self, **kw): + """Return the signature contents of this action list. + + Simple concatenation of the signatures of the elements. + """ + + return reduce(lambda x, y: x + str(y.get_contents()), self.list, "") diff --git a/src/engine/SCons/BuilderTests.py b/src/engine/SCons/BuilderTests.py index e24784d..89ba1fc 100644 --- a/src/engine/SCons/BuilderTests.py +++ b/src/engine/SCons/BuilderTests.py @@ -161,7 +161,7 @@ class BuilderTestCase(unittest.TestCase): c = test.read(outfile, 'r') assert c == "act.py: out5 XYZZY\nact.py: xyzzy\n", c - def function1(kw): + def function1(**kw): open(kw['out'], 'w').write("function1\n") return 1 @@ -172,7 +172,7 @@ class BuilderTestCase(unittest.TestCase): assert c == "function1\n", c class class1a: - def __init__(self, kw): + def __init__(self, **kw): open(kw['out'], 'w').write("class1a\n") builder = SCons.Builder.Builder(action = class1a) @@ -182,7 +182,7 @@ class BuilderTestCase(unittest.TestCase): assert c == "class1a\n", c class class1b: - def __call__(self, kw): + def __call__(self, **kw): open(kw['out'], 'w').write("class1b\n") return 2 @@ -194,17 +194,17 @@ class BuilderTestCase(unittest.TestCase): cmd2 = r'%s %s %s syzygy' % (python, act_py, outfile) - def function2(kw): + def function2(**kw): open(kw['out'], 'a').write("function2\n") return 0 class class2a: - def __call__(self, kw): + def __call__(self, **kw): open(kw['out'], 'a').write("class2a\n") return 0 class class2b: - def __init__(self, kw): + def __init__(self, **kw): open(kw['out'], 'a').write("class2b\n") builder = SCons.Builder.Builder(action = [cmd2, function2, class2a(), class2b]) @@ -213,6 +213,25 @@ class BuilderTestCase(unittest.TestCase): c = test.read(outfile, 'r') assert c == "act.py: syzygy\nfunction2\nclass2a\nclass2b\n", c + def test_get_contents(self): + """Test returning the signature contents of a Builder + """ + + b1 = SCons.Builder.Builder(action = "foo") + contents = b1.get_contents() + assert contents == "foo", contents + + def func(): + pass + + b2 = SCons.Builder.Builder(action = func) + contents = b2.get_contents() + assert contents == "\177\340\0\177\341\0d\0\0S", contents + + b3 = SCons.Builder.Builder(action = ["foo", func, "bar"]) + contents = b3.get_contents() + assert contents == "foo\177\340\0\177\341\0d\0\0Sbar", contents + def test_name(self): """Test Builder creation with a specified name """ diff --git a/src/engine/SCons/Node/NodeTests.py b/src/engine/SCons/Node/NodeTests.py index 035bc90..24ad955 100644 --- a/src/engine/SCons/Node/NodeTests.py +++ b/src/engine/SCons/Node/NodeTests.py @@ -39,6 +39,8 @@ class Builder: global built_it built_it = 1 return 0 + def get_contents(self, env): + return 7 class FailBuilder: def execute(self, **kw): @@ -89,6 +91,15 @@ class NodeTestCase(unittest.TestCase): node.builder_set(b) assert node.builder == b + def test_builder_sig_adapter(self): + """Test the node's adapter for builder signatures + """ + node = SCons.Node.Node() + node.builder_set(Builder()) + node.env_set(Environment()) + c = node.builder_sig_adapter().get_contents() + assert c == 7, c + def test_current(self): """Test the default current() method """ diff --git a/src/engine/SCons/Node/__init__.py b/src/engine/SCons/Node/__init__.py index 0e1a8d9..513b260 100644 --- a/src/engine/SCons/Node/__init__.py +++ b/src/engine/SCons/Node/__init__.py @@ -79,6 +79,23 @@ class Node: def builder_set(self, builder): self.builder = builder + def builder_sig_adapter(self): + """Create an adapter for calculating a builder's signature. + + The underlying signature class will call get_contents() + to fetch the signature of a builder, but the actual + content of that signature depends on the node and the + environment (for construction variable substitution), + so this adapter provides the right glue between the two. + """ + class Adapter: + def __init__(self, node): + self.node = node + def get_contents(self): + env = self.node.env.Dictionary() + return self.node.builder.get_contents(env = env) + return Adapter(self) + def env_set(self, env): self.env = env diff --git a/src/engine/SCons/Sig/MD5.py b/src/engine/SCons/Sig/MD5.py index e13669e..bd5643f 100644 --- a/src/engine/SCons/Sig/MD5.py +++ b/src/engine/SCons/Sig/MD5.py @@ -79,9 +79,10 @@ def signature(obj): """Generate a signature for an object """ try: - contents = obj.get_contents() - except AttributeError: - raise AttributeError, "unable to fetch contents of '%s'" % str(obj) + contents = str(obj.get_contents()) + except AttributeError, e: + raise AttributeError, \ + "unable to fetch contents of '%s': %s" % (str(obj), e) return hexdigest(md5.new(contents).digest()) def to_string(signature): diff --git a/src/engine/SCons/Sig/MD5Tests.py b/src/engine/SCons/Sig/MD5Tests.py index f9cf61f..2d42fc0 100644 --- a/src/engine/SCons/Sig/MD5Tests.py +++ b/src/engine/SCons/Sig/MD5Tests.py @@ -73,12 +73,17 @@ class MD5TestCase(unittest.TestCase): def test_signature(self): """Test generating a signature""" o1 = my_obj(value = '111') - assert '698d51a19d8a121ce581499d7b701668' == signature(o1) + s = signature(o1) + assert '698d51a19d8a121ce581499d7b701668' == s, s + + o2 = my_obj(value = 222) + s = signature(o2) + assert 'bcbe3365e6ac95ea2c0343a2395834dd' == s, s try: signature('string') except AttributeError, e: - assert str(e) == "unable to fetch contents of 'string'" + assert str(e) == "unable to fetch contents of 'string': 'string' object has no attribute 'get_contents'", e else: raise AttributeError, "unexpected get_contents() attribute" diff --git a/src/engine/SCons/Sig/SigTests.py b/src/engine/SCons/Sig/SigTests.py index 95789e8..0870c93 100644 --- a/src/engine/SCons/Sig/SigTests.py +++ b/src/engine/SCons/Sig/SigTests.py @@ -87,9 +87,23 @@ class DummyNode: def get_bsig(self): return self.bsig + def set_csig(self, csig): + self.csig = csig + + def get_csig(self): + return self.bsig + def get_prevsiginfo(self): return (self.oldtime, self.oldbsig, self.oldcsig) + def builder_sig_adapter(self): + class Adapter: + def get_contents(self): + return 111 + def get_timestamp(self): + return 222 + return Adapter() + def create_files(test): args = [(test.workpath('f1.c'), 'blah blah', 111, 0), #0 @@ -269,6 +283,13 @@ class CalcTestCase(unittest.TestCase): return 0, self.bsig, self.csig def get_timestamp(self): return 1 + def builder_sig_adapter(self): + class MyAdapter: + def get_csig(self): + return 333 + def get_timestamp(self): + return 444 + return MyAdapter() self.module = MySigModule() self.nodeclass = MyNode @@ -318,7 +339,7 @@ class CalcTestCase(unittest.TestCase): n4 = self.nodeclass('n4', None, None) n4.builder = 1 n4.kids = [n2, n3] - assert self.calc.get_signature(n4) == 57 + assert self.calc.get_signature(n4) == 390 n5 = NE('n5', 55, 56) assert self.calc.get_signature(n5) is None diff --git a/src/engine/SCons/Sig/__init__.py b/src/engine/SCons/Sig/__init__.py index 4095792..43e3b8a 100644 --- a/src/engine/SCons/Sig/__init__.py +++ b/src/engine/SCons/Sig/__init__.py @@ -150,9 +150,13 @@ class Calculator: already built and updated by someone else, if that's what's wanted. """ + if not node.use_signature: + return None #XXX If configured, use the content signatures from the #XXX .sconsign file if the timestamps match. sigs = map(lambda n,s=self: s.get_signature(n), node.children()) + if node.builder: + sigs.append(self.module.signature(node.builder_sig_adapter())) return self.module.collect(filter(lambda x: not x is None, sigs)) def csig(self, node): @@ -163,6 +167,8 @@ class Calculator: node - the node returns - the content signature """ + if not node.use_signature: + return None #XXX If configured, use the content signatures from the #XXX .sconsign file if the timestamps match. return self.module.signature(node) |