Fix Action comparison when a Builder has a suffix key of None. (Kevin Quick)
[scons.git] / src / engine / SCons / Action.py
1 """SCons.Action
2
3 This encapsulates information about executing any sort of action that
4 can build one or more target Nodes (typically files) from one or more
5 source Nodes (also typically files) given a specific Environment.
6
7 The base class here is ActionBase.  The base class supplies just a few
8 OO utility methods and some generic methods for displaying information
9 about an Action in response to the various commands that control printing.
10
11 The heavy lifting is handled by subclasses for the different types of
12 actions we might execute:
13
14     CommandAction
15     CommandGeneratorAction
16     FunctionAction
17     ListAction
18
19 The subclasses supply the following public interface methods used by
20 other modules:
21
22     __call__()
23         THE public interface, "calling" an Action object executes the
24         command or Python function.  This also takes care of printing
25         a pre-substitution command for debugging purposes.
26
27     get_contents()
28         Fetches the "contents" of an Action for signature calculation.
29         This is what the Sig/*.py subsystem uses to decide if a target
30         needs to be rebuilt because its action changed.
31
32     genstring()
33         Returns a string representation of the Action *without* command
34         substitution, but allows a CommandGeneratorAction to generate
35         the right action based on the specified target, source and env.
36         This is used by the Signature subsystem (through the Executor)
37         to compare the actions used to build a target last time and
38         this time.
39
40 Subclasses also supply the following methods for internal use within
41 this module:
42
43     __str__()
44         Returns a string representation of the Action *without* command
45         substitution.  This is used by the __call__() methods to display
46         the pre-substitution command whenever the --debug=presub option
47         is used.
48
49     strfunction()
50         Returns a substituted string representation of the Action.
51         This is used by the ActionBase.show() command to display the
52         command/function that will be executed to generate the target(s).
53
54     execute()
55         The internal method that really, truly, actually handles the
56         execution of a command or Python function.  This is used so
57         that the __call__() methods can take care of displaying any
58         pre-substitution representations, and *then* execute an action
59         without worrying about the specific Actions involved.
60
61 There is a related independent ActionCaller class that looks like a
62 regular Action, and which serves as a wrapper for arbitrary functions
63 that we want to let the user specify the arguments to now, but actually
64 execute later (when an out-of-date check determines that it's needed to
65 be executed, for example).  Objects of this class are returned by an
66 ActionFactory class that provides a __call__() method as a convenient
67 way for wrapping up the functions.
68
69 """
70
71 #
72 # __COPYRIGHT__
73 #
74 # Permission is hereby granted, free of charge, to any person obtaining
75 # a copy of this software and associated documentation files (the
76 # "Software"), to deal in the Software without restriction, including
77 # without limitation the rights to use, copy, modify, merge, publish,
78 # distribute, sublicense, and/or sell copies of the Software, and to
79 # permit persons to whom the Software is furnished to do so, subject to
80 # the following conditions:
81 #
82 # The above copyright notice and this permission notice shall be included
83 # in all copies or substantial portions of the Software.
84 #
85 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
86 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
87 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
88 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
89 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
90 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
91 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
92 #
93
94 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
95
96 import os
97 import os.path
98 import re
99 import string
100 import sys
101
102 from SCons.Debug import logInstanceCreation
103 import SCons.Errors
104 import SCons.Util
105
106 class _Null:
107     pass
108
109 _null = _Null
110
111 print_actions = 1
112 execute_actions = 1
113 print_actions_presub = 0
114
115 default_ENV = None
116
117 def rfile(n):
118     try:
119         return n.rfile()
120     except AttributeError:
121         return n
122
123 def _actionAppend(act1, act2):
124     # This function knows how to slap two actions together.
125     # Mainly, it handles ListActions by concatenating into
126     # a single ListAction.
127     a1 = Action(act1)
128     a2 = Action(act2)
129     if a1 is None or a2 is None:
130         raise TypeError, "Cannot append %s to %s" % (type(act1), type(act2))
131     if isinstance(a1, ListAction):
132         if isinstance(a2, ListAction):
133             return ListAction(a1.list + a2.list)
134         else:
135             return ListAction(a1.list + [ a2 ])
136     else:
137         if isinstance(a2, ListAction):
138             return ListAction([ a1 ] + a2.list)
139         else:
140             return ListAction([ a1, a2 ])
141
142 class CommandGenerator:
143     """
144     Wraps a command generator function so the Action() factory
145     function can tell a generator function from a function action.
146     """
147     def __init__(self, generator):
148         self.generator = generator
149
150     def __add__(self, other):
151         return _actionAppend(self, other)
152
153     def __radd__(self, other):
154         return _actionAppend(other, self)
155
156 def _do_create_action(act, *args, **kw):
157     """This is the actual "implementation" for the
158     Action factory method, below.  This handles the
159     fact that passing lists to Action() itself has
160     different semantics than passing lists as elements
161     of lists.
162
163     The former will create a ListAction, the latter
164     will create a CommandAction by converting the inner
165     list elements to strings."""
166
167     if isinstance(act, ActionBase):
168         return act
169     if SCons.Util.is_List(act):
170         return apply(CommandAction, (act,)+args, kw)
171     if isinstance(act, CommandGenerator):
172         return apply(CommandGeneratorAction, (act.generator,)+args, kw)
173     if callable(act):
174         return apply(FunctionAction, (act,)+args, kw)
175     if SCons.Util.is_String(act):
176         var=SCons.Util.get_environment_var(act)
177         if var:
178             # This looks like a string that is purely an Environment
179             # variable reference, like "$FOO" or "${FOO}".  We do
180             # something special here...we lazily evaluate the contents
181             # of that Environment variable, so a user could put something
182             # like a function or a CommandGenerator in that variable
183             # instead of a string.
184             lcg = LazyCmdGenerator(var)
185             return apply(CommandGeneratorAction, (lcg,)+args, kw)
186         commands = string.split(str(act), '\n')
187         if len(commands) == 1:
188             return apply(CommandAction, (commands[0],)+args, kw)
189         else:
190             listCmdActions = map(lambda x: CommandAction(x), commands)
191             return apply(ListAction, (listCmdActions,)+args, kw)
192     return None
193
194 def Action(act, strfunction=_null, varlist=[], presub=_null):
195     """A factory for action objects."""
196     if SCons.Util.is_List(act):
197         acts = map(lambda x, s=strfunction, v=varlist, ps=presub:
198                           _do_create_action(x, strfunction=s, varlist=v, presub=ps),
199                    act)
200         acts = filter(lambda x: not x is None, acts)
201         if len(acts) == 1:
202             return acts[0]
203         else:
204             return ListAction(acts, strfunction=strfunction, varlist=varlist, presub=presub)
205     else:
206         return _do_create_action(act, strfunction=strfunction, varlist=varlist, presub=presub)
207
208 class ActionBase:
209     """Base class for actions that create output objects."""
210     def __init__(self, strfunction=_null, presub=_null, **kw):
211         if not strfunction is _null:
212             self.strfunction = strfunction
213         if presub is _null:
214             self.presub = print_actions_presub
215         else:
216             self.presub = presub
217
218     def __cmp__(self, other):
219         return cmp(self.__dict__, other)
220
221     def print_cmd_line(self, s, target, source, env):
222         sys.stdout.write(s + "\n")
223
224     def __call__(self, target, source, env,
225                                errfunc=None,
226                                presub=_null,
227                                show=_null,
228                                execute=_null):
229         if not SCons.Util.is_List(target):
230             target = [target]
231         if not SCons.Util.is_List(source):
232             source = [source]
233         if presub is _null:  presub = self.presub
234         if show is _null:  show = print_actions
235         if execute is _null:  execute = execute_actions
236         if presub:
237             t = string.join(map(str, target), 'and')
238             l = string.join(self.presub_lines(env), '\n  ')
239             out = "Building %s with action(s):\n  %s\n" % (t, l)
240             sys.stdout.write(out)
241         if show and self.strfunction:
242             s = self.strfunction(target, source, env)
243             if s:
244                 try:
245                     get = env.get
246                 except AttributeError:
247                     print_func = self.print_cmd_line
248                 else:
249                     print_func = get('PRINT_CMD_LINE_FUNC')
250                     if not print_func:
251                         print_func = self.print_cmd_line
252                 print_func(s, target, source, env)
253         if execute:
254             stat = self.execute(target, source, env)
255             if stat and errfunc:
256                 errfunc(stat)
257             return stat
258         else:
259             return 0
260
261     def presub_lines(self, env):
262         # CommandGeneratorAction needs a real environment
263         # in order to return the proper string here, since
264         # it may call LazyCmdGenerator, which looks up a key
265         # in that env.  So we temporarily remember the env here,
266         # and CommandGeneratorAction will use this env
267         # when it calls its __generate method.
268         self.presub_env = env
269         lines = string.split(str(self), '\n')
270         self.presub_env = None      # don't need this any more
271         return lines
272
273     def genstring(self, target, source, env):
274         return str(self)
275
276     def get_actions(self):
277         return [self]
278
279     def __add__(self, other):
280         return _actionAppend(self, other)
281
282     def __radd__(self, other):
283         return _actionAppend(other, self)
284
285 def _string_from_cmd_list(cmd_list):
286     """Takes a list of command line arguments and returns a pretty
287     representation for printing."""
288     cl = []
289     for arg in map(str, cmd_list):
290         if ' ' in arg or '\t' in arg:
291             arg = '"' + arg + '"'
292         cl.append(arg)
293     return string.join(cl)
294
295 class CommandAction(ActionBase):
296     """Class for command-execution actions."""
297     def __init__(self, cmd, **kw):
298         # Cmd list can actually be a list or a single item...basically
299         # anything that we could pass in as the first arg to
300         # Environment.subst_list().
301         if __debug__: logInstanceCreation(self)
302         apply(ActionBase.__init__, (self,), kw)
303         self.cmd_list = cmd
304
305     def __str__(self):
306         return str(self.cmd_list)
307
308     def strfunction(self, target, source, env):
309         cmd_list = env.subst_list(self.cmd_list, 0, target, source)
310         return string.join(map(_string_from_cmd_list, cmd_list), "\n")
311
312     def execute(self, target, source, env):
313         """Execute a command action.
314
315         This will handle lists of commands as well as individual commands,
316         because construction variable substitution may turn a single
317         "command" into a list.  This means that this class can actually
318         handle lists of commands, even though that's not how we use it
319         externally.
320         """
321         from SCons.Util import is_String, is_List, flatten, escape_list
322
323         try:
324             shell = env['SHELL']
325         except KeyError:
326             raise SCons.Errors.UserError('Missing SHELL construction variable.')
327
328         try:
329             spawn = env['SPAWN']
330         except KeyError:
331             raise SCons.Errors.UserError('Missing SPAWN construction variable.')
332
333         escape = env.get('ESCAPE', lambda x: x)
334
335         try:
336             ENV = env['ENV']
337         except KeyError:
338             global default_ENV
339             if not default_ENV:
340                 import SCons.Environment
341                 default_ENV = SCons.Environment.Environment()['ENV']
342             ENV = default_ENV
343
344         # Ensure that the ENV values are all strings:
345         for key, value in ENV.items():
346             if not is_String(value):
347                 if is_List(value):
348                     # If the value is a list, then we assume it is a
349                     # path list, because that's a pretty common list-like
350                     # value to stick in an environment variable:
351                     value = flatten(value)
352                     ENV[key] = string.join(map(str, value), os.pathsep)
353                 else:
354                     # If it isn't a string or a list, then we just coerce
355                     # it to a string, which is the proper way to handle
356                     # Dir and File instances and will produce something
357                     # reasonable for just about everything else:
358                     ENV[key] = str(value)
359
360         cmd_list = env.subst_list(self.cmd_list, 0, target, source)
361
362         # Use len() to filter out any "command" that's zero-length.
363         for cmd_line in filter(len, cmd_list):
364             # Escape the command line for the interpreter we are using.
365             cmd_line = escape_list(cmd_line, escape)
366             result = spawn(shell, escape, cmd_line[0], cmd_line, ENV)
367             if result:
368                 return result
369         return 0
370
371     def get_contents(self, target, source, env, dict=None):
372         """Return the signature contents of this action's command line.
373
374         This strips $(-$) and everything in between the string,
375         since those parts don't affect signatures.
376         """
377         cmd = self.cmd_list
378         if SCons.Util.is_List(cmd):
379             cmd = string.join(map(str, cmd))
380         else:
381             cmd = str(cmd)
382         return env.subst_target_source(cmd, SCons.Util.SUBST_SIG, target, source, dict)
383
384 class CommandGeneratorAction(ActionBase):
385     """Class for command-generator actions."""
386     def __init__(self, generator, **kw):
387         if __debug__: logInstanceCreation(self)
388         apply(ActionBase.__init__, (self,), kw)
389         self.generator = generator
390
391     def __generate(self, target, source, env, for_signature):
392         # ensure that target is a list, to make it easier to write
393         # generator functions:
394         if not SCons.Util.is_List(target):
395             target = [target]
396
397         ret = self.generator(target=target, source=source, env=env, for_signature=for_signature)
398         gen_cmd = Action(ret)
399         if not gen_cmd:
400             raise SCons.Errors.UserError("Object returned from command generator: %s cannot be used to create an Action." % repr(ret))
401         return gen_cmd
402
403     def strfunction(self, target, source, env):
404         if not SCons.Util.is_List(source):
405             source = [source]
406         rsources = map(rfile, source)
407         act = self.__generate(target, source, env, 0)
408         if act.strfunction:
409             return act.strfunction(target, rsources, env)
410         else:
411             return None
412
413     def __str__(self):
414         try:
415             env = self.presub_env or {}
416         except AttributeError:
417             env = {}
418         act = self.__generate([], [], env, 0)
419         return str(act)
420
421     def genstring(self, target, source, env):
422         return str(self.__generate(target, source, env, 0))
423
424     def execute(self, target, source, env):
425         rsources = map(rfile, source)
426         act = self.__generate(target, source, env, 0)
427         return act.execute(target, source, env)
428
429     def get_contents(self, target, source, env, dict=None):
430         """Return the signature contents of this action's command line.
431
432         This strips $(-$) and everything in between the string,
433         since those parts don't affect signatures.
434         """
435         return self.__generate(target, source, env, 1).get_contents(target, source, env, dict=None)
436
437 class LazyCmdGenerator:
438     """This is not really an Action, although it kind of looks like one.
439     This is really a simple callable class that acts as a command
440     generator.  It holds on to a key into an Environment dictionary,
441     then waits until execution time to see what type it is, then tries
442     to create an Action out of it."""
443     def __init__(self, var):
444         if __debug__: logInstanceCreation(self)
445         self.var = SCons.Util.to_String(var)
446
447     def strfunction(self, target, source, env):
448         try:
449             return env[self.var]
450         except KeyError:
451             # The variable reference substitutes to nothing.
452             return ''
453
454     def __str__(self):
455         return 'LazyCmdGenerator: %s'%str(self.var)
456
457     def __call__(self, target, source, env, for_signature):
458         try:
459             return env[self.var]
460         except KeyError:
461             # The variable reference substitutes to nothing.
462             return ''
463
464     def __cmp__(self, other):
465         return cmp(self.__dict__, other)
466
467 class FunctionAction(ActionBase):
468     """Class for Python function actions."""
469
470     def __init__(self, execfunction, **kw):
471         if __debug__: logInstanceCreation(self)
472         self.execfunction = execfunction
473         apply(ActionBase.__init__, (self,), kw)
474         self.varlist = kw.get('varlist', [])
475
476     def function_name(self):
477         try:
478             return self.execfunction.__name__
479         except AttributeError:
480             try:
481                 return self.execfunction.__class__.__name__
482             except AttributeError:
483                 return "unknown_python_function"
484
485     def strfunction(self, target, source, env):
486         def array(a):
487             def quote(s):
488                 return '"' + str(s) + '"'
489             return '[' + string.join(map(quote, a), ", ") + ']'
490         name = self.function_name()
491         tstr = array(target)
492         sstr = array(source)
493         return "%s(%s, %s)" % (name, tstr, sstr)
494
495     def __str__(self):
496         return "%s(env, target, source)" % self.function_name()
497
498     def execute(self, target, source, env):
499         rsources = map(rfile, source)
500         return self.execfunction(target=target, source=rsources, env=env)
501
502     def get_contents(self, target, source, env, dict=None):
503         """Return the signature contents of this callable action.
504
505         By providing direct access to the code object of the
506         function, Python makes this extremely easy.  Hooray!
507         """
508         try:
509             # "self.execfunction" is a function.
510             contents = str(self.execfunction.func_code.co_code)
511         except AttributeError:
512             # "self.execfunction" is a callable object.
513             try:
514                 contents = str(self.execfunction.__call__.im_func.func_code.co_code)
515             except AttributeError:
516                 try:
517                     # See if execfunction will do the heavy lifting for us.
518                     gc = self.execfunction.get_contents
519                 except AttributeError:
520                     # This is weird, just do the best we can.
521                     contents = str(self.execfunction)
522                 else:
523                     contents = gc(target, source, env, dict)
524         return contents + env.subst(string.join(map(lambda v: '${'+v+'}',
525                                                      self.varlist)))
526
527 class ListAction(ActionBase):
528     """Class for lists of other actions."""
529     def __init__(self, list, **kw):
530         if __debug__: logInstanceCreation(self)
531         apply(ActionBase.__init__, (self,), kw)
532         self.list = map(lambda x: Action(x), list)
533
534     def get_actions(self):
535         return self.list
536
537     def __str__(self):
538         s = []
539         for l in self.list:
540             s.append(str(l))
541         return string.join(s, "\n")
542
543     def strfunction(self, target, source, env):
544         s = []
545         for l in self.list:
546             if l.strfunction:
547                 x = l.strfunction(target, source, env)
548                 if not SCons.Util.is_List(x):
549                     x = [x]
550                 s.extend(x)
551         return string.join(s, "\n")
552
553     def execute(self, target, source, env):
554         for l in self.list:
555             r = l.execute(target, source, env)
556             if r:
557                 return r
558         return 0
559
560     def get_contents(self, target, source, env, dict=None):
561         """Return the signature contents of this action list.
562
563         Simple concatenation of the signatures of the elements.
564         """
565         dict = SCons.Util.subst_dict(target, source)
566         return string.join(map(lambda x, t=target, s=source, e=env, d=dict:
567                                       x.get_contents(t, s, e, d),
568                                self.list),
569                            "")
570
571 class ActionCaller:
572     """A class for delaying calling an Action function with specific
573     (positional and keyword) arguments until the Action is actually
574     executed.
575
576     This class looks to the rest of the world like a normal Action object,
577     but what it's really doing is hanging on to the arguments until we
578     have a target, source and env to use for the expansion.
579     """
580     def __init__(self, parent, args, kw):
581         self.parent = parent
582         self.args = args
583         self.kw = kw
584     def get_contents(self, target, source, env, dict=None):
585         actfunc = self.parent.actfunc
586         try:
587             # "self.actfunc" is a function.
588             contents = str(actfunc.func_code.co_code)
589         except AttributeError:
590             # "self.actfunc" is a callable object.
591             try:
592                 contents = str(actfunc.__call__.im_func.func_code.co_code)
593             except AttributeError:
594                 # No __call__() method, so it might be a builtin
595                 # or something like that.  Do the best we can.
596                 contents = str(actfunc)
597         return contents
598     def subst_args(self, target, source, env):
599         return map(lambda x, e=env, t=target, s=source:
600                           e.subst(x, 0, t, s),
601                    self.args)
602     def subst_kw(self, target, source, env):
603         kw = {}
604         for key in self.kw.keys():
605             kw[key] = env.subst(self.kw[key], 0, target, source)
606         return kw
607     def __call__(self, target, source, env):
608         args = self.subst_args(target, source, env)
609         kw = self.subst_kw(target, source, env)
610         return apply(self.parent.actfunc, args, kw)
611     def strfunction(self, target, source, env):
612         args = self.subst_args(target, source, env)
613         kw = self.subst_kw(target, source, env)
614         return apply(self.parent.strfunc, args, kw)
615
616 class ActionFactory:
617     """A factory class that will wrap up an arbitrary function
618     as an SCons-executable Action object.
619
620     The real heavy lifting here is done by the ActionCaller class.
621     We just collect the (positional and keyword) arguments that we're
622     called with and give them to the ActionCaller object we create,
623     so it can hang onto them until it needs them.
624     """
625     def __init__(self, actfunc, strfunc):
626         self.actfunc = actfunc
627         self.strfunc = strfunc
628     def __call__(self, *args, **kw):
629         ac = ActionCaller(self, args, kw)
630         return Action(ac, strfunction=ac.strfunction)