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 plus the varlist. This is what gets MD5 checksummed to decide
35 if a target 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 get_presig()
61 Fetches the "contents" of a subclass for signature calculation.
62 The varlist is added to this to produce the Action's contents.
63
64 strfunction()
65 Returns a substituted string representation of the Action.
66 This is used by the _ActionAction.show() command to display the
67 command/function that will be executed to generate the target(s).
68
69 There is a related independent ActionCaller class that looks like a
70 regular Action, and which serves as a wrapper for arbitrary functions
71 that we want to let the user specify the arguments to now, but actually
72 execute later (when an out-of-date check determines that it's needed to
73 be executed, for example). Objects of this class are returned by an
74 ActionFactory class that provides a __call__() method as a convenient
75 way for wrapping up the functions.
76
77 """
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100 __revision__ = "src/engine/SCons/Action.py 3842 2008/12/20 22:59:52 scons"
101
102 import cPickle
103 import dis
104 import os
105 import string
106 import sys
107 import subprocess
108
109 from SCons.Debug import logInstanceCreation
110 import SCons.Errors
111 import SCons.Executor
112 import SCons.Util
113 import SCons.Subst
114
115
116 is_String = SCons.Util.is_String
117 is_List = SCons.Util.is_List
118
121
122 print_actions = 1
123 execute_actions = 1
124 print_actions_presub = 0
125
127 try:
128 return n.rfile()
129 except AttributeError:
130 return n
131
134
135 try:
136 SET_LINENO = dis.SET_LINENO
137 HAVE_ARGUMENT = dis.HAVE_ARGUMENT
138 except AttributeError:
139 remove_set_lineno_codes = lambda x: x
140 else:
156
157
159 """Return the signature contents of a callable Python object.
160 """
161 try:
162
163 return _function_contents(obj.im_func)
164
165 except AttributeError:
166 try:
167
168 return _function_contents(obj.__call__.im_func)
169
170 except AttributeError:
171 try:
172
173 return _code_contents(obj)
174
175 except AttributeError:
176
177 return _function_contents(obj)
178
179
181 """Return the signature contents of any Python object.
182
183 We have to handle the case where object contains a code object
184 since it can be pickled directly.
185 """
186 try:
187
188 return _function_contents(obj.im_func)
189
190 except AttributeError:
191 try:
192
193 return _function_contents(obj.__call__.im_func)
194
195 except AttributeError:
196 try:
197
198 return _code_contents(obj)
199
200 except AttributeError:
201 try:
202
203 return _function_contents(obj)
204
205 except AttributeError:
206
207 try:
208 return cPickle.dumps(obj)
209 except (cPickle.PicklingError, TypeError):
210
211
212
213
214
215 return str(obj)
216
217
218 -def _code_contents(code):
219 """Return the signature contents of a code object.
220
221 By providing direct access to the code object of the
222 function, Python makes this extremely easy. Hooray!
223
224 Unfortunately, older versions of Python include line
225 number indications in the compiled byte code. Boo!
226 So we remove the line number byte codes to prevent
227 recompilations from moving a Python function.
228 """
229
230 contents = []
231
232
233
234 contents.append("%s,%s" % (code.co_argcount, len(code.co_varnames)))
235 try:
236 contents.append(",%s,%s" % (len(code.co_cellvars), len(code.co_freevars)))
237 except AttributeError:
238
239 contents.append(",0,0")
240
241
242
243
244
245
246
247
248
249 contents.append(',(' + string.join(map(_object_contents,code.co_consts[1:]),',') + ')')
250
251
252
253
254
255 contents.append(',(' + string.join(map(_object_contents,code.co_names),',') + ')')
256
257
258
259 contents.append(',(' + str(remove_set_lineno_codes(code.co_code)) + ')')
260
261 return string.join(contents, '')
262
263
265 """Return the signature contents of a function."""
266
267 contents = [_code_contents(func.func_code)]
268
269
270 if func.func_defaults:
271 contents.append(',(' + string.join(map(_object_contents,func.func_defaults),',') + ')')
272 else:
273 contents.append(',()')
274
275
276 try:
277 closure = func.func_closure or []
278 except AttributeError:
279
280 closure = []
281
282
283 try:
284 xxx = map(lambda x: _object_contents(x.cell_contents), closure)
285 except AttributeError:
286 xxx = []
287 contents.append(',(' + string.join(xxx, ',') + ')')
288
289 return string.join(contents, '')
290
291
293
294
295
296 a1 = Action(act1)
297 a2 = Action(act2)
298 if a1 is None or a2 is None:
299 raise TypeError, "Cannot append %s to %s" % (type(act1), type(act2))
300 if isinstance(a1, ListAction):
301 if isinstance(a2, ListAction):
302 return ListAction(a1.list + a2.list)
303 else:
304 return ListAction(a1.list + [ a2 ])
305 else:
306 if isinstance(a2, ListAction):
307 return ListAction([ a1 ] + a2.list)
308 else:
309 return ListAction([ a1, a2 ])
310
312 """This converts any arguments after the action argument into
313 their equivalent keywords and adds them to the kw argument.
314 """
315 v = kw.get('varlist', ())
316
317 if is_String(v): v = (v,)
318 kw['varlist'] = tuple(v)
319 if args:
320
321 cmdstrfunc = args[0]
322 if cmdstrfunc is None or is_String(cmdstrfunc):
323 kw['cmdstr'] = cmdstrfunc
324 elif callable(cmdstrfunc):
325 kw['strfunction'] = cmdstrfunc
326 else:
327 raise SCons.Errors.UserError(
328 'Invalid command display variable type. '
329 'You must either pass a string or a callback which '
330 'accepts (target, source, env) as parameters.')
331 if len(args) > 1:
332 kw['varlist'] = args[1:] + kw['varlist']
333 if kw.get('strfunction', _null) is not _null \
334 and kw.get('cmdstr', _null) is not _null:
335 raise SCons.Errors.UserError(
336 'Cannot have both strfunction and cmdstr args to Action()')
337
339 """This is the actual "implementation" for the
340 Action factory method, below. This handles the
341 fact that passing lists to Action() itself has
342 different semantics than passing lists as elements
343 of lists.
344
345 The former will create a ListAction, the latter
346 will create a CommandAction by converting the inner
347 list elements to strings."""
348
349 if isinstance(act, ActionBase):
350 return act
351
352 if is_List(act):
353
354 return apply(CommandAction, (act,), kw)
355
356 if callable(act):
357 try:
358 gen = kw['generator']
359 del kw['generator']
360 except KeyError:
361 gen = 0
362 if gen:
363 action_type = CommandGeneratorAction
364 else:
365 action_type = FunctionAction
366 return action_type(act, kw)
367
368 if is_String(act):
369 var=SCons.Util.get_environment_var(act)
370 if var:
371
372
373
374
375
376
377 return LazyAction(var, kw)
378 commands = string.split(str(act), '\n')
379 if len(commands) == 1:
380
381 return apply(CommandAction, (commands[0],), kw)
382
383
384 return _do_create_list_action(commands, kw)
385 return None
386
388 """A factory for list actions. Convert the input list into Actions
389 and then wrap them in a ListAction."""
390 acts = []
391 for a in act:
392 aa = _do_create_action(a, kw)
393 if aa is not None: acts.append(aa)
394 if not acts:
395 return None
396 elif len(acts) == 1:
397 return acts[0]
398 else:
399 return ListAction(acts)
400
402 """A factory for action objects."""
403
404 _do_create_keywords(args, kw)
405 if is_List(act):
406 return _do_create_list_action(act, kw)
407 return _do_create_action(act, kw)
408
410 """Base class for all types of action objects that can be held by
411 other objects (Builders, Executors, etc.) This provides the
412 common methods for manipulating and combining those actions."""
413
415 return cmp(self.__dict__, other)
416
419
420 - def get_contents(self, target, source, env):
421 result = [ self.get_presig(target, source, env) ]
422
423
424
425 vl = self.varlist
426 if is_String(vl): vl = (vl,)
427 for v in vl:
428 result.append(env.subst('${'+v+'}'))
429 return string.join(result, '')
430
432 return _actionAppend(self, other)
433
435 return _actionAppend(other, self)
436
438
439
440
441
442
443
444 self.presub_env = env
445 lines = string.split(str(self), '\n')
446 self.presub_env = None
447 return lines
448
449 - def get_executor(self, env, overrides, tlist, slist, executor_kw):
450 """Return the Executor for this Action."""
451 return SCons.Executor.Executor(self, env, overrides,
452 tlist, slist, executor_kw)
453
455 """Base class for actions that create output objects."""
456 - def __init__(self, cmdstr=_null, strfunction=_null, varlist=(),
457 presub=_null, chdir=None, exitstatfunc=None,
458 **kw):
459 self.cmdstr = cmdstr
460 if strfunction is not _null:
461 if strfunction is None:
462 self.cmdstr = None
463 else:
464 self.strfunction = strfunction
465 self.varlist = varlist
466 self.presub = presub
467 self.chdir = chdir
468 if not exitstatfunc:
469 exitstatfunc = default_exitstatfunc
470 self.exitstatfunc = exitstatfunc
471
473 sys.stdout.write(s + "\n")
474
481 if not is_List(target):
482 target = [target]
483 if not is_List(source):
484 source = [source]
485
486 if presub is _null:
487 presub = self.presub
488 if presub is _null:
489 presub = print_actions_presub
490 if exitstatfunc is _null: exitstatfunc = self.exitstatfunc
491 if show is _null: show = print_actions
492 if execute is _null: execute = execute_actions
493 if chdir is _null: chdir = self.chdir
494 save_cwd = None
495 if chdir:
496 save_cwd = os.getcwd()
497 try:
498 chdir = str(chdir.abspath)
499 except AttributeError:
500 if not is_String(chdir):
501 chdir = str(target[0].dir)
502 if presub:
503 t = string.join(map(str, target), ' and ')
504 l = string.join(self.presub_lines(env), '\n ')
505 out = "Building %s with action:\n %s\n" % (t, l)
506 sys.stdout.write(out)
507 cmd = None
508 if show and self.strfunction:
509 cmd = self.strfunction(target, source, env)
510 if cmd:
511 if chdir:
512 cmd = ('os.chdir(%s)\n' % repr(chdir)) + cmd
513 try:
514 get = env.get
515 except AttributeError:
516 print_func = self.print_cmd_line
517 else:
518 print_func = get('PRINT_CMD_LINE_FUNC')
519 if not print_func:
520 print_func = self.print_cmd_line
521 print_func(cmd, target, source, env)
522 stat = 0
523 if execute:
524 if chdir:
525 os.chdir(chdir)
526 try:
527 stat = self.execute(target, source, env)
528 if isinstance(stat, SCons.Errors.BuildError):
529 s = exitstatfunc(stat.status)
530 if s:
531 stat.status = s
532 else:
533 stat = s
534 else:
535 stat = exitstatfunc(stat)
536 finally:
537 if save_cwd:
538 os.chdir(save_cwd)
539 if cmd and save_cwd:
540 print_func('os.chdir(%s)' % repr(save_cwd), target, source, env)
541
542 return stat
543
544
546 """Takes a list of command line arguments and returns a pretty
547 representation for printing."""
548 cl = []
549 for arg in map(str, cmd_list):
550 if ' ' in arg or '\t' in arg:
551 arg = '"' + arg + '"'
552 cl.append(arg)
553 return string.join(cl)
554
555
556
557
558
559
560 default_ENV = None
575
576
577
578
579
580 -def _subproc(env, cmd, error = 'ignore', **kw):
581 """Do common setup for a subprocess.Popen() call"""
582
583 io = kw.get('stdin')
584 if is_String(io) and io == 'devnull':
585 kw['stdin'] = open(os.devnull)
586 io = kw.get('stdout')
587 if is_String(io) and io == 'devnull':
588 kw['stdout'] = open(os.devnull, 'w')
589 io = kw.get('stderr')
590 if is_String(io) and io == 'devnull':
591 kw['stderr'] = open(os.devnull, 'w')
592
593
594 ENV = kw.get('env', None)
595 if ENV is None: ENV = get_default_ENV(env)
596
597
598 new_env = {}
599 for key, value in ENV.items():
600 if is_List(value):
601
602
603
604 value = SCons.Util.flatten_sequence(value)
605 new_env[key] = string.join(map(str, value), os.pathsep)
606 else:
607
608
609
610
611
612
613 new_env[key] = str(value)
614 kw['env'] = new_env
615
616 try:
617
618 return apply(subprocess.Popen, (cmd,), kw)
619 except EnvironmentError, e:
620 if error == 'raise': raise
621
622 class dummyPopen:
623 def __init__(self, e): self.exception = e
624 def communicate(self): return ('','')
625 def wait(self): return -self.exception.errno
626 stdin = None
627 class f:
628 def read(self): return ''
629 def readline(self): return ''
630 stdout = stderr = f()
631 return dummyPopen(e)
632
634 """Class for command-execution actions."""
636
637
638
639
640
641
642
643
644
645 if __debug__: logInstanceCreation(self, 'Action.CommandAction')
646
647
648 apply(_ActionAction.__init__, (self,), kw)
649 if is_List(cmd):
650 if filter(is_List, cmd):
651 raise TypeError, "CommandAction should be given only " \
652 "a single command"
653 self.cmd_list = cmd
654
656 if is_List(self.cmd_list):
657 return string.join(map(str, self.cmd_list), ' ')
658 return str(self.cmd_list)
659
660 - def process(self, target, source, env):
661 result = env.subst_list(self.cmd_list, 0, target, source)
662 silent = None
663 ignore = None
664 while 1:
665 try: c = result[0][0][0]
666 except IndexError: c = None
667 if c == '@': silent = 1
668 elif c == '-': ignore = 1
669 else: break
670 result[0][0] = result[0][0][1:]
671 try:
672 if not result[0][0]:
673 result[0] = result[0][1:]
674 except IndexError:
675 pass
676 return result, ignore, silent
677
679 if self.cmdstr is None:
680 return None
681 if self.cmdstr is not _null:
682 from SCons.Subst import SUBST_RAW
683 c = env.subst(self.cmdstr, SUBST_RAW, target, source)
684 if c:
685 return c
686 cmd_list, ignore, silent = self.process(target, source, env)
687 if silent:
688 return ''
689 return _string_from_cmd_list(cmd_list[0])
690
691 - def execute(self, target, source, env):
692 """Execute a command action.
693
694 This will handle lists of commands as well as individual commands,
695 because construction variable substitution may turn a single
696 "command" into a list. This means that this class can actually
697 handle lists of commands, even though that's not how we use it
698 externally.
699 """
700 escape_list = SCons.Subst.escape_list
701 flatten_sequence = SCons.Util.flatten_sequence
702
703 try:
704 shell = env['SHELL']
705 except KeyError:
706 raise SCons.Errors.UserError('Missing SHELL construction variable.')
707
708 try:
709 spawn = env['SPAWN']
710 except KeyError:
711 raise SCons.Errors.UserError('Missing SPAWN construction variable.')
712 else:
713 if is_String(spawn):
714 spawn = env.subst(spawn, raw=1, conv=lambda x: x)
715
716 escape = env.get('ESCAPE', lambda x: x)
717
718 ENV = get_default_ENV(env)
719
720
721 for key, value in ENV.items():
722 if not is_String(value):
723 if is_List(value):
724
725
726
727 value = flatten_sequence(value)
728 ENV[key] = string.join(map(str, value), os.pathsep)
729 else:
730
731
732
733
734 ENV[key] = str(value)
735
736 cmd_list, ignore, silent = self.process(target, map(rfile, source), env)
737
738
739 for cmd_line in filter(len, cmd_list):
740
741 cmd_line = escape_list(cmd_line, escape)
742 result = spawn(shell, escape, cmd_line[0], cmd_line, ENV)
743 if not ignore and result:
744 msg = "Error %s" % result
745 return SCons.Errors.BuildError(errstr=msg,
746 status=result,
747 action=self,
748 command=cmd_line)
749 return 0
750
752 """Return the signature contents of this action's command line.
753
754 This strips $(-$) and everything in between the string,
755 since those parts don't affect signatures.
756 """
757 from SCons.Subst import SUBST_SIG
758 cmd = self.cmd_list
759 if is_List(cmd):
760 cmd = string.join(map(str, cmd))
761 else:
762 cmd = str(cmd)
763 return env.subst_target_source(cmd, SUBST_SIG, target, source)
764
766 icd = env.get('IMPLICIT_COMMAND_DEPENDENCIES', True)
767 if is_String(icd) and icd[:1] == '$':
768 icd = env.subst(icd)
769 if not icd or icd in ('0', 'None'):
770 return []
771 from SCons.Subst import SUBST_SIG
772 cmd_list = env.subst_list(self.cmd_list, SUBST_SIG, target, source)
773 res = []
774 for cmd_line in cmd_list:
775 if cmd_line:
776 d = env.WhereIs(str(cmd_line[0]))
777 if d:
778 res.append(env.fs.File(d))
779 return res
780
782 """Class for command-generator actions."""
784 if __debug__: logInstanceCreation(self, 'Action.CommandGeneratorAction')
785 self.generator = generator
786 self.gen_kw = kw
787 self.varlist = kw.get('varlist', ())
788
789 - def _generate(self, target, source, env, for_signature):
790
791
792 if not is_List(target):
793 target = [target]
794
795 ret = self.generator(target=target, source=source, env=env, for_signature=for_signature)
796
797 gen_cmd = apply(Action, (ret,), self.gen_kw)
798 if not gen_cmd:
799 raise SCons.Errors.UserError("Object returned from command generator: %s cannot be used to create an Action." % repr(ret))
800 return gen_cmd
801
803 try:
804 env = self.presub_env
805 except AttributeError:
806 env = None
807 if env is None:
808 env = SCons.Defaults.DefaultEnvironment()
809 act = self._generate([], [], env, 1)
810 return str(act)
811
814
817 act = self._generate(target, source, env, 0)
818 return act(target, source, env, exitstatfunc, presub,
819 show, execute, chdir)
820
822 """Return the signature contents of this action's command line.
823
824 This strips $(-$) and everything in between the string,
825 since those parts don't affect signatures.
826 """
827 return self._generate(target, source, env, 1).get_presig(target, source, env)
828
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851 -class LazyAction(CommandGeneratorAction, CommandAction):
852
859
865
867 c = env.get(self.var, '')
868
869 gen_cmd = apply(Action, (c,), self.gen_kw)
870 if not gen_cmd:
871 raise SCons.Errors.UserError("$%s value %s cannot be used to create an Action." % (self.var, repr(c)))
872 return gen_cmd
873
874 - def _generate(self, target, source, env, for_signature):
876
877 - def __call__(self, target, source, env, *args, **kw):
878 args = (self, target, source, env) + args
879 c = self.get_parent_class(env)
880
881 return apply(c.__call__, args, kw)
882
886
887
888
890 """Class for Python function actions."""
891
893 if __debug__: logInstanceCreation(self, 'Action.FunctionAction')
894
895 self.execfunction = execfunction
896 try:
897 self.funccontents = _callable_contents(execfunction)
898 except AttributeError:
899 try:
900
901 self.gc = execfunction.get_contents
902 except AttributeError:
903
904 self.funccontents = _object_contents(execfunction)
905
906
907 apply(_ActionAction.__init__, (self,), kw)
908
910 try:
911 return self.execfunction.__name__
912 except AttributeError:
913 try:
914 return self.execfunction.__class__.__name__
915 except AttributeError:
916 return "unknown_python_function"
917
935 return '[' + string.join(map(quote, a), ", ") + ']'
936 try:
937 strfunc = self.execfunction.strfunction
938 except AttributeError:
939 pass
940 else:
941 if strfunc is None:
942 return None
943 if callable(strfunc):
944 return strfunc(target, source, env)
945 name = self.function_name()
946 tstr = array(target)
947 sstr = array(source)
948 return "%s(%s, %s)" % (name, tstr, sstr)
949
951 name = self.function_name()
952 if name == 'ActionCaller':
953 return str(self.execfunction)
954 return "%s(target, source, env)" % name
955
956 - def execute(self, target, source, env):
957 exc_info = (None,None,None)
958 try:
959 rsources = map(rfile, source)
960 try:
961 result = self.execfunction(target=target, source=rsources, env=env)
962 except KeyboardInterrupt, e:
963 raise
964 except SystemExit, e:
965 raise
966 except Exception, e:
967 result = e
968 exc_info = sys.exc_info()
969
970 if result:
971 result = SCons.Errors.convert_to_BuildError(result, exc_info)
972 result.node=target
973 result.action=self
974 result.command=self.strfunction(target, source, env)
975
976
977
978
979
980
981
982 if (exc_info[1] and
983 not isinstance(exc_info[1],EnvironmentError)):
984 raise result
985
986 return result
987 finally:
988
989
990
991 del exc_info
992
993
995 """Return the signature contents of this callable action."""
996 try:
997 return self.gc(target, source, env)
998 except AttributeError:
999 return self.funccontents
1000
1003
1005 """Class for lists of other actions."""
1012 self.list = map(list_of_actions, list)
1013
1014
1015 self.varlist = ()
1016
1018 return string.join(map(lambda a, t=target, s=source, e=env:
1019 a.genstring(t, s, e),
1020 self.list),
1021 '\n')
1022
1024 return string.join(map(str, self.list), '\n')
1025
1029
1031 """Return the signature contents of this action list.
1032
1033 Simple concatenation of the signatures of the elements.
1034 """
1035 return string.join(map(lambda x, t=target, s=source, e=env:
1036 x.get_contents(t, s, e),
1037 self.list),
1038 "")
1039
1042 for act in self.list:
1043 stat = act(target, source, env, exitstatfunc, presub,
1044 show, execute, chdir)
1045 if stat:
1046 return stat
1047 return 0
1048
1054
1056 """A class for delaying calling an Action function with specific
1057 (positional and keyword) arguments until the Action is actually
1058 executed.
1059
1060 This class looks to the rest of the world like a normal Action object,
1061 but what it's really doing is hanging on to the arguments until we
1062 have a target, source and env to use for the expansion.
1063 """
1065 self.parent = parent
1066 self.args = args
1067 self.kw = kw
1068
1069 - def get_contents(self, target, source, env):
1070 actfunc = self.parent.actfunc
1071 try:
1072
1073 contents = str(actfunc.func_code.co_code)
1074 except AttributeError:
1075
1076 try:
1077 contents = str(actfunc.__call__.im_func.func_code.co_code)
1078 except AttributeError:
1079
1080
1081 contents = str(actfunc)
1082 contents = remove_set_lineno_codes(contents)
1083 return contents
1084
1085 - def subst(self, s, target, source, env):
1086
1087
1088 if is_List(s):
1089 result = []
1090 for elem in s:
1091 result.append(self.subst(elem, target, source, env))
1092 return self.parent.convert(result)
1093
1094
1095
1096
1097 if s == '$__env__':
1098 return env
1099 elif is_String(s):
1100 return env.subst(s, 1, target, source)
1101 return self.parent.convert(s)
1102
1104 return map(lambda x, self=self, t=target, s=source, e=env:
1105 self.subst(x, t, s, e),
1106 self.args)
1107
1108 - def subst_kw(self, target, source, env):
1109 kw = {}
1110 for key in self.kw.keys():
1111 kw[key] = self.subst(self.kw[key], target, source, env)
1112 return kw
1113
1114 - def __call__(self, target, source, env):
1115 args = self.subst_args(target, source, env)
1116 kw = self.subst_kw(target, source, env)
1117
1118 return apply(self.parent.actfunc, args, kw)
1119
1121 args = self.subst_args(target, source, env)
1122 kw = self.subst_kw(target, source, env)
1123
1124 return apply(self.parent.strfunc, args, kw)
1125
1127
1128 return apply(self.parent.strfunc, self.args, self.kw)
1129
1131 """A factory class that will wrap up an arbitrary function
1132 as an SCons-executable Action object.
1133
1134 The real heavy lifting here is done by the ActionCaller class.
1135 We just collect the (positional and keyword) arguments that we're
1136 called with and give them to the ActionCaller object we create,
1137 so it can hang onto them until it needs them.
1138 """
1139 - def __init__(self, actfunc, strfunc, convert=lambda x: x):
1140 self.actfunc = actfunc
1141 self.strfunc = strfunc
1142 self.convert = convert
1143
1148