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__"
54 except AttributeError:
57 def _actionAppend(act1, act2):
58 # This function knows how to slap two actions together.
59 # Mainly, it handles ListActions by concatenating into
60 # a single ListAction.
63 if a1 is None or a2 is None:
64 raise TypeError, "Cannot append %s to %s" % (type(act1), type(act2))
65 if isinstance(a1, ListAction):
66 if isinstance(a2, ListAction):
67 return ListAction(a1.list + a2.list)
69 return ListAction(a1.list + [ a2 ])
71 if isinstance(a2, ListAction):
72 return ListAction([ a1 ] + a2.list)
74 return ListAction([ a1, a2 ])
76 class CommandGenerator:
78 Wraps a command generator function so the Action() factory
79 function can tell a generator function from a function action.
81 def __init__(self, generator):
82 self.generator = generator
84 def __add__(self, other):
85 return _actionAppend(self, other)
87 def __radd__(self, other):
88 return _actionAppend(other, self)
90 def _do_create_action(act, strfunction=_null, varlist=[]):
91 """This is the actual "implementation" for the
92 Action factory method, below. This handles the
93 fact that passing lists to Action() itself has
94 different semantics than passing lists as elements
97 The former will create a ListAction, the latter
98 will create a CommandAction by converting the inner
99 list elements to strings."""
101 if isinstance(act, ActionBase):
103 elif SCons.Util.is_List(act):
104 return CommandAction(act)
105 elif isinstance(act, CommandGenerator):
106 return CommandGeneratorAction(act.generator)
108 return FunctionAction(act, strfunction=strfunction, varlist=varlist)
109 elif SCons.Util.is_String(act):
110 var=SCons.Util.get_environment_var(act)
112 # This looks like a string that is purely an Environment
113 # variable reference, like "$FOO" or "${FOO}". We do
114 # something special here...we lazily evaluate the contents
115 # of that Environment variable, so a user could but something
116 # like a function or a CommandGenerator in that variable
117 # instead of a string.
118 return CommandGeneratorAction(LazyCmdGenerator(var))
119 listCmds = map(lambda x: CommandAction(x), string.split(act, '\n'))
120 if len(listCmds) == 1:
123 return ListAction(listCmds)
127 def Action(act, strfunction=_null, varlist=[]):
128 """A factory for action objects."""
129 if SCons.Util.is_List(act):
130 acts = map(lambda x, s=strfunction, v=varlist:
131 _do_create_action(x, s, v),
133 acts = filter(lambda x: not x is None, acts)
137 return ListAction(acts)
139 return _do_create_action(act, strfunction=strfunction, varlist=varlist)
142 """Base class for actions that create output objects."""
143 def __cmp__(self, other):
144 return cmp(self.__dict__, other.__dict__)
146 def show(self, string):
148 sys.stdout.write(string + '\n')
150 def get_actions(self):
153 def __add__(self, other):
154 return _actionAppend(self, other)
156 def __radd__(self, other):
157 return _actionAppend(other, self)
159 def _string_from_cmd_list(cmd_list):
160 """Takes a list of command line arguments and returns a pretty
161 representation for printing."""
163 for arg in map(str, cmd_list):
164 if ' ' in arg or '\t' in arg:
165 arg = '"' + arg + '"'
167 return string.join(cl)
169 class CommandAction(ActionBase):
170 """Class for command-execution actions."""
171 def __init__(self, cmd):
172 # Cmd list can actually be a list or a single item...basically
173 # anything that we could pass in as the first arg to
174 # scons_subst_list().
177 def strfunction(self, target, source, env):
178 cmd_list = SCons.Util.scons_subst_list(self.cmd_list, env,
179 SCons.Util.SUBST_CMD,
181 return map(_string_from_cmd_list, cmd_list)
183 def __call__(self, target, source, env):
184 """Execute a command action.
186 This will handle lists of commands as well as individual commands,
187 because construction variable substitution may turn a single
188 "command" into a list. This means that this class can actually
189 handle lists of commands, even though that's not how we use it
194 escape = env.get('ESCAPE', lambda x: x)
196 if env.has_key('SHELL'):
199 raise SCons.Errors.UserError('Missing SHELL construction variable.')
201 # for SConf support (by now): check, if we want to pipe the command
202 # output to somewhere else
203 if env.has_key('PIPE_BUILD'):
205 if env.has_key('PSPAWN'):
206 pspawn = env['PSPAWN']
208 raise SCons.Errors.UserError('Missing PSPAWN construction variable.')
209 if env.has_key('PSTDOUT'):
210 pstdout = env['PSTDOUT']
212 raise SCons.Errors.UserError('Missing PSTDOUT construction variable.')
213 if env.has_key('PSTDERR'):
214 pstderr = env['PSTDERR']
216 raise SCons.Errors.UserError('Missing PSTDOUT construction variable.')
219 if env.has_key('SPAWN'):
222 raise SCons.Errors.UserError('Missing SPAWN construction variable.')
224 cmd_list = SCons.Util.scons_subst_list(self.cmd_list, env,
225 SCons.Util.SUBST_CMD,
227 for cmd_line in cmd_list:
230 self.show(_string_from_cmd_list(cmd_line))
237 import SCons.Environment
238 default_ENV = SCons.Environment.Environment()['ENV']
241 # ensure that the ENV values are all strings:
242 for key, value in ENV.items():
243 if SCons.Util.is_List(value):
244 # If the value is a list, then we assume
245 # it is a path list, because that's a pretty
246 # common list like value to stick in an environment
248 ENV[key] = string.join(map(str, value), os.pathsep)
249 elif not SCons.Util.is_String(value):
250 # If it isn't a string or a list, then
251 # we just coerce it to a string, which
252 # is proper way to handle Dir and File instances
253 # and will produce something reasonable for
254 # just about everything else:
255 ENV[key] = str(value)
257 # Escape the command line for the command
258 # interpreter we are using
259 map(lambda x, e=escape: x.escape(e), cmd_line)
260 cmd_line = map(str, cmd_line)
262 ret = pspawn( shell, escape, cmd_line[0], cmd_line,
263 ENV, pstdout, pstderr )
265 ret = spawn(shell, escape, cmd_line[0], cmd_line, ENV)
270 def get_raw_contents(self, target, source, env):
271 """Return the complete contents of this action's command line.
274 if not SCons.Util.is_List(cmd):
276 return SCons.Util.scons_subst(string.join(map(str, cmd)),
278 SCons.Util.SUBST_RAW,
279 SCons.Util.target_prep(target),
280 SCons.Util.source_prep(source))
282 def get_contents(self, target, source, env):
283 """Return the signature contents of this action's command line.
285 This strips $(-$) and everything in between the string,
286 since those parts don't affect signatures.
289 if not SCons.Util.is_List(cmd):
291 return SCons.Util.scons_subst(string.join(map(str, cmd)),
293 SCons.Util.SUBST_SIG,
294 SCons.Util.target_prep(target),
295 SCons.Util.source_prep(source))
297 class CommandGeneratorAction(ActionBase):
298 """Class for command-generator actions."""
299 def __init__(self, generator):
300 self.generator = generator
302 def __generate(self, target, source, env, for_signature):
303 # ensure that target is a list, to make it easier to write
304 # generator functions:
305 if not SCons.Util.is_List(target):
308 ret = self.generator(target=target, source=source, env=env, for_signature=for_signature)
309 gen_cmd = Action(ret)
311 raise SCons.Errors.UserError("Object returned from command generator: %s cannot be used to create an Action." % repr(ret))
314 def strfunction(self, target, source, env):
315 if not SCons.Util.is_List(source):
317 rsources = map(rfile, source)
318 act = self.__generate(target, source, env, 0)
319 return act.strfunction(target, rsources, env)
321 def __call__(self, target, source, env):
322 if not SCons.Util.is_List(source):
324 rsources = map(rfile, source)
325 act = self.__generate(target, source, env, 0)
326 return act(target, rsources, env)
328 def get_contents(self, target, source, env):
329 """Return the signature contents of this action's command line.
331 This strips $(-$) and everything in between the string,
332 since those parts don't affect signatures.
334 return self.__generate(target, source, env, 1).get_contents(target, source, env)
336 class LazyCmdGenerator:
337 """This is a simple callable class that acts as a command generator.
338 It holds on to a key into an Environment dictionary, then waits
339 until execution time to see what type it is, then tries to
340 create an Action out of it."""
341 def __init__(self, var):
342 self.var = SCons.Util.to_String(var)
344 def strfunction(self, target, source, env):
348 # The variable reference substitutes to nothing.
351 def __call__(self, target, source, env, for_signature):
355 # The variable reference substitutes to nothing.
358 def __cmp__(self, other):
359 return cmp(self.__dict__, other.__dict__)
361 class FunctionAction(ActionBase):
362 """Class for Python function actions."""
364 def __init__(self, execfunction, strfunction=_null, varlist=[]):
365 self.execfunction = execfunction
366 if strfunction is _null:
367 def strfunction(target, source, env, execfunction=execfunction):
369 return '"' + str(s) + '"'
370 def array(a, q=quote):
371 return '[' + string.join(map(lambda x, q=q: q(x), a), ", ") + ']'
373 name = execfunction.__name__
374 except AttributeError:
376 name = execfunction.__class__.__name__
377 except AttributeError:
378 name = "unknown_python_function"
379 tstr = len(target) == 1 and quote(target[0]) or array(target)
380 sstr = len(source) == 1 and quote(source[0]) or array(source)
381 return "%s(%s, %s)" % (name, tstr, sstr)
382 self.strfunction = strfunction
383 self.varlist = varlist
385 def __call__(self, target, source, env):
387 if not SCons.Util.is_List(target):
389 if not SCons.Util.is_List(source):
391 if print_actions and self.strfunction:
392 s = self.strfunction(target, source, env)
396 rsources = map(rfile, source)
397 r = self.execfunction(target=target, source=rsources, env=env)
400 def get_contents(self, target, source, env):
401 """Return the signature contents of this callable action.
403 By providing direct access to the code object of the
404 function, Python makes this extremely easy. Hooray!
407 # "self.execfunction" is a function.
408 code = self.execfunction.func_code.co_code
409 except AttributeError:
410 # "self.execfunction" is a callable object.
411 code = self.execfunction.__call__.im_func.func_code.co_code
412 return str(code) + env.subst(string.join(map(lambda v: '${'+v+'}',
415 class ListAction(ActionBase):
416 """Class for lists of other actions."""
417 def __init__(self, list):
418 self.list = map(lambda x: Action(x), list)
420 def get_actions(self):
423 def strfunction(self, target, source, env):
427 x = l.strfunction(target, source, env)
428 if not SCons.Util.is_List(x):
431 return string.join(s, "\n")
433 def __call__(self, target, source, env):
435 r = l(target, source, env)
440 def get_contents(self, target, source, env):
441 """Return the signature contents of this action list.
443 Simple concatenation of the signatures of the elements.
445 target = SCons.Util.target_prep(target)
446 source = SCons.Util.source_prep(source)
447 return string.join(map(lambda x, t=target, s=source, e=env:
448 x.get_contents(t, s, e),