1"""distutils.spawn 2 3Provides the 'spawn()' function, a front-end to various platform- 4specific functions for launching another program in a sub-process. 5Also provides the 'find_executable()' to search the path for a given 6executable name. 7""" 8 9import sys 10import os 11import subprocess 12 13from distutils.errors import DistutilsPlatformError, DistutilsExecError 14from distutils.debug import DEBUG 15from distutils import log 16 17 18if sys.platform == 'darwin': 19 _cfg_target = None 20 _cfg_target_split = None 21 22 23def spawn(cmd, search_path=1, verbose=0, dry_run=0, env=None): 24 """Run another program, specified as a command list 'cmd', in a new process. 25 26 'cmd' is just the argument list for the new process, ie. 27 cmd[0] is the program to run and cmd[1:] are the rest of its arguments. 28 There is no way to run a program with a name different from that of its 29 executable. 30 31 If 'search_path' is true (the default), the system's executable 32 search path will be used to find the program; otherwise, cmd[0] 33 must be the exact path to the executable. If 'dry_run' is true, 34 the command will not actually be run. 35 36 Raise DistutilsExecError if running the program fails in any way; just 37 return on success. 38 """ 39 # cmd is documented as a list, but just in case some code passes a tuple 40 # in, protect our %-formatting code against horrible death 41 cmd = list(cmd) 42 43 log.info(' '.join(cmd)) 44 if dry_run: 45 return 46 47 if search_path: 48 executable = find_executable(cmd[0]) 49 if executable is not None: 50 cmd[0] = executable 51 52 env = env if env is not None else dict(os.environ) 53 54 if sys.platform == 'darwin': 55 global _cfg_target, _cfg_target_split 56 if _cfg_target is None: 57 from distutils import sysconfig 58 _cfg_target = sysconfig.get_config_var( 59 'MACOSX_DEPLOYMENT_TARGET') or '' 60 if _cfg_target: 61 _cfg_target_split = [int(x) for x in _cfg_target.split('.')] 62 if _cfg_target: 63 # ensure that the deployment target of build process is not less 64 # than that used when the interpreter was built. This ensures 65 # extension modules are built with correct compatibility values 66 cur_target = os.environ.get('MACOSX_DEPLOYMENT_TARGET', _cfg_target) 67 if _cfg_target_split > [int(x) for x in cur_target.split('.')]: 68 my_msg = ('$MACOSX_DEPLOYMENT_TARGET mismatch: ' 69 'now "%s" but "%s" during configure' 70 % (cur_target, _cfg_target)) 71 raise DistutilsPlatformError(my_msg) 72 env.update(MACOSX_DEPLOYMENT_TARGET=cur_target) 73 74 try: 75 proc = subprocess.Popen(cmd, env=env) 76 proc.wait() 77 exitcode = proc.returncode 78 except OSError as exc: 79 if not DEBUG: 80 cmd = cmd[0] 81 raise DistutilsExecError( 82 "command %r failed: %s" % (cmd, exc.args[-1])) from exc 83 84 if exitcode: 85 if not DEBUG: 86 cmd = cmd[0] 87 raise DistutilsExecError( 88 "command %r failed with exit code %s" % (cmd, exitcode)) 89 90 91def find_executable(executable, path=None): 92 """Tries to find 'executable' in the directories listed in 'path'. 93 94 A string listing directories separated by 'os.pathsep'; defaults to 95 os.environ['PATH']. Returns the complete filename or None if not found. 96 """ 97 _, ext = os.path.splitext(executable) 98 if (sys.platform == 'win32') and (ext != '.exe'): 99 executable = executable + '.exe' 100 101 if os.path.isfile(executable): 102 return executable 103 104 if path is None: 105 path = os.environ.get('PATH', None) 106 if path is None: 107 try: 108 path = os.confstr("CS_PATH") 109 except (AttributeError, ValueError): 110 # os.confstr() or CS_PATH is not available 111 path = os.defpath 112 # bpo-35755: Don't use os.defpath if the PATH environment variable is 113 # set to an empty string 114 115 # PATH='' doesn't match, whereas PATH=':' looks in the current directory 116 if not path: 117 return None 118 119 paths = path.split(os.pathsep) 120 for p in paths: 121 f = os.path.join(p, executable) 122 if os.path.isfile(f): 123 # the file exists, we have a shot at spawn working 124 return f 125 return None 126