http://scons.tigris.org/issues/show_bug.cgi?id=2329
[scons.git] / src / engine / SCons / Action.py
index cfd4a50c57cac72804478aef1aab705a5f27a0ac..9d497526856b40e17c2a2434697c0c1f362ea6c6 100644 (file)
@@ -96,14 +96,14 @@ way for wrapping up the functions.
 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+from __future__ import generators  ### KEEP FOR COMPATIBILITY FIXERS
 
 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 
 import cPickle
 import dis
 import os
-import os.path
-import string
+import re
 import sys
 import subprocess
 
@@ -153,7 +153,9 @@ else:
             else:
                 result.append(c)
                 i = i+1
-        return string.join(result, '')
+        return ''.join(result)
+
+strip_quotes = re.compile('^[\'"](.*)[\'"]$')
 
 
 def _callable_contents(obj):
@@ -247,19 +249,19 @@ def _code_contents(code):
     # Note that we also always ignore the first entry of co_consts
     # which contains the function doc string. We assume that the
     # function does not access its doc string.
-    contents.append(',(' + string.join(map(_object_contents,code.co_consts[1:]),',') + ')')
+    contents.append(',(' + ','.join(map(_object_contents,code.co_consts[1:])) + ')')
 
     # The code contents depends on the variable names used to
     # accessed global variable, as changing the variable name changes
     # the variable actually accessed and therefore changes the
     # function result.
-    contents.append(',(' + string.join(map(_object_contents,code.co_names),',') + ')')
+    contents.append(',(' + ','.join(map(_object_contents,code.co_names)) + ')')
 
 
     # The code contents depends on its actual code!!!
     contents.append(',(' + str(remove_set_lineno_codes(code.co_code)) + ')')
 
-    return string.join(contents, '')
+    return ''.join(contents)
 
 
 def _function_contents(func):
@@ -269,7 +271,7 @@ def _function_contents(func):
 
     # The function contents depends on the value of defaults arguments
     if func.func_defaults:
-        contents.append(',(' + string.join(map(_object_contents,func.func_defaults),',') + ')')
+        contents.append(',(' + ','.join(map(_object_contents,func.func_defaults)) + ')')
     else:
         contents.append(',()')
 
@@ -282,12 +284,12 @@ def _function_contents(func):
 
     #xxx = [_object_contents(x.cell_contents) for x in closure]
     try:
-        xxx = map(lambda x: _object_contents(x.cell_contents), closure)
+        xxx = [_object_contents(x.cell_contents) for x in closure]
     except AttributeError:
         xxx = []
-    contents.append(',(' + string.join(xxx, ',') + ')')
+    contents.append(',(' + ','.join(xxx) + ')')
 
-    return string.join(contents, '')
+    return ''.join(contents)
 
 
 def _actionAppend(act1, act2):
@@ -352,7 +354,7 @@ def _do_create_action(act, kw):
 
     if is_List(act):
         #TODO(1.5) return CommandAction(act, **kw)
-        return apply(CommandAction, (act,), kw)
+        return CommandAction(act, **kw)
 
     if callable(act):
         try:
@@ -376,10 +378,10 @@ def _do_create_action(act, kw):
             # like a function or a CommandGenerator in that variable
             # instead of a string.
             return LazyAction(var, kw)
-        commands = string.split(str(act), '\n')
+        commands = str(act).split('\n')
         if len(commands) == 1:
             #TODO(1.5) return CommandAction(commands[0], **kw)
-            return apply(CommandAction, (commands[0],), kw)
+            return CommandAction(commands[0], **kw)
         # The list of string commands may include a LazyAction, so we
         # reprocess them via _do_create_list_action.
         return _do_create_list_action(commands, kw)
@@ -393,7 +395,7 @@ def _do_create_list_action(act, kw):
         aa = _do_create_action(a, kw)
         if aa is not None: acts.append(aa)
     if not acts:
-        return None
+        return ListAction([])
     elif len(acts) == 1:
         return acts[0]
     else:
@@ -415,6 +417,11 @@ class ActionBase:
     def __cmp__(self, other):
         return cmp(self.__dict__, other)
 
