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__"
58 except AttributeError:
61 def SetCommandHandler(func, escape = lambda x: x):
62 raise SCons.Errors.UserError("SetCommandHandler() is no longer supported, use the SPAWN and ESCAPE construction variables.")
64 def GetCommandHandler():
65 raise SCons.Errors.UserError("GetCommandHandler() is no longer supported, use the SPAWN construction variable.")
67 def _actionAppend(act1, act2):
68 # This function knows how to slap two actions together.
69 # Mainly, it handles ListActions by concatenating into
70 # a single ListAction.
73 if a1 is None or a2 is None:
74 raise TypeError, "Cannot append %s to %s" % (type(act1), type(act2))
75 if isinstance(a1, ListAction):
76 if isinstance(a2, ListAction):
77 return ListAction(a1.list + a2.list)
79 return ListAction(a1.list + [ a2 ])
81 if isinstance(a2, ListAction):
82 return ListAction([ a1 ] + a2.list)
84 return ListAction([ a1, a2 ])
86 class CommandGenerator:
88 Wraps a command generator function so the Action() factory
89 function can tell a generator function from a function action.
91 def __init__(self, generator):
92 self.generator = generator
94 def __add__(self, other):
95 return _actionAppend(self, other)
97 def __radd__(self, other):
98 return _actionAppend(other, self)
100 def _do_create_action(act, strfunction=_null, varlist=[]):
101 """This is the actual "implementation" for the
102 Action factory method, below. This handles the
103 fact that passing lists to Action() itself has
104 different semantics than passing lists as elements
107 The former will create a ListAction, the latter
108 will create a CommandAction by converting the inner
109 list elements to strings."""
111 if isinstance(act, ActionBase):
113 elif SCons.Util.is_List(act):
114 return CommandAction(act)
115 elif isinstance(act, CommandGenerator):
116 return CommandGeneratorAction(act.generator)
118 return FunctionAction(act, strfunction=strfunction, varlist=varlist)
119 elif SCons.Util.is_String(act):
120 var=SCons.Util.get_environment_var(act)
122 # This looks like a string that is purely an Environment
123 # variable reference, like "$FOO" or "${FOO}". We do
124 # something special here...we lazily evaluate the contents
125 # of that Environment variable, so a user could but something
126 # like a function or a CommandGenerator in that variable
127 # instead of a string.
128 return CommandGeneratorAction(LazyCmdGenerator(var))
129 listCmds = map(lambda x: CommandAction(x), string.split(act, '\n'))
130 if len(listCmds) == 1:
133 return ListAction(listCmds)
137 def Action(act, strfunction=_null, varlist=[]):
138 """A factory for action objects."""
139 if SCons.Util.is_List(act):
140 acts = map(lambda x, s=strfunction, v=varlist:
141 _do_create_action(x, s, v),
143 acts = filter(lambda x: not x is None, acts)
147 return ListAction(acts)
149 return _do_create_action(act, strfunction=strfunction, varlist=varlist)
152 """Base class for actions that create output objects."""
153 def __cmp__(self, other):
154 return cmp(self.__dict__, other.__dict__)
156 def show(self, string):
160 def get_actions(self):
163 def __add__(self, other):
164 return _actionAppend(self, other)
166 def __radd__(self, other):
167 return _actionAppend(other, self)
169 def _string_from_cmd_list(cmd_list):
170 """Takes a list of command line arguments and returns a pretty
171 representation for printing."""
173 for arg in map(str, cmd_list):
174 if ' ' in arg or '\t' in arg:
175 arg = '"' + arg + '"'
177 return string.join(cl)
179 _rm = re.compile(r'\$[()]')
180 _remove = re.compile(r'\$\(([^\$]|\$[^\(])*?\$\)')
182 class CommandAction(ActionBase):
183 """Class for command-execution actions."""
184 def __init__(self, cmd):
185 # Cmd list can actually be a list or a single item...basically
186 # anything that we could pass in as the first arg to
187 # scons_subst_list().
190 def strfunction(self, target, source, env):
191 cmd_list = SCons.Util.scons_subst_list(self.cmd_list, env, _rm,
193 return map(_string_from_cmd_list, cmd_list)
195 def __call__(self, target, source, env):
196 """Execute a command action.
198 This will handle lists of commands as well as individual commands,
199 because construction variable substitution may turn a single
200 "command" into a list. This means that this class can actually
201 handle lists of commands, even though that's not how we use it
206 escape = env.get('ESCAPE', lambda x: x)
208 if env.has_key('SHELL'):
211 raise SCons.Errors.UserError('Missing SHELL construction variable.')
213 if env.has_key('SPAWN'):
216 raise SCons.Errors.UserError('Missing SPAWN construction variable.')
218 cmd_list = SCons.Util.scons_subst_list(self.cmd_list, env, _rm,
220 for cmd_line in cmd_list:
223 self.show(_string_from_cmd_list(cmd_line))
230 import SCons.Environment
231 default_ENV = SCons.Environment.Environment()['ENV']
233 # Escape the command line for the command
234 # interpreter we are using
235 map(lambda x, e=escape: x.escape(e), cmd_line)
236 cmd_line = map(str, cmd_line)
237 ret = spawn(shell, escape, cmd_line[0], cmd_line, ENV)
242 def get_raw_contents(self, target, source, env):
243 """Return the complete contents of this action's command line.
245 # We've discusssed using the real target and source names in
246 # a CommandAction's signature contents. This would have the
247 # advantage of recompiling when a file's name changes (keeping
248 # debug info current), but it would currently break repository
249 # logic that will change the file name based on whether the
250 # files come from a repository or locally. If we ever move to
251 # that scheme, though, here's how we'd do it:
252 #return SCons.Util.scons_subst(string.join(self.cmd_list),
253 # self.subst_dict(target, source, env),
256 if not SCons.Util.is_List(cmd):
258 return SCons.Util.scons_subst(string.join(map(str, cmd)),
261 def get_contents(self, target, source, env):
262 """Return the signature contents of this action's command line.
264 This strips $(-$) and everything in between the string,
265 since those parts don't affect signatures.
267 # We've discusssed using the real target and source names in
268 # a CommandAction's signature contents. This would have the
269 # advantage of recompiling when a file's name changes (keeping
270 # debug info current), but it would currently break repository
271 # logic that will change the file name based on whether the
272 # files come from a repository or locally. If we ever move to
273 # that scheme, though, here's how we'd do it:
274 #return SCons.Util.scons_subst(string.join(map(str, self.cmd_list)),
275 # self.subst_dict(target, source, env),
279 if not SCons.Util.is_List(cmd):
281 return SCons.Util.scons_subst(string.join(map(str, cmd)),
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 __call__(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(target, rsources, env)
309 def get_contents(self, target, source, env):
310 """Return the signature contents of this action's command line.
312 This strips $(-$) and everything in between the string,
313 since those parts don't affect signatures.
315 return self.__generate(target, source, env, 1).get_contents(target, source, env)
317 class LazyCmdGenerator:
318 """This is a simple callable class that acts as a command generator.
319 It holds on to a key into an Environment dictionary, then waits
320 until execution time to see what type it is, then tries to
321 create an Action out of it."""
322 def __init__(self, var):
323 self.var = SCons.Util.to_String(var)
325 def __call__(self, target, source, env, for_signature):
326 if env.has_key(self.var):
329 # The variable reference substitutes to nothing.
332 class FunctionAction(ActionBase):
333 """Class for Python function actions."""
335 def __init__(self, execfunction, strfunction=_null, varlist=[]):
336 self.execfunction = execfunction
337 if strfunction is _null:
338 def strfunction(target, source, env, execfunction=execfunction):
340 return '"' + str(s) + '"'
341 def array(a, q=quote):
342 return '[' + string.join(map(lambda x, q=q: q(x), a), ", ") + ']'
344 name = execfunction.__name__
345 except AttributeError:
347 name = execfunction.__class__.__name__
348 except AttributeError:
349 name = "unknown_python_function"
350 tstr = len(target) == 1 and quote(target[0]) or array(target)
351 sstr = len(source) == 1 and quote(source[0]) or array(source)
352 return "%s(%s, %s)" % (name, tstr, sstr)
353 self.strfunction = strfunction
354 self.varlist = varlist
356 def __call__(self, target, source, env):
358 if not SCons.Util.is_List(target):
360 if not SCons.Util.is_List(source):
362 if print_actions and self.strfunction:
363 s = self.strfunction(target, source, env)
367 rsources = map(rfile, source)
368 r = self.execfunction(target=target, source=rsources, env=env)
371 def get_contents(self, target, source, env):
372 """Return the signature contents of this callable action.
374 By providing direct access to the code object of the
375 function, Python makes this extremely easy. Hooray!
378 # "self.execfunction" is a function.
379 code = self.execfunction.func_code.co_code
381 # "self.execfunction" is a callable object.
382 code = self.execfunction.__call__.im_func.func_code.co_code
383 return str(code) + string.join(map(lambda v, e=env: str(e[v]),
386 class ListAction(ActionBase):
387 """Class for lists of other actions."""
388 def __init__(self, list):
389 self.list = map(lambda x: Action(x), list)
391 def get_actions(self):
394 def __call__(self, target, source, env):
396 r = l(target, source, env)
401 def get_contents(self, target, source, env):
402 """Return the signature contents of this action list.
404 Simple concatenation of the signatures of the elements.
406 return string.join(map(lambda x, t=target, s=source, e=env:
407 x.get_contents(t, s, e),