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