+    def no_batch_key(self, env, target, source):
+        return None
+
+    batch_key = no_batch_key
+
     def genstring(self, target, source, env):
         return str(self)
 
@@ -423,11 +430,11 @@ class ActionBase:
         # This should never happen, as the Action() factory should wrap
         # the varlist, but just in case an action is created directly,
         # we duplicate this check here.
-        vl = self.varlist
+        vl = self.get_varlist(target, source, env)
         if is_String(vl): vl = (vl,)
         for v in vl:
             result.append(env.subst('${'+v+'}'))
-        return string.join(result, '')
+        return ''.join(result)
 
     def __add__(self, other):
         return _actionAppend(self, other)
@@ -443,19 +450,25 @@ class ActionBase:
         # and CommandGeneratorAction will use this env
         # when it calls its _generate method.
         self.presub_env = env
-        lines = string.split(str(self), '\n')
+        lines = str(self).split('\n')
         self.presub_env = None      # don't need this any more
         return lines
 
-    def get_executor(self, env, overrides, tlist, slist, executor_kw):
-        """Return the Executor for this Action."""
-        return SCons.Executor.Executor(self, env, overrides,
-                                       tlist, slist, executor_kw)
+    def get_varlist(self, target, source, env, executor=None):
+        return self.varlist
+
+    def get_targets(self, env, executor):
+        """
+        Returns the type of targets ($TARGETS, $CHANGED_TARGETS) used
+        by this action.
+        """
+        return self.targets
 
 class _ActionAction(ActionBase):
     """Base class for actions that create output objects."""
     def __init__(self, cmdstr=_null, strfunction=_null, varlist=(),
                        presub=_null, chdir=None, exitstatfunc=None,
+                       batch_key=None, targets='$TARGETS',
                  **kw):
         self.cmdstr = cmdstr
         if strfunction is not _null:
@@ -470,6 +483,19 @@ class _ActionAction(ActionBase):
             exitstatfunc = default_exitstatfunc
         self.exitstatfunc = exitstatfunc
 
+        self.targets = targets
+
+        if batch_key:
+            if not callable(batch_key):
+                # They have set batch_key, but not to their own
+                # callable.  The default behavior here will batch
+                # *all* targets+sources using this action, separated
+                # for each construction environment.
+                def default_batch_key(self, env, target, source):
+                    return (id(self), id(env))
+                batch_key = default_batch_key
+            SCons.Util.AddMethod(self, batch_key, 'batch_key')
+
     def print_cmd_line(self, s, target, source, env):
         sys.stdout.write(s + "\n")
 
@@ -478,7 +504,8 @@ class _ActionAction(ActionBase):
                                presub=_null,
                                show=_null,
                                execute=_null,
-                               chdir=_null):
+                               chdir=_null,
+                               executor=None):
         if not is_List(target):
             target = [target]
         if not is_List(source):
@@ -499,18 +526,30 @@ class _ActionAction(ActionBase):
                 chdir = str(chdir.abspath)
             except AttributeError:
                 if not is_String(chdir):
-                    chdir = str(target[0].dir)
+                    if executor:
+                        chdir = str(executor.batches[0].targets[0].dir)
+                    else:
+                        chdir = str(target[0].dir)
         if presub:
-            t = string.join(map(str, target), ' and ')
-            l = string.join(self.presub_lines(env), '\n  ')
+            if executor:
+                target = executor.get_all_targets()
+                source = executor.get_all_sources()
+            t = ' and '.join(map(str, target))
+            l = '\n  '.join(self.presub_lines(env))
             out = "Building %s with action:\n  %s\n" % (t, l)
             sys.stdout.write(out)
-        s = None
+        cmd = None
         if show and self.strfunction:
-            s = self.strfunction(target, source, env)
-            if s:
+            if executor:
+                target = executor.get_all_targets()
+                source = executor.get_all_sources()
+            try:
+                cmd = self.strfunction(target, source, env, executor)
+            except TypeError:
+                cmd = self.strfunction(target, source, env)
+            if cmd:
                 if chdir:
