spawn.py 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  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. import subprocess
  10. from distutils.errors import DistutilsExecError
  11. from distutils.debug import DEBUG
  12. from distutils import log
  13. def spawn(cmd, search_path=1, verbose=0, dry_run=0, env=None): # noqa: C901
  14. """Run another program, specified as a command list 'cmd', in a new process.
  15. 'cmd' is just the argument list for the new process, ie.
  16. cmd[0] is the program to run and cmd[1:] are the rest of its arguments.
  17. There is no way to run a program with a name different from that of its
  18. executable.
  19. If 'search_path' is true (the default), the system's executable
  20. search path will be used to find the program; otherwise, cmd[0]
  21. must be the exact path to the executable. If 'dry_run' is true,
  22. the command will not actually be run.
  23. Raise DistutilsExecError if running the program fails in any way; just
  24. return on success.
  25. """
  26. # cmd is documented as a list, but just in case some code passes a tuple
  27. # in, protect our %-formatting code against horrible death
  28. cmd = list(cmd)
  29. log.info(subprocess.list2cmdline(cmd))
  30. if dry_run:
  31. return
  32. if search_path:
  33. executable = find_executable(cmd[0])
  34. if executable is not None:
  35. cmd[0] = executable
  36. env = env if env is not None else dict(os.environ)
  37. if sys.platform == 'darwin':
  38. from distutils.util import MACOSX_VERSION_VAR, get_macosx_target_ver
  39. macosx_target_ver = get_macosx_target_ver()
  40. if macosx_target_ver:
  41. env[MACOSX_VERSION_VAR] = macosx_target_ver
  42. try:
  43. proc = subprocess.Popen(cmd, env=env)
  44. proc.wait()
  45. exitcode = proc.returncode
  46. except OSError as exc:
  47. if not DEBUG:
  48. cmd = cmd[0]
  49. raise DistutilsExecError(
  50. "command {!r} failed: {}".format(cmd, exc.args[-1])
  51. ) from exc
  52. if exitcode:
  53. if not DEBUG:
  54. cmd = cmd[0]
  55. raise DistutilsExecError(
  56. "command {!r} failed with exit code {}".format(cmd, exitcode)
  57. )
  58. def find_executable(executable, path=None):
  59. """Tries to find 'executable' in the directories listed in 'path'.
  60. A string listing directories separated by 'os.pathsep'; defaults to
  61. os.environ['PATH']. Returns the complete filename or None if not found.
  62. """
  63. _, ext = os.path.splitext(executable)
  64. if (sys.platform == 'win32') and (ext != '.exe'):
  65. executable = executable + '.exe'
  66. if os.path.isfile(executable):
  67. return executable
  68. if path is None:
  69. path = os.environ.get('PATH', None)
  70. if path is None:
  71. try:
  72. path = os.confstr("CS_PATH")
  73. except (AttributeError, ValueError):
  74. # os.confstr() or CS_PATH is not available
  75. path = os.defpath
  76. # bpo-35755: Don't use os.defpath if the PATH environment variable is
  77. # set to an empty string
  78. # PATH='' doesn't match, whereas PATH=':' looks in the current directory
  79. if not path:
  80. return None
  81. paths = path.split(os.pathsep)
  82. for p in paths:
  83. f = os.path.join(p, executable)
  84. if os.path.isfile(f):
  85. # the file exists, we have a shot at spawn working
  86. return f
  87. return None