8 # Copyright (c) 2001 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__"
48 if os.name == 'posix':
50 def defaultSpawn(cmd, args, env):
55 args = [ 'sh', '-c' ] + \
56 [ string.join(map(lambda x: string.replace(str(x),
61 os.execvpe('sh', args, env)
63 exitval = exitvalmap[e[0]]
64 sys.stderr.write("scons: %s: %s\n" % (cmd, e[1]))
68 pid, stat = os.waitpid(pid, 0)
74 def pathsearch(cmd, env):
75 # In order to deal with the fact that 1.5.2 doesn't have
76 # os.spawnvpe(), roll our own PATH search.
77 if os.path.isabs(cmd):
78 if not os.path.exists(cmd):
80 if not SCons.Util.is_List(exts):
81 exts = string.split(exts, os.pathsep)
90 if not SCons.Util.is_List(path):
91 path = string.split(path, os.pathsep)
93 if not SCons.Util.is_List(exts):
94 exts = string.split(exts, os.pathsep)
98 pairs.append((dir, e))
99 for dir, ext in pairs:
100 f = os.path.join(dir, cmd)
103 if os.path.exists(f):
107 # Attempt to find cmd.exe (for WinNT/2k/XP) or
108 # command.com for Win9x
111 # First see if we can look in the registry...
112 if SCons.Util.can_read_reg:
114 # Look for Windows NT system root
115 k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE,
116 'Software\\Microsoft\\Windows NT\\CurrentVersion')
117 val, tok = SCons.Util.RegQueryValueEx(k, 'SystemRoot')
118 cmd_interp = os.path.join(val, 'System32\\cmd.exe')
119 except SCons.Util.RegError:
121 # Okay, try the Windows 9x system root
122 k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE,
123 'Software\\Microsoft\\Windows\\CurrentVersion')
124 val, tok = SCons.Util.RegQueryValueEx(k, 'SystemRoot')
125 cmd_interp = os.path.join(val, 'command.com')
129 cmd_interp = pathsearch('cmd', os.environ)
131 cmd_interp = pathsearch('command', os.environ)
133 # The upshot of all this is that, if you are using Python 1.5.2,
134 # you had better have cmd or command.com in your PATH when you run
137 def defaultSpawn(cmd, args, env):
139 sys.stderr.write("scons: Could not find command interpreter, is it in your PATH?\n")
143 args = [ cmd_interp, '/C' ] + args
144 ret = os.spawnve(os.P_WAIT, cmd_interp, args, env)
146 ret = exitvalmap[e[0]]
147 sys.stderr.write("scons: %s: %s\n" % (cmd, e[1]))
150 def defaultSpawn(cmd, args, env):
151 sys.stderr.write("scons: Unknown os '%s', cannot spawn command interpreter.\n" % os.name)
152 sys.stderr.write("scons: Set your command handler with SetCommandHandler().\n")
157 def SetCommandHandler(func):
162 """A factory for action objects."""
163 if isinstance(act, ActionBase):
166 return FunctionAction(act)
167 elif SCons.Util.is_String(act):
168 return CommandAction(act)
169 elif SCons.Util.is_List(act):
170 return ListAction(act)
175 """Base class for actions that create output objects."""
176 def __cmp__(self, other):
177 return cmp(self.__dict__, other.__dict__)
179 def show(self, string):
182 def subst_dict(self, **kw):
183 """Create a dictionary for substitution of construction
186 This translates the following special arguments:
188 env - the construction environment itself,
189 the values of which (CC, CCFLAGS, etc.)
190 are copied straight into the dictionary
192 target - the target (object or array of objects),
193 used to generate the TARGET and TARGETS
194 construction variables
196 source - the source (object or array of objects),
197 used to generate the SOURCES construction
200 Any other keyword arguments are copied into the
204 if kw.has_key('env'):
205 dict.update(kw['env'])
215 if kw.has_key('target'):
218 if not SCons.Util.is_List(t):
222 except AttributeError:
224 dict['TARGETS'] = SCons.Util.PathList(map(os.path.normpath, map(str, t)))
226 dict['TARGET'] = dict['TARGETS'][0]
228 if kw.has_key('source'):
231 if not SCons.Util.is_List(s):
233 dict['SOURCES'] = SCons.Util.PathList(map(os.path.normpath, map(str, s)))
237 # Autogenerate necessary construction variables.
238 SCons.Util.autogenerate(dict, dir = cwd)
242 _rm = re.compile(r'\$[()]')
243 _remove = re.compile(r'\$\(([^\$]|\$[^\(])*?\$\)')
245 class CommandAction(ActionBase):
246 """Class for command-execution actions."""
247 def __init__(self, string):
248 self.command = string
250 def execute(self, **kw):
252 dict = apply(self.subst_dict, (), kw)
253 cmd_list = SCons.Util.scons_subst_list(self.command, dict, {}, _rm)
254 for cmd_line in cmd_list:
257 self.show(string.join(cmd_line))
260 ENV = kw['env']['ENV']
262 import SCons.Defaults
263 ENV = SCons.Defaults.ConstructionEnvironment['ENV']
264 ret = spawn(cmd_line[0], cmd_line, ENV)
269 def _sig_dict(self, kw):
270 """Supply a dictionary for use in computing signatures.
272 For signature purposes, it doesn't matter what targets or
273 sources we use, so long as we use the same ones every time
274 so the signature stays the same. We supply an array of two
275 of each to allow for distinction between TARGET and TARGETS.
277 kw['target'] = ['__t1__', '__t2__']
278 kw['source'] = ['__s1__', '__s2__']
279 return apply(self.subst_dict, (), kw)
281 def get_raw_contents(self, **kw):
282 """Return the complete contents of this action's command line.
284 return SCons.Util.scons_subst(self.command, self._sig_dict(kw), {})
286 def get_contents(self, **kw):
287 """Return the signature contents of this action's command line.
289 This strips $(-$) and everything in between the string,
290 since those parts don't affect signatures.
292 return SCons.Util.scons_subst(self.command, self._sig_dict(kw), {}, _remove)
294 class FunctionAction(ActionBase):
295 """Class for Python function actions."""
296 def __init__(self, function):
297 self.function = function
299 def execute(self, **kw):
301 # XXX: WHAT SHOULD WE PRINT HERE?
303 if kw.has_key('target'):
304 if SCons.Util.is_List(kw['target']):
305 kw['target'] = map(str, kw['target'])
307 kw['target'] = str(kw['target'])
308 if kw.has_key('source'):
309 kw['source'] = map(str, kw['source'])
310 return apply(self.function, (), kw)
312 def get_contents(self, **kw):
313 """Return the signature contents of this callable action.
315 By providing direct access to the code object of the
316 function, Python makes this extremely easy. Hooray!
318 #XXX DOES NOT ACCOUNT FOR CHANGES IN ENVIRONMENT VARIABLES
319 #THE FUNCTION MAY USE
321 # "self.function" is a function.
322 code = self.function.func_code.co_code
324 # "self.function" is a callable object.
325 code = self.function.__call__.im_func.func_code.co_code
328 class ListAction(ActionBase):
329 """Class for lists of other actions."""
330 def __init__(self, list):
331 self.list = map(lambda x: Action(x), list)
333 def execute(self, **kw):
335 r = apply(l.execute, (), kw)
340 def get_contents(self, **kw):
341 """Return the signature contents of this action list.
343 Simple concatenation of the signatures of the elements.
346 return reduce(lambda x, y: x + str(y.get_contents()), self.list, "")