Annotate objects with their creation SConscript and line.
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Sun, 9 Feb 2003 07:48:59 +0000 (07:48 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Sun, 9 Feb 2003 07:48:59 +0000 (07:48 +0000)
git-svn-id: http://scons.tigris.org/svn/scons/trunk@581 fdb21ef1-2011-0410-befe-b5e4ea1792b1

src/CHANGES.txt
src/engine/SCons/Node/NodeTests.py
src/engine/SCons/Node/__init__.py
src/engine/SCons/Script/SConscript.py

index e519fd24f4105a9bdb0981a3b84918354616309f..14ec5e248ec804b80e4668c3b2dfb07ba73dd76f 100644 (file)
@@ -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
index 48da843f6f847835ba788919c61ea556499fc9ba..86dcc56472315c6c40288d10c69a3d85c46b9435 100644 (file)
@@ -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__":
index 8b9a542e956baca73aa89218feed923c92b7748e..e1bc8f1c0e3214964f4f8f58a13eb3097991aa24 100644 (file)
@@ -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)
 
index 5c4a2b36e1f359b0912cf172d3c5bb3f47c3dc86..a8aa732cc61f01b773a8be0ed7ec17e4d35b1d1d 100644 (file)
@@ -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