Group --debug=count output by object modules.
[scons.git] / src / engine / SCons / Action.py
index 3b62702bd28304dfcb7afb66b5edec3b8134bcee..077ddfb60d959f603bee66ca160b9bbd2103fa72 100644 (file)
@@ -185,8 +185,7 @@ def _do_create_action(act, *args, **kw):
             # of that Environment variable, so a user could put something
             # like a function or a CommandGenerator in that variable
             # instead of a string.
-            lcg = LazyCmdGenerator(var)
-            return apply(CommandGeneratorAction, (lcg,)+args, kw)
+            return apply(LazyAction, (var,)+args, kw)
         commands = string.split(str(act), '\n')
         if len(commands) == 1:
             return apply(CommandAction, (commands[0],)+args, kw)
@@ -216,6 +215,8 @@ class ActionBase:
     other objects (Builders, Executors, etc.)  This provides the
     common methods for manipulating and combining those actions."""
     
+    __metaclass__ = SCons.Memoize.Memoized_Metaclass
+
     def __cmp__(self, other):
         return cmp(self.__dict__, other)
 
@@ -231,15 +232,23 @@ 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.
+        # when it calls its _generate method.
         self.presub_env = env
         lines = string.split(str(self), '\n')
         self.presub_env = None      # don't need this any more
         return lines
 
+if not SCons.Memoize.has_metaclass:
+    _Base = ActionBase
+    class ActionBase(SCons.Memoize.Memoizer, _Base):
+        "Cache-backed version of ActionBase"
+        def __init__(self, *args, **kw):
+            apply(_Base.__init__, (self,)+args, kw)
+            SCons.Memoize.Memoizer.__init__(self)
+
 
 class _ActionAction(ActionBase):
     """Base class for actions that create output objects."""
@@ -324,7 +333,7 @@ def _string_from_cmd_list(cmd_list):
 
 class CommandAction(_ActionAction):
     """Class for command-execution actions."""
-    def __init__(self, cmd, *args, **kw):
+    def __init__(self, cmd, cmdstr=None, *args, **kw):
         # Cmd can actually be a list or a single item; if it's a
         # single item it should be the command string to execute; if a
         # list then it should be the words of the command string to
@@ -334,13 +343,14 @@ class CommandAction(_ActionAction):
         # factory above does).  cmd will be passed to
         # Environment.subst_list() for substituting environment
         # variables.
-        if __debug__: logInstanceCreation(self)
+        if __debug__: logInstanceCreation(self, 'Action.CommandAction')
         apply(_ActionAction.__init__, (self,)+args, kw)
         if SCons.Util.is_List(cmd):
             if filter(SCons.Util.is_List, cmd):
                 raise TypeError, "CommandAction should be given only " \
                       "a single command"
         self.cmd_list = cmd
+        self.cmdstr = cmdstr
 
     def __str__(self):
         if SCons.Util.is_List(self.cmd_list):
@@ -348,6 +358,10 @@ class CommandAction(_ActionAction):
         return str(self.cmd_list)
 
     def strfunction(self, target, source, env):
+        if not self.cmdstr is None:
+            c = env.subst(self.cmdstr, 0, target, source)
+            if c:
+                return c
         cmd_list = env.subst_list(self.cmd_list, 0, target, source)
         return _string_from_cmd_list(cmd_list[0])
 
@@ -411,7 +425,7 @@ class CommandAction(_ActionAction):
                 return result
         return 0
 
-    def get_contents(self, target, source, env, dict=None):
+    def get_contents(self, target, source, env):
         """Return the signature contents of this action's command line.
 
         This strips $(-$) and everything in between the string,
@@ -422,16 +436,16 @@ class CommandAction(_ActionAction):
             cmd = string.join(map(str, cmd))
         else:
             cmd = str(cmd)
-        return env.subst_target_source(cmd, SCons.Util.SUBST_SIG, target, source, dict)
+        return env.subst_target_source(cmd, SCons.Util.SUBST_SIG, target, source)
 
 class CommandGeneratorAction(ActionBase):
     """Class for command-generator actions."""
     def __init__(self, generator, *args, **kw):
