10 # Permission is hereby granted, free of charge, to any person obtaining
11 # a copy of this software and associated documentation files (the
12 # "Software"), to deal in the Software without restriction, including
13 # without limitation the rights to use, copy, modify, merge, publish,
14 # distribute, sublicense, and/or sell copies of the Software, and to
15 # permit persons to whom the Software is furnished to do so, subject to
16 # the following conditions:
18 # The above copyright notice and this permission notice shall be included
19 # in all copies or substantial portions of the Software.
21 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
22 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
23 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
53 except AttributeError:
56 def SetCommandHandler(func, escape = lambda x: x):
57 raise SCons.Errors.UserError("SetCommandHandler() is no longer supported, use the SPAWN and ESCAPE construction variables.")
59 def GetCommandHandler():
60 raise SCons.Errors.UserError("GetCommandHandler() is no longer supported, use the SPAWN construction variable.")
62 def _actionAppend(act1, act2):
63 # This function knows how to slap two actions together.
64 # Mainly, it handles ListActions by concatenating into
65 # a single ListAction.
68 if a1 is None or a2 is None:
69 raise TypeError, "Cannot append %s to %s" % (type(act1), type(act2))
70 if isinstance(a1, ListAction):
71 if isinstance(a2, ListAction):
72 return ListAction(a1.list + a2.list)
74 return ListAction(a1.list + [ a2 ])
76 if isinstance(a2, ListAction):
77 return ListAction([ a1 ] + a2.list)
79 return ListAction([ a1, a2 ])
81 class CommandGenerator:
83 Wraps a command generator function so the Action() factory
84 function can tell a generator function from a function action.
86 def __init__(self, generator):
87 self.generator = generator
89 def __add__(self, other):
90 return _actionAppend(self, other)
92 def __radd__(self, other):
93 return _actionAppend(other, self)
95 def _do_create_action(act, strfunction=_null, varlist=[]):
96 """This is the actual "implementation" for the
97 Action factory method, below. This handles the
98 fact that passing lists to Action() itself has
99 different semantics than passing lists as elements
102 The former will create a ListAction, the latter
103 will create a CommandAction by converting the inner
104 list elements to strings."""
106 if isinstance(act, ActionBase):
108 elif SCons.Util.is_List(act):
109 return CommandAction(act)
110 elif isinstance(act, CommandGenerator):
111 return CommandGeneratorAction(act.generator)
113 return FunctionAction(act, strfunction=strfunction, varlist=varlist)
114 elif SCons.Util.is_String(act):
115 var=SCons.Util.get_environment_var(act)
117 # This looks like a string that is purely an Environment
118 # variable reference, like "$FOO" or "${FOO}". We do
119 # something special here...we lazily evaluate the contents
120 # of that Environment variable, so a user could but something
121 # like a function or a CommandGenerator in that variable
122 # instead of a string.
123 return CommandGeneratorAction(LazyCmdGenerator(var))
124 listCmds = map(lambda x: CommandAction(x), string.split(act, '\n'))
125 if len(listCmds) == 1:
128 return ListAction(listCmds)
132 def Action(act, strfunction=_null, varlist=[]):
133 """A factory for action objects."""
134 if SCons.Util.is_List(act):
135 acts = map(lambda x, s=strfunction, v=varlist:
136 _do_create_action(x, s, v),
138 acts = filter(lambda x: not x is None, acts)
142 return ListAction(acts)
144 return _do_create_action(act, strfunction=strfunction, varlist=varlist)
147 """Base class for actions that create output objects."""
148 def __cmp__(self, other):
149 return cmp(self.__dict__, other.__dict__)
151 def show(self, string):
155 def get_actions(self):
158 def __add__(self, other):
159 return _actionAppend(self, other)
161 def __radd__(self, other):
162 return _actionAppend(other, self)
164 def _string_from_cmd_list(cmd_list):
165 """Takes a list of command line arguments and returns a pretty
166 representation for printing."""
168 for arg in map(str, cmd_list):
169 if ' ' in arg or '\t' in arg:
170 arg = '"' + arg + '"'
172 return string.join(cl)
174 class CommandAction(ActionBase):
175 """Class for command-execution actions."""
176 def __init__(self, cmd):
177 # Cmd list can actually be a list or a single item...basically
178 # anything that we could pass in as the first arg to
179 # scons_subst_list().
182 def strfunction(self, target, source, env):
183 cmd_list = SCons.Util.scons_subst_list(self.cmd_list, env,
184 SCons.Util.SUBST_CMD,
186 return map(_string_from_cmd_list, cmd_list)
188 def __call__(self, target, source, env):
189 """Execute a command action.
191 This will handle lists of commands as well as individual commands,
192 because construction variable substitution may turn a single
193 "command" into a list. This means that this class can actually
194 handle lists of commands, even though that's not how we use it
199 escape = env.get('ESCAPE', lambda x: x)
201 if env.has_key('SHELL'):
204 raise SCons.Errors.UserError('Missing SHELL construction variable.')
206 # for SConf support (by now): check, if we want to pipe the command
207 # output to somewhere else
208 if env.has_key('PIPE_BUILD'):
210 if env.has_key('PSPAWN'):
211 pspawn = env['PSPAWN']
213 raise SCons.Errors.UserError('Missing PSPAWN construction variable.')
214 if env.has_key('PSTDOUT'):
215 pstdout = env['PSTDOUT']
217 raise SCons.Errors.UserError('Missing PSTDOUT construction variable.')
218 if env.has_key('PSTDERR'):
219 pstderr = env['PSTDERR']
221 raise SCons.Errors.UserError('Missing PSTDOUT construction variable.')
224 if env.has_key('SPAWN'):
227 raise SCons.Errors.UserError('Missing SPAWN construction variable.')
229 cmd_list = SCons.Util.scons_subst_list(self.cmd_list, env,
230 SCons.Util.SUBST_CMD,
232 for cmd_line in cmd_list:
235 self.show(_string_from_cmd_list(cmd_line))
242 import SCons.Environment
243 default_ENV = SCons.Environment.Environment()['ENV']
245 # Escape the command line for the command
246 # interpreter we are using
247 map(lambda x, e=escape: x.escape(e), cmd_line)
248 cmd_line = map(str, cmd_line)
250 ret = pspawn( shell, escape, cmd_line[0], cmd_line,
251 ENV, pstdout, pstderr )
253 ret = spawn(shell, escape, cmd_line[0], cmd_line, ENV)
258 def get_raw_contents(self, target, source, env):
259 """Return the complete contents of this action's command line.
262 if not SCons.Util.is_List(cmd):
264 return SCons.Util.scons_subst(string.join(map(str, cmd)),
266 SCons.Util.SUBST_RAW,
267 SCons.Util.target_prep(target),
268 SCons.Util.source_prep(source))
270 def get_contents(self, target, source, env):
271 """Return the signature contents of this action's command line.
273 This strips $(-$) and everything in between the string,
274 since those parts don't affect signatures.
277 if not SCons.Util.is_List(cmd):
279 return SCons.Util.scons_subst(string.join(map(str, cmd)),
281 SCons.Util.SUBST_SIG,
282 SCons.Util.target_prep(target),
283 SCons.Util.source_prep(source))
285 class CommandGeneratorAction(ActionBase):
286 """Class for command-generator actions."""
287 def __init__(self, generator):
288 self.generator = generator
290 def __generate(self, target, source, env, for_signature):
291 # ensure that target is a list, to make it easier to write
292 # generator functions:
293 if not SCons.Util.is_List(target):
296 ret = self.generator(target=target, source=source, env=env, for_signature=for_signature)
297 gen_cmd = Action(ret)
299 raise SCons.Errors.UserError("Object returned from command generator: %s cannot be used to create an Action." % repr(ret))
302 def strfunction(self, target, source, env):
303 if not SCons.Util.is_List(source):
305 rsources = map(rfile, source)
306 act = self.__generate(target, source, env, 0)
307 return act.strfunction(target, rsources, env)
309 def __call__(self, target, source, env):
310 if not SCons.Util.is_List(source):
312 rsources = map(rfile, source)
313 act = self.__generate(target, source, env, 0)
314 return act(target, rsources, env)
316 def get_contents(self, target, source, env):
317 """Return the signature contents of this action's command line.
319 This strips $(-$) and everything in between the string,
320 since those parts don't affect signatures.
322 return self.__generate(target, source, env, 1).get_contents(target, source, env)
324 class LazyCmdGenerator:
325 """This is a simple callable class that acts as a command generator.
326 It holds on to a key into an Environment dictionary, then waits
327 until execution time to see what type it is, then tries to
328 create an Action out of it."""
329 def __init__(self, var):
330 self.var = SCons.Util.to_String(var)
332 def strfunction(self, target, source, env):
336 # The variable reference substitutes to nothing.
339 def __call__(self, target, source, env, for_signature):
343 # The variable reference substitutes to nothing.
346 def __cmp__(self, other):
347 return cmp(self.__dict__, other.__dict__)
349 class FunctionAction(ActionBase):
350 """Class for Python function actions."""
352 def __init__(self, execfunction, strfunction=_null, varlist=[]):
353 self.execfunction = execfunction
354 if strfunction is _null:
355 def strfunction(target, source, env, execfunction=execfunction):
357 return '"' + str(s) + '"'
358 def array(a, q=quote):
359 return '[' + string.join(map(lambda x, q=q: q(x), a), ", ") + ']'
361 name = execfunction.__name__
362 except AttributeError:
364 name = execfunction.__class__.__name__
365 except AttributeError:
366 name = "unknown_python_function"
367 tstr = len(target) == 1 and quote(target[0]) or array(target)
368 sstr = len(source) == 1 and quote(source[0]) or array(source)
369 return "%s(%s, %s)" % (name, tstr, sstr)
370 self.strfunction = strfunction
371 self.varlist = varlist
373 def __call__(self, target, source, env):
375 if not SCons.Util.is_List(target):
377 if not SCons.Util.is_List(source):
379 if print_actions and self.strfunction:
380 s = self.strfunction(target, source, env)
384 rsources = map(rfile, source)
385 r = self.execfunction(target=target, source=rsources, env=env)
388 def get_contents(self, target, source, env):
389 """Return the signature contents of this callable action.
391 By providing direct access to the code object of the
392 function, Python makes this extremely easy. Hooray!
395 # "self.execfunction" is a function.
396 code = self.execfunction.func_code.co_code
398 # "self.execfunction" is a callable object.
399 code = self.execfunction.__call__.im_func.func_code.co_code
400 return str(code) + env.subst(string.join(map(lambda v: '${'+v+'}',
403 class ListAction(ActionBase):
404 """Class for lists of other actions."""
405 def __init__(self, list):
406 self.list = map(lambda x: Action(x), list)
408 def get_actions(self):
411 def strfunction(self, target, source, env):
415 x = l.strfunction(target, source, env)
416 if not SCons.Util.is_List(x):
419 return string.join(s, "\n")
421 def __call__(self, target, source, env):
423 r = l(target, source, env)
428 def get_contents(self, target, source, env):
429 """Return the signature contents of this action list.
431 Simple concatenation of the signatures of the elements.
433 target = SCons.Util.target_prep(target)
434 source = SCons.Util.source_prep(source)
435 return string.join(map(lambda x, t=target, s=source, e=env:
436 x.get_contents(t, s, e),