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 rel_2.4.0:3365:9259ea1c13d7 2015/09/21 14:03:43 bdbaddog"
101
102 import dis
103 import os
104
105 import pickle
106 import re
107 import sys
108 import subprocess
109
110 import SCons.Debug
111 from SCons.Debug import logInstanceCreation
112 import SCons.Errors
113 import SCons.Util
114 import SCons.Subst
115
116
117 is_String = SCons.Util.is_String
118 is_List = SCons.Util.is_List
119
122
123 print_actions = 1
124 execute_actions = 1
125 print_actions_presub = 0
126
128 try:
129 return n.rfile()
130 except AttributeError:
131 return n
132
135
136 try:
137 SET_LINENO = dis.SET_LINENO
138 HAVE_ARGUMENT = dis.HAVE_ARGUMENT
139 except AttributeError:
140 remove_set_lineno_codes = lambda x: x
141 else:
157
158 strip_quotes = re.compile('^[\'"](.*)[\'"]$')
159
160
162 """Return the signature contents of a callable Python object.
163 """
164 try:
165
166 return _function_contents(obj.im_func)
167
168 except AttributeError:
169 try:
170
171 return _function_contents(obj.__call__.im_func)
172
173 except AttributeError:
174 try:
175
176 return _code_contents(obj)
177
178 except AttributeError:
179
180 return _function_contents(obj)
181
182
184 """Return the signature contents of any Python object.
185
186 We have to handle the case where object contains a code object
187 since it can be pickled directly.
188 """
189 try:
190
191 return _function_contents(obj.im_func)
192
193 except AttributeError:
194 try:
195
196 return _function_contents(obj.__call__.im_func)
197
198 except AttributeError:
199 try:
200
201 return _code_contents(obj)
202
203 except AttributeError:
204 try:
205
206 return _function_contents(obj)
207
208 except AttributeError:
209
210 try:
211 return pickle.dumps(obj)
212 except (pickle.PicklingError, TypeError):
213
214
215
216
217
218 return str(obj)
219
220
221 -def _code_contents(code):
222 """Return the signature contents of a code object.
223
224 By providing direct access to the code object of the
225 function, Python makes this extremely easy. Hooray!
226
227 Unfortunately, older versions of Python include line
228 number indications in the compiled byte code. Boo!
229 So we remove the line number byte codes to prevent
230 recompilations from moving a Python function.
231 """
232
233 contents = []
234
235
236
237 contents.append("%s,%s" % (code.co_argcount, len(code.co_varnames)))
238 try:
239 contents.append(",%s,%s" % (len(code.co_cellvars), len(code.co_freevars)))
240 except AttributeError:
241
242 contents.append(",0,0")
243
244
245
246
247
248
249
250
251
252 contents.append(',(' + ','.join(map(_object_contents,code.co_consts[1:])) + ')')
253
254
255
256
257
258 contents.append(',(' + ','.join(map(_object_contents,code.co_names)) + ')')
259
260
261
262 contents.append(',(' + str(remove_set_lineno_codes(code.co_code)) + ')')
263
264 return ''.join(contents)
265
266
268 """Return the signature contents of a function."""
269
270 contents = [_code_contents(func.func_code)]
271
272
273 if func.func_defaults:
274 contents.append(',(' + ','.join(map(_object_contents,func.func_defaults)) + ')')
275 else:
276 contents.append(',()')
277
278
279 try:
280 closure = func.func_closure or []
281 except AttributeError:
282
283 closure = []
284
285
286 try:
287 xxx = [_object_contents(x.cell_contents) for x in closure]
288 except AttributeError:
289 xxx = []
290 contents.append(',(' + ','.join(xxx) + ')')
291
292 return ''.join(contents)
293
294
296
297
298
299 a1 = Action(act1)
300 a2 = Action(act2)
301 if a1 is None:
302 return a2
303 if a2 is None:
304 return a1
305 if isinstance(a1, ListAction):
306 if isinstance(a2, ListAction):
307 return ListAction(a1.list + a2.list)
308 else:
309 return ListAction(a1.list + [ a2 ])
310 else:
311 if isinstance(a2, ListAction):
312 return ListAction([ a1 ] + a2.list)
313 else:
314 return ListAction([ a1, a2 ])
315
317 """This converts any arguments after the action argument into
318 their equivalent keywords and adds them to the kw argument.
319 """
320 v = kw.get('varlist', ())
321
322 if is_String(v): v = (v,)
323 kw['varlist'] = tuple(v)
324 if args:
325
326 cmdstrfunc = args[0]
327 if cmdstrfunc is None or is_String(cmdstrfunc):
328 kw['cmdstr'] = cmdstrfunc
329 elif callable(cmdstrfunc):
330 kw['strfunction'] = cmdstrfunc
331 else:
332 raise SCons.Errors.UserError(
333 'Invalid command display variable type. '
334 'You must either pass a string or a callback which '
335 'accepts (target, source, env) as parameters.')
336 if len(args) > 1:
337 kw['varlist'] = tuple(SCons.Util.flatten(args[1:])) + kw['varlist']
338 if kw.get('strfunction', _null) is not _null \
339 and kw.get('cmdstr', _null) is not _null:
340 raise SCons.Errors.UserError(
341 'Cannot have both strfunction and cmdstr args to Action()')
342
344 """This is the actual "implementation" for the
345 Action factory method, below. This handles the
346 fact that passing lists to Action() itself has
347 different semantics than passing lists as elements
348 of lists.
349
350 The former will create a ListAction, the latter
351 will create a CommandAction by converting the inner
352 list elements to strings."""
353
354 if isinstance(act, ActionBase):
355 return act
356
357 if is_String(act):
358 var=SCons.Util.get_environment_var(act)
359 if var:
360
361
362
363
364
365
366 return LazyAction(var, kw)
367 commands = str(act).split('\n')
368 if len(commands) == 1:
369 return CommandAction(commands[0], **kw)
370
371
372 return _do_create_list_action(commands, kw)
373
374 if is_List(act):
375 return CommandAction(act, **kw)
376
377 if callable(act):
378 try:
379 gen = kw['generator']
380 del kw['generator']
381 except KeyError:
382 gen = 0
383 if gen:
384 action_type = CommandGeneratorAction
385 else:
386 action_type = FunctionAction
387 return action_type(act, kw)
388
389
390 if isinstance(act, int) or isinstance(act, float):
391 raise TypeError("Don't know how to create an Action from a number (%s)"%act)
392
393 return None
394
396 """A factory for list actions. Convert the input list into Actions
397 and then wrap them in a ListAction."""
398 acts = []
399 for a in act:
400 aa = _do_create_action(a, kw)
401 if aa is not None: acts.append(aa)
402 if not acts:
403 return ListAction([])
404 elif len(acts) == 1:
405 return acts[0]
406 else:
407 return ListAction(acts)
408
410 """A factory for action objects."""
411
412 _do_create_keywords(args, kw)
413 if is_List(act):
414 return _do_create_list_action(act, kw)
415 return _do_create_action(act, kw)
416
418 """Base class for all types of action objects that can be held by
419 other objects (Builders, Executors, etc.) This provides the
420 common methods for manipulating and combining those actions."""
421
423 return cmp(self.__dict__, other)
424
427
428 batch_key = no_batch_key
429
432
433 - def get_contents(self, target, source, env):
434 result = [ self.get_presig(target, source, env) ]
435
436
437
438 vl = self.get_varlist(target, source, env)
439 if is_String(vl): vl = (vl,)
440 for v in vl:
441
442 result.append(env.subst_target_source('${'+v+'}', SCons.Subst.SUBST_SIG, target, source))
443 return ''.join(result)
444
446 return _actionAppend(self, other)
447
449 return _actionAppend(other, self)
450
452
453
454
455
456
457
458 self.presub_env = env
459 lines = str(self).split('\n')
460 self.presub_env = None
461 return lines
462
463 - def get_varlist(self, target, source, env, executor=None):
465
467 """
468 Returns the type of targets ($TARGETS, $CHANGED_TARGETS) used
469 by this action.
470 """
471 return self.targets
472
474 """Base class for actions that create output objects."""
475 - def __init__(self, cmdstr=_null, strfunction=_null, varlist=(),
476 presub=_null, chdir=None, exitstatfunc=None,
477 batch_key=None, targets='$TARGETS',
478 **kw):
479 self.cmdstr = cmdstr
480 if strfunction is not _null:
481 if strfunction is None:
482 self.cmdstr = None
483 else:
484 self.strfunction = strfunction
485 self.varlist = varlist
486 self.presub = presub
487 self.chdir = chdir
488 if not exitstatfunc:
489 exitstatfunc = default_exitstatfunc
490 self.exitstatfunc = exitstatfunc
491
492 self.targets = targets
493
494 if batch_key:
495 if not callable(batch_key):
496
497
498
499
500 def default_batch_key(self, env, target, source):
501 return (id(self), id(env))
502 batch_key = default_batch_key
503 SCons.Util.AddMethod(self, batch_key, 'batch_key')
504
506
507
508
509
510
511
512
513
514 try:
515 sys.stdout.write(unicode(s + "\n"))
516 except UnicodeDecodeError:
517 sys.stdout.write(s + "\n")
518
526 if not is_List(target):
527 target = [target]
528 if not is_List(source):
529 source = [source]
530
531 if presub is _null:
532 presub = self.presub
533 if presub is _null:
534 presub = print_actions_presub
535 if exitstatfunc is _null: exitstatfunc = self.exitstatfunc
536 if show is _null: show = print_actions
537 if execute is _null: execute = execute_actions
538 if chdir is _null: chdir = self.chdir
539 save_cwd = None
540 if chdir:
541 save_cwd = os.getcwd()
542 try:
543 chdir = str(chdir.get_abspath())
544 except AttributeError:
545 if not is_String(chdir):
546 if executor:
547 chdir = str(executor.batches[0].targets[0].dir)
548 else:
549 chdir = str(target[0].dir)
550 if presub:
551 if executor:
552 target = executor.get_all_targets()
553 source = executor.get_all_sources()
554 t = ' and '.join(map(str, target))
555 l = '\n '.join(self.presub_lines(env))
556 out = u"Building %s with action:\n %s\n" % (t, l)
557 sys.stdout.write(out)
558 cmd = None
559 if show and self.strfunction:
560 if executor:
561 target = executor.get_all_targets()
562 source = executor.get_all_sources()
563 try:
564 cmd = self.strfunction(target, source, env, executor)
565 except TypeError:
566 cmd = self.strfunction(target, source, env)
567 if cmd:
568 if chdir:
569 cmd = ('os.chdir(%s)\n' % repr(chdir)) + cmd
570 try:
571 get = env.get
572 except AttributeError:
573 print_func = self.print_cmd_line
574 else:
575 print_func = get('PRINT_CMD_LINE_FUNC')
576 if not print_func:
577 print_func = self.print_cmd_line
578 print_func(cmd, target, source, env)
579 stat = 0
580 if execute:
581 if chdir:
582 os.chdir(chdir)
583 try:
584 stat = self.execute(target, source, env, executor=executor)
585 if isinstance(stat, SCons.Errors.BuildError):
586 s = exitstatfunc(stat.status)
587 if s:
588 stat.status = s
589 else:
590 stat = s
591 else:
592 stat = exitstatfunc(stat)
593 finally:
594 if save_cwd:
595 os.chdir(save_cwd)
596 if cmd and save_cwd:
597 print_func('os.chdir(%s)' % repr(save_cwd), target, source, env)
598
599 return stat
600
601
603 """Takes a list of command line arguments and returns a pretty
604 representation for printing."""
605 cl = []
606 for arg in map(str, cmd_list):
607 if ' ' in arg or '\t' in arg:
608 arg = '"' + arg + '"'
609 cl.append(arg)
610 return ' '.join(cl)
611
612
613
614
615
616
617 default_ENV = None
632
633
634
635
636
637 -def _subproc(scons_env, cmd, error = 'ignore', **kw):
638 """Do common setup for a subprocess.Popen() call"""
639
640 io = kw.get('stdin')
641 if is_String(io) and io == 'devnull':
642 kw['stdin'] = open(os.devnull)
643 io = kw.get('stdout')
644 if is_String(io) and io == 'devnull':
645 kw['stdout'] = open(os.devnull, 'w')
646 io = kw.get('stderr')
647 if is_String(io) and io == 'devnull':
648 kw['stderr'] = open(os.devnull, 'w')
649
650
651 ENV = kw.get('env', None)
652 if ENV is None: ENV = get_default_ENV(scons_env)
653
654
655 new_env = {}
656 for key, value in ENV.items():
657 if is_List(value):
658
659
660
661 value = SCons.Util.flatten_sequence(value)
662 new_env[key] = os.pathsep.join(map(str, value))
663 else:
664
665
666
667
668
669
670 new_env[key] = str(value)
671 kw['env'] = new_env
672
673 try:
674 return subprocess.Popen(cmd, **kw)
675 except EnvironmentError, e:
676 if error == 'raise': raise
677
678 class dummyPopen(object):
679 def __init__(self, e): self.exception = e
680 def communicate(self,input=None): return ('','')
681 def wait(self): return -self.exception.errno
682 stdin = None
683 class f(object):
684 def read(self): return ''
685 def readline(self): return ''
686 def __iter__(self): return iter(())
687 stdout = stderr = f()
688 return dummyPopen(e)
689
691 """Class for command-execution actions."""
693
694
695
696
697
698
699
700
701
702 if SCons.Debug.track_instances: logInstanceCreation(self, 'Action.CommandAction')
703
704 _ActionAction.__init__(self, **kw)
705 if is_List(cmd):
706 if list(filter(is_List, cmd)):
707 raise TypeError("CommandAction should be given only " \
708 "a single command")
709 self.cmd_list = cmd
710
712 if is_List(self.cmd_list):
713 return ' '.join(map(str, self.cmd_list))
714 return str(self.cmd_list)
715
716 - def process(self, target, source, env, executor=None):
736
737 - def strfunction(self, target, source, env, executor=None):
738 if self.cmdstr is None:
739 return None
740 if self.cmdstr is not _null:
741 from SCons.Subst import SUBST_RAW
742 if executor:
743 c = env.subst(self.cmdstr, SUBST_RAW, executor=executor)
744 else:
745 c = env.subst(self.cmdstr, SUBST_RAW, target, source)
746 if c:
747 return c
748 cmd_list, ignore, silent = self.process(target, source, env, executor)
749 if silent:
750 return ''
751 return _string_from_cmd_list(cmd_list[0])
752
753 - def execute(self, target, source, env, executor=None):
754 """Execute a command action.
755
756 This will handle lists of commands as well as individual commands,
757 because construction variable substitution may turn a single
758 "command" into a list. This means that this class can actually
759 handle lists of commands, even though that's not how we use it
760 externally.
761 """
762 escape_list = SCons.Subst.escape_list
763 flatten_sequence = SCons.Util.flatten_sequence
764
765 try:
766 shell = env['SHELL']
767 except KeyError:
768 raise SCons.Errors.UserError('Missing SHELL construction variable.')
769
770 try:
771 spawn = env['SPAWN']
772 except KeyError:
773 raise SCons.Errors.UserError('Missing SPAWN construction variable.')
774 else:
775 if is_String(spawn):
776 spawn = env.subst(spawn, raw=1, conv=lambda x: x)
777
778 escape = env.get('ESCAPE', lambda x: x)
779
780 ENV = get_default_ENV(env)
781
782
783 for key, value in ENV.items():
784 if not is_String(value):
785 if is_List(value):
786
787
788
789 value = flatten_sequence(value)
790 ENV[key] = os.pathsep.join(map(str, value))
791 else:
792
793
794
795
796 ENV[key] = str(value)
797
798 if executor:
799 target = executor.get_all_targets()
800 source = executor.get_all_sources()
801 cmd_list, ignore, silent = self.process(target, list(map(rfile, source)), env, executor)
802
803
804 for cmd_line in filter(len, cmd_list):
805
806 cmd_line = escape_list(cmd_line, escape)
807 result = spawn(shell, escape, cmd_line[0], cmd_line, ENV)
808 if not ignore and result:
809 msg = "Error %s" % result
810 return SCons.Errors.BuildError(errstr=msg,
811 status=result,
812 action=self,
813 command=cmd_line)
814 return 0
815
816 - def get_presig(self, target, source, env, executor=None):
817 """Return the signature contents of this action's command line.
818
819 This strips $(-$) and everything in between the string,
820 since those parts don't affect signatures.
821 """
822 from SCons.Subst import SUBST_SIG
823 cmd = self.cmd_list
824 if is_List(cmd):
825 cmd = ' '.join(map(str, cmd))
826 else:
827 cmd = str(cmd)
828 if executor:
829 return env.subst_target_source(cmd, SUBST_SIG, executor=executor)
830 else:
831 return env.subst_target_source(cmd, SUBST_SIG, target, source)
832
855
857 """Class for command-generator actions."""
864
865 - def _generate(self, target, source, env, for_signature, executor=None):
882
884 try:
885 env = self.presub_env
886 except AttributeError:
887 env = None
888 if env is None:
889 env = SCons.Defaults.DefaultEnvironment()
890 act = self._generate([], [], env, 1)
891 return str(act)
892
895
896 - def genstring(self, target, source, env, executor=None):
898
901 act = self._generate(target, source, env, 0, executor)
902 if act is None:
903 raise SCons.Errors.UserError("While building `%s': "
904 "Cannot deduce file extension from source files: %s"
905 % (repr(list(map(str, target))), repr(list(map(str, source)))))
906 return act(target, source, env, exitstatfunc, presub,
907 show, execute, chdir, executor)
908
909 - def get_presig(self, target, source, env, executor=None):
910 """Return the signature contents of this action's command line.
911
912 This strips $(-$) and everything in between the string,
913 since those parts don't affect signatures.
914 """
915 return self._generate(target, source, env, 1, executor).get_presig(target, source, env)
916
919
920 - def get_varlist(self, target, source, env, executor=None):
922
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945 -class LazyAction(CommandGeneratorAction, CommandAction):
946
953
959
961 if env:
962 c = env.get(self.var, '')
963 else:
964 c = ''
965 gen_cmd = Action(c, **self.gen_kw)
966 if not gen_cmd:
967 raise SCons.Errors.UserError("$%s value %s cannot be used to create an Action." % (self.var, repr(c)))
968 return gen_cmd
969
970 - def _generate(self, target, source, env, for_signature, executor=None):
972
973 - def __call__(self, target, source, env, *args, **kw):
976
980
981 - def get_varlist(self, target, source, env, executor=None):
984
985
987 """Class for Python function actions."""
988
990 if SCons.Debug.track_instances: logInstanceCreation(self, 'Action.FunctionAction')
991
992 self.execfunction = execfunction
993 try:
994 self.funccontents = _callable_contents(execfunction)
995 except AttributeError:
996 try:
997
998 self.gc = execfunction.get_contents
999 except AttributeError:
1000
1001 self.funccontents = _object_contents(execfunction)
1002
1003 _ActionAction.__init__(self, **kw)
1004
1006 try:
1007 return self.execfunction.__name__
1008 except AttributeError:
1009 try:
1010 return self.execfunction.__class__.__name__
1011 except AttributeError:
1012 return "unknown_python_function"
1013
1014 - def strfunction(self, target, source, env, executor=None):
1034 return '[' + ", ".join(map(quote, a)) + ']'
1035 try:
1036 strfunc = self.execfunction.strfunction
1037 except AttributeError:
1038 pass
1039 else:
1040 if strfunc is None:
1041 return None
1042 if callable(strfunc):
1043 return strfunc(target, source, env)
1044 name = self.function_name()
1045 tstr = array(target)
1046 sstr = array(source)
1047 return "%s(%s, %s)" % (name, tstr, sstr)
1048
1050 name = self.function_name()
1051 if name == 'ActionCaller':
1052 return str(self.execfunction)
1053 return "%s(target, source, env)" % name
1054
1055 - def execute(self, target, source, env, executor=None):
1056 exc_info = (None,None,None)
1057 try:
1058 if executor:
1059 target = executor.get_all_targets()
1060 source = executor.get_all_sources()
1061 rsources = list(map(rfile, source))
1062 try:
1063 result = self.execfunction(target=target, source=rsources, env=env)
1064 except KeyboardInterrupt, e:
1065 raise
1066 except SystemExit, e:
1067 raise
1068 except Exception, e:
1069 result = e
1070 exc_info = sys.exc_info()
1071
1072 if result:
1073 result = SCons.Errors.convert_to_BuildError(result, exc_info)
1074 result.node=target
1075 result.action=self
1076 try:
1077 result.command=self.strfunction(target, source, env, executor)
1078 except TypeError:
1079 result.command=self.strfunction(target, source, env)
1080
1081
1082
1083
1084
1085
1086
1087 if (exc_info[1] and
1088 not isinstance(exc_info[1],EnvironmentError)):
1089 raise result
1090
1091 return result
1092 finally:
1093
1094
1095
1096 del exc_info
1097
1098
1100 """Return the signature contents of this callable action."""
1101 try:
1102 return self.gc(target, source, env)
1103 except AttributeError:
1104 return self.funccontents
1105
1108
1110 """Class for lists of other actions."""
1117 self.list = list(map(list_of_actions, actionlist))
1118
1119
1120 self.varlist = ()
1121 self.targets = '$TARGETS'
1122
1124 return '\n'.join([a.genstring(target, source, env) for a in self.list])
1125
1127 return '\n'.join(map(str, self.list))
1128
1132
1134 """Return the signature contents of this action list.
1135
1136 Simple concatenation of the signatures of the elements.
1137 """
1138 return "".join([x.get_contents(target, source, env) for x in self.list])
1139
1151
1157
1158 - def get_varlist(self, target, source, env, executor=None):
1164
1166 """A class for delaying calling an Action function with specific
1167 (positional and keyword) arguments until the Action is actually
1168 executed.
1169
1170 This class looks to the rest of the world like a normal Action object,
1171 but what it's really doing is hanging on to the arguments until we
1172 have a target, source and env to use for the expansion.
1173 """
1175 self.parent = parent
1176 self.args = args
1177 self.kw = kw
1178
1179 - def get_contents(self, target, source, env):
1180 actfunc = self.parent.actfunc
1181 try:
1182
1183 contents = str(actfunc.func_code.co_code)
1184 except AttributeError:
1185
1186 try:
1187 contents = str(actfunc.__call__.im_func.func_code.co_code)
1188 except AttributeError:
1189
1190
1191 contents = str(actfunc)
1192 contents = remove_set_lineno_codes(contents)
1193 return contents
1194
1195 - def subst(self, s, target, source, env):
1196
1197
1198 if is_List(s):
1199 result = []
1200 for elem in s:
1201 result.append(self.subst(elem, target, source, env))
1202 return self.parent.convert(result)
1203
1204
1205
1206
1207 if s == '$__env__':
1208 return env
1209 elif is_String(s):
1210 return env.subst(s, 1, target, source)
1211 return self.parent.convert(s)
1212
1214 return [self.subst(x, target, source, env) for x in self.args]
1215
1216 - def subst_kw(self, target, source, env):
1217 kw = {}
1218 for key in self.kw.keys():
1219 kw[key] = self.subst(self.kw[key], target, source, env)
1220 return kw
1221
1222 - def __call__(self, target, source, env, executor=None):
1223 args = self.subst_args(target, source, env)
1224 kw = self.subst_kw(target, source, env)
1225 return self.parent.actfunc(*args, **kw)
1226
1228 args = self.subst_args(target, source, env)
1229 kw = self.subst_kw(target, source, env)
1230 return self.parent.strfunc(*args, **kw)
1231
1233 return self.parent.strfunc(*self.args, **self.kw)
1234
1236 """A factory class that will wrap up an arbitrary function
1237 as an SCons-executable Action object.
1238
1239 The real heavy lifting here is done by the ActionCaller class.
1240 We just collect the (positional and keyword) arguments that we're
1241 called with and give them to the ActionCaller object we create,
1242 so it can hang onto them until it needs them.
1243 """
1244 - def __init__(self, actfunc, strfunc, convert=lambda x: x):
1245 self.actfunc = actfunc
1246 self.strfunc = strfunc
1247 self.convert = convert
1248
1253
1254
1255
1256
1257
1258
1259