Commit | Line | Data |
---|---|---|
1e4a197e RD |
1 | """distutils.spawn |
2 | ||
3 | Provides the 'spawn()' function, a front-end to various platform- | |
4 | specific functions for launching another program in a sub-process. | |
5 | Also provides the 'find_executable()' to search the path for a given | |
6 | executable name. | |
7 | """ | |
8 | ||
9 | # This module should be kept compatible with Python 1.5.2. | |
10 | ||
11 | __revision__ = "$Id$" | |
12 | ||
13 | import sys, os, string | |
14 | from distutils.errors import * | |
15 | from distutils import log | |
16 | ||
17 | def 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 | ||
49 | def _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 | ||
66 | def _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, 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 | ||
91 | def _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, 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 | ||
117 | def _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, 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 | (pid, status) = os.waitpid(pid, 0) | |
148 | if os.WIFSIGNALED(status): | |
149 | raise DistutilsExecError, \ | |
150 | "command '%s' terminated by signal %d" % \ | |
151 | (cmd[0], os.WTERMSIG(status)) | |
152 | ||
153 | elif os.WIFEXITED(status): | |
154 | exit_status = os.WEXITSTATUS(status) | |
155 | if exit_status == 0: | |
156 | return # hey, it succeeded! | |
157 | else: | |
158 | raise DistutilsExecError, \ | |
159 | "command '%s' failed with exit status %d" % \ | |
160 | (cmd[0], exit_status) | |
161 | ||
162 | elif os.WIFSTOPPED(status): | |
163 | continue | |
164 | ||
165 | else: | |
166 | raise DistutilsExecError, \ | |
167 | "unknown error executing '%s': termination status %d" % \ | |
168 | (cmd[0], status) | |
169 | # _spawn_posix () | |
170 | ||
171 | ||
172 | def find_executable(executable, path=None): | |
173 | """Try to find 'executable' in the directories listed in 'path' (a | |
174 | string listing directories separated by 'os.pathsep'; defaults to | |
175 | os.environ['PATH']). Returns the complete filename or None if not | |
176 | found. | |
177 | """ | |
178 | if path is None: | |
179 | path = os.environ['PATH'] | |
180 | paths = string.split(path, os.pathsep) | |
181 | (base, ext) = os.path.splitext(executable) | |
182 | if (sys.platform == 'win32' or os.name == 'os2') and (ext != '.exe'): | |
183 | executable = executable + '.exe' | |
184 | if not os.path.isfile(executable): | |
185 | for p in paths: | |
186 | f = os.path.join(p, executable) | |
187 | if os.path.isfile(f): | |
188 | # the file exists, we have a shot at spawn working | |
189 | return f | |
190 | return None | |
191 | else: | |
192 | return executable | |
193 | ||
194 | # find_executable() |