-                    s = ('os.chdir(%s)\n' % repr(chdir)) + s
+                    cmd = ('os.chdir(%s)\n' % repr(chdir)) + cmd
                 try:
                     get = env.get
                 except AttributeError:
@@ -519,13 +558,13 @@ class _ActionAction(ActionBase):
                     print_func = get('PRINT_CMD_LINE_FUNC')
                     if not print_func:
                         print_func = self.print_cmd_line
-                print_func(s, target, source, env)
+                print_func(cmd, target, source, env)
         stat = 0
         if execute:
             if chdir:
                 os.chdir(chdir)
             try:
-                stat = self.execute(target, source, env)
+                stat = self.execute(target, source, env, executor=executor)
                 if isinstance(stat, SCons.Errors.BuildError):
                     s = exitstatfunc(stat.status)
                     if s:
@@ -537,7 +576,7 @@ class _ActionAction(ActionBase):
             finally:
                 if save_cwd:
                     os.chdir(save_cwd)
-        if s and save_cwd:
+        if cmd and save_cwd:
             print_func('os.chdir(%s)' % repr(save_cwd), target, source, env)
 
         return stat
@@ -551,7 +590,7 @@ def _string_from_cmd_list(cmd_list):
         if ' ' in arg or '\t' in arg:
             arg = '"' + arg + '"'
         cl.append(arg)
-    return string.join(cl)
+    return ' '.join(cl)
 
 # A fiddlin' little function that has an 'import SCons.Environment' which
 # can't be moved to the top level without creating an import loop.  Since
@@ -579,8 +618,17 @@ def get_default_ENV(env):
 # it'll have to be tweaked to get the full desired functionality.
 # one special arg (so far?), 'error', to tell what to do with exceptions.
 def _subproc(env, cmd, error = 'ignore', **kw):
-    """Do setup for a subprocess.Popen() call"""
-    ### TODO: allow std{in,out,err} to be "'devnull'" (see issue 2228)
+    """Do common setup for a subprocess.Popen() call"""
+    # allow std{in,out,err} to be "'devnull'"
+    io = kw.get('stdin')
+    if is_String(io) and io == 'devnull':
+        kw['stdin'] = open(os.devnull)
+    io = kw.get('stdout')
+    if is_String(io) and io == 'devnull':
+        kw['stdout'] = open(os.devnull, 'w')
+    io = kw.get('stderr')
+    if is_String(io) and io == 'devnull':
+        kw['stderr'] = open(os.devnull, 'w')
 
     # Figure out what shell environment to use
     ENV = kw.get('env', None)
@@ -594,7 +642,7 @@ def _subproc(env, cmd, error = 'ignore', **kw):
             # because that's a pretty common list-like value to stick
             # in an environment variable:
             value = SCons.Util.flatten_sequence(value)
-            new_env[key] = string.join(map(str, value), os.pathsep)
+            new_env[key] = os.pathsep.join(map(str, value))
         else:
             # It's either a string or something else.  If it's a string,
             # we still want to call str() because it might be a *Unicode*
@@ -607,7 +655,7 @@ def _subproc(env, cmd, error = 'ignore', **kw):
 
     try:
         #FUTURE return subprocess.Popen(cmd, **kw)
-        return apply(subprocess.Popen, (cmd,), kw)
+        return subprocess.Popen(cmd, **kw)
     except EnvironmentError, e:
         if error == 'raise': raise
         # return a dummy Popen instance that only returns error
@@ -637,23 +685,26 @@ class CommandAction(_ActionAction):
         if __debug__: logInstanceCreation(self, 'Action.CommandAction')
 
         #TODO(1.5) _ActionAction.__init__(self, **kw)
-        apply(_ActionAction.__init__, (self,), kw)
+        _ActionAction.__init__(self, **kw)
         if is_List(cmd):
-            if filter(is_List, cmd):
+            if list(filter(is_List, cmd)):
                 raise TypeError, "CommandAction should be given only " \
                       "a single command"
         self.cmd_list = cmd
 
     def __str__(self):
         if is_List(self.cmd_list):
-            return string.join(map(str, self.cmd_list), ' ')
+            return ' '.join(map(str, self.cmd_list))
         return str(self.cmd_list)
 
