spawn.py 7.0 KB

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