-        if __debug__: logInstanceCreation(self)
+        if __debug__: logInstanceCreation(self, 'Action.CommandGeneratorAction')
         self.generator = generator
         self.gen_kw = kw
 
-    def __generate(self, target, source, env, for_signature):
+    def _generate(self, target, source, env, for_signature):
         # ensure that target is a list, to make it easier to write
         # generator functions:
         if not SCons.Util.is_List(target):
@@ -448,54 +462,95 @@ class CommandGeneratorAction(ActionBase):
             env = self.presub_env or {}
         except AttributeError:
             env = {}
-        act = self.__generate([], [], env, 1)
+        act = self._generate([], [], env, 1)
         return str(act)
 
     def genstring(self, target, source, env):
-        return self.__generate(target, source, env, 1).genstring(target, source, env)
+        return self._generate(target, source, env, 1).genstring(target, source, env)
 
     def __call__(self, target, source, env, errfunc=None, presub=_null,
                  show=_null, execute=_null, chdir=_null):
-        act = self.__generate(target, source, env, 0)
+        act = self._generate(target, source, env, 0)
         return act(target, source, env, errfunc, presub,
                    show, execute, chdir)
 
-    def get_contents(self, target, source, env, dict=None):
+    def get_contents(self, target, source, env):
         """Return the signature contents of this action's command line.
 
         This strips $(-$) and everything in between the string,
         since those parts don't affect signatures.
         """
-        return self.__generate(target, source, env, 1).get_contents(target, source, env, dict=None)
-
-class LazyCmdGenerator:
-    """This is not really an Action, although it kind of looks like one.
-    This is really a simple callable class that acts as a command
-    generator.  It holds on to a key into an Environment dictionary,
-    then waits until execution time to see what type it is, then tries
-    to create an Action out of it."""
-    def __init__(self, var):
-        if __debug__: logInstanceCreation(self)
+        return self._generate(target, source, env, 1).get_contents(target, source, env)
+
+
+
+# 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
+# environment.  Consequently, we want lazy evaluation and creation of
+# an Action in the case of the function, but that's overkill in the more
+# normal case of expansion to other strings.
+#
+# So we do this with a subclass that's both a generator *and*
+# a command action.  The overridden methods all do a quick check
+# of the construction variable, and if it's a string we just call
+# 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, 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, 'Action.LazyAction')
+        apply(CommandAction.__init__, (self, '$'+var)+args, kw)
         self.var = SCons.Util.to_String(var)
+        self.gen_kw = kw
 
-    def __str__(self):
-        return 'LazyCmdGenerator: %s'%str(self.var)
+    def get_parent_class(self, env):
+        c = env.get(self.var)
+        if SCons.Util.is_String(c) and not '\n' in c:
+            return CommandAction
+        return CommandGeneratorAction
+
+    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)
+        return apply(c.__call__, args, kw)
+
+    def get_contents(self, target, source, env):
+        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)
 
-    def __call__(self, target, source, env, for_signature):
-        try:
-            return env[self.var]
-        except KeyError:
-            # The variable reference substitutes to nothing.
-            return ''
 
-    def __cmp__(self, other):
-        return cmp(self.__dict__, other)
 
 class FunctionAction(_ActionAction):
     """Class for Python function actions."""
 
     def __init__(self, execfunction, *args, **kw):
-        if __debug__: logInstanceCreation(self)
+        if __debug__: logInstanceCreation(self, 'Action.FunctionAction')
         self.execfunction = execfunction
         apply(_ActionAction.__init__, (self,)+args, kw)
         self.varlist = kw.get('varlist', [])
@@ -529,7 +584,10 @@ class FunctionAction(_ActionAction):
         return "%s(%s, %s)" % (name, tstr, sstr)
 
     def __str__(self):
-        return "%s(target, source, env)" % self.function_name()
+        name = self.function_name()
+        if name == 'ActionCaller':
+            return str(self.execfunction)
+        return "%s(target, source, env)" % name
 
     def execute(self, target, source, env):
         rsources = map(rfile, source)
