Merged revisions 1826-1882 via svnmerge from
[scons.git] / src / engine / SCons / Action.py
1 """SCons.Action
2
3 This encapsulates information about executing any sort of action that
4 can build one or more target Nodes (typically files) from one or more
5 source Nodes (also typically files) given a specific Environment.
6
7 The base class here is ActionBase.  The base class supplies just a few
8 OO utility methods and some generic methods for displaying information
9 about an Action in response to the various commands that control printing.
10
11 A second-level base class is _ActionAction.  This extends ActionBase
12 by providing the methods that can be used to show and perform an
13 action.  True Action objects will subclass _ActionAction; Action
14 factory class objects will subclass ActionBase.
15
16 The heavy lifting is handled by subclasses for the different types of
17 actions we might execute:
18
19     CommandAction
20     CommandGeneratorAction
21     FunctionAction
22     ListAction
23
24 The subclasses supply the following public interface methods used by
25 other modules:
26
27     __call__()
28         THE public interface, "calling" an Action object executes the
29         command or Python function.  This also takes care of printing
30         a pre-substitution command for debugging purposes.
31
32     get_contents()
33         Fetches the "contents" of an Action for signature calculation.
34         This is what the Sig/*.py subsystem uses to decide if a target
35         needs to be rebuilt because its action changed.
36
37     genstring()
38         Returns a string representation of the Action *without*
39         command substitution, but allows a CommandGeneratorAction to
40         generate the right action based on the specified target,
41         source and env.  This is used by the Signature subsystem
42         (through the Executor) to obtain an (imprecise) representation
43         of the Action operation for informative purposes.
44
45
46 Subclasses also supply the following methods for internal use within
47 this module:
48
49     __str__()
50         Returns a string approximation of the Action; no variable
51         substitution is performed.
52         
53     execute()
54         The internal method that really, truly, actually handles the
55         execution of a command or Python function.  This is used so
56         that the __call__() methods can take care of displaying any
57         pre-substitution representations, and *then* execute an action
58         without worrying about the specific Actions involved.
59
60     strfunction()
61         Returns a substituted string representation of the Action.
62         This is used by the _ActionAction.show() command to display the
63         command/function that will be executed to generate the target(s).
64
65 There is a related independent ActionCaller class that looks like a
66 regular Action, and which serves as a wrapper for arbitrary functions
67 that we want to let the user specify the arguments to now, but actually
68 execute later (when an out-of-date check determines that it's needed to
69 be executed, for example).  Objects of this class are returned by an
70 ActionFactory class that provides a __call__() method as a convenient
71 way for wrapping up the functions.
72
73 """
74
75 #
76 # __COPYRIGHT__
77 #
78 # Permission is hereby granted, free of charge, to any person obtaining
79 # a copy of this software and associated documentation files (the
80 # "Software"), to deal in the Software without restriction, including
81 # without limitation the rights to use, copy, modify, merge, publish,
82 # distribute, sublicense, and/or sell copies of the Software, and to
83 # permit persons to whom the Software is furnished to do so, subject to
84 # the following conditions:
85 #
86 # The above copyright notice and this permission notice shall be included
87 # in all copies or substantial portions of the Software.
88 #
89 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
90 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
91 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
92 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
93 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
94 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
95 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
96 #
97
98 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
99
100 import dis
101 import os
102 import os.path
103 import string
104 import sys
105
106 from SCons.Debug import logInstanceCreation
107 import SCons.Errors
108 import SCons.Executor
109 import SCons.Util
110
111 class _Null:
112     pass
113
114 _null = _Null
115
116 print_actions = 1
117 execute_actions = 1
118 print_actions_presub = 0
119
120 default_ENV = None
121
122 def rfile(n):
123     try:
124         return n.rfile()
125     except AttributeError:
126         return n
127
128 def default_exitstatfunc(s):
129     return s
130
131 try:
132     SET_LINENO = dis.SET_LINENO
133     HAVE_ARGUMENT = dis.HAVE_ARGUMENT
134 except AttributeError:
135     remove_set_lineno_codes = lambda x: x
136 else:
137     def remove_set_lineno_codes(code):
138         result = []
139         n = len(code)
140         i = 0
141         while i < n:
142             c = code[i]
143             op = ord(c)
144             if op >= HAVE_ARGUMENT:
145                 if op != SET_LINENO:
146                     result.append(code[i:i+3])
147                 i = i+3
148             else:
149                 result.append(c)
150                 i = i+1
151         return string.join(result, '')
152
153 def _actionAppend(act1, act2):
154     # This function knows how to slap two actions together.
155     # Mainly, it handles ListActions by concatenating into
156     # a single ListAction.
157     a1 = Action(act1)
158     a2 = Action(act2)
159     if a1 is None or a2 is None:
160         raise TypeError, "Cannot append %s to %s" % (type(act1), type(act2))
161     if isinstance(a1, ListAction):
162         if isinstance(a2, ListAction):
163             return ListAction(a1.list + a2.list)
164         else:
165             return ListAction(a1.list + [ a2 ])
166     else:
167         if isinstance(a2, ListAction):
168             return ListAction([ a1 ] + a2.list)
169         else:
170             return ListAction([ a1, a2 ])
171
172 def _do_create_action(act, *args, **kw):
173     """This is the actual "implementation" for the
174     Action factory method, below.  This handles the
175     fact that passing lists to Action() itself has
176     different semantics than passing lists as elements
177     of lists.
178
179     The former will create a ListAction, the latter
180     will create a CommandAction by converting the inner
181     list elements to strings."""
182
183     if isinstance(act, ActionBase):
184         return act
185     if SCons.Util.is_List(act):
186         return apply(CommandAction, (act,)+args, kw)
187     if callable(act):
188         try:
189             gen = kw['generator']
190             del kw['generator']
191         except KeyError:
192             gen = 0
193         if gen:
194             action_type = CommandGeneratorAction
195         else:
196             action_type = FunctionAction
197         return apply(action_type, (act,)+args, kw)
198     if SCons.Util.is_String(act):
199         var=SCons.Util.get_environment_var(act)
200         if var:
201             # This looks like a string that is purely an Environment
202             # variable reference, like "$FOO" or "${FOO}".  We do
203             # something special here...we lazily evaluate the contents
204             # of that Environment variable, so a user could put something
205             # like a function or a CommandGenerator in that variable
206             # instead of a string.
207             return apply(LazyAction, (var,)+args, kw)
208         commands = string.split(str(act), '\n')
209         if len(commands) == 1:
210             return apply(CommandAction, (commands[0],)+args, kw)
211         else:
212             listCmdActions = map(lambda x, args=args, kw=kw:
213                                  apply(CommandAction, (x,)+args, kw),
214                                  commands)
215             return ListAction(listCmdActions)
216     return None
217
218 def Action(act, *args, **kw):
219     """A factory for action objects."""
220     if SCons.Util.is_List(act):
221         acts = map(lambda a, args=args, kw=kw:
222                           apply(_do_create_action, (a,)+args, kw),
223                    act)
224         acts = filter(None, acts)
225         if len(acts) == 1:
226             return acts[0]
227         else:
228             return ListAction(acts)
229     else:
230         return apply(_do_create_action, (act,)+args, kw)
231
232 class ActionBase:
233     """Base class for all types of action objects that can be held by
234     other objects (Builders, Executors, etc.)  This provides the
235     common methods for manipulating and combining those actions."""
236
237     def __cmp__(self, other):
238         return cmp(self.__dict__, other)
239
240     def genstring(self, target, source, env):
241         return str(self)
242
243     def __add__(self, other):
244         return _actionAppend(self, other)
245
246     def __radd__(self, other):
247         return _actionAppend(other, self)
248
249     def presub_lines(self, env):
250         # CommandGeneratorAction needs a real environment
251         # in order to return the proper string here, since
252         # it may call LazyAction, which looks up a key
253         # in that env.  So we temporarily remember the env here,
254         # and CommandGeneratorAction will use this env
255         # when it calls its _generate method.
256         self.presub_env = env
257         lines = string.split(str(self), '\n')
258         self.presub_env = None      # don't need this any more
259         return lines
260
261     def get_executor(self, env, overrides, tlist, slist, executor_kw):
262         """Return the Executor for this Action."""
263         return SCons.Executor.Executor(self, env, overrides,
264                                        tlist, slist, executor_kw)
265
266 class _ActionAction(ActionBase):
267     """Base class for actions that create output objects."""
268     def __init__(self, strfunction=_null, presub=_null, chdir=None, exitstatfunc=None, **kw):
269         if not strfunction is _null:
270             self.strfunction = strfunction
271         self.presub = presub
272         self.chdir = chdir
273         if not exitstatfunc:
274             exitstatfunc = default_exitstatfunc
275         self.exitstatfunc = exitstatfunc
276
277     def print_cmd_line(self, s, target, source, env):
278         sys.stdout.write(s + "\n")
279
280     def __call__(self, target, source, env,
281                                exitstatfunc=_null,
282                                presub=_null,
283                                show=_null,
284                                execute=_null,
285                                chdir=_null):
286         if not SCons.Util.is_List(target):
287             target = [target]
288         if not SCons.Util.is_List(source):
289             source = [source]
290         if exitstatfunc is _null: exitstatfunc = self.exitstatfunc
291         if presub is _null:
292             presub = self.presub
293         if presub is _null:
294             presub = print_actions_presub
295         if show is _null:  show = print_actions
296         if execute is _null:  execute = execute_actions
297         if chdir is _null: chdir = self.chdir
298         save_cwd = None
299         if chdir:
300             save_cwd = os.getcwd()
301             try:
302                 chdir = str(chdir.abspath)
303             except AttributeError:
304                 if not SCons.Util.is_String(chdir):
305                     chdir = str(target[0].dir)
306         if presub:
307             t = string.join(map(str, target), ' and ')
308             l = string.join(self.presub_lines(env), '\n  ')
309             out = "Building %s with action:\n  %s\n" % (t, l)
310             sys.stdout.write(out)
311         s = None
312         if show and self.strfunction:
313             s = self.strfunction(target, source, env)
314             if s:
315                 if chdir:
316                     s = ('os.chdir(%s)\n' % repr(chdir)) + s
317                 try:
318                     get = env.get
319                 except AttributeError:
320                     print_func = self.print_cmd_line
321                 else:
322                     print_func = get('PRINT_CMD_LINE_FUNC')
323                     if not print_func:
324                         print_func = self.print_cmd_line
325                 print_func(s, target, source, env)
326         stat = 0
327         if execute:
328             if chdir:
329                 os.chdir(chdir)
330             try:
331                 stat = self.execute(target, source, env)
332                 stat = exitstatfunc(stat)
333             finally:
334                 if save_cwd:
335                     os.chdir(save_cwd)
336         if s and save_cwd:
337             print_func('os.chdir(%s)' % repr(save_cwd), target, source, env)
338         return stat
339
340
341 def _string_from_cmd_list(cmd_list):
342     """Takes a list of command line arguments and returns a pretty
343     representation for printing."""
344     cl = []
345     for arg in map(str, cmd_list):
346         if ' ' in arg or '\t' in arg:
347             arg = '"' + arg + '"'
348         cl.append(arg)
349     return string.join(cl)
350
351 class CommandAction(_ActionAction):
352     """Class for command-execution actions."""
353     def __init__(self, cmd, cmdstr=None, *args, **kw):
354         # Cmd can actually be a list or a single item; if it's a
355         # single item it should be the command string to execute; if a
356         # list then it should be the words of the command string to
357         # execute.  Only a single command should be executed by this
358         # object; lists of commands should be handled by embedding
359         # these objects in a ListAction object (which the Action()
360         # factory above does).  cmd will be passed to
361         # Environment.subst_list() for substituting environment
362         # variables.
363         if __debug__: logInstanceCreation(self, 'Action.CommandAction')
364
365         if not cmdstr is None:
366             if callable(cmdstr):
367                 args = (cmdstr,)+args
368             elif not SCons.Util.is_String(cmdstr):
369                 raise SCons.Errors.UserError(\
370                     'Invalid command display variable type. ' \
371                     'You must either pass a string or a callback which ' \
372                     'accepts (target, source, env) as parameters.')
373
374         apply(_ActionAction.__init__, (self,)+args, kw)
375         if SCons.Util.is_List(cmd):
376             if filter(SCons.Util.is_List, cmd):
377                 raise TypeError, "CommandAction should be given only " \
378                       "a single command"
379         self.cmd_list = cmd
380         self.cmdstr = cmdstr
381
382     def __str__(self):
383         if SCons.Util.is_List(self.cmd_list):
384             return string.join(map(str, self.cmd_list), ' ')
385         return str(self.cmd_list)
386
387     def process(self, target, source, env):
388         result = env.subst_list(self.cmd_list, 0, target, source)
389         silent = None
390         ignore = None
391         while 1:
392             try: c = result[0][0][0]
393             except IndexError: c = None
394             if c == '@': silent = 1
395             elif c == '-': ignore = 1
396             else: break
397             result[0][0] = result[0][0][1:]
398         try:
399             if not result[0][0]:
400                 result[0] = result[0][1:]
401         except IndexError:
402             pass
403         return result, ignore, silent
404
405     def strfunction(self, target, source, env):
406         if not self.cmdstr is None:
407             from SCons.Subst import SUBST_RAW
408             c = env.subst(self.cmdstr, SUBST_RAW, target, source)
409             if c:
410                 return c
411         cmd_list, ignore, silent = self.process(target, source, env)
412         if silent:
413             return ''
414         return _string_from_cmd_list(cmd_list[0])
415
416     def execute(self, target, source, env):
417         """Execute a command action.
418
419         This will handle lists of commands as well as individual commands,
420         because construction variable substitution may turn a single
421         "command" into a list.  This means that this class can actually
422         handle lists of commands, even though that's not how we use it
423         externally.
424         """
425         from SCons.Subst import escape_list
426         import SCons.Util
427         flatten = SCons.Util.flatten
428         is_String = SCons.Util.is_String
429         is_List = SCons.Util.is_List
430
431         try:
432             shell = env['SHELL']
433         except KeyError:
434             raise SCons.Errors.UserError('Missing SHELL construction variable.')
435
436         try:
437             spawn = env['SPAWN']
438         except KeyError:
439             raise SCons.Errors.UserError('Missing SPAWN construction variable.')
440         else:
441             if is_String(spawn):
442                 spawn = env.subst(spawn, raw=1, conv=lambda x: x)
443
444         escape = env.get('ESCAPE', lambda x: x)
445
446         try:
447             ENV = env['ENV']
448         except KeyError:
449             global default_ENV
450             if not default_ENV:
451                 import SCons.Environment
452                 default_ENV = SCons.Environment.Environment()['ENV']
453             ENV = default_ENV
454
455         # Ensure that the ENV values are all strings:
456         for key, value in ENV.items():
457             if not is_String(value):
458                 if is_List(value):
459                     # If the value is a list, then we assume it is a
460                     # path list, because that's a pretty common list-like
461                     # value to stick in an environment variable:
462                     value = flatten(value)
463                     ENV[key] = string.join(map(str, value), os.pathsep)
464                 else:
465                     # If it isn't a string or a list, then we just coerce
466                     # it to a string, which is the proper way to handle
467                     # Dir and File instances and will produce something
468                     # reasonable for just about everything else:
469                     ENV[key] = str(value)
470
471         cmd_list, ignore, silent = self.process(target, map(rfile, source), env)
472
473         # Use len() to filter out any "command" that's zero-length.
474         for cmd_line in filter(len, cmd_list):
475             # Escape the command line for the interpreter we are using.
476             cmd_line = escape_list(cmd_line, escape)
477             result = spawn(shell, escape, cmd_line[0], cmd_line, ENV)
478             if not ignore and result:
479                 return result
480         return 0
481
482     def get_contents(self, target, source, env):
483         """Return the signature contents of this action's command line.
484
485         This strips $(-$) and everything in between the string,
486         since those parts don't affect signatures.
487         """
488         from SCons.Subst import SUBST_SIG
489         cmd = self.cmd_list
490         if SCons.Util.is_List(cmd):
491             cmd = string.join(map(str, cmd))
492         else:
493             cmd = str(cmd)
494         return env.subst_target_source(cmd, SUBST_SIG, target, source)
495
496 class CommandGeneratorAction(ActionBase):
497     """Class for command-generator actions."""
498     def __init__(self, generator, *args, **kw):
499         if __debug__: logInstanceCreation(self, 'Action.CommandGeneratorAction')
500         self.generator = generator
501         self.gen_args = args
502         self.gen_kw = kw
503
504     def _generate(self, target, source, env, for_signature):
505         # ensure that target is a list, to make it easier to write
506         # generator functions:
507         if not SCons.Util.is_List(target):
508             target = [target]
509
510         ret = self.generator(target=target, source=source, env=env, for_signature=for_signature)
511         gen_cmd = apply(Action, (ret,)+self.gen_args, self.gen_kw)
512         if not gen_cmd:
513             raise SCons.Errors.UserError("Object returned from command generator: %s cannot be used to create an Action." % repr(ret))
514         return gen_cmd
515
516     def __str__(self):
517         try:
518             env = self.presub_env or {}
519         except AttributeError:
520             env = {}
521         act = self._generate([], [], env, 1)
522         return str(act)
523
524     def genstring(self, target, source, env):
525         return self._generate(target, source, env, 1).genstring(target, source, env)
526
527     def __call__(self, target, source, env, exitstatfunc=_null, presub=_null,
528                  show=_null, execute=_null, chdir=_null):
529         act = self._generate(target, source, env, 0)
530         return act(target, source, env, exitstatfunc, presub,
531                    show, execute, chdir)
532
533     def get_contents(self, target, source, env):
534         """Return the signature contents of this action's command line.
535
536         This strips $(-$) and everything in between the string,
537         since those parts don't affect signatures.
538         """
539         return self._generate(target, source, env, 1).get_contents(target, source, env)
540
541
542
543 # A LazyAction is a kind of hybrid generator and command action for
544 # strings of the form "$VAR".  These strings normally expand to other
545 # strings (think "$CCCOM" to "$CC -c -o $TARGET $SOURCE"), but we also
546 # want to be able to replace them with functions in the construction
547 # environment.  Consequently, we want lazy evaluation and creation of
548 # an Action in the case of the function, but that's overkill in the more
549 # normal case of expansion to other strings.
550 #
551 # So we do this with a subclass that's both a generator *and*
552 # a command action.  The overridden methods all do a quick check
553 # of the construction variable, and if it's a string we just call
554 # the corresponding CommandAction method to do the heavy lifting.
555 # If not, then we call the same-named CommandGeneratorAction method.
556 # The CommandGeneratorAction methods work by using the overridden
557 # _generate() method, that is, our own way of handling "generation" of
558 # an action based on what's in the construction variable.
559
560 class LazyAction(CommandGeneratorAction, CommandAction):
561
562     def __init__(self, var, *args, **kw):
563         if __debug__: logInstanceCreation(self, 'Action.LazyAction')
564         apply(CommandAction.__init__, (self, '$'+var)+args, kw)
565         self.var = SCons.Util.to_String(var)
566         self.gen_args = args
567         self.gen_kw = kw
568
569     def get_parent_class(self, env):
570         c = env.get(self.var)
571         if SCons.Util.is_String(c) and not '\n' in c:
572             return CommandAction
573         return CommandGeneratorAction
574
575     def _generate_cache(self, env):
576         c = env.get(self.var, '')
577         gen_cmd = apply(Action, (c,)+self.gen_args, self.gen_kw)
578         if not gen_cmd:
579             raise SCons.Errors.UserError("$%s value %s cannot be used to create an Action." % (self.var, repr(c)))
580         return gen_cmd
581
582     def _generate(self, target, source, env, for_signature):
583         return self._generate_cache(env)
584
585     def __call__(self, target, source, env, *args, **kw):
586         args = (self, target, source, env) + args
587         c = self.get_parent_class(env)
588         return apply(c.__call__, args, kw)
589
590     def get_contents(self, target, source, env):
591         c = self.get_parent_class(env)
592         return c.get_contents(self, target, source, env)
593
594
595
596 class FunctionAction(_ActionAction):
597     """Class for Python function actions."""
598
599     def __init__(self, execfunction, cmdstr=_null, *args, **kw):
600         if __debug__: logInstanceCreation(self, 'Action.FunctionAction')
601
602         if not cmdstr is _null:
603             if callable(cmdstr):
604                 args = (cmdstr,)+args
605             elif not (cmdstr is None or SCons.Util.is_String(cmdstr)):
606                 raise SCons.Errors.UserError(\
607                     'Invalid function display variable type. ' \
608                     'You must either pass a string or a callback which ' \
609                     'accepts (target, source, env) as parameters.')
610
611         self.execfunction = execfunction
612         apply(_ActionAction.__init__, (self,)+args, kw)
613         self.varlist = kw.get('varlist', [])
614         self.cmdstr = cmdstr
615
616     def function_name(self):
617         try:
618             return self.execfunction.__name__
619         except AttributeError:
620             try:
621                 return self.execfunction.__class__.__name__
622             except AttributeError:
623                 return "unknown_python_function"
624
625     def strfunction(self, target, source, env):
626         if self.cmdstr is None:
627             return None
628         if not self.cmdstr is _null:
629             from SCons.Subst import SUBST_RAW
630             c = env.subst(self.cmdstr, SUBST_RAW, target, source)
631             if c:
632                 return c
633         def array(a):
634             def quote(s):
635                 return '"' + str(s) + '"'
636             return '[' + string.join(map(quote, a), ", ") + ']'
637         try:
638             strfunc = self.execfunction.strfunction
639         except AttributeError:
640             pass
641         else:
642             if strfunc is None:
643                 return None
644             if callable(strfunc):
645                 return strfunc(target, source, env)
646         name = self.function_name()
647         tstr = array(target)
648         sstr = array(source)
649         return "%s(%s, %s)" % (name, tstr, sstr)
650
651     def __str__(self):
652         name = self.function_name()
653         if name == 'ActionCaller':
654             return str(self.execfunction)
655         return "%s(target, source, env)" % name
656
657     def execute(self, target, source, env):
658         rsources = map(rfile, source)
659         try:
660             result = self.execfunction(target=target, source=rsources, env=env)
661         except EnvironmentError, e:
662             # If an IOError/OSError happens, raise a BuildError.
663             # Report the name of the file or directory that caused the
664             # error, which might be different from the target being built
665             # (for example, failure to create the directory in which the
666             # target file will appear).
667             try: filename = e.filename
668             except AttributeError: filename = None
669             raise SCons.Errors.BuildError(node=target,
670                                           errstr=e.strerror,
671                                           filename=filename)
672         return result
673
674     def get_contents(self, target, source, env):
675         """Return the signature contents of this callable action.
676
677         By providing direct access to the code object of the
678         function, Python makes this extremely easy.  Hooray!
679
680         Unfortunately, older versions of Python include line
681         number indications in the compiled byte code.  Boo!
682         So we remove the line number byte codes to prevent
683         recompilations from moving a Python function.
684         """
685         execfunction = self.execfunction
686         try:
687             # Test if execfunction is a function.
688             code = execfunction.func_code.co_code
689         except AttributeError:
690             try:
691                 # Test if execfunction is a method.
692                 code = execfunction.im_func.func_code.co_code
693             except AttributeError:
694                 try:
695                     # Test if execfunction is a callable object.
696                     code = execfunction.__call__.im_func.func_code.co_code
697                 except AttributeError:
698                     try:
699                         # See if execfunction will do the heavy lifting for us.
700                         gc = self.execfunction.get_contents
701                     except AttributeError:
702                         # This is weird, just do the best we can.
703                         contents = str(self.execfunction)
704                     else:
705                         contents = gc(target, source, env)
706                 else:
707                     contents = str(code)
708             else:
709                 contents = str(code)
710         else:
711             contents = str(code)
712         contents = remove_set_lineno_codes(contents)
713         return contents + env.subst(string.join(map(lambda v: '${'+v+'}',
714                                                      self.varlist)))
715
716 class ListAction(ActionBase):
717     """Class for lists of other actions."""
718     def __init__(self, list):
719         if __debug__: logInstanceCreation(self, 'Action.ListAction')
720         def list_of_actions(x):
721             if isinstance(x, ActionBase):
722                 return x
723             return Action(x)
724         self.list = map(list_of_actions, list)
725
726     def genstring(self, target, source, env):
727         return string.join(map(lambda a, t=target, s=source, e=env:
728                                   a.genstring(t, s, e),
729                                self.list),
730                            '\n')
731
732     def __str__(self):
733         return string.join(map(str, self.list), '\n')
734     
735     def presub_lines(self, env):
736         return SCons.Util.flatten(map(lambda a, env=env:
737                                       a.presub_lines(env),
738                                       self.list))
739
740     def get_contents(self, target, source, env):
741         """Return the signature contents of this action list.
742
743         Simple concatenation of the signatures of the elements.
744         """
745         return string.join(map(lambda x, t=target, s=source, e=env:
746                                       x.get_contents(t, s, e),
747                                self.list),
748                            "")
749
750     def __call__(self, target, source, env, exitstatfunc=_null, presub=_null,
751                  show=_null, execute=_null, chdir=_null):
752         for act in self.list:
753             stat = act(target, source, env, exitstatfunc, presub,
754                        show, execute, chdir)
755             if stat:
756                 return stat
757         return 0
758
759 class ActionCaller:
760     """A class for delaying calling an Action function with specific
761     (positional and keyword) arguments until the Action is actually
762     executed.
763
764     This class looks to the rest of the world like a normal Action object,
765     but what it's really doing is hanging on to the arguments until we
766     have a target, source and env to use for the expansion.
767     """
768     def __init__(self, parent, args, kw):
769         self.parent = parent
770         self.args = args
771         self.kw = kw
772     def get_contents(self, target, source, env):
773         actfunc = self.parent.actfunc
774         try:
775             # "self.actfunc" is a function.
776             contents = str(actfunc.func_code.co_code)
777         except AttributeError:
778             # "self.actfunc" is a callable object.
779             try:
780                 contents = str(actfunc.__call__.im_func.func_code.co_code)
781             except AttributeError:
782                 # No __call__() method, so it might be a builtin
783                 # or something like that.  Do the best we can.
784                 contents = str(actfunc)
785         contents = remove_set_lineno_codes(contents)
786         return contents
787     def subst(self, s, target, source, env):
788         # Special-case hack:  Let a custom function wrapped in an
789         # ActionCaller get at the environment through which the action
790         # was called by using this hard-coded value as a special return.
791         if s == '$__env__':
792             return env
793         else:
794             return env.subst(s, 0, target, source)
795     def subst_args(self, target, source, env):
796         return map(lambda x, self=self, t=target, s=source, e=env:
797                           self.subst(x, t, s, e),
798                    self.args)
799     def subst_kw(self, target, source, env):
800         kw = {}
801         for key in self.kw.keys():
802             kw[key] = self.subst(self.kw[key], target, source, env)
803         return kw
804     def __call__(self, target, source, env):
805         args = self.subst_args(target, source, env)
806         kw = self.subst_kw(target, source, env)
807         return apply(self.parent.actfunc, args, kw)
808     def strfunction(self, target, source, env):
809         args = self.subst_args(target, source, env)
810         kw = self.subst_kw(target, source, env)
811         return apply(self.parent.strfunc, args, kw)
812     def __str__(self):
813         return apply(self.parent.strfunc, self.args, self.kw)
814
815 class ActionFactory:
816     """A factory class that will wrap up an arbitrary function
817     as an SCons-executable Action object.
818
819     The real heavy lifting here is done by the ActionCaller class.
820     We just collect the (positional and keyword) arguments that we're
821     called with and give them to the ActionCaller object we create,
822     so it can hang onto them until it needs them.
823     """
824     def __init__(self, actfunc, strfunc):
825         self.actfunc = actfunc
826         self.strfunc = strfunc
827     def __call__(self, *args, **kw):
828         ac = ActionCaller(self, args, kw)
829         action = Action(ac, strfunction=ac.strfunction)
830         return action