-    def process(self, target, source, env):
-        result = env.subst_list(self.cmd_list, 0, target, source)
+    def process(self, target, source, env, executor=None):
+        if executor:
+            result = env.subst_list(self.cmd_list, 0, executor=executor)
+        else:
+            result = env.subst_list(self.cmd_list, 0, target, source)
         silent = None
         ignore = None
-        while 1:
+        while True:
             try: c = result[0][0][0]
             except IndexError: c = None
             if c == '@': silent = 1
@@ -667,20 +718,23 @@ class CommandAction(_ActionAction):
             pass
         return result, ignore, silent
 
-    def strfunction(self, target, source, env):
+    def strfunction(self, target, source, env, executor=None):
         if self.cmdstr is None:
             return None
         if self.cmdstr is not _null:
             from SCons.Subst import SUBST_RAW
-            c = env.subst(self.cmdstr, SUBST_RAW, target, source)
+            if executor:
+                c = env.subst(self.cmdstr, SUBST_RAW, executor=executor)
+            else:
+                c = env.subst(self.cmdstr, SUBST_RAW, target, source)
             if c:
                 return c
-        cmd_list, ignore, silent = self.process(target, source, env)
+        cmd_list, ignore, silent = self.process(target, source, env, executor)
         if silent:
             return ''
         return _string_from_cmd_list(cmd_list[0])
 
-    def execute(self, target, source, env):
+    def execute(self, target, source, env, executor=None):
         """Execute a command action.
 
         This will handle lists of commands as well as individual commands,
@@ -717,7 +771,7 @@ class CommandAction(_ActionAction):
                     # path list, because that's a pretty common list-like
                     # value to stick in an environment variable:
                     value = flatten_sequence(value)
-                    ENV[key] = string.join(map(str, value), os.pathsep)
+                    ENV[key] = os.pathsep.join(map(str, value))
                 else:
                     # If it isn't a string or a list, then we just coerce
                     # it to a string, which is the proper way to handle
@@ -725,7 +779,10 @@ class CommandAction(_ActionAction):
                     # reasonable for just about everything else:
                     ENV[key] = str(value)
 
-        cmd_list, ignore, silent = self.process(target, map(rfile, source), env)
+        if executor:
+            target = executor.get_all_targets()
+            source = executor.get_all_sources()
+        cmd_list, ignore, silent = self.process(target, list(map(rfile, source)), env, executor)
 
         # Use len() to filter out any "command" that's zero-length.
         for cmd_line in filter(len, cmd_list):
@@ -740,7 +797,7 @@ class CommandAction(_ActionAction):
                                                command=cmd_line)
         return 0
 
-    def get_presig(self, target, source, env):
+    def get_presig(self, target, source, env, executor=None):
         """Return the signature contents of this action's command line.
 
         This strips $(-$) and everything in between the string,
@@ -749,23 +806,33 @@ class CommandAction(_ActionAction):
         from SCons.Subst import SUBST_SIG
         cmd = self.cmd_list
         if is_List(cmd):
-            cmd = string.join(map(str, cmd))
+            cmd = ' '.join(map(str, cmd))
         else:
             cmd = str(cmd)
-        return env.subst_target_source(cmd, SUBST_SIG, target, source)
+        if executor:
+            return env.subst_target_source(cmd, SUBST_SIG, executor=executor)
+        else:
+            return env.subst_target_source(cmd, SUBST_SIG, target, source)
 
-    def get_implicit_deps(self, target, source, env):
+    def get_implicit_deps(self, target, source, env, executor=None):
         icd = env.get('IMPLICIT_COMMAND_DEPENDENCIES', True)
         if is_String(icd) and icd[:1] == '$':
             icd = env.subst(icd)
         if not icd or icd in ('0', 'None'):
             return []
         from SCons.Subst import SUBST_SIG
-        cmd_list = env.subst_list(self.cmd_list, SUBST_SIG, target, source)
+        if executor:
+            cmd_list = env.subst_list(self.cmd_list, SUBST_SIG, executor=executor)
+        else:
+            cmd_list = env.subst_list(self.cmd_list, SUBST_SIG, target, source)
         res = []
         for cmd_line in cmd_list:
             if cmd_line:
