You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

194 lines
7.6 KiB

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