spawn.py revision be19ed77ddb047e02fe94d142181062af6d99dcc
1"""distutils.spawn 2 3Provides the 'spawn()' function, a front-end to various platform- 4specific functions for launching another program in a sub-process. 5Also provides the 'find_executable()' to search the path for a given 6executable name. 7""" 8 9# This module should be kept compatible with Python 2.1. 10 11__revision__ = "$Id$" 12 13import sys, os, string 14from distutils.errors import * 15from distutils import log 16 17def spawn (cmd, 18 search_path=1, 19 verbose=0, 20 dry_run=0): 21 22 """Run another program, specified as a command list 'cmd', in a new 23 process. 'cmd' is just the argument list for the new process, ie. 24 cmd[0] is the program to run and cmd[1:] are the rest of its arguments. 25 There is no way to run a program with a name different from that of its 26 executable. 27 28 If 'search_path' is true (the default), the system's executable 29 search path will be used to find the program; otherwise, cmd[0] 30 must be the exact path to the executable. If 'dry_run' is true, 31 the command will not actually be run. 32 33 Raise DistutilsExecError if running the program fails in any way; just 34 return on success. 35 """ 36 if os.name == 'posix': 37 _spawn_posix(cmd, search_path, dry_run=dry_run) 38 elif os.name == 'nt': 39 _spawn_nt(cmd, search_path, dry_run=dry_run) 40 elif os.name == 'os2': 41 _spawn_os2(cmd, search_path, dry_run=dry_run) 42 else: 43 raise DistutilsPlatformError, \ 44 "don't know how to spawn programs on platform '%s'" % os.name 45 46# spawn () 47 48 49def _nt_quote_args (args): 50 """Quote command-line arguments for DOS/Windows conventions: just 51 wraps every argument which contains blanks in double quotes, and 52 returns a new argument list. 53 """ 54 55 # XXX this doesn't seem very robust to me -- but if the Windows guys 56 # say it'll work, I guess I'll have to accept it. (What if an arg 57 # contains quotes? What other magic characters, other than spaces, 58 # have to be escaped? Is there an escaping mechanism other than 59 # quoting?) 60 61 for i in range(len(args)): 62 if string.find(args[i], ' ') != -1: 63 args[i] = '"%s"' % args[i] 64 return args 65 66def _spawn_nt (cmd, 67 search_path=1, 68 verbose=0, 69 dry_run=0): 70 71 executable = cmd[0] 72 cmd = _nt_quote_args(cmd) 73 if search_path: 74 # either we find one or it stays the same 75 executable = find_executable(executable) or executable 76 log.info(string.join([executable] + cmd[1:], ' ')) 77 if not dry_run: 78 # spawn for NT requires a full path to the .exe 79 try: 80 rc = os.spawnv(os.P_WAIT, executable, cmd) 81 except OSError as exc: 82 # this seems to happen when the command isn't found 83 raise DistutilsExecError, \ 84 "command '%s' failed: %s" % (cmd[0], exc[-1]) 85 if rc != 0: 86 # and this reflects the command running but failing 87 raise DistutilsExecError, \ 88 "command '%s' failed with exit status %d" % (cmd[0], rc) 89 90 91def _spawn_os2 (cmd, 92 search_path=1, 93 verbose=0, 94 dry_run=0): 95 96 executable = cmd[0] 97 #cmd = _nt_quote_args(cmd) 98 if search_path: 99 # either we find one or it stays the same 100 executable = find_executable(executable) or executable 101 log.info(string.join([executable] + cmd[1:], ' ')) 102 if not dry_run: 103 # spawnv for OS/2 EMX requires a full path to the .exe 104 try: 105 rc = os.spawnv(os.P_WAIT, executable, cmd) 106 except OSError as exc: 107 # this seems to happen when the command isn't found 108 raise DistutilsExecError, \ 109 "command '%s' failed: %s" % (cmd[0], exc[-1]) 110 if rc != 0: 111 # and this reflects the command running but failing 112 print("command '%s' failed with exit status %d" % (cmd[0], rc)) 113 raise DistutilsExecError, \ 114 "command '%s' failed with exit status %d" % (cmd[0], rc) 115 116 117def _spawn_posix (cmd, 118 search_path=1, 119 verbose=0, 120 dry_run=0): 121 122 log.info(string.join(cmd, ' ')) 123 if dry_run: 124 return 125 exec_fn = search_path and os.execvp or os.execv 126 127 pid = os.fork() 128 129 if pid == 0: # in the child 130 try: 131 #print "cmd[0] =", cmd[0] 132 #print "cmd =", cmd 133 exec_fn(cmd[0], cmd) 134 except OSError as e: 135 sys.stderr.write("unable to execute %s: %s\n" % 136 (cmd[0], e.strerror)) 137 os._exit(1) 138 139 sys.stderr.write("unable to execute %s for unknown reasons" % cmd[0]) 140 os._exit(1) 141 142 143 else: # in the parent 144 # Loop until the child either exits or is terminated by a signal 145 # (ie. keep waiting if it's merely stopped) 146 while 1: 147 try: 148 (pid, status) = os.waitpid(pid, 0) 149 except OSError as exc: 150 import errno 151 if exc.errno == errno.EINTR: 152 continue 153 raise DistutilsExecError, \ 154 "command '%s' failed: %s" % (cmd[0], exc[-1]) 155 if os.WIFSIGNALED(status): 156 raise DistutilsExecError, \ 157 "command '%s' terminated by signal %d" % \ 158 (cmd[0], os.WTERMSIG(status)) 159 160 elif os.WIFEXITED(status): 161 exit_status = os.WEXITSTATUS(status) 162 if exit_status == 0: 163 return # hey, it succeeded! 164 else: 165 raise DistutilsExecError, \ 166 "command '%s' failed with exit status %d" % \ 167 (cmd[0], exit_status) 168 169 elif os.WIFSTOPPED(status): 170 continue 171 172 else: 173 raise DistutilsExecError, \ 174 "unknown error executing '%s': termination status %d" % \ 175 (cmd[0], status) 176# _spawn_posix () 177 178 179def find_executable(executable, path=None): 180 """Try to find 'executable' in the directories listed in 'path' (a 181 string listing directories separated by 'os.pathsep'; defaults to 182 os.environ['PATH']). Returns the complete filename or None if not 183 found. 184 """ 185 if path is None: 186 path = os.environ['PATH'] 187 paths = string.split(path, os.pathsep) 188 (base, ext) = os.path.splitext(executable) 189 if (sys.platform == 'win32' or os.name == 'os2') and (ext != '.exe'): 190 executable = executable + '.exe' 191 if not os.path.isfile(executable): 192 for p in paths: 193 f = os.path.join(p, executable) 194 if os.path.isfile(f): 195 # the file exists, we have a shot at spawn working 196 return f 197 return None 198 else: 199 return executable 200 201# find_executable() 202