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.
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.
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.
16
The heavy lifting is handled by subclasses for the different types of
17
actions we might execute:
20
CommandGeneratorAction
24
The subclasses supply the following public interface methods used by
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.
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.
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.
46
Subclasses also supply the following methods for internal use within
50
Returns a string approximation of the Action; no variable
51
substitution is performed.
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.
61
Fetches the "contents" of a subclass for signature calculation.
62
The varlist is added to this to produce the Action's contents.
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).
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.
79
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation
81
# Permission is hereby granted, free of charge, to any person obtaining
82
# a copy of this software and associated documentation files (the
83
# "Software"), to deal in the Software without restriction, including
84
# without limitation the rights to use, copy, modify, merge, publish,
85
# distribute, sublicense, and/or sell copies of the Software, and to
86
# permit persons to whom the Software is furnished to do so, subject to
87
# the following conditions:
89
# The above copyright notice and this permission notice shall be included
90
# in all copies or substantial portions of the Software.
92
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
93
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
94
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
95
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
96
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
97
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
98
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
100
__revision__ = "src/engine/SCons/Action.py 2013/03/03 09:48:35 garyo"
106
# compat layer imports "cPickle" for us if it's available.
112
from SCons.Debug import logInstanceCreation
114
import SCons.Executor
118
# we use these a lot, so try to optimize them
119
is_String = SCons.Util.is_String
120
is_List = SCons.Util.is_List
127
print_actions_presub = 0
132
except AttributeError:
135
def default_exitstatfunc(s):
139
SET_LINENO = dis.SET_LINENO
140
HAVE_ARGUMENT = dis.HAVE_ARGUMENT
141
except AttributeError:
142
remove_set_lineno_codes = lambda x: x
144
def remove_set_lineno_codes(code):
151
if op >= HAVE_ARGUMENT:
153
result.append(code[i:i+3])
158
return ''.join(result)
160
strip_quotes = re.compile('^[\'"](.*)[\'"]$')
163
def _callable_contents(obj):
164
"""Return the signature contents of a callable Python object.
167
# Test if obj is a method.
168
return _function_contents(obj.im_func)
170
except AttributeError:
172
# Test if obj is a callable object.
173
return _function_contents(obj.__call__.im_func)
175
except AttributeError:
177
# Test if obj is a code object.
178
return _code_contents(obj)
180
except AttributeError:
181
# Test if obj is a function object.
182
return _function_contents(obj)
185
def _object_contents(obj):
186
"""Return the signature contents of any Python object.
188
We have to handle the case where object contains a code object
189
since it can be pickled directly.
192
# Test if obj is a method.
193
return _function_contents(obj.im_func)
195
except AttributeError:
197
# Test if obj is a callable object.
198
return _function_contents(obj.__call__.im_func)
200
except AttributeError:
202
# Test if obj is a code object.
203
return _code_contents(obj)
205
except AttributeError:
207
# Test if obj is a function object.
208
return _function_contents(obj)
210
except AttributeError:
211
# Should be a pickable Python object.
213
return pickle.dumps(obj)
214
except (pickle.PicklingError, TypeError):
215
# This is weird, but it seems that nested classes
216
# are unpickable. The Python docs say it should
217
# always be a PicklingError, but some Python
218
# versions seem to return TypeError. Just do
223
def _code_contents(code):
224
"""Return the signature contents of a code object.
226
By providing direct access to the code object of the
227
function, Python makes this extremely easy. Hooray!
229
Unfortunately, older versions of Python include line
230
number indications in the compiled byte code. Boo!
231
So we remove the line number byte codes to prevent
232
recompilations from moving a Python function.
237
# The code contents depends on the number of local variables
238
# but not their actual names.
239
contents.append("%s,%s" % (code.co_argcount, len(code.co_varnames)))
241
contents.append(",%s,%s" % (len(code.co_cellvars), len(code.co_freevars)))
242
except AttributeError:
243
# Older versions of Python do not support closures.
244
contents.append(",0,0")
246
# The code contents depends on any constants accessed by the
247
# function. Note that we have to call _object_contents on each
248
# constants because the code object of nested functions can
249
# show-up among the constants.
251
# Note that we also always ignore the first entry of co_consts
252
# which contains the function doc string. We assume that the
253
# function does not access its doc string.
254
contents.append(',(' + ','.join(map(_object_contents,code.co_consts[1:])) + ')')
256
# The code contents depends on the variable names used to
257
# accessed global variable, as changing the variable name changes
258
# the variable actually accessed and therefore changes the
260
contents.append(',(' + ','.join(map(_object_contents,code.co_names)) + ')')
263
# The code contents depends on its actual code!!!
264
contents.append(',(' + str(remove_set_lineno_codes(code.co_code)) + ')')
266
return ''.join(contents)
269
def _function_contents(func):
270
"""Return the signature contents of a function."""
272
contents = [_code_contents(func.func_code)]
274
# The function contents depends on the value of defaults arguments
275
if func.func_defaults:
276
contents.append(',(' + ','.join(map(_object_contents,func.func_defaults)) + ')')
278
contents.append(',()')
280
# The function contents depends on the closure captured cell values.
282
closure = func.func_closure or []
283
except AttributeError:
284
# Older versions of Python do not support closures.
287
#xxx = [_object_contents(x.cell_contents) for x in closure]
289
xxx = [_object_contents(x.cell_contents) for x in closure]
290
except AttributeError:
292
contents.append(',(' + ','.join(xxx) + ')')
294
return ''.join(contents)
297
def _actionAppend(act1, act2):
298
# This function knows how to slap two actions together.
299
# Mainly, it handles ListActions by concatenating into
300
# a single ListAction.
307
if isinstance(a1, ListAction):
308
if isinstance(a2, ListAction):
309
return ListAction(a1.list + a2.list)
311
return ListAction(a1.list + [ a2 ])
313
if isinstance(a2, ListAction):
314
return ListAction([ a1 ] + a2.list)
316
return ListAction([ a1, a2 ])
318
def _do_create_keywords(args, kw):
319
"""This converts any arguments after the action argument into
320
their equivalent keywords and adds them to the kw argument.
322
v = kw.get('varlist', ())
323
# prevent varlist="FOO" from being interpreted as ['F', 'O', 'O']
324
if is_String(v): v = (v,)
325
kw['varlist'] = tuple(v)
327
# turn positional args into equivalent keywords
329
if cmdstrfunc is None or is_String(cmdstrfunc):
330
kw['cmdstr'] = cmdstrfunc
331
elif callable(cmdstrfunc):
332
kw['strfunction'] = cmdstrfunc
334
raise SCons.Errors.UserError(
335
'Invalid command display variable type. '
336
'You must either pass a string or a callback which '
337
'accepts (target, source, env) as parameters.')
339
kw['varlist'] = args[1:] + kw['varlist']
340
if kw.get('strfunction', _null) is not _null \
341
and kw.get('cmdstr', _null) is not _null:
342
raise SCons.Errors.UserError(
343
'Cannot have both strfunction and cmdstr args to Action()')
345
def _do_create_action(act, kw):
346
"""This is the actual "implementation" for the
347
Action factory method, below. This handles the
348
fact that passing lists to Action() itself has
349
different semantics than passing lists as elements
352
The former will create a ListAction, the latter
353
will create a CommandAction by converting the inner
354
list elements to strings."""
356
if isinstance(act, ActionBase):
360
return CommandAction(act, **kw)
364
gen = kw['generator']
369
action_type = CommandGeneratorAction
371
action_type = FunctionAction
372
return action_type(act, kw)
375
var=SCons.Util.get_environment_var(act)
377
# This looks like a string that is purely an Environment
378
# variable reference, like "$FOO" or "${FOO}". We do
379
# something special here...we lazily evaluate the contents
380
# of that Environment variable, so a user could put something
381
# like a function or a CommandGenerator in that variable
382
# instead of a string.
383
return LazyAction(var, kw)
384
commands = str(act).split('\n')
385
if len(commands) == 1:
386
return CommandAction(commands[0], **kw)
387
# The list of string commands may include a LazyAction, so we
388
# reprocess them via _do_create_list_action.
389
return _do_create_list_action(commands, kw)
390
# Catch a common error case with a nice message:
391
if isinstance(act, int) or isinstance(act, float):
392
raise TypeError("Don't know how to create an Action from a number (%s)"%act)
393
# Else fail silently (???)
396
def _do_create_list_action(act, kw):
397
"""A factory for list actions. Convert the input list into Actions
398
and then wrap them in a ListAction."""
401
aa = _do_create_action(a, kw)
402
if aa is not None: acts.append(aa)
404
return ListAction([])
408
return ListAction(acts)
410
def Action(act, *args, **kw):
411
"""A factory for action objects."""
412
# Really simple: the _do_create_* routines do the heavy lifting.
413
_do_create_keywords(args, kw)
415
return _do_create_list_action(act, kw)
416
return _do_create_action(act, kw)
418
class ActionBase(object):
419
"""Base class for all types of action objects that can be held by
420
other objects (Builders, Executors, etc.) This provides the
421
common methods for manipulating and combining those actions."""
423
def __cmp__(self, other):
424
return cmp(self.__dict__, other)
426
def no_batch_key(self, env, target, source):
429
batch_key = no_batch_key
431
def genstring(self, target, source, env):
434
def get_contents(self, target, source, env):
435
result = [ self.get_presig(target, source, env) ]
436
# This should never happen, as the Action() factory should wrap
437
# the varlist, but just in case an action is created directly,
438
# we duplicate this check here.
439
vl = self.get_varlist(target, source, env)
440
if is_String(vl): vl = (vl,)
442
result.append(env.subst('${'+v+'}'))
443
return ''.join(result)
445
def __add__(self, other):
446
return _actionAppend(self, other)
448
def __radd__(self, other):
449
return _actionAppend(other, self)
451
def presub_lines(self, env):
452
# CommandGeneratorAction needs a real environment
453
# in order to return the proper string here, since
454
# it may call LazyAction, which looks up a key
455
# in that env. So we temporarily remember the env here,
456
# and CommandGeneratorAction will use this env
457
# when it calls its _generate method.
458
self.presub_env = env
459
lines = str(self).split('\n')
460
self.presub_env = None # don't need this any more
463
def get_varlist(self, target, source, env, executor=None):
466
def get_targets(self, env, executor):
468
Returns the type of targets ($TARGETS, $CHANGED_TARGETS) used
473
class _ActionAction(ActionBase):
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',
480
if strfunction is not _null:
481
if strfunction is None:
484
self.strfunction = strfunction
485
self.varlist = varlist
489
exitstatfunc = default_exitstatfunc
490
self.exitstatfunc = exitstatfunc
492
self.targets = targets
495
if not callable(batch_key):
496
# They have set batch_key, but not to their own
497
# callable. The default behavior here will batch
498
# *all* targets+sources using this action, separated
499
# for each construction environment.
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')
505
def print_cmd_line(self, s, target, source, env):
506
# In python 3, and in some of our tests, sys.stdout is
507
# a String io object, and it takes unicode strings only
508
# In other cases it's a regular Python 2.x file object
509
# which takes strings (bytes), and if you pass those a
510
# unicode object they try to decode with 'ascii' codec
511
# which fails if the cmd line has any hi-bit-set chars.
512
# This code assumes s is a regular string, but should
513
# work if it's unicode too.
515
sys.stdout.write(unicode(s + "\n"))
516
except UnicodeDecodeError:
517
sys.stdout.write(s + "\n")
519
def __call__(self, target, source, env,
526
if not is_List(target):
528
if not is_List(source):
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
541
save_cwd = os.getcwd()
543
chdir = str(chdir.abspath)
544
except AttributeError:
545
if not is_String(chdir):
547
chdir = str(executor.batches[0].targets[0].dir)
549
chdir = str(target[0].dir)
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)
559
if show and self.strfunction:
561
target = executor.get_all_targets()
562
source = executor.get_all_sources()
564
cmd = self.strfunction(target, source, env, executor)
566
cmd = self.strfunction(target, source, env)
569
cmd = ('os.chdir(%s)\n' % repr(chdir)) + cmd
572
except AttributeError:
573
print_func = self.print_cmd_line
575
print_func = get('PRINT_CMD_LINE_FUNC')
577
print_func = self.print_cmd_line
578
print_func(cmd, target, source, env)
584
stat = self.execute(target, source, env, executor=executor)
585
if isinstance(stat, SCons.Errors.BuildError):
586
s = exitstatfunc(stat.status)
592
stat = exitstatfunc(stat)
597
print_func('os.chdir(%s)' % repr(save_cwd), target, source, env)
602
def _string_from_cmd_list(cmd_list):
603
"""Takes a list of command line arguments and returns a pretty
604
representation for printing."""
606
for arg in map(str, cmd_list):
607
if ' ' in arg or '\t' in arg:
608
arg = '"' + arg + '"'
612
# A fiddlin' little function that has an 'import SCons.Environment' which
613
# can't be moved to the top level without creating an import loop. Since
614
# this import creates a local variable named 'SCons', it blocks access to
615
# the global variable, so we move it here to prevent complaints about local
616
# variables being used uninitialized.
618
def get_default_ENV(env):
624
import SCons.Environment
625
# This is a hideously expensive way to get a default shell
626
# environment. What it really should do is run the platform
627
# setup to get the default ENV. Fortunately, it's incredibly
628
# rare for an Environment not to have a shell environment, so
629
# we're not going to worry about it overmuch.
630
default_ENV = SCons.Environment.Environment()['ENV']
633
# This function is still in draft mode. We're going to need something like
634
# it in the long run as more and more places use subprocess, but I'm sure
635
# it'll have to be tweaked to get the full desired functionality.
636
# one special arg (so far?), 'error', to tell what to do with exceptions.
637
def _subproc(scons_env, cmd, error = 'ignore', **kw):
638
"""Do common setup for a subprocess.Popen() call"""
639
# allow std{in,out,err} to be "'devnull'"
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')
650
# Figure out what shell environment to use
651
ENV = kw.get('env', None)
652
if ENV is None: ENV = get_default_ENV(scons_env)
654
# Ensure that the ENV values are all strings:
656
for key, value in ENV.items():
658
# If the value is a list, then we assume it is a path list,
659
# because that's a pretty common list-like value to stick
660
# in an environment variable:
661
value = SCons.Util.flatten_sequence(value)
662
new_env[key] = os.pathsep.join(map(str, value))
664
# It's either a string or something else. If it's a string,
665
# we still want to call str() because it might be a *Unicode*
666
# string, which makes subprocess.Popen() gag. If it isn't a
667
# string or a list, then we just coerce it to a string, which
668
# is the proper way to handle Dir and File instances and will
669
# produce something reasonable for just about everything else:
670
new_env[key] = str(value)
674
return subprocess.Popen(cmd, **kw)
675
except EnvironmentError, e:
676
if error == 'raise': raise
677
# return a dummy Popen instance that only returns error
678
class dummyPopen(object):
679
def __init__(self, e): self.exception = e
680
def communicate(self): return ('','')
681
def wait(self): return -self.exception.errno
684
def read(self): return ''
685
def readline(self): return ''
686
stdout = stderr = f()
689
class CommandAction(_ActionAction):
690
"""Class for command-execution actions."""
691
def __init__(self, cmd, **kw):
692
# Cmd can actually be a list or a single item; if it's a
693
# single item it should be the command string to execute; if a
694
# list then it should be the words of the command string to
695
# execute. Only a single command should be executed by this
696
# object; lists of commands should be handled by embedding
697
# these objects in a ListAction object (which the Action()
698
# factory above does). cmd will be passed to
699
# Environment.subst_list() for substituting environment
701
if __debug__: logInstanceCreation(self, 'Action.CommandAction')
703
_ActionAction.__init__(self, **kw)
705
if list(filter(is_List, cmd)):
706
raise TypeError("CommandAction should be given only " \
711
if is_List(self.cmd_list):
712
return ' '.join(map(str, self.cmd_list))
713
return str(self.cmd_list)
715
def process(self, target, source, env, executor=None):
717
result = env.subst_list(self.cmd_list, 0, executor=executor)
719
result = env.subst_list(self.cmd_list, 0, target, source)
723
try: c = result[0][0][0]
724
except IndexError: c = None
725
if c == '@': silent = 1
726
elif c == '-': ignore = 1
728
result[0][0] = result[0][0][1:]
731
result[0] = result[0][1:]
734
return result, ignore, silent
736
def strfunction(self, target, source, env, executor=None):
737
if self.cmdstr is None:
739
if self.cmdstr is not _null:
740
from SCons.Subst import SUBST_RAW
742
c = env.subst(self.cmdstr, SUBST_RAW, executor=executor)
744
c = env.subst(self.cmdstr, SUBST_RAW, target, source)
747
cmd_list, ignore, silent = self.process(target, source, env, executor)
750
return _string_from_cmd_list(cmd_list[0])
752
def execute(self, target, source, env, executor=None):
753
"""Execute a command action.
755
This will handle lists of commands as well as individual commands,
756
because construction variable substitution may turn a single
757
"command" into a list. This means that this class can actually
758
handle lists of commands, even though that's not how we use it
761
escape_list = SCons.Subst.escape_list
762
flatten_sequence = SCons.Util.flatten_sequence
767
raise SCons.Errors.UserError('Missing SHELL construction variable.')
772
raise SCons.Errors.UserError('Missing SPAWN construction variable.')
775
spawn = env.subst(spawn, raw=1, conv=lambda x: x)
777
escape = env.get('ESCAPE', lambda x: x)
779
ENV = get_default_ENV(env)
781
# Ensure that the ENV values are all strings:
782
for key, value in ENV.items():
783
if not is_String(value):
785
# If the value is a list, then we assume it is a
786
# path list, because that's a pretty common list-like
787
# value to stick in an environment variable:
788
value = flatten_sequence(value)
789
ENV[key] = os.pathsep.join(map(str, value))
791
# If it isn't a string or a list, then we just coerce
792
# it to a string, which is the proper way to handle
793
# Dir and File instances and will produce something
794
# reasonable for just about everything else:
795
ENV[key] = str(value)
798
target = executor.get_all_targets()
799
source = executor.get_all_sources()
800
cmd_list, ignore, silent = self.process(target, list(map(rfile, source)), env, executor)
802
# Use len() to filter out any "command" that's zero-length.
803
for cmd_line in filter(len, cmd_list):
804
# Escape the command line for the interpreter we are using.
805
cmd_line = escape_list(cmd_line, escape)
806
result = spawn(shell, escape, cmd_line[0], cmd_line, ENV)
807
if not ignore and result:
808
msg = "Error %s" % result
809
return SCons.Errors.BuildError(errstr=msg,
815
def get_presig(self, target, source, env, executor=None):
816
"""Return the signature contents of this action's command line.
818
This strips $(-$) and everything in between the string,
819
since those parts don't affect signatures.
821
from SCons.Subst import SUBST_SIG
824
cmd = ' '.join(map(str, cmd))
828
return env.subst_target_source(cmd, SUBST_SIG, executor=executor)
830
return env.subst_target_source(cmd, SUBST_SIG, target, source)
832
def get_implicit_deps(self, target, source, env, executor=None):
833
icd = env.get('IMPLICIT_COMMAND_DEPENDENCIES', True)
834
if is_String(icd) and icd[:1] == '$':
836
if not icd or icd in ('0', 'None'):
838
from SCons.Subst import SUBST_SIG
840
cmd_list = env.subst_list(self.cmd_list, SUBST_SIG, executor=executor)
842
cmd_list = env.subst_list(self.cmd_list, SUBST_SIG, target, source)
844
for cmd_line in cmd_list:
847
m = strip_quotes.match(d)
852
res.append(env.fs.File(d))
855
class CommandGeneratorAction(ActionBase):
856
"""Class for command-generator actions."""
857
def __init__(self, generator, kw):
858
if __debug__: logInstanceCreation(self, 'Action.CommandGeneratorAction')
859
self.generator = generator
861
self.varlist = kw.get('varlist', ())
862
self.targets = kw.get('targets', '$TARGETS')
864
def _generate(self, target, source, env, for_signature, executor=None):
865
# ensure that target is a list, to make it easier to write
866
# generator functions:
867
if not is_List(target):
871
target = executor.get_all_targets()
872
source = executor.get_all_sources()
873
ret = self.generator(target=target,
876
for_signature=for_signature)
877
gen_cmd = Action(ret, **self.gen_kw)
879
raise SCons.Errors.UserError("Object returned from command generator: %s cannot be used to create an Action." % repr(ret))
884
env = self.presub_env
885
except AttributeError:
888
env = SCons.Defaults.DefaultEnvironment()
889
act = self._generate([], [], env, 1)
892
def batch_key(self, env, target, source):
893
return self._generate(target, source, env, 1).batch_key(env, target, source)
895
def genstring(self, target, source, env, executor=None):
896
return self._generate(target, source, env, 1, executor).genstring(target, source, env)
898
def __call__(self, target, source, env, exitstatfunc=_null, presub=_null,
899
show=_null, execute=_null, chdir=_null, executor=None):
900
act = self._generate(target, source, env, 0, executor)
902
raise SCons.Errors.UserError("While building `%s': "
903
"Cannot deduce file extension from source files: %s"
904
% (repr(list(map(str, target))), repr(list(map(str, source)))))
905
return act(target, source, env, exitstatfunc, presub,
906
show, execute, chdir, executor)
908
def get_presig(self, target, source, env, executor=None):
909
"""Return the signature contents of this action's command line.
911
This strips $(-$) and everything in between the string,
912
since those parts don't affect signatures.
914
return self._generate(target, source, env, 1, executor).get_presig(target, source, env)
916
def get_implicit_deps(self, target, source, env, executor=None):
917
return self._generate(target, source, env, 1, executor).get_implicit_deps(target, source, env)
919
def get_varlist(self, target, source, env, executor=None):
920
return self._generate(target, source, env, 1, executor).get_varlist(target, source, env, executor)
922
def get_targets(self, env, executor):
923
return self._generate(None, None, env, 1, executor).get_targets(env, executor)
927
# A LazyAction is a kind of hybrid generator and command action for
928
# strings of the form "$VAR". These strings normally expand to other
929
# strings (think "$CCCOM" to "$CC -c -o $TARGET $SOURCE"), but we also
930
# want to be able to replace them with functions in the construction
931
# environment. Consequently, we want lazy evaluation and creation of
932
# an Action in the case of the function, but that's overkill in the more
933
# normal case of expansion to other strings.
935
# So we do this with a subclass that's both a generator *and*
936
# a command action. The overridden methods all do a quick check
937
# of the construction variable, and if it's a string we just call
938
# the corresponding CommandAction method to do the heavy lifting.
939
# If not, then we call the same-named CommandGeneratorAction method.
940
# The CommandGeneratorAction methods work by using the overridden
941
# _generate() method, that is, our own way of handling "generation" of
942
# an action based on what's in the construction variable.
944
class LazyAction(CommandGeneratorAction, CommandAction):
946
def __init__(self, var, kw):
947
if __debug__: logInstanceCreation(self, 'Action.LazyAction')
948
#FUTURE CommandAction.__init__(self, '${'+var+'}', **kw)
949
CommandAction.__init__(self, '${'+var+'}', **kw)
950
self.var = SCons.Util.to_String(var)
953
def get_parent_class(self, env):
954
c = env.get(self.var)
955
if is_String(c) and not '\n' in c:
957
return CommandGeneratorAction
959
def _generate_cache(self, env):
961
c = env.get(self.var, '')
964
gen_cmd = Action(c, **self.gen_kw)
966
raise SCons.Errors.UserError("$%s value %s cannot be used to create an Action." % (self.var, repr(c)))
969
def _generate(self, target, source, env, for_signature, executor=None):
970
return self._generate_cache(env)
972
def __call__(self, target, source, env, *args, **kw):
973
c = self.get_parent_class(env)
974
return c.__call__(self, target, source, env, *args, **kw)
976
def get_presig(self, target, source, env):
977
c = self.get_parent_class(env)
978
return c.get_presig(self, target, source, env)
980
def get_varlist(self, target, source, env, executor=None):
981
c = self.get_parent_class(env)
982
return c.get_varlist(self, target, source, env, executor)
985
class FunctionAction(_ActionAction):
986
"""Class for Python function actions."""
988
def __init__(self, execfunction, kw):
989
if __debug__: logInstanceCreation(self, 'Action.FunctionAction')
991
self.execfunction = execfunction
993
self.funccontents = _callable_contents(execfunction)
994
except AttributeError:
996
# See if execfunction will do the heavy lifting for us.
997
self.gc = execfunction.get_contents
998
except AttributeError:
999
# This is weird, just do the best we can.
1000
self.funccontents = _object_contents(execfunction)
1002
_ActionAction.__init__(self, **kw)
1004
def function_name(self):
1006
return self.execfunction.__name__
1007
except AttributeError:
1009
return self.execfunction.__class__.__name__
1010
except AttributeError:
1011
return "unknown_python_function"
1013
def strfunction(self, target, source, env, executor=None):
1014
if self.cmdstr is None:
1016
if self.cmdstr is not _null:
1017
from SCons.Subst import SUBST_RAW
1019
c = env.subst(self.cmdstr, SUBST_RAW, executor=executor)
1021
c = env.subst(self.cmdstr, SUBST_RAW, target, source)
1027
str_for_display = s.str_for_display
1028
except AttributeError:
1031
s = str_for_display()
1033
return '[' + ", ".join(map(quote, a)) + ']'
1035
strfunc = self.execfunction.strfunction
1036
except AttributeError:
1041
if callable(strfunc):
1042
return strfunc(target, source, env)
1043
name = self.function_name()
1044
tstr = array(target)
1045
sstr = array(source)
1046
return "%s(%s, %s)" % (name, tstr, sstr)
1049
name = self.function_name()
1050
if name == 'ActionCaller':
1051
return str(self.execfunction)
1052
return "%s(target, source, env)" % name
1054
def execute(self, target, source, env, executor=None):
1055
exc_info = (None,None,None)
1058
target = executor.get_all_targets()
1059
source = executor.get_all_sources()
1060
rsources = list(map(rfile, source))
1062
result = self.execfunction(target=target, source=rsources, env=env)
1063
except KeyboardInterrupt, e:
1065
except SystemExit, e:
1067
except Exception, e:
1069
exc_info = sys.exc_info()
1072
result = SCons.Errors.convert_to_BuildError(result, exc_info)
1076
result.command=self.strfunction(target, source, env, executor)
1078
result.command=self.strfunction(target, source, env)
1080
# FIXME: This maintains backward compatibility with respect to
1081
# which type of exceptions were returned by raising an
1082
# exception and which ones were returned by value. It would
1083
# probably be best to always return them by value here, but
1084
# some codes do not check the return value of Actions and I do
1085
# not have the time to modify them at this point.
1087
not isinstance(exc_info[1],EnvironmentError)):
1092
# Break the cycle between the traceback object and this
1093
# function stack frame. See the sys.exc_info() doc info for
1094
# more information about this issue.
1098
def get_presig(self, target, source, env):
1099
"""Return the signature contents of this callable action."""
1101
return self.gc(target, source, env)
1102
except AttributeError:
1103
return self.funccontents
1105
def get_implicit_deps(self, target, source, env):
1108
class ListAction(ActionBase):
1109
"""Class for lists of other actions."""
1110
def __init__(self, actionlist):
1111
if __debug__: logInstanceCreation(self, 'Action.ListAction')
1112
def list_of_actions(x):
1113
if isinstance(x, ActionBase):
1116
self.list = list(map(list_of_actions, actionlist))
1117
# our children will have had any varlist
1118
# applied; we don't need to do it again
1120
self.targets = '$TARGETS'
1122
def genstring(self, target, source, env):
1123
return '\n'.join([a.genstring(target, source, env) for a in self.list])
1126
return '\n'.join(map(str, self.list))
1128
def presub_lines(self, env):
1129
return SCons.Util.flatten_sequence(
1130
[a.presub_lines(env) for a in self.list])
1132
def get_presig(self, target, source, env):
1133
"""Return the signature contents of this action list.
1135
Simple concatenation of the signatures of the elements.
1137
return "".join([x.get_contents(target, source, env) for x in self.list])
1139
def __call__(self, target, source, env, exitstatfunc=_null, presub=_null,
1140
show=_null, execute=_null, chdir=_null, executor=None):
1142
target = executor.get_all_targets()
1143
source = executor.get_all_sources()
1144
for act in self.list:
1145
stat = act(target, source, env, exitstatfunc, presub,
1146
show, execute, chdir, executor)
1151
def get_implicit_deps(self, target, source, env):
1153
for act in self.list:
1154
result.extend(act.get_implicit_deps(target, source, env))
1157
def get_varlist(self, target, source, env, executor=None):
1158
result = SCons.Util.OrderedDict()
1159
for act in self.list:
1160
for var in act.get_varlist(target, source, env, executor):
1162
return list(result.keys())
1164
class ActionCaller(object):
1165
"""A class for delaying calling an Action function with specific
1166
(positional and keyword) arguments until the Action is actually
1169
This class looks to the rest of the world like a normal Action object,
1170
but what it's really doing is hanging on to the arguments until we
1171
have a target, source and env to use for the expansion.
1173
def __init__(self, parent, args, kw):
1174
self.parent = parent
1178
def get_contents(self, target, source, env):
1179
actfunc = self.parent.actfunc
1181
# "self.actfunc" is a function.
1182
contents = str(actfunc.func_code.co_code)
1183
except AttributeError:
1184
# "self.actfunc" is a callable object.
1186
contents = str(actfunc.__call__.im_func.func_code.co_code)
1187
except AttributeError:
1188
# No __call__() method, so it might be a builtin
1189
# or something like that. Do the best we can.
1190
contents = str(actfunc)
1191
contents = remove_set_lineno_codes(contents)
1194
def subst(self, s, target, source, env):
1195
# If s is a list, recursively apply subst()
1196
# to every element in the list
1200
result.append(self.subst(elem, target, source, env))
1201
return self.parent.convert(result)
1203
# Special-case hack: Let a custom function wrapped in an
1204
# ActionCaller get at the environment through which the action
1205
# was called by using this hard-coded value as a special return.
1209
return env.subst(s, 1, target, source)
1210
return self.parent.convert(s)
1212
def subst_args(self, target, source, env):
1213
return [self.subst(x, target, source, env) for x in self.args]
1215
def subst_kw(self, target, source, env):
1217
for key in self.kw.keys():
1218
kw[key] = self.subst(self.kw[key], target, source, env)
1221
def __call__(self, target, source, env, executor=None):
1222
args = self.subst_args(target, source, env)
1223
kw = self.subst_kw(target, source, env)
1224
return self.parent.actfunc(*args, **kw)
1226
def strfunction(self, target, source, env):
1227
args = self.subst_args(target, source, env)
1228
kw = self.subst_kw(target, source, env)
1229
return self.parent.strfunc(*args, **kw)
1232
return self.parent.strfunc(*self.args, **self.kw)
1234
class ActionFactory(object):
1235
"""A factory class that will wrap up an arbitrary function
1236
as an SCons-executable Action object.
1238
The real heavy lifting here is done by the ActionCaller class.
1239
We just collect the (positional and keyword) arguments that we're
1240
called with and give them to the ActionCaller object we create,
1241
so it can hang onto them until it needs them.
1243
def __init__(self, actfunc, strfunc, convert=lambda x: x):
1244
self.actfunc = actfunc
1245
self.strfunc = strfunc
1246
self.convert = convert
1248
def __call__(self, *args, **kw):
1249
ac = ActionCaller(self, args, kw)
1250
action = Action(ac, strfunction=ac.strfunction)
1255
# indent-tabs-mode:nil
1257
# vim: set expandtab tabstop=4 shiftwidth=4: