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 TODO(?): Change this to always return ascii/bytes and not unicode (or py3 strings)
64
65 strfunction()
66 Returns a substituted string representation of the Action.
67 This is used by the _ActionAction.show() command to display the
68 command/function that will be executed to generate the target(s).
69
70 There is a related independent ActionCaller class that looks like a
71 regular Action, and which serves as a wrapper for arbitrary functions
72 that we want to let the user specify the arguments to now, but actually
73 execute later (when an out-of-date check determines that it's needed to
74 be executed, for example). Objects of this class are returned by an
75 ActionFactory class that provides a __call__() method as a convenient
76 way for wrapping up the functions.
77
78 """
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101 __revision__ = "src/engine/SCons/Action.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan"
102
103 import os
104 import pickle
105 import re
106 import sys
107 import subprocess
108 import itertools
109 import inspect
110 from collections import OrderedDict
111
112 import SCons.Debug
113 from SCons.Debug import logInstanceCreation
114 import SCons.Errors
115 import SCons.Util
116 import SCons.Subst
117
118
119 from SCons.Util import is_String, is_List
120
123
124 print_actions = 1
125 execute_actions = 1
126 print_actions_presub = 0
127
128
129
130
131
132
133
134 ACTION_SIGNATURE_PICKLE_PROTOCOL = 1
135
136
138 try:
139 return n.rfile()
140 except AttributeError:
141 return n
142
143
146
147 strip_quotes = re.compile('^[\'"](.*)[\'"]$')
148
149
151 """Return the signature contents of a callable Python object.
152 """
153 try:
154
155 return _function_contents(obj.__func__)
156
157 except AttributeError:
158 try:
159
160 return _function_contents(obj.__call__.__func__)
161
162 except AttributeError:
163 try:
164
165 return _code_contents(obj)
166
167 except AttributeError:
168
169 return _function_contents(obj)
170
171
173 """Return the signature contents of any Python object.
174
175 We have to handle the case where object contains a code object
176 since it can be pickled directly.
177 """
178 try:
179
180 return _function_contents(obj.__func__)
181
182 except AttributeError:
183 try:
184
185 return _function_contents(obj.__call__.__func__)
186
187 except AttributeError:
188 try:
189
190 return _code_contents(obj)
191
192 except AttributeError:
193 try:
194
195 return _function_contents(obj)
196
197 except AttributeError as ae:
198
199 try:
200 return _object_instance_content(obj)
201
202
203
204 except (pickle.PicklingError, TypeError, AttributeError) as ex:
205
206
207
208
209
210 return bytearray(repr(obj), 'utf-8')
211
212
213 -def _code_contents(code, docstring=None):
214 r"""Return the signature contents of a code object.
215
216 By providing direct access to the code object of the
217 function, Python makes this extremely easy. Hooray!
218
219 Unfortunately, older versions of Python include line
220 number indications in the compiled byte code. Boo!
221 So we remove the line number byte codes to prevent
222 recompilations from moving a Python function.
223
224 See:
225 - https://docs.python.org/2/library/inspect.html
226 - http://python-reference.readthedocs.io/en/latest/docs/code/index.html
227
228 For info on what each co\_ variable provides
229
230 The signature is as follows (should be byte/chars):
231 co_argcount, len(co_varnames), len(co_cellvars), len(co_freevars),
232 ( comma separated signature for each object in co_consts ),
233 ( comma separated signature for each object in co_names ),
234 ( The bytecode with line number bytecodes removed from co_code )
235
236 co_argcount - Returns the number of positional arguments (including arguments with default values).
237 co_varnames - Returns a tuple containing the names of the local variables (starting with the argument names).
238 co_cellvars - Returns a tuple containing the names of local variables that are referenced by nested functions.
239 co_freevars - Returns a tuple containing the names of free variables. (?)
240 co_consts - Returns a tuple containing the literals used by the bytecode.
241 co_names - Returns a tuple containing the names used by the bytecode.
242 co_code - Returns a string representing the sequence of bytecode instructions.
243
244 """
245
246
247
248
249
250 contents = bytearray("{}, {}".format(code.co_argcount, len(code.co_varnames)), 'utf-8')
251
252 contents.extend(b", ")
253 contents.extend(bytearray(str(len(code.co_cellvars)), 'utf-8'))
254 contents.extend(b", ")
255 contents.extend(bytearray(str(len(code.co_freevars)), 'utf-8'))
256
257
258
259
260
261 z = [_object_contents(cc) for cc in code.co_consts if cc != docstring]
262 contents.extend(b',(')
263 contents.extend(bytearray(',', 'utf-8').join(z))
264 contents.extend(b')')
265
266
267
268
269
270 z= [bytearray(_object_contents(cc)) for cc in code.co_names]
271 contents.extend(b',(')
272 contents.extend(bytearray(',','utf-8').join(z))
273 contents.extend(b')')
274
275
276 contents.extend(b',(')
277 contents.extend(code.co_code)
278 contents.extend(b')')
279
280 return contents
281
282
284 """
285 The signature is as follows (should be byte/chars):
286 < _code_contents (see above) from func.__code__ >
287 ,( comma separated _object_contents for function argument defaults)
288 ,( comma separated _object_contents for any closure contents )
289
290
291 See also: https://docs.python.org/3/reference/datamodel.html
292 - func.__code__ - The code object representing the compiled function body.
293 - func.__defaults__ - A tuple containing default argument values for those arguments that have defaults, or None if no arguments have a default value
294 - func.__closure__ - None or a tuple of cells that contain bindings for the function's free variables.
295
296 :Returns:
297 Signature contents of a function. (in bytes)
298 """
299
300 contents = [_code_contents(func.__code__, func.__doc__)]
301
302
303 if func.__defaults__:
304
305 function_defaults_contents = [_object_contents(cc) for cc in func.__defaults__]
306
307 defaults = bytearray(b',(')
308 defaults.extend(bytearray(b',').join(function_defaults_contents))
309 defaults.extend(b')')
310
311 contents.append(defaults)
312 else:
313 contents.append(b',()')
314
315
316 closure = func.__closure__ or []
317
318 try:
319 closure_contents = [_object_contents(x.cell_contents) for x in closure]
320 except AttributeError:
321 closure_contents = []
322
323 contents.append(b',(')
324 contents.append(bytearray(b',').join(closure_contents))
325 contents.append(b')')
326
327 retval = bytearray(b'').join(contents)
328 return retval
329
330
332 """
333 Returns consistant content for a action class or an instance thereof
334
335 :Parameters:
336 - `obj` Should be either and action class or an instance thereof
337
338 :Returns:
339 bytearray or bytes representing the obj suitable for generating a signature from.
340 """
341 retval = bytearray()
342
343 if obj is None:
344 return b'N.'
345
346 if isinstance(obj, SCons.Util.BaseStringTypes):
347 return SCons.Util.to_bytes(obj)
348
349 inst_class = obj.__class__
350 inst_class_name = bytearray(obj.__class__.__name__,'utf-8')
351 inst_class_module = bytearray(obj.__class__.__module__,'utf-8')
352 inst_class_hierarchy = bytearray(repr(inspect.getclasstree([obj.__class__,])),'utf-8')
353
354
355 properties = [(p, getattr(obj, p, "None")) for p in dir(obj) if not (p[:2] == '__' or inspect.ismethod(getattr(obj, p)) or inspect.isbuiltin(getattr(obj,p))) ]
356 properties.sort()
357 properties_str = ','.join(["%s=%s"%(p[0],p[1]) for p in properties])
358 properties_bytes = bytearray(properties_str,'utf-8')
359
360 methods = [p for p in dir(obj) if inspect.ismethod(getattr(obj, p))]
361 methods.sort()
362
363 method_contents = []
364 for m in methods:
365
366 v = _function_contents(getattr(obj, m))
367
368 method_contents.append(v)
369
370 retval = bytearray(b'{')
371 retval.extend(inst_class_name)
372 retval.extend(b":")
373 retval.extend(inst_class_module)
374 retval.extend(b'}[[')
375 retval.extend(inst_class_hierarchy)
376 retval.extend(b']]{{')
377 retval.extend(bytearray(b",").join(method_contents))
378 retval.extend(b"}}{{{")
379 retval.extend(properties_bytes)
380 retval.extend(b'}}}')
381 return retval
382
383
384
385
386
387
388
389
391
392
393
394 a1 = Action(act1)
395 a2 = Action(act2)
396 if a1 is None:
397 return a2
398 if a2 is None:
399 return a1
400 if isinstance(a1, ListAction):
401 if isinstance(a2, ListAction):
402 return ListAction(a1.list + a2.list)
403 else:
404 return ListAction(a1.list + [ a2 ])
405 else:
406 if isinstance(a2, ListAction):
407 return ListAction([ a1 ] + a2.list)
408 else:
409 return ListAction([ a1, a2 ])
410
411
413 """This converts any arguments after the action argument into
414 their equivalent keywords and adds them to the kw argument.
415 """
416 v = kw.get('varlist', ())
417
418 if is_String(v): v = (v,)
419 kw['varlist'] = tuple(v)
420 if args:
421
422 cmdstrfunc = args[0]
423 if cmdstrfunc is None or is_String(cmdstrfunc):
424 kw['cmdstr'] = cmdstrfunc
425 elif callable(cmdstrfunc):
426 kw['strfunction'] = cmdstrfunc
427 else:
428 raise SCons.Errors.UserError(
429 'Invalid command display variable type. '
430 'You must either pass a string or a callback which '
431 'accepts (target, source, env) as parameters.')
432 if len(args) > 1:
433 kw['varlist'] = tuple(SCons.Util.flatten(args[1:])) + kw['varlist']
434 if kw.get('strfunction', _null) is not _null \
435 and kw.get('cmdstr', _null) is not _null:
436 raise SCons.Errors.UserError(
437 'Cannot have both strfunction and cmdstr args to Action()')
438
439
441 """This is the actual "implementation" for the
442 Action factory method, below. This handles the
443 fact that passing lists to Action() itself has
444 different semantics than passing lists as elements
445 of lists.
446
447 The former will create a ListAction, the latter
448 will create a CommandAction by converting the inner
449 list elements to strings."""
450
451 if isinstance(act, ActionBase):
452 return act
453
454 if is_String(act):
455 var=SCons.Util.get_environment_var(act)
456 if var:
457
458
459
460
461
462
463 return LazyAction(var, kw)
464 commands = str(act).split('\n')
465 if len(commands) == 1:
466 return CommandAction(commands[0], **kw)
467
468
469 return _do_create_list_action(commands, kw)
470
471 if is_List(act):
472 return CommandAction(act, **kw)
473
474 if callable(act):
475 try:
476 gen = kw['generator']
477 del kw['generator']
478 except KeyError:
479 gen = 0
480 if gen:
481 action_type = CommandGeneratorAction
482 else:
483 action_type = FunctionAction
484 return action_type(act, kw)
485
486
487 if isinstance(act, int) or isinstance(act, float):
488 raise TypeError("Don't know how to create an Action from a number (%s)"%act)
489
490 return None
491
492
494 """A factory for list actions. Convert the input list into Actions
495 and then wrap them in a ListAction."""
496 acts = []
497 for a in act:
498 aa = _do_create_action(a, kw)
499 if aa is not None: acts.append(aa)
500 if not acts:
501 return ListAction([])
502 elif len(acts) == 1:
503 return acts[0]
504 else:
505 return ListAction(acts)
506
507
509 """A factory for action objects."""
510
511 _do_create_keywords(args, kw)
512 if is_List(act):
513 return _do_create_list_action(act, kw)
514 return _do_create_action(act, kw)
515
516
518 """Base class for all types of action objects that can be held by
519 other objects (Builders, Executors, etc.) This provides the
520 common methods for manipulating and combining those actions."""
521
523 return self.__dict__ == other
524
527
528 batch_key = no_batch_key
529
532
533 - def get_contents(self, target, source, env):
534 result = self.get_presig(target, source, env)
535
536 if not isinstance(result,(bytes, bytearray)):
537 result = bytearray(result, 'utf-8')
538 else:
539
540
541
542 result = bytearray(result)
543
544
545
546
547
548
549 vl = self.get_varlist(target, source, env)
550 if is_String(vl): vl = (vl,)
551 for v in vl:
552
553 if isinstance(result, bytearray):
554 result.extend(SCons.Util.to_bytes(env.subst_target_source('${'+v+'}', SCons.Subst.SUBST_SIG, target, source)))
555 else:
556 raise Exception("WE SHOULD NEVER GET HERE result should be bytearray not:%s"%type(result))
557
558
559
560 if isinstance(result, (bytes,bytearray)):
561 return result
562 else:
563 raise Exception("WE SHOULD NEVER GET HERE - #2 result should be bytearray not:%s" % type(result))
564
565
567 return _actionAppend(self, other)
568
570 return _actionAppend(other, self)
571
573
574
575
576
577
578
579 self.presub_env = env
580 lines = str(self).split('\n')
581 self.presub_env = None
582 return lines
583
584 - def get_varlist(self, target, source, env, executor=None):
586
588 """
589 Returns the type of targets ($TARGETS, $CHANGED_TARGETS) used
590 by this action.
591 """
592 return self.targets
593
594
596 """Base class for actions that create output objects."""
597 - def __init__(self, cmdstr=_null, strfunction=_null, varlist=(),
598 presub=_null, chdir=None, exitstatfunc=None,
599 batch_key=None, targets='$TARGETS',
600 **kw):
601 self.cmdstr = cmdstr
602 if strfunction is not _null:
603 if strfunction is None:
604 self.cmdstr = None
605 else:
606 self.strfunction = strfunction
607 self.varlist = varlist
608 self.presub = presub
609 self.chdir = chdir
610 if not exitstatfunc:
611 exitstatfunc = default_exitstatfunc
612 self.exitstatfunc = exitstatfunc
613
614 self.targets = targets
615
616 if batch_key:
617 if not callable(batch_key):
618
619
620
621
622 def default_batch_key(self, env, target, source):
623 return (id(self), id(env))
624 batch_key = default_batch_key
625 SCons.Util.AddMethod(self, batch_key, 'batch_key')
626
628 """
629 In python 3, and in some of our tests, sys.stdout is
630 a String io object, and it takes unicode strings only
631 In other cases it's a regular Python 2.x file object
632 which takes strings (bytes), and if you pass those a
633 unicode object they try to decode with 'ascii' codec
634 which fails if the cmd line has any hi-bit-set chars.
635 This code assumes s is a regular string, but should
636 work if it's unicode too.
637 """
638 try:
639 sys.stdout.write(s + u"\n")
640 except UnicodeDecodeError:
641 sys.stdout.write(s + "\n")
642
650 if not is_List(target):
651 target = [target]
652 if not is_List(source):
653 source = [source]
654
655 if presub is _null:
656 presub = self.presub
657 if presub is _null:
658 presub = print_actions_presub
659 if exitstatfunc is _null: exitstatfunc = self.exitstatfunc
660 if show is _null: show = print_actions
661 if execute is _null: execute = execute_actions
662 if chdir is _null: chdir = self.chdir
663 save_cwd = None
664 if chdir:
665 save_cwd = os.getcwd()
666 try:
667 chdir = str(chdir.get_abspath())
668 except AttributeError:
669 if not is_String(chdir):
670 if executor:
671 chdir = str(executor.batches[0].targets[0].dir)
672 else:
673 chdir = str(target[0].dir)
674 if presub:
675 if executor:
676 target = executor.get_all_targets()
677 source = executor.get_all_sources()
678 t = ' and '.join(map(str, target))
679 l = '\n '.join(self.presub_lines(env))
680 out = u"Building %s with action:\n %s\n" % (t, l)
681 sys.stdout.write(out)
682 cmd = None
683 if show and self.strfunction:
684 if executor:
685 target = executor.get_all_targets()
686 source = executor.get_all_sources()
687 try:
688 cmd = self.strfunction(target, source, env, executor)
689 except TypeError:
690 cmd = self.strfunction(target, source, env)
691 if cmd:
692 if chdir:
693 cmd = ('os.chdir(%s)\n' % repr(chdir)) + cmd
694 try:
695 get = env.get
696 except AttributeError:
697 print_func = self.print_cmd_line
698 else:
699 print_func = get('PRINT_CMD_LINE_FUNC')
700 if not print_func:
701 print_func = self.print_cmd_line
702 print_func(cmd, target, source, env)
703 stat = 0
704 if execute:
705 if chdir:
706 os.chdir(chdir)
707 try:
708 stat = self.execute(target, source, env, executor=executor)
709 if isinstance(stat, SCons.Errors.BuildError):
710 s = exitstatfunc(stat.status)
711 if s:
712 stat.status = s
713 else:
714 stat = s
715 else:
716 stat = exitstatfunc(stat)
717 finally:
718 if save_cwd:
719 os.chdir(save_cwd)
720 if cmd and save_cwd:
721 print_func('os.chdir(%s)' % repr(save_cwd), target, source, env)
722
723 return stat
724
725
727 """Takes a list of command line arguments and returns a pretty
728 representation for printing."""
729 cl = []
730 for arg in map(str, cmd_list):
731 if ' ' in arg or '\t' in arg:
732 arg = '"' + arg + '"'
733 cl.append(arg)
734 return ' '.join(cl)
735
736 default_ENV = None
737
738
740 """
741 A fiddlin' little function that has an 'import SCons.Environment' which
742 can't be moved to the top level without creating an import loop. Since
743 this import creates a local variable named 'SCons', it blocks access to
744 the global variable, so we move it here to prevent complaints about local
745 variables being used uninitialized.
746 """
747 global default_ENV
748 try:
749 return env['ENV']
750 except KeyError:
751 if not default_ENV:
752 import SCons.Environment
753
754
755
756
757
758 default_ENV = SCons.Environment.Environment()['ENV']
759 return default_ENV
760
761
762 -def _subproc(scons_env, cmd, error = 'ignore', **kw):
763 """Do common setup for a subprocess.Popen() call
764
765 This function is still in draft mode. We're going to need something like
766 it in the long run as more and more places use subprocess, but I'm sure
767 it'll have to be tweaked to get the full desired functionality.
768 one special arg (so far?), 'error', to tell what to do with exceptions.
769 """
770
771
772
773
774 try:
775 from subprocess import DEVNULL
776 except ImportError:
777 DEVNULL = None
778
779 for stream in 'stdin', 'stdout', 'stderr':
780 io = kw.get(stream)
781 if is_String(io) and io == 'devnull':
782 if DEVNULL:
783 kw[stream] = DEVNULL
784 else:
785 kw[stream] = open(os.devnull, "r+")
786
787
788 ENV = kw.get('env', None)
789 if ENV is None: ENV = get_default_ENV(scons_env)
790
791
792 new_env = {}
793 for key, value in ENV.items():
794 if is_List(value):
795
796
797
798 value = SCons.Util.flatten_sequence(value)
799 new_env[key] = os.pathsep.join(map(str, value))
800 else:
801
802
803
804
805
806
807 new_env[key] = str(value)
808 kw['env'] = new_env
809
810 try:
811 pobj = subprocess.Popen(cmd, **kw)
812 except EnvironmentError as e:
813 if error == 'raise': raise
814
815 class dummyPopen(object):
816 def __init__(self, e): self.exception = e
817 def communicate(self, input=None): return ('', '')
818 def wait(self): return -self.exception.errno
819 stdin = None
820 class f(object):
821 def read(self): return ''
822 def readline(self): return ''
823 def __iter__(self): return iter(())
824 stdout = stderr = f()
825 pobj = dummyPopen(e)
826 finally:
827
828 for k, v in kw.items():
829 if inspect.ismethod(getattr(v, 'close', None)):
830 v.close()
831
832 return pobj
833
834
836 """Class for command-execution actions."""
838
839
840
841
842
843
844
845
846
847 if SCons.Debug.track_instances: logInstanceCreation(self, 'Action.CommandAction')
848
849 _ActionAction.__init__(self, **kw)
850 if is_List(cmd):
851 if [c for c in cmd if is_List(c)]:
852 raise TypeError("CommandAction should be given only "
853 "a single command")
854 self.cmd_list = cmd
855
857 if is_List(self.cmd_list):
858 return ' '.join(map(str, self.cmd_list))
859 return str(self.cmd_list)
860
861 - def process(self, target, source, env, executor=None):
881
882 - def strfunction(self, target, source, env, executor=None):
883 if self.cmdstr is None:
884 return None
885 if self.cmdstr is not _null:
886 from SCons.Subst import SUBST_RAW
887 if executor:
888 c = env.subst(self.cmdstr, SUBST_RAW, executor=executor)
889 else:
890 c = env.subst(self.cmdstr, SUBST_RAW, target, source)
891 if c:
892 return c
893 cmd_list, ignore, silent = self.process(target, source, env, executor)
894 if silent:
895 return ''
896 return _string_from_cmd_list(cmd_list[0])
897
898 - def execute(self, target, source, env, executor=None):
899 """Execute a command action.
900
901 This will handle lists of commands as well as individual commands,
902 because construction variable substitution may turn a single
903 "command" into a list. This means that this class can actually
904 handle lists of commands, even though that's not how we use it
905 externally.
906 """
907 escape_list = SCons.Subst.escape_list
908 flatten_sequence = SCons.Util.flatten_sequence
909
910 try:
911 shell = env['SHELL']
912 except KeyError:
913 raise SCons.Errors.UserError('Missing SHELL construction variable.')
914
915 try:
916 spawn = env['SPAWN']
917 except KeyError:
918 raise SCons.Errors.UserError('Missing SPAWN construction variable.')
919 else:
920 if is_String(spawn):
921 spawn = env.subst(spawn, raw=1, conv=lambda x: x)
922
923 escape = env.get('ESCAPE', lambda x: x)
924
925 ENV = get_default_ENV(env)
926
927
928 for key, value in ENV.items():
929 if not is_String(value):
930 if is_List(value):
931
932
933
934 value = flatten_sequence(value)
935 ENV[key] = os.pathsep.join(map(str, value))
936 else:
937
938
939
940
941 ENV[key] = str(value)
942
943 if executor:
944 target = executor.get_all_targets()
945 source = executor.get_all_sources()
946 cmd_list, ignore, silent = self.process(target, list(map(rfile, source)), env, executor)
947
948
949 for cmd_line in filter(len, cmd_list):
950
951 cmd_line = escape_list(cmd_line, escape)
952 result = spawn(shell, escape, cmd_line[0], cmd_line, ENV)
953 if not ignore and result:
954 msg = "Error %s" % result
955 return SCons.Errors.BuildError(errstr=msg,
956 status=result,
957 action=self,
958 command=cmd_line)
959 return 0
960
961 - def get_presig(self, target, source, env, executor=None):
962 """Return the signature contents of this action's command line.
963
964 This strips $(-$) and everything in between the string,
965 since those parts don't affect signatures.
966 """
967 from SCons.Subst import SUBST_SIG
968 cmd = self.cmd_list
969 if is_List(cmd):
970 cmd = ' '.join(map(str, cmd))
971 else:
972 cmd = str(cmd)
973 if executor:
974 return env.subst_target_source(cmd, SUBST_SIG, executor=executor)
975 else:
976 return env.subst_target_source(cmd, SUBST_SIG, target, source)
977
1000
1001
1003 """Class for command-generator actions."""
1010
1011 - def _generate(self, target, source, env, for_signature, executor=None):
1028
1030 try:
1031 env = self.presub_env
1032 except AttributeError:
1033 env = None
1034 if env is None:
1035 env = SCons.Defaults.DefaultEnvironment()
1036 act = self._generate([], [], env, 1)
1037 return str(act)
1038
1041
1042 - def genstring(self, target, source, env, executor=None):
1044
1047 act = self._generate(target, source, env, 0, executor)
1048 if act is None:
1049 raise SCons.Errors.UserError("While building `%s': "
1050 "Cannot deduce file extension from source files: %s"
1051 % (repr(list(map(str, target))), repr(list(map(str, source)))))
1052 return act(target, source, env, exitstatfunc, presub,
1053 show, execute, chdir, executor)
1054
1055 - def get_presig(self, target, source, env, executor=None):
1056 """Return the signature contents of this action's command line.
1057
1058 This strips $(-$) and everything in between the string,
1059 since those parts don't affect signatures.
1060 """
1061 return self._generate(target, source, env, 1, executor).get_presig(target, source, env)
1062
1065
1066 - def get_varlist(self, target, source, env, executor=None):
1068
1071
1072
1073 -class LazyAction(CommandGeneratorAction, CommandAction):
1074 """
1075 A LazyAction is a kind of hybrid generator and command action for
1076 strings of the form "$VAR". These strings normally expand to other
1077 strings (think "$CCCOM" to "$CC -c -o $TARGET $SOURCE"), but we also
1078 want to be able to replace them with functions in the construction
1079 environment. Consequently, we want lazy evaluation and creation of
1080 an Action in the case of the function, but that's overkill in the more
1081 normal case of expansion to other strings.
1082
1083 So we do this with a subclass that's both a generator *and*
1084 a command action. The overridden methods all do a quick check
1085 of the construction variable, and if it's a string we just call
1086 the corresponding CommandAction method to do the heavy lifting.
1087 If not, then we call the same-named CommandGeneratorAction method.
1088 The CommandGeneratorAction methods work by using the overridden
1089 _generate() method, that is, our own way of handling "generation" of
1090 an action based on what's in the construction variable.
1091 """
1092
1098
1104
1106 if env:
1107 c = env.get(self.var, '')
1108 else:
1109 c = ''
1110 gen_cmd = Action(c, **self.gen_kw)
1111 if not gen_cmd:
1112 raise SCons.Errors.UserError("$%s value %s cannot be used to create an Action." % (self.var, repr(c)))
1113 return gen_cmd
1114
1115 - def _generate(self, target, source, env, for_signature, executor=None):
1117
1118 - def __call__(self, target, source, env, *args, **kw):
1121
1125
1126 - def get_varlist(self, target, source, env, executor=None):
1129
1130
1132 """Class for Python function actions."""
1133
1135 if SCons.Debug.track_instances: logInstanceCreation(self, 'Action.FunctionAction')
1136
1137 self.execfunction = execfunction
1138 try:
1139 self.funccontents = _callable_contents(execfunction)
1140 except AttributeError:
1141 try:
1142
1143 self.gc = execfunction.get_contents
1144 except AttributeError:
1145
1146 self.funccontents = _object_contents(execfunction)
1147
1148 _ActionAction.__init__(self, **kw)
1149
1151 try:
1152 return self.execfunction.__name__
1153 except AttributeError:
1154 try:
1155 return self.execfunction.__class__.__name__
1156 except AttributeError:
1157 return "unknown_python_function"
1158
1159 - def strfunction(self, target, source, env, executor=None):
1180 return '[' + ", ".join(map(quote, a)) + ']'
1181 try:
1182 strfunc = self.execfunction.strfunction
1183 except AttributeError:
1184 pass
1185 else:
1186 if strfunc is None:
1187 return None
1188 if callable(strfunc):
1189 return strfunc(target, source, env)
1190 name = self.function_name()
1191 tstr = array(target)
1192 sstr = array(source)
1193 return "%s(%s, %s)" % (name, tstr, sstr)
1194
1196 name = self.function_name()
1197 if name == 'ActionCaller':
1198 return str(self.execfunction)
1199 return "%s(target, source, env)" % name
1200
1201 - def execute(self, target, source, env, executor=None):
1202 exc_info = (None,None,None)
1203 try:
1204 if executor:
1205 target = executor.get_all_targets()
1206 source = executor.get_all_sources()
1207 rsources = list(map(rfile, source))
1208 try:
1209 result = self.execfunction(target=target, source=rsources, env=env)
1210 except KeyboardInterrupt as e:
1211 raise
1212 except SystemExit as e:
1213 raise
1214 except Exception as e:
1215 result = e
1216 exc_info = sys.exc_info()
1217
1218 if result:
1219 result = SCons.Errors.convert_to_BuildError(result, exc_info)
1220 result.node=target
1221 result.action=self
1222 try:
1223 result.command=self.strfunction(target, source, env, executor)
1224 except TypeError:
1225 result.command=self.strfunction(target, source, env)
1226
1227
1228
1229
1230
1231
1232
1233 if (exc_info[1] and
1234 not isinstance(exc_info[1],EnvironmentError)):
1235 raise result
1236
1237 return result
1238 finally:
1239
1240
1241
1242 del exc_info
1243
1245 """Return the signature contents of this callable action."""
1246 try:
1247 return self.gc(target, source, env)
1248 except AttributeError:
1249 return self.funccontents
1250
1253
1255 """Class for lists of other actions."""
1262 self.list = list(map(list_of_actions, actionlist))
1263
1264
1265 self.varlist = ()
1266 self.targets = '$TARGETS'
1267
1269 return '\n'.join([a.genstring(target, source, env) for a in self.list])
1270
1272 return '\n'.join(map(str, self.list))
1273
1277
1279 """Return the signature contents of this action list.
1280
1281 Simple concatenation of the signatures of the elements.
1282 """
1283 return b"".join([bytes(x.get_contents(target, source, env)) for x in self.list])
1284
1296
1302
1303 - def get_varlist(self, target, source, env, executor=None):
1309
1310
1312 """A class for delaying calling an Action function with specific
1313 (positional and keyword) arguments until the Action is actually
1314 executed.
1315
1316 This class looks to the rest of the world like a normal Action object,
1317 but what it's really doing is hanging on to the arguments until we
1318 have a target, source and env to use for the expansion.
1319 """
1321 self.parent = parent
1322 self.args = args
1323 self.kw = kw
1324
1325 - def get_contents(self, target, source, env):
1326 actfunc = self.parent.actfunc
1327 try:
1328
1329 contents = actfunc.__code__.co_code
1330 except AttributeError:
1331
1332 try:
1333 contents = actfunc.__call__.__func__.__code__.co_code
1334 except AttributeError:
1335
1336
1337 contents = repr(actfunc)
1338
1339 return contents
1340
1341 - def subst(self, s, target, source, env):
1342
1343
1344 if is_List(s):
1345 result = []
1346 for elem in s:
1347 result.append(self.subst(elem, target, source, env))
1348 return self.parent.convert(result)
1349
1350
1351
1352
1353 if s == '$__env__':
1354 return env
1355 elif is_String(s):
1356 return env.subst(s, 1, target, source)
1357 return self.parent.convert(s)
1358
1360 return [self.subst(x, target, source, env) for x in self.args]
1361
1362 - def subst_kw(self, target, source, env):
1363 kw = {}
1364 for key in list(self.kw.keys()):
1365 kw[key] = self.subst(self.kw[key], target, source, env)
1366 return kw
1367
1368 - def __call__(self, target, source, env, executor=None):
1369 args = self.subst_args(target, source, env)
1370 kw = self.subst_kw(target, source, env)
1371 return self.parent.actfunc(*args, **kw)
1372
1374 args = self.subst_args(target, source, env)
1375 kw = self.subst_kw(target, source, env)
1376 return self.parent.strfunc(*args, **kw)
1377
1379 return self.parent.strfunc(*self.args, **self.kw)
1380
1381
1383 """A factory class that will wrap up an arbitrary function
1384 as an SCons-executable Action object.
1385
1386 The real heavy lifting here is done by the ActionCaller class.
1387 We just collect the (positional and keyword) arguments that we're
1388 called with and give them to the ActionCaller object we create,
1389 so it can hang onto them until it needs them.
1390 """
1391 - def __init__(self, actfunc, strfunc, convert=lambda x: x):
1392 self.actfunc = actfunc
1393 self.strfunc = strfunc
1394 self.convert = convert
1395
1400
1401
1402
1403
1404
1405
1406