Reduce the number of actions created by caching generated LazyActions.
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Tue, 1 Feb 2005 12:02:12 +0000 (12:02 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Tue, 1 Feb 2005 12:02:12 +0000 (12:02 +0000)
git-svn-id: http://scons.tigris.org/svn/scons/trunk@1222 fdb21ef1-2011-0410-befe-b5e4ea1792b1

src/CHANGES.txt
src/engine/SCons/Action.py
src/engine/SCons/ActionTests.py

index eafac2d12fb91a10ac86cd106e215bd6853783e7..db0145b26df847e4f1ef25dc95bc3ae637d58520 100644 (file)
@@ -187,6 +187,9 @@ RELEASE 0.97 - XXX
   - Speed things up when there are lists of targets and/or sources by
     getting rid of some N^2 walks of the lists involved.
 
+  - Cache evaluation of LazyActions so we don't create a new object
+    for each invocation.
+
   From Wayne Lee:
 
   - Avoid "maximum recursion limit" errors when removing $(-$) pairs
index a3b62eb98f0d437222bd24c152601f9445da41b2..fa8f1d25b539382f911e5a87dd2a026df12b58ae 100644 (file)
@@ -232,7 +232,7 @@ class ActionBase:
     def presub_lines(self, env):
         # CommandGeneratorAction needs a real environment
         # in order to return the proper string here, since
-        # it may call LazyCmdGenerator, which looks up a key
+        # it may call LazyAction, which looks up a key
         # in that env.  So we temporarily remember the env here,
         # and CommandGeneratorAction will use this env
         # when it calls its _generate method.
@@ -482,9 +482,9 @@ class CommandGeneratorAction(ActionBase):
         """
         return self._generate(target, source, env, 1).get_contents(target, source, env)
 
-# Ooh, polymorphism -- pretty scary, eh, kids?
-#
-# A LazyCmdAction is a kind of hybrid generator and command action for
+
+
+# A LazyAction is a kind of hybrid generator and command action for
 # strings of the form "$VAR".  These strings normally expand to other
 # strings (think "$CCCOM" to "$CC -c -o $TARGET $SOURCE"), but we also
 # want to be able to replace them with functions in the construction
@@ -498,10 +498,13 @@ class CommandGeneratorAction(ActionBase):
 # the corresponding CommandAction method to do the heavy lifting.
 # If not, then we call the same-named CommandGeneratorAction method.
 # The CommandGeneratorAction methods work by using the overridden
-# _generate() method, uses our own way of handling "generation" of an
-# action based on what's in the construction variable.
+# _generate() method, that is, our own way of handling "generation" of
+# an action based on what's in the construction variable.
 
 class LazyAction(CommandGeneratorAction, CommandAction):
+
+    __metaclass__ = SCons.Memoize.Memoized_Metaclass
+
     def __init__(self, var, *args, **kw):
         if __debug__: logInstanceCreation(self)
         apply(CommandAction.__init__, (self, '$'+var)+args, kw)
@@ -514,13 +517,17 @@ class LazyAction(CommandGeneratorAction, CommandAction):
             return CommandAction
         return CommandGeneratorAction
 
-    def _generate(self, target, source, env, for_signature):
+    def _generate_cache(self, env):
+        """__cacheable__"""
         c = env.get(self.var, '')
         gen_cmd = apply(Action, (c,), self.gen_kw)
         if not gen_cmd:
             raise SCons.Errors.UserError("$%s value %s cannot be used to create an Action." % (self.var, repr(c)))
         return gen_cmd
 
+    def _generate(self, target, source, env, for_signature):
+        return self._generate_cache(env)
+
     def __call__(self, target, source, env, *args, **kw):
         args = (self, target, source, env) + args
         c = self.get_parent_class(env)
@@ -530,6 +537,15 @@ class LazyAction(CommandGeneratorAction, CommandAction):
         c = self.get_parent_class(env)
         return c.get_contents(self, target, source, env)
 
+if not SCons.Memoize.has_metaclass:
+    _Base = LazyAction
+    class LazyAction(SCons.Memoize.Memoizer, _Base):
+        def __init__(self, *args, **kw):
+            SCons.Memoize.Memoizer.__init__(self)
+            apply(_Base.__init__, (self,)+args, kw)
+
+
+
 class FunctionAction(_ActionAction):
     """Class for Python function actions."""
 
index 4bcb0843d4aa30a2749e778aa27070295e706675..36117a4cac5167b489b75947379f948aba3bb76d 100644 (file)
@@ -1491,9 +1491,11 @@ class LazyActionTestCase(unittest.TestCase):
         def f(target, source, env):
             pass
         a = SCons.Action.Action('$BAR')
-        s = a.genstring([], [], env=Environment(BAR=f, s=self))
+        env1 = Environment(BAR=f, s=self)
+        env2 = Environment(BAR='xxx', s=self)
+        s = a.genstring([], [], env=env1)
         assert s == "f(target, source, env)", s
-        s = a.genstring([], [], env=Environment(BAR='xxx', s=self))
+        s = a.genstring([], [], env=env2)
         assert s == 'xxx', s
 
     def test_execute(self):