@@ -540,7 +598,7 @@ class FunctionAction(_ActionAction):
             raise SCons.Errors.BuildError(node=target, errstr=e.strerror)
         return result
 
-    def get_contents(self, target, source, env, dict=None):
+    def get_contents(self, target, source, env):
         """Return the signature contents of this callable action.
 
         By providing direct access to the code object of the
@@ -561,14 +619,14 @@ class FunctionAction(_ActionAction):
                     # This is weird, just do the best we can.
                     contents = str(self.execfunction)
                 else:
-                    contents = gc(target, source, env, dict)
+                    contents = gc(target, source, env)
         return contents + env.subst(string.join(map(lambda v: '${'+v+'}',
                                                      self.varlist)))
 
 class ListAction(ActionBase):
     """Class for lists of other actions."""
     def __init__(self, list):
-        if __debug__: logInstanceCreation(self)
+        if __debug__: logInstanceCreation(self, 'Action.ListAction')
         def list_of_actions(x):
             if isinstance(x, ActionBase):
                 return x
@@ -589,14 +647,13 @@ class ListAction(ActionBase):
                                       a.presub_lines(env),
                                       self.list))
 
-    def get_contents(self, target, source, env, dict=None):
+    def get_contents(self, target, source, env):
         """Return the signature contents of this action list.
 
         Simple concatenation of the signatures of the elements.
         """
-        dict = SCons.Util.subst_dict(target, source)
-        return string.join(map(lambda x, t=target, s=source, e=env, d=dict:
-                                      x.get_contents(t, s, e, d),
+        return string.join(map(lambda x, t=target, s=source, e=env:
+                                      x.get_contents(t, s, e),
                                self.list),
                            "")
 
@@ -622,7 +679,7 @@ class ActionCaller:
         self.parent = parent
         self.args = args
         self.kw = kw
-    def get_contents(self, target, source, env, dict=None):
+    def get_contents(self, target, source, env):
         actfunc = self.parent.actfunc
         try:
             # "self.actfunc" is a function.
@@ -636,14 +693,22 @@ class ActionCaller:
                 # or something like that.  Do the best we can.
                 contents = str(actfunc)
         return contents
+    def subst(self, s, target, source, env):
+        # Special-case hack:  Let a custom function wrapped in an
+        # ActionCaller get at the environment through which the action
+        # was called by using this hard-coded value as a special return.
+        if s == '$__env__':
+            return env
+        else:
+            return env.subst(s, 0, target, source)
     def subst_args(self, target, source, env):
-        return map(lambda x, e=env, t=target, s=source:
-                          e.subst(x, 0, t, s),
+        return map(lambda x, self=self, t=target, s=source, e=env:
+                          self.subst(x, t, s, e),
                    self.args)
     def subst_kw(self, target, source, env):
         kw = {}
         for key in self.kw.keys():
-            kw[key] = env.subst(self.kw[key], 0, target, source)
+            kw[key] = self.subst(self.kw[key], target, source, env)
         return kw
     def __call__(self, target, source, env):
         args = self.subst_args(target, source, env)
@@ -653,6 +718,8 @@ class ActionCaller:
         args = self.subst_args(target, source, env)
         kw = self.subst_kw(target, source, env)
         return apply(self.parent.strfunc, args, kw)
+    def __str__(self):
+        return apply(self.parent.strfunc, self.args, self.kw)
 
 class ActionFactory:
     """A factory class that will wrap up an arbitrary function
@@ -669,11 +736,4 @@ class ActionFactory:
     def __call__(self, *args, **kw):
         ac = ActionCaller(self, args, kw)
         action = Action(ac, strfunction=ac.strfunction)
-        # action will be a FunctionAction; if left to its own devices,
-        # a genstr or str of this action will just show
-        # "ActionCaller(target, source, env)".  Override that with the
-        # description from strfunc.  Note that the apply is evaluated
-        # right now; __str__ is set to a (lambda) function that just
-        # returns the stored result of the evaluation whenever called.
-        action.__str__ = lambda name=apply(self.strfunc, args, kw): name
         return action