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(string.split(x)),
130 string.split(act, '\n'))
131 if len(listCmds) == 1:
134 return ListAction(listCmds)
138 def Action(act, strfunction=_null, varlist=[]):
139 """A factory for action objects."""
140 if SCons.Util.is_List(act):
141 acts = map(lambda x, s=strfunction, v=varlist:
142 _do_create_action(x, s, v),
144 acts = filter(lambda x: not x is None, acts)
148 return ListAction(acts)
150 return _do_create_action(act, strfunction=strfunction, varlist=varlist)
153 """Base class for actions that create output objects."""
154 def __cmp__(self, other):
155 return cmp(self.__dict__, other.__dict__)
157 def show(self, string):
160 def get_actions(self):
163 def subst_dict(self, target, source, env):
164 """Create a dictionary for substitution of construction
167 This translates the following special arguments:
169 env - the construction environment itself,
170 the values of which (CC, CCFLAGS, etc.)
171 are copied straight into the dictionary
173 target - the target (object or array of objects),
174 used to generate the TARGET and TARGETS
175 construction variables
177 source - the source (object or array of objects),
178 used to generate the SOURCES and SOURCE
179 construction variables
184 for k,v in env.items(): dict[k] = v
186 if not SCons.Util.is_List(target):
189 dict['TARGETS'] = SCons.Util.PathList(map(os.path.normpath, map(str, target)))
191 dict['TARGET'] = dict['TARGETS'][0]
196 except AttributeError:
198 if not SCons.Util.is_List(source):
200 dict['SOURCES'] = SCons.Util.PathList(map(os.path.normpath, map(rstr, source)))
202 dict['SOURCE'] = dict['SOURCES'][0]
206 def __add__(self, other):
207 return _actionAppend(self, other)
209 def __radd__(self, other):
210 return _actionAppend(other, self)
212 def _string_from_cmd_list(cmd_list):
213 """Takes a list of command line arguments and returns a pretty
214 representation for printing."""
216 for arg in map(str, cmd_list):
217 if ' ' in arg or '\t' in arg:
218 arg = '"' + arg + '"'
220 return string.join(cl)
222 _rm = re.compile(r'\$[()]')
223 _remove = re.compile(r'\$\(([^\$]|\$[^\(])*?\$\)')
225 class CommandAction(ActionBase):
226 """Class for command-execution actions."""
227 def __init__(self, cmd):
230 def __call__(self, target, source, env):
231 """Execute a command action.
233 This will handle lists of commands as well as individual commands,
234 because construction variable substitution may turn a single
235 "command" into a list. This means that this class can actually
236 handle lists of commands, even though that's not how we use it
241 escape = env.get('ESCAPE', lambda x: x)
243 if env.has_key('SHELL'):
246 raise SCons.Errors.UserError('Missing SHELL construction variable.')
248 if env.has_key('SPAWN'):
251 raise SCons.Errors.UserError('Missing SPAWN construction variable.')
253 dict = self.subst_dict(target, source, env)
254 cmd_list = SCons.Util.scons_subst_list(self.cmd_list, dict, {}, _rm)
255 for cmd_line in cmd_list:
258 self.show(_string_from_cmd_list(cmd_line))
265 import SCons.Environment
266 default_ENV = SCons.Environment.Environment()['ENV']
268 # Escape the command line for the command
269 # interpreter we are using
270 map(lambda x, e=escape: x.escape(e), cmd_line)
271 cmd_line = map(str, cmd_line)
272 ret = spawn(shell, escape, cmd_line[0], cmd_line, ENV)
277 def _sig_dict(self, target, source, env):
278 """Supply a dictionary for use in computing signatures.
280 For signature purposes, it doesn't matter what targets or
281 sources we use, so long as we use the same ones every time
282 so the signature stays the same. We supply an array of two
283 of each to allow for distinction between TARGET and TARGETS.
285 return self.subst_dict(['__t1__', '__t2__'], ['__s1__', '__s2__'], env)
287 def get_raw_contents(self, target, source, env):
288 """Return the complete contents of this action's command line.
290 return SCons.Util.scons_subst(string.join(self.cmd_list),
291 self._sig_dict(target, source, env), {})
293 def get_contents(self, target, source, env):
294 """Return the signature contents of this action's command line.
296 This strips $(-$) and everything in between the string,
297 since those parts don't affect signatures.
299 return SCons.Util.scons_subst(string.join(map(str, self.cmd_list)),
300 self._sig_dict(target, source, env), {}, _remove)
302 class CommandGeneratorAction(ActionBase):
303 """Class for command-generator actions."""
304 def __init__(self, generator):
305 self.generator = generator
307 def __generate(self, target, source, env, for_signature):
308 # ensure that target is a list, to make it easier to write
309 # generator functions:
310 if not SCons.Util.is_List(target):
313 ret = self.generator(target=target, source=source, env=env, for_signature=for_signature)
314 gen_cmd = Action(ret)
316 raise SCons.Errors.UserError("Object returned from command generator: %s cannot be used to create an Action." % repr(ret))
319 def __call__(self, target, source, env):
320 if not SCons.Util.is_List(source):
322 rsources = map(rfile, source)
323 act = self.__generate(target, source, env, 0)
324 return act(target, rsources, env)
326 def get_contents(self, target, source, env):
327 """Return the signature contents of this action's command line.
329 This strips $(-$) and everything in between the string,
330 since those parts don't affect signatures.
332 return self.__generate(target, source, env, 1).get_contents(target, source, env)
334 class LazyCmdGenerator:
335 """This is a simple callable class that acts as a command generator.
336 It holds on to a key into an Environment dictionary, then waits
337 until execution time to see what type it is, then tries to
338 create an Action out of it."""
339 def __init__(self, var):
340 self.var = SCons.Util.to_String(var)
342 def __call__(self, target, source, env, for_signature):
343 if env.has_key(self.var):
346 # The variable reference substitutes to nothing.
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, execfunction=execfunction):
357 return '"' + str(s) + '"'
359 name = execfunction.__name__
360 except AttributeError:
362 name = execfunction.__class__.__name__
363 except AttributeError:
364 name = "unknown_python_function"
366 tstr = quote(target[0])
368 tstr = str(map(lambda x, q=quote: q(x), target))
370 sstr = quote(source[0])
372 sstr = str(map(lambda x, q=quote: q(x), source))
373 return "%s(%s, %s)" % (name, tstr, sstr)
374 self.strfunction = strfunction
375 self.varlist = varlist
377 def __call__(self, target, source, env):
379 if not SCons.Util.is_List(target):
381 if not SCons.Util.is_List(source):
383 if print_actions and self.strfunction:
384 s = self.strfunction(target, source)
388 rsources = map(rfile, source)
389 r = self.execfunction(target=target, source=rsources, env=env)
392 def get_contents(self, target, source, env):
393 """Return the signature contents of this callable action.
395 By providing direct access to the code object of the
396 function, Python makes this extremely easy. Hooray!
399 # "self.execfunction" is a function.
400 code = self.execfunction.func_code.co_code
402 # "self.execfunction" is a callable object.
403 code = self.execfunction.__call__.im_func.func_code.co_code
404 return str(code) + string.join(map(lambda v, e=env: str(e[v]),
407 class ListAction(ActionBase):
408 """Class for lists of other actions."""
409 def __init__(self, list):
410 self.list = map(lambda x: Action(x), list)
412 def get_actions(self):
415 def __call__(self, target, source, env):
417 r = l(target, source, env)
422 def get_contents(self, target, source, env):
423 """Return the signature contents of this action list.
425 Simple concatenation of the signatures of the elements.
427 return string.join(map(lambda x, t=target, s=source, e=env:
428 x.get_contents(t, s, e),