8 # Copyright (c) 2001, 2002, 2003 Steven Knight
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 class CommandGenerator:
69 Wraps a command generator function so the Action() factory
70 function can tell a generator function from a function action.
72 def __init__(self, generator):
73 self.generator = generator
75 def _do_create_action(act, strfunction=_null):
76 """This is the actual "implementation" for the
77 Action factory method, below. This handles the
78 fact that passing lists to Action() itself has
79 different semantics than passing lists as elements
82 The former will create a ListAction, the latter
83 will create a CommandAction by converting the inner
84 list elements to strings."""
86 if isinstance(act, ActionBase):
88 elif SCons.Util.is_List(act):
89 return CommandAction(act)
90 elif isinstance(act, CommandGenerator):
91 return CommandGeneratorAction(act.generator)
93 return FunctionAction(act, strfunction=strfunction)
94 elif SCons.Util.is_String(act):
95 var=SCons.Util.get_environment_var(act)
97 # This looks like a string that is purely an Environment
98 # variable reference, like "$FOO" or "${FOO}". We do
99 # something special here...we lazily evaluate the contents
100 # of that Environment variable, so a user could but something
101 # like a function or a CommandGenerator in that variable
102 # instead of a string.
103 return CommandGeneratorAction(LazyCmdGenerator(var))
104 listCmds = map(lambda x: CommandAction(string.split(x)),
105 string.split(act, '\n'))
106 if len(listCmds) == 1:
109 return ListAction(listCmds)
113 def Action(act, strfunction=_null):
114 """A factory for action objects."""
115 if SCons.Util.is_List(act):
116 acts = map(lambda x, s=strfunction: _do_create_action(x, s), act)
117 acts = filter(lambda x: not x is None, acts)
121 return ListAction(acts)
123 return _do_create_action(act, strfunction=strfunction)
126 """Base class for actions that create output objects."""
127 def __cmp__(self, other):
128 return cmp(self.__dict__, other.__dict__)
130 def show(self, string):
133 def get_actions(self):
136 def subst_dict(self, target, source, env):
137 """Create a dictionary for substitution of construction
140 This translates the following special arguments:
142 env - the construction environment itself,
143 the values of which (CC, CCFLAGS, etc.)
144 are copied straight into the dictionary
146 target - the target (object or array of objects),
147 used to generate the TARGET and TARGETS
148 construction variables
150 source - the source (object or array of objects),
151 used to generate the SOURCES and SOURCE
152 construction variables
157 for k,v in env.items(): dict[k] = v
159 if not SCons.Util.is_List(target):
162 dict['TARGETS'] = SCons.Util.PathList(map(os.path.normpath, map(str, target)))
164 dict['TARGET'] = dict['TARGETS'][0]
169 except AttributeError:
171 if not SCons.Util.is_List(source):
173 dict['SOURCES'] = SCons.Util.PathList(map(os.path.normpath, map(rstr, source)))
175 dict['SOURCE'] = dict['SOURCES'][0]
179 def _string_from_cmd_list(cmd_list):
180 """Takes a list of command line arguments and returns a pretty
181 representation for printing."""
183 for arg in map(str, cmd_list):
184 if ' ' in arg or '\t' in arg:
185 arg = '"' + arg + '"'
187 return string.join(cl)
189 _rm = re.compile(r'\$[()]')
190 _remove = re.compile(r'\$\(([^\$]|\$[^\(])*?\$\)')
192 class CommandAction(ActionBase):
193 """Class for command-execution actions."""
194 def __init__(self, cmd):
197 def __call__(self, target, source, env):
198 """Execute a command action.
200 This will handle lists of commands as well as individual commands,
201 because construction variable substitution may turn a single
202 "command" into a list. This means that this class can actually
203 handle lists of commands, even though that's not how we use it
208 escape = env.get('ESCAPE', lambda x: x)
210 if env.has_key('SHELL'):
213 raise SCons.Errors.UserError('Missing SHELL construction variable.')
215 if env.has_key('SPAWN'):
218 raise SCons.Errors.UserError('Missing SPAWN construction variable.')
220 dict = self.subst_dict(target, source, env)
221 cmd_list = SCons.Util.scons_subst_list(self.cmd_list, dict, {}, _rm)
222 for cmd_line in cmd_list:
225 self.show(_string_from_cmd_list(cmd_line))
232 import SCons.Environment
233 default_ENV = SCons.Environment.Environment()['ENV']
235 # Escape the command line for the command
236 # interpreter we are using
237 map(lambda x, e=escape: x.escape(e), cmd_line)
238 cmd_line = map(str, cmd_line)
239 ret = spawn(shell, escape, cmd_line[0], cmd_line, ENV)
244 def _sig_dict(self, target, source, env):
245 """Supply a dictionary for use in computing signatures.
247 For signature purposes, it doesn't matter what targets or
248 sources we use, so long as we use the same ones every time
249 so the signature stays the same. We supply an array of two
250 of each to allow for distinction between TARGET and TARGETS.
252 return self.subst_dict(['__t1__', '__t2__'], ['__s1__', '__s2__'], env)
254 def get_raw_contents(self, target, source, env):
255 """Return the complete contents of this action's command line.
257 return SCons.Util.scons_subst(string.join(self.cmd_list),
258 self._sig_dict(target, source, env), {})
260 def get_contents(self, target, source, env):
261 """Return the signature contents of this action's command line.
263 This strips $(-$) and everything in between the string,
264 since those parts don't affect signatures.
266 return SCons.Util.scons_subst(string.join(map(str, self.cmd_list)),
267 self._sig_dict(target, source, env), {}, _remove)
269 class CommandGeneratorAction(ActionBase):
270 """Class for command-generator actions."""
271 def __init__(self, generator):
272 self.generator = generator
274 def __generate(self, target, source, env, for_signature):
275 # ensure that target is a list, to make it easier to write
276 # generator functions:
277 if not SCons.Util.is_List(target):
280 ret = self.generator(target=target, source=source, env=env, for_signature=for_signature)
281 gen_cmd = Action(ret)
283 raise SCons.Errors.UserError("Object returned from command generator: %s cannot be used to create an Action." % repr(ret))
286 def __call__(self, target, source, env):
287 if not SCons.Util.is_List(source):
289 rsources = map(rfile, source)
290 act = self.__generate(target, source, env, 0)
291 return act(target, rsources, 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 self.__generate(target, source, env, 1).get_contents(target, source, env)
301 class LazyCmdGenerator:
302 """This is a simple callable class that acts as a command generator.
303 It holds on to a key into an Environment dictionary, then waits
304 until execution time to see what type it is, then tries to
305 create an Action out of it."""
306 def __init__(self, var):
307 self.var = SCons.Util.to_String(var)
309 def __call__(self, target, source, env, for_signature):
310 if env.has_key(self.var):
313 # The variable reference substitutes to nothing.
316 class FunctionAction(ActionBase):
317 """Class for Python function actions."""
319 def __init__(self, execfunction, strfunction=_null):
320 self.execfunction = execfunction
321 if strfunction is _null:
322 def strfunction(target, source, execfunction=execfunction):
324 return '"' + str(s) + '"'
326 name = execfunction.__name__
327 except AttributeError:
329 name = execfunction.__class__.__name__
330 except AttributeError:
331 name = "unknown_python_function"
333 tstr = quote(target[0])
335 tstr = str(map(lambda x, q=quote: q(x), target))
337 sstr = quote(source[0])
339 sstr = str(map(lambda x, q=quote: q(x), source))
340 return "%s(%s, %s)" % (name, tstr, sstr)
341 self.strfunction = strfunction
343 def __call__(self, target, source, env):
345 if not SCons.Util.is_List(target):
347 if not SCons.Util.is_List(source):
349 if print_actions and self.strfunction:
350 s = self.strfunction(target, source)
354 rsources = map(rfile, source)
355 r = self.execfunction(target=target, source=rsources, env=env)
358 def get_contents(self, target, source, env):
359 """Return the signature contents of this callable action.
361 By providing direct access to the code object of the
362 function, Python makes this extremely easy. Hooray!
364 #XXX DOES NOT ACCOUNT FOR CHANGES IN ENVIRONMENT VARIABLES
365 #THE FUNCTION MAY USE
367 # "self.execfunction" is a function.
368 code = self.execfunction.func_code.co_code
370 # "self.execfunction" is a callable object.
371 code = self.execfunction.__call__.im_func.func_code.co_code
374 class ListAction(ActionBase):
375 """Class for lists of other actions."""
376 def __init__(self, list):
377 self.list = map(lambda x: Action(x), list)
379 def get_actions(self):
382 def __call__(self, target, source, env):
384 r = l(target, source, env)
389 def get_contents(self, target, source, env):
390 """Return the signature contents of this action list.
392 Simple concatenation of the signatures of the elements.
394 return string.join(map(lambda x, t=target, s=source, e=env:
395 x.get_contents(t, s, e),