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):
161 def get_actions(self):
164 def subst_dict(self, target, source, env):
165 """Create a dictionary for substitution of construction
168 This translates the following special arguments:
170 env - the construction environment itself,
171 the values of which (CC, CCFLAGS, etc.)
172 are copied straight into the dictionary
174 target - the target (object or array of objects),
175 used to generate the TARGET and TARGETS
176 construction variables
178 source - the source (object or array of objects),
179 used to generate the SOURCES and SOURCE
180 construction variables
185 for k,v in env.items(): dict[k] = v
187 if not SCons.Util.is_List(target):
190 dict['TARGETS'] = SCons.Util.PathList(map(os.path.normpath, map(str, target)))
192 dict['TARGET'] = dict['TARGETS'][0]
197 except AttributeError:
199 if not SCons.Util.is_List(source):
201 dict['SOURCES'] = SCons.Util.PathList(map(os.path.normpath, map(rstr, source)))
203 dict['SOURCE'] = dict['SOURCES'][0]
207 def __add__(self, other):
208 return _actionAppend(self, other)
210 def __radd__(self, other):
211 return _actionAppend(other, self)
213 def _string_from_cmd_list(cmd_list):
214 """Takes a list of command line arguments and returns a pretty
215 representation for printing."""
217 for arg in map(str, cmd_list):
218 if ' ' in arg or '\t' in arg:
219 arg = '"' + arg + '"'
221 return string.join(cl)
223 _rm = re.compile(r'\$[()]')
224 _remove = re.compile(r'\$\(([^\$]|\$[^\(])*?\$\)')
226 class CommandAction(ActionBase):
227 """Class for command-execution actions."""
228 def __init__(self, cmd):
231 def strfunction(self, target, source, env):
232 dict = self.subst_dict(target, source, env)
233 cmd_list = SCons.Util.scons_subst_list(self.cmd_list, dict, {}, _rm)
234 return map(_string_from_cmd_list, cmd_list)
236 def __call__(self, target, source, env):
237 """Execute a command action.
239 This will handle lists of commands as well as individual commands,
240 because construction variable substitution may turn a single
241 "command" into a list. This means that this class can actually
242 handle lists of commands, even though that's not how we use it
247 escape = env.get('ESCAPE', lambda x: x)
249 if env.has_key('SHELL'):
252 raise SCons.Errors.UserError('Missing SHELL construction variable.')
254 if env.has_key('SPAWN'):
257 raise SCons.Errors.UserError('Missing SPAWN construction variable.')
259 dict = self.subst_dict(target, source, env)
260 cmd_list = SCons.Util.scons_subst_list(self.cmd_list, dict, {}, _rm)
261 for cmd_line in cmd_list:
264 self.show(_string_from_cmd_list(cmd_line))
271 import SCons.Environment
272 default_ENV = SCons.Environment.Environment()['ENV']
274 # Escape the command line for the command
275 # interpreter we are using
276 map(lambda x, e=escape: x.escape(e), cmd_line)
277 cmd_line = map(str, cmd_line)
278 ret = spawn(shell, escape, cmd_line[0], cmd_line, ENV)
283 def _sig_dict(self, target, source, env):
284 """Supply a dictionary for use in computing signatures.
286 For signature purposes, it doesn't matter what targets or
287 sources we use, so long as we use the same ones every time
288 so the signature stays the same. We supply an array of two
289 of each to allow for distinction between TARGET and TARGETS.
291 return self.subst_dict(['__t1__', '__t2__'], ['__s1__', '__s2__'], env)
293 def get_raw_contents(self, target, source, env):
294 """Return the complete contents of this action's command line.
296 # We've discusssed using the real target and source names in
297 # a CommandAction's signature contents. This would have the
298 # advantage of recompiling when a file's name changes (keeping
299 # debug info current), but it would currently break repository
300 # logic that will change the file name based on whether the
301 # files come from a repository or locally. If we ever move to
302 # that scheme, though, here's how we'd do it:
303 #return SCons.Util.scons_subst(string.join(self.cmd_list),
304 # self.subst_dict(target, source, env),
306 return SCons.Util.scons_subst(string.join(self.cmd_list),
310 def get_contents(self, target, source, env):
311 """Return the signature contents of this action's command line.
313 This strips $(-$) and everything in between the string,
314 since those parts don't affect signatures.
316 # We've discusssed using the real target and source names in
317 # a CommandAction's signature contents. This would have the
318 # advantage of recompiling when a file's name changes (keeping
319 # debug info current), but it would currently break repository
320 # logic that will change the file name based on whether the
321 # files come from a repository or locally. If we ever move to
322 # that scheme, though, here's how we'd do it:
323 #return SCons.Util.scons_subst(string.join(map(str, self.cmd_list)),
324 # self.subst_dict(target, source, env),
327 return SCons.Util.scons_subst(string.join(map(str, self.cmd_list)),
332 class CommandGeneratorAction(ActionBase):
333 """Class for command-generator actions."""
334 def __init__(self, generator):
335 self.generator = generator
337 def __generate(self, target, source, env, for_signature):
338 # ensure that target is a list, to make it easier to write
339 # generator functions:
340 if not SCons.Util.is_List(target):
343 ret = self.generator(target=target, source=source, env=env, for_signature=for_signature)
344 gen_cmd = Action(ret)
346 raise SCons.Errors.UserError("Object returned from command generator: %s cannot be used to create an Action." % repr(ret))
349 def __call__(self, target, source, env):
350 if not SCons.Util.is_List(source):
352 rsources = map(rfile, source)
353 act = self.__generate(target, source, env, 0)
354 return act(target, rsources, env)
356 def get_contents(self, target, source, env):
357 """Return the signature contents of this action's command line.
359 This strips $(-$) and everything in between the string,
360 since those parts don't affect signatures.
362 return self.__generate(target, source, env, 1).get_contents(target, source, env)
364 class LazyCmdGenerator:
365 """This is a simple callable class that acts as a command generator.
366 It holds on to a key into an Environment dictionary, then waits
367 until execution time to see what type it is, then tries to
368 create an Action out of it."""
369 def __init__(self, var):
370 self.var = SCons.Util.to_String(var)
372 def __call__(self, target, source, env, for_signature):
373 if env.has_key(self.var):
376 # The variable reference substitutes to nothing.
379 class FunctionAction(ActionBase):
380 """Class for Python function actions."""
382 def __init__(self, execfunction, strfunction=_null, varlist=[]):
383 self.execfunction = execfunction
384 if strfunction is _null:
385 def strfunction(target, source, env, execfunction=execfunction):
387 return '"' + str(s) + '"'
388 def array(a, q=quote):
389 return '[' + string.join(map(lambda x, q=q: q(x), a), ", ") + ']'
391 name = execfunction.__name__
392 except AttributeError:
394 name = execfunction.__class__.__name__
395 except AttributeError:
396 name = "unknown_python_function"
397 tstr = len(target) == 1 and quote(target[0]) or array(target)
398 sstr = len(source) == 1 and quote(source[0]) or array(source)
399 return "%s(%s, %s)" % (name, tstr, sstr)
400 self.strfunction = strfunction
401 self.varlist = varlist
403 def __call__(self, target, source, env):
405 if not SCons.Util.is_List(target):
407 if not SCons.Util.is_List(source):
409 if print_actions and self.strfunction:
410 s = self.strfunction(target, source, env)
414 rsources = map(rfile, source)
415 r = self.execfunction(target=target, source=rsources, env=env)
418 def get_contents(self, target, source, env):
419 """Return the signature contents of this callable action.
421 By providing direct access to the code object of the
422 function, Python makes this extremely easy. Hooray!
425 # "self.execfunction" is a function.
426 code = self.execfunction.func_code.co_code
428 # "self.execfunction" is a callable object.
429 code = self.execfunction.__call__.im_func.func_code.co_code
430 return str(code) + string.join(map(lambda v, e=env: str(e[v]),
433 class ListAction(ActionBase):
434 """Class for lists of other actions."""
435 def __init__(self, list):
436 self.list = map(lambda x: Action(x), list)
438 def get_actions(self):
441 def __call__(self, target, source, env):
443 r = l(target, source, env)
448 def get_contents(self, target, source, env):
449 """Return the signature contents of this action list.
451 Simple concatenation of the signatures of the elements.
453 return string.join(map(lambda x, t=target, s=source, e=env:
454 x.get_contents(t, s, e),