-                d = env.WhereIs(str(cmd_line[0]))
+                d = str(cmd_line[0])
+                m = strip_quotes.match(d)
+                if m:
+                    d = m.group(1)
+                d = env.WhereIs(d)
                 if d:
                     res.append(env.fs.File(d))
         return res
@@ -777,16 +844,23 @@ class CommandGeneratorAction(ActionBase):
         self.generator = generator
         self.gen_kw = kw
         self.varlist = kw.get('varlist', ())
+        self.targets = kw.get('targets', '$TARGETS')
 
-    def _generate(self, target, source, env, for_signature):
+    def _generate(self, target, source, env, for_signature, executor=None):
         # ensure that target is a list, to make it easier to write
         # generator functions:
         if not is_List(target):
             target = [target]
 
-        ret = self.generator(target=target, source=source, env=env, for_signature=for_signature)
+        if executor:
+            target = executor.get_all_targets()
+            source = executor.get_all_sources()
+        ret = self.generator(target=target,
+                             source=source,
+                             env=env,
+                             for_signature=for_signature)
         #TODO(1.5) gen_cmd = Action(ret, **self.gen_kw)
-        gen_cmd = apply(Action, (ret,), self.gen_kw)
+        gen_cmd = Action(ret, **self.gen_kw)
         if not gen_cmd:
             raise SCons.Errors.UserError("Object returned from command generator: %s cannot be used to create an Action." % repr(ret))
         return gen_cmd
@@ -801,25 +875,38 @@ class CommandGeneratorAction(ActionBase):
         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)
+    def batch_key(self, env, target, source):
+        return self._generate(target, source, env, 1).batch_key(env, target, source)
+
+    def genstring(self, target, source, env, executor=None):
+        return self._generate(target, source, env, 1, executor).genstring(target, source, env)
 
     def __call__(self, target, source, env, exitstatfunc=_null, presub=_null,
-                 show=_null, execute=_null, chdir=_null):
-        act = self._generate(target, source, env, 0)
+                 show=_null, execute=_null, chdir=_null, executor=None):
+        act = self._generate(target, source, env, 0, executor)
+        if act is None:
+            raise UserError("While building `%s': "
+                            "Cannot deduce file extension from source files: %s"
+                % (repr(list(map(str, target))), repr(list(map(str, source)))))
         return act(target, source, env, exitstatfunc, presub,
-                   show, execute, chdir)
+                   show, execute, chdir, executor)
 
