From: stevenknight Date: Sun, 9 Feb 2003 07:48:59 +0000 (+0000) Subject: Annotate objects with their creation SConscript and line. X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=00e870e2f3158e996bdfcc4a805a9837cef0ffc9;p=scons.git Annotate objects with their creation SConscript and line. git-svn-id: http://scons.tigris.org/svn/scons/trunk@581 fdb21ef1-2011-0410-befe-b5e4ea1792b1 --- diff --git a/src/CHANGES.txt b/src/CHANGES.txt index e519fd24..14ec5e24 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -77,6 +77,9 @@ RELEASE 0.11 - XXX - Fix the ability to directly import and use Platform and Tool modules that have been implicitly imported into an Environment(). + - Add support for allowing an embedding interface to annotate a node + when it's created. + From Steve Leblanc: - Fix the output of -c -n when directories are involved, so it diff --git a/src/engine/SCons/Node/NodeTests.py b/src/engine/SCons/Node/NodeTests.py index 48da843f..86dcc564 100644 --- a/src/engine/SCons/Node/NodeTests.py +++ b/src/engine/SCons/Node/NodeTests.py @@ -764,6 +764,25 @@ class NodeTestCase(unittest.TestCase): """Test the is_literal() function.""" n=SCons.Node.Node() assert n.is_literal() + + def test_Annotate(self): + """Test using an interface-specific Annotate function.""" + def my_annotate(node, self=self): + node.annotation = self.node_string + + save_Annotate = SCons.Node.Annotate + SCons.Node.Annotate = my_annotate + + try: + self.node_string = '#1' + n = SCons.Node.Node() + assert n.annotation == '#1', n.annotation + + self.node_string = '#2' + n = SCons.Node.Node() + assert n.annotation == '#2', n.annotation + finally: + SCons.Node.Annotate = save_Annotate if __name__ == "__main__": diff --git a/src/engine/SCons/Node/__init__.py b/src/engine/SCons/Node/__init__.py index 8b9a542e..e1bc8f1c 100644 --- a/src/engine/SCons/Node/__init__.py +++ b/src/engine/SCons/Node/__init__.py @@ -74,6 +74,11 @@ implicit_deps_unchanged = 0 # controls whether the cached implicit deps are ignored: implicit_deps_changed = 0 +# A variable that can be set to an interface-specific function be called +# to annotate a Node with information about its creation. +def do_nothing(node): pass + +Annotate = do_nothing class Node: """The base Node class, for entities that we know how to @@ -105,6 +110,11 @@ class Node: self.pre_actions = [] self.post_actions = [] + # Let the interface in which the build engine is embedded + # annotate this Node with its own info (like a description of + # what line in what file created the node, for example). + Annotate(self) + def generate_build_env(self): return self.env.Override(self.overrides) diff --git a/src/engine/SCons/Script/SConscript.py b/src/engine/SCons/Script/SConscript.py index 5c4a2b36..a8aa732c 100644 --- a/src/engine/SCons/Script/SConscript.py +++ b/src/engine/SCons/Script/SConscript.py @@ -47,6 +47,7 @@ import os import os.path import string import sys +import traceback def do_nothing(text): pass HelpFunction = do_nothing @@ -172,7 +173,7 @@ def SConscript(*ls, **kw): else: f = SCons.Node.FS.default_fs.File(str(fn)) if f.rexists(): - file = open(f.rstr(), "r") + _file_ = open(f.rstr(), "r") SCons.Node.FS.default_fs.chdir(f.dir) if sconscript_chdir: old_dir = os.getcwd() @@ -183,7 +184,13 @@ def SConscript(*ls, **kw): # be easily imported sys.path = [os.path.abspath(str(f.dir))] + sys.path - exec file in stack[-1].globals + # This is the magic line that actually reads up and + # executes the stuff in the SConscript file. We + # look for the "exec _file_ " from the beginning + # of this line to find the right stack frame (the + # next one) describing the SConscript file and line + # number that creates a node. + exec _file_ in stack[-1].globals else: sys.stderr.write("Ignoring missing SConscript '%s'\n" % f.path) @@ -202,6 +209,23 @@ def SConscript(*ls, **kw): return results[0] else: return tuple(results) + +def annotate(node): + """Annotate a node with the stack frame describing the + SConscript file and line number that created it.""" + stack = traceback.extract_stack() + last_text = "" + for frame in stack: + # If the script text of the previous frame begins with the + # magic "exec _file_ " string, then this frame describes the + # SConscript file and line number that caused this node to be + # created. Record the tuple and carry on. + if not last_text is None and last_text[:12] == "exec _file_ ": + node.creator = frame + return + last_text = frame[3] + +SCons.Node.Annotate = annotate def Default(*targets): global default_targets