Ant-like tasks: Chmod(), Copy(), Delete(), Mkdir(), Move(), Touch().
[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, strfunction=_null, varlist=[]):
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     elif SCons.Util.is_List(act):
170         return CommandAction(act)
171     elif isinstance(act, CommandGenerator):
172         return CommandGeneratorAction(act.generator)
173     elif callable(act):
174         return FunctionAction(act, strfunction=strfunction, varlist=varlist)
175     elif 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 but something
182             # like a function or a CommandGenerator in that variable
183             # instead of a string.
184             return CommandGeneratorAction(LazyCmdGenerator(var))
185         listCmds = map(lambda x: CommandAction(x),
186                        string.split(str(act), '\n'))
187         if len(listCmds) == 1:
188             return listCmds[0]
189         else:
190             return ListAction(listCmds)
191     else:
192         return None
193
194 def Action(act, strfunction=_null, varlist=[]):
195     """A factory for action objects."""
196     if SCons.Util.is_List(act):
197         acts = map(lambda x, s=strfunction, v=varlist:
198                           _do_create_action(x, s, v),
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)
205     else:
206         return _do_create_action(act, strfunction=strfunction, varlist=varlist)
207
208 class ActionBase:
209     """Base class for actions that create output objects."""
210     def __cmp__(self, other):
211         return cmp(self.__dict__, other.__dict__)
212
213     def show(self, s):
214         if print_actions:
215             sys.stdout.write(s + '\n')
216
217     def presub(self, target, env):
218         if print_actions_presub:
219             if not SCons.Util.is_List(target):
220                 target = [target]
221             # CommandGeneratorAction needs a real environment
222             # in order to return the proper string here, since
223             # it may call LazyCmdGenerator, which looks up a key
224             # in that env.  So we temporarily remember the env here,
225             # and CommandGeneratorAction will use this env
226             # when it calls its __generate method.
227             self.presub_env = env
228             lines = string.split(str(self), '\n')
229             self.presub_env = None      # don't need this any more
230             sys.stdout.write("Building %s with action(s):\n  %s\n"%
231                 (string.join(map(lambda x: str(x), target), ' and '),
232                  string.join(lines, '\n  ')))
233
234     def genstring(self, target, source, env):
235         return str(self)
236
237     def get_actions(self):
238         return [self]
239
240     def __add__(self, other):
241         return _actionAppend(self, other)
242
243     def __radd__(self, other):
244         return _actionAppend(other, self)
245
246 def _string_from_cmd_list(cmd_list):
247     """Takes a list of command line arguments and returns a pretty
248     representation for printing."""
249     cl = []
250     for arg in map(str, cmd_list):
251         if ' ' in arg or '\t' in arg:
252             arg = '"' + arg + '"'
253         cl.append(arg)
254     return string.join(cl)
255
256 class CommandAction(ActionBase):
257     """Class for command-execution actions."""
258     def __init__(self, cmd):
259         # Cmd list can actually be a list or a single item...basically
260         # anything that we could pass in as the first arg to
261         # Environment.subst_list().
262         if __debug__: logInstanceCreation(self)
263         self.cmd_list = cmd
264
265     def __str__(self):
266         return str(self.cmd_list)
267
268     def strfunction(self, target, source, env):
269         cmd_list = env.subst_list(self.cmd_list, 0, target, source)
270         return map(_string_from_cmd_list, cmd_list)
271
272     def _execute(self, target, source, env):
273         """Execute a command action.
274
275         This will handle lists of commands as well as individual commands,
276         because construction variable substitution may turn a single
277         "command" into a list.  This means that this class can actually
278         handle lists of commands, even though that's not how we use it
279         externally.
280         """
281         import SCons.Util
282
283         escape = env.get('ESCAPE', lambda x: x)
284
285         if env.has_key('SHELL'):
286             shell = env['SHELL']
287         else:
288             raise SCons.Errors.UserError('Missing SHELL construction variable.')
289
290         # for SConf support (by now): check, if we want to pipe the command
291         # output to somewhere else
292         if env.has_key('PIPE_BUILD'):
293             pipe_build = 1
294             if env.has_key('PSPAWN'):
295                 pspawn = env['PSPAWN']
296             else:
297                 raise SCons.Errors.UserError('Missing PSPAWN construction variable.')
298             if env.has_key('PSTDOUT'):
299                 pstdout = env['PSTDOUT']
300             else:
301                 raise SCons.Errors.UserError('Missing PSTDOUT construction variable.')
302             if env.has_key('PSTDERR'):
303                 pstderr = env['PSTDERR']
304             else:
305                 raise SCons.Errors.UserError('Missing PSTDOUT construction variable.')
306         else:
307             pipe_build = 0
308             if env.has_key('SPAWN'):
309                 spawn = env['SPAWN']
310             else:
311                 raise SCons.Errors.UserError('Missing SPAWN construction variable.')
312
313         cmd_list = env.subst_list(self.cmd_list, 0, target, source)
314         for cmd_line in cmd_list:
315             if len(cmd_line):
316                 if print_actions:
317                     self.show(_string_from_cmd_list(cmd_line))
318                 if execute_actions:
319                     try:
320                         ENV = env['ENV']
321                     except KeyError:
322                         global default_ENV
323                         if not default_ENV:
324                             import SCons.Environment
325                             default_ENV = SCons.Environment.Environment()['ENV']
326                         ENV = default_ENV
327
328                     # ensure that the ENV values are all strings:
329                     for key, value in ENV.items():
330                         if SCons.Util.is_List(value):
331                             # If the value is a list, then we assume
332                             # it is a path list, because that's a pretty
333                             # common list like value to stick in an environment
334                             # variable:
335                             ENV[key] = string.join(map(str, value), os.pathsep)
336                         elif not SCons.Util.is_String(value):
337                             # If it isn't a string or a list, then
338                             # we just coerce it to a string, which
339                             # is proper way to handle Dir and File instances
340                             # and will produce something reasonable for
341                             # just about everything else:
342                             ENV[key] = str(value)
343
344                     # Escape the command line for the command
345                     # interpreter we are using
346                     cmd_line = SCons.Util.escape_list(cmd_line, escape)
347                     if pipe_build:
348                         ret = pspawn( shell, escape, cmd_line[0], cmd_line,
349                                       ENV, pstdout, pstderr )
350                     else:
351                         ret = spawn(shell, escape, cmd_line[0], cmd_line, ENV)
352                     if ret:
353                         return ret
354         return 0
355
356     def __call__(self, target, source, env):
357         self.presub(target, env)
358         return self._execute(target, source, env)
359
360     def get_contents(self, target, source, env, dict=None):
361         """Return the signature contents of this action's command line.
362
363         This strips $(-$) and everything in between the string,
364         since those parts don't affect signatures.
365         """
366         cmd = self.cmd_list
367         if SCons.Util.is_List(cmd):
368             cmd = string.join(map(str, cmd))
369         else:
370             cmd = str(cmd)
371         return env.subst_target_source(cmd, SCons.Util.SUBST_SIG, target, source, dict)
372
373 class CommandGeneratorAction(ActionBase):
374     """Class for command-generator actions."""
375     def __init__(self, generator):
376         if __debug__: logInstanceCreation(self)
377         self.generator = generator
378
379     def __generate(self, target, source, env, for_signature):
380         # ensure that target is a list, to make it easier to write
381         # generator functions:
382         if not SCons.Util.is_List(target):
383             target = [target]
384
385         ret = self.generator(target=target, source=source, env=env, for_signature=for_signature)
386         gen_cmd = Action(ret)
387         if not gen_cmd:
388             raise SCons.Errors.UserError("Object returned from command generator: %s cannot be used to create an Action." % repr(ret))
389         return gen_cmd
390
391     def strfunction(self, target, source, env):
392         if not SCons.Util.is_List(source):
393             source = [source]
394         rsources = map(rfile, source)
395         act = self.__generate(target, source, env, 0)
396         return act.strfunction(target, rsources, env)
397
398     def __str__(self):
399         try:
400             env = self.presub_env or {}
401         except AttributeError:
402             env = {}
403         act = self.__generate([], [], env, 0)
404         return str(act)
405
406     def genstring(self, target, source, env):
407         return str(self.__generate(target, source, env, 0))
408
409     def _execute(self, target, source, env):
410         if not SCons.Util.is_List(source):
411             source = [source]
412         rsources = map(rfile, source)
413         act = self.__generate(target, source, env, 0)
414         return act._execute(target, rsources, env)
415
416     def __call__(self, target, source, env):
417         if not SCons.Util.is_List(source):
418             source = [source]
419         rsources = map(rfile, source)
420         act = self.__generate(target, source, env, 0)
421         act.presub(target, env)
422         return act._execute(target, source, env)
423
424     def get_contents(self, target, source, env, dict=None):
425         """Return the signature contents of this action's command line.
426
427         This strips $(-$) and everything in between the string,
428         since those parts don't affect signatures.
429         """
430         return self.__generate(target, source, env, 1).get_contents(target, source, env, dict=None)
431
432 class LazyCmdGenerator:
433     """This is not really an Action, although it kind of looks like one.
434     This is really a simple callable class that acts as a command
435     generator.  It holds on to a key into an Environment dictionary,
436     then waits until execution time to see what type it is, then tries
437     to create an Action out of it."""
438     def __init__(self, var):
439         if __debug__: logInstanceCreation(self)
440         self.var = SCons.Util.to_String(var)
441
442     def strfunction(self, target, source, env):
443         try:
444             return env[self.var]
445         except KeyError:
446             # The variable reference substitutes to nothing.
447             return ''
448
449     def __str__(self):
450         return 'LazyCmdGenerator: %s'%str(self.var)
451
452     def _execute(self, target, source, env, for_signature):
453         try:
454             return env[self.var]
455         except KeyError:
456             # The variable reference substitutes to nothing.
457             return ''
458
459     def __call__(self, target, source, env, for_signature):
460         return self._execute(target, source, env, for_signature)
461
462     def __cmp__(self, other):
463         return cmp(self.__dict__, other.__dict__)
464
465 class FunctionAction(ActionBase):
466     """Class for Python function actions."""
467
468     def __init__(self, execfunction, strfunction=_null, varlist=[]):
469         if __debug__: logInstanceCreation(self)
470         self.execfunction = execfunction
471         if strfunction is _null:
472             def strfunction(target, source, env, self=self):
473                 def quote(s):
474                     return '"' + str(s) + '"'
475                 def array(a, q=quote):
476                     return '[' + string.join(map(lambda x, q=q: q(x), a), ", ") + ']'
477                 name = self.function_name()
478                 tstr = len(target) == 1 and quote(target[0]) or array(target)
479                 sstr = len(source) == 1 and quote(source[0]) or array(source)
480                 return "%s(%s, %s)" % (name, tstr, sstr)
481         self.strfunction = strfunction
482         self.varlist = varlist
483
484     def function_name(self):
485         try:
486             return self.execfunction.__name__
487         except AttributeError:
488             try:
489                 return self.execfunction.__class__.__name__
490             except AttributeError:
491                 return "unknown_python_function"
492
493     def __str__(self):
494         return "%s(env, target, source)" % self.function_name()
495
496     def _execute(self, target, source, env):
497         r = 0
498         if not SCons.Util.is_List(target):
499             target = [target]
500         if not SCons.Util.is_List(source):
501             source = [source]
502         if print_actions and self.strfunction:
503             s = self.strfunction(target, source, env)
504             if s:
505                 self.show(s)
506         if execute_actions:
507             rsources = map(rfile, source)
508             r = self.execfunction(target=target, source=rsources, env=env)
509         return r
510
511     def __call__(self, target, source, env):
512         self.presub(target, env)
513         return self._execute(target, source, env)
514
515     def get_contents(self, target, source, env, dict=None):
516         """Return the signature contents of this callable action.
517
518         By providing direct access to the code object of the
519         function, Python makes this extremely easy.  Hooray!
520         """
521         try:
522             # "self.execfunction" is a function.
523             contents = str(self.execfunction.func_code.co_code)
524         except AttributeError:
525             # "self.execfunction" is a callable object.
526             try:
527                 contents = str(self.execfunction.__call__.im_func.func_code.co_code)
528             except AttributeError:
529                 try:
530                     # See if execfunction will do the heavy lifting for us.
531                     gc = self.execfunction.get_contents
532                 except AttributeError:
533                     # This is weird, just do the best we can.
534                     contents = str(self.execfunction)
535                 else:
536                     contents = gc(target, source, env, dict)
537         return contents + env.subst(string.join(map(lambda v: '${'+v+'}',
538                                                      self.varlist)))
539
540 class ListAction(ActionBase):
541     """Class for lists of other actions."""
542     def __init__(self, list):
543         if __debug__: logInstanceCreation(self)
544         self.list = map(lambda x: Action(x), list)
545
546     def get_actions(self):
547         return self.list
548
549     def __str__(self):
550         s = []
551         for l in self.list:
552             s.append(str(l))
553         return string.join(s, "\n")
554
555     def strfunction(self, target, source, env):
556         s = []
557         for l in self.list:
558             if l.strfunction:
559                 x = l.strfunction(target, source, env)
560                 if not SCons.Util.is_List(x):
561                     x = [x]
562                 s.extend(x)
563         return string.join(s, "\n")
564
565     def _execute(self, target, source, env):
566         for l in self.list:
567             r = l._execute(target, source, env)
568             if r:
569                 return r
570         return 0
571
572     def __call__(self, target, source, env):
573         self.presub(target, env)
574         return self._execute(target, source, env)
575
576     def get_contents(self, target, source, env, dict=None):
577         """Return the signature contents of this action list.
578
579         Simple concatenation of the signatures of the elements.
580         """
581         dict = SCons.Util.subst_dict(target, source)
582         return string.join(map(lambda x, t=target, s=source, e=env, d=dict:
583                                       x.get_contents(t, s, e, d),
584                                self.list),
585                            "")
586
587 class ActionCaller:
588     """A class for delaying calling an Action function with specific
589     (positional and keyword) arguments until the Action is actually
590     executed.
591
592     This class looks to the rest of the world like a normal Action object,
593     but what it's really doing is hanging on to the arguments until we
594     have a target, source and env to use for the expansion.
595     """
596     def __init__(self, parent, args, kw):
597         self.parent = parent
598         self.args = args
599         self.kw = kw
600     def get_contents(self, target, source, env, dict=None):
601         actfunc = self.parent.actfunc
602         try:
603             # "self.actfunc" is a function.
604             contents = str(actfunc.func_code.co_code)
605         except AttributeError:
606             # "self.actfunc" is a callable object.
607             try:
608                 contents = str(actfunc.__call__.im_func.func_code.co_code)
609             except AttributeError:
610                 # No __call__() method, so it might be a builtin
611                 # or something like that.  Do the best we can.
612                 contents = str(actfunc)
613         return contents
614     def subst_args(self, target, source, env):
615         return map(lambda x, e=env, t=target, s=source:
616                           e.subst(x, 0, t, s),
617                    self.args)
618     def subst_kw(self, target, source, env):
619         kw = {}
620         for key in self.kw.keys():
621             kw[key] = env.subst(self.kw[key], 0, target, source)
622         return kw
623     def __call__(self, target, source, env):
624         args = self.subst_args(target, source, env)
625         kw = self.subst_kw(target, source, env)
626         return apply(self.parent.actfunc, args, kw)
627     def strfunction(self, target, source, env):
628         args = self.subst_args(target, source, env)
629         kw = self.subst_kw(target, source, env)
630         return apply(self.parent.strfunc, args, kw)
631
632 class ActionFactory:
633     """A factory class that will wrap up an arbitrary function
634     as an SCons-executable Action object.
635
636     The real heavy lifting here is done by the ActionCaller class.
637     We just collect the (positional and keyword) arguments that we're
638     called with and give them to the ActionCaller object we create,
639     so it can hang onto them until it needs them.
640     """
641     def __init__(self, actfunc, strfunc):
642         self.actfunc = actfunc
643         self.strfunc = strfunc
644     def __call__(self, *args, **kw):
645         ac = ActionCaller(self, args, kw)
646         return Action(ac, strfunction=ac.strfunction)