-    def get_presig(self, target, source, env):
+    def get_presig(self, target, source, env, executor=None):
         """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_presig(target, source, env)
+        return self._generate(target, source, env, 1, executor).get_presig(target, source, env)
 
-    def get_implicit_deps(self, target, source, env):
-        return self._generate(target, source, env, 1).get_implicit_deps(target, source, env)
+    def get_implicit_deps(self, target, source, env, executor=None):
+        return self._generate(target, source, env, 1, executor).get_implicit_deps(target, source, env)
+
+    def get_varlist(self, target, source, env, executor=None):
+        return self._generate(target, source, env, 1, executor).get_varlist(target, source, env, executor)
+
+    def get_targets(self, env, executor):
+        return self._generate(None, None, env, 1, executor).get_targets(env, executor)
 
 
 
@@ -845,7 +932,7 @@ class LazyAction(CommandGeneratorAction, CommandAction):
     def __init__(self, var, kw):
         if __debug__: logInstanceCreation(self, 'Action.LazyAction')
         #FUTURE CommandAction.__init__(self, '${'+var+'}', **kw)
-        apply(CommandAction.__init__, (self, '${'+var+'}'), kw)
+        CommandAction.__init__(self, '${'+var+'}', **kw)
         self.var = SCons.Util.to_String(var)
         self.gen_kw = kw
 
@@ -856,26 +943,30 @@ class LazyAction(CommandGeneratorAction, CommandAction):
         return CommandGeneratorAction
 
     def _generate_cache(self, env):
-        c = env.get(self.var, '')
+        if env:
+            c = env.get(self.var, '')
+        else:
+            c = ''
         #TODO(1.5) gen_cmd = Action(c, **self.gen_kw)
-        gen_cmd = apply(Action, (c,), self.gen_kw)
+        gen_cmd = 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):
+    def _generate(self, target, source, env, for_signature, executor=None):
         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)
-        #TODO(1.5) return c.__call__(*args, **kw)
-        return apply(c.__call__, args, kw)
+        return c.__call__(self, target, source, env, *args, **kw)
 
     def get_presig(self, target, source, env):
         c = self.get_parent_class(env)
         return c.get_presig(self, target, source, env)
 
+    def get_varlist(self, target, source, env, executor=None):
+        c = self.get_parent_class(env)
+        return c.get_varlist(self, target, source, env, executor)
 
 
 class FunctionAction(_ActionAction):
@@ -896,7 +987,7 @@ class FunctionAction(_ActionAction):
                 self.funccontents = _object_contents(execfunction)
 
         #TODO(1.5) _ActionAction.__init__(self, **kw)
-        apply(_ActionAction.__init__, (self,), kw)
+        _ActionAction.__init__(self, **kw)
 
     def function_name(self):
         try:
@@ -907,12 +998,15 @@ class FunctionAction(_ActionAction):
             except AttributeError:
                 return "unknown_python_function"
 
-    def strfunction(self, target, source, env):
+    def strfunction(self, target, source, env, executor=None):
         if self.cmdstr is None:
             return None
         if self.cmdstr is not _null:
             from SCons.Subst import SUBST_RAW
-            c = env.subst(self.cmdstr, SUBST_RAW, target, source)
+            if executor:
+                c = env.subst(self.cmdstr, SUBST_RAW, executor=executor)
+            else:
+                c = env.subst(self.cmdstr, SUBST_RAW, target, source)
             if c:
                 return c
         def array(a):
@@ -924,7 +1018,7 @@ class FunctionAction(_ActionAction):
                 else:
                     s = str_for_display()
                 return s
-            return '[' + string.join(map(quote, a), ", ") + ']'
+            return '[' + ", ".join(map(quote, a)) + ']'
         try:
             strfunc = self.execfunction.strfunction
         except AttributeError:
@@ -945,12 +1039,19 @@ class FunctionAction(_ActionAction):
             return str(self.execfunction)
         return "%s(target, source, env)" % name
 
-    def execute(self, target, source, env):
+    def execute(self, target, source, env, executor=None):
         exc_info = (None,None,None)
         try:
-            rsources = map(rfile, source)
+            if executor:
+                target = executor.get_all_targets()
+                source = executor.get_all_sources()
+            rsources = list(map(rfile, source))
             try:
                 result = self.execfunction(target=target, source=rsources, env=env)
+            except KeyboardInterrupt, e:
+                raise
+            except SystemExit, e:
+                raise
             except Exception, e:
                 result = e
                 exc_info = sys.exc_info()
@@ -959,7 +1060,10 @@ class FunctionAction(_ActionAction):
                 result = SCons.Errors.convert_to_BuildError(result, exc_info)
                 result.node=target
                 result.action=self
-                result.command=self.strfunction(target, source, env)
+                try:
+                    result.command=self.strfunction(target, source, env, executor)
+                except TypeError:
+                    result.command=self.strfunction(target, source, env)
 
                 # FIXME: This maintains backward compatibility with respect to
                 # which type of exceptions were returned by raising an
@@ -967,7 +1071,7 @@ class FunctionAction(_ActionAction):
                 # probably be best to always return them by value here, but
                 # some codes do not check the return value of Actions and I do
                 # not have the time to modify them at this point.
-                if (exc_info[1] and 
+                if (exc_info[1] and
                     not isinstance(exc_info[1],EnvironmentError)):
                     raise result
 
@@ -991,45 +1095,43 @@ class FunctionAction(_ActionAction):
 
 class ListAction(ActionBase):
     """Class for lists of other actions."""
-    def __init__(self, list):
+    def __init__(self, actionlist):
         if __debug__: logInstanceCreation(self, 'Action.ListAction')
         def list_of_actions(x):
             if isinstance(x, ActionBase):
                 return x
             return Action(x)
-        self.list = map(list_of_actions, list)
+        self.list = list(map(list_of_actions, actionlist))
         # our children will have had any varlist
         # applied; we don't need to do it again
         self.varlist = ()
+        self.targets = '$TARGETS'
 
     def genstring(self, target, source, env):
-        return string.join(map(lambda a, t=target, s=source, e=env:
-                                  a.genstring(t, s, e),
-                               self.list),
-                           '\n')
+        return '\n'.join([a.genstring(target, source, env) for a in self.list])
 
     def __str__(self):
-        return string.join(map(str, self.list), '\n')
+        return '\n'.join(map(str, self.list))
 
     def presub_lines(self, env):
         return SCons.Util.flatten_sequence(
-            map(lambda a, env=env: a.presub_lines(env), self.list))
+            [a.presub_lines(env) for a in self.list])
 
     def get_presig(self, target, source, env):
         """Return the signature contents of this action list.
 
         Simple concatenation of the signatures of the elements.
         """
-        return string.join(map(lambda x, t=target, s=source, e=env:
-                                      x.get_contents(t, s, e),
-                               self.list),
-                           "")
+        return "".join([x.get_contents(target, source, env) for x in self.list])
 
     def __call__(self, target, source, env, exitstatfunc=_null, presub=_null,
-                 show=_null, execute=_null, chdir=_null):
+                 show=_null, execute=_null, chdir=_null, executor=None):
+        if executor:
+            target = executor.get_all_targets()
+            source = executor.get_all_sources()
         for act in self.list:
             stat = act(target, source, env, exitstatfunc, presub,
-                       show, execute, chdir)
+                       show, execute, chdir, executor)
             if stat:
                 return stat
         return 0
@@ -1040,6 +1142,13 @@ class ListAction(ActionBase):
             result.extend(act.get_implicit_deps(target, source, env))
         return result
 
+    def get_varlist(self, target, source, env, executor=None):
+        result = SCons.Util.OrderedDict()
+        for act in self.list:
+            for var in act.get_varlist(target, source, env, executor):
+                result[var] = True
+        return result.keys()
+
 class ActionCaller:
     """A class for delaying calling an Action function with specific
     (positional and keyword) arguments until the Action is actually
@@ -1089,9 +1198,7 @@ class ActionCaller:
         return self.parent.convert(s)
 
     def subst_args(self, target, source, env):
-        return map(lambda x, self=self, t=target, s=source, e=env:
-                          self.subst(x, t, s, e),
-                   self.args)
+        return [self.subst(x, target, source, env) for x in self.args]
 
     def subst_kw(self, target, source, env):
         kw = {}
@@ -1099,21 +1206,21 @@ class ActionCaller:
             kw[key] = self.subst(self.kw[key], target, source, env)
         return kw
 
-    def __call__(self, target, source, env):
+    def __call__(self, target, source, env, executor=None):
         args = self.subst_args(target, source, env)
         kw = self.subst_kw(target, source, env)
         #TODO(1.5) return self.parent.actfunc(*args, **kw)
-        return apply(self.parent.actfunc, args, kw)
+        return self.parent.actfunc(*args, **kw)
 
     def strfunction(self, target, source, env):
         args = self.subst_args(target, source, env)
         kw = self.subst_kw(target, source, env)
         #TODO(1.5) return self.parent.strfunc(*args, **kw)
-        return apply(self.parent.strfunc, args, kw)
+        return self.parent.strfunc(*args, **kw)
 
     def __str__(self):
         #TODO(1.5) return self.parent.strfunc(*self.args, **self.kw)
-        return apply(self.parent.strfunc, self.args, self.kw)
+        return self.parent.strfunc(*self.args, **self.kw)
 
 class ActionFactory:
     """A factory class that will wrap up an arbitrary function
@@ -1133,3 +1240,9 @@ class ActionFactory:
         ac = ActionCaller(self, args, kw)
         action = Action(ac, strfunction=ac.strfunction)
         return action
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4: