Path: blob/main/test/lib/python3.9/site-packages/setuptools/_distutils/msvc9compiler.py
4799 views
"""distutils.msvc9compiler12Contains MSVCCompiler, an implementation of the abstract CCompiler class3for the Microsoft Visual Studio 2008.45The module is compatible with VS 2005 and VS 2008. You can find legacy support6for older versions of VS in distutils.msvccompiler.7"""89# Written by Perry Stoll10# hacked by Robin Becker and Thomas Heller to do a better job of11# finding DevStudio (through the registry)12# ported to VS2005 and VS 2008 by Christian Heimes1314import os15import subprocess16import sys17import re1819from distutils.errors import DistutilsExecError, DistutilsPlatformError, \20CompileError, LibError, LinkError21from distutils.ccompiler import CCompiler, gen_lib_options22from distutils import log23from distutils.util import get_platform2425import winreg2627RegOpenKeyEx = winreg.OpenKeyEx28RegEnumKey = winreg.EnumKey29RegEnumValue = winreg.EnumValue30RegError = winreg.error3132HKEYS = (winreg.HKEY_USERS,33winreg.HKEY_CURRENT_USER,34winreg.HKEY_LOCAL_MACHINE,35winreg.HKEY_CLASSES_ROOT)3637NATIVE_WIN64 = (sys.platform == 'win32' and sys.maxsize > 2**32)38if NATIVE_WIN64:39# Visual C++ is a 32-bit application, so we need to look in40# the corresponding registry branch, if we're running a41# 64-bit Python on Win6442VS_BASE = r"Software\Wow6432Node\Microsoft\VisualStudio\%0.1f"43WINSDK_BASE = r"Software\Wow6432Node\Microsoft\Microsoft SDKs\Windows"44NET_BASE = r"Software\Wow6432Node\Microsoft\.NETFramework"45else:46VS_BASE = r"Software\Microsoft\VisualStudio\%0.1f"47WINSDK_BASE = r"Software\Microsoft\Microsoft SDKs\Windows"48NET_BASE = r"Software\Microsoft\.NETFramework"4950# A map keyed by get_platform() return values to values accepted by51# 'vcvarsall.bat'. Note a cross-compile may combine these (eg, 'x86_amd64' is52# the param to cross-compile on x86 targeting amd64.)53PLAT_TO_VCVARS = {54'win32' : 'x86',55'win-amd64' : 'amd64',56}5758class Reg:59"""Helper class to read values from the registry60"""6162def get_value(cls, path, key):63for base in HKEYS:64d = cls.read_values(base, path)65if d and key in d:66return d[key]67raise KeyError(key)68get_value = classmethod(get_value)6970def read_keys(cls, base, key):71"""Return list of registry keys."""72try:73handle = RegOpenKeyEx(base, key)74except RegError:75return None76L = []77i = 078while True:79try:80k = RegEnumKey(handle, i)81except RegError:82break83L.append(k)84i += 185return L86read_keys = classmethod(read_keys)8788def read_values(cls, base, key):89"""Return dict of registry keys and values.9091All names are converted to lowercase.92"""93try:94handle = RegOpenKeyEx(base, key)95except RegError:96return None97d = {}98i = 099while True:100try:101name, value, type = RegEnumValue(handle, i)102except RegError:103break104name = name.lower()105d[cls.convert_mbcs(name)] = cls.convert_mbcs(value)106i += 1107return d108read_values = classmethod(read_values)109110def convert_mbcs(s):111dec = getattr(s, "decode", None)112if dec is not None:113try:114s = dec("mbcs")115except UnicodeError:116pass117return s118convert_mbcs = staticmethod(convert_mbcs)119120class MacroExpander:121122def __init__(self, version):123self.macros = {}124self.vsbase = VS_BASE % version125self.load_macros(version)126127def set_macro(self, macro, path, key):128self.macros["$(%s)" % macro] = Reg.get_value(path, key)129130def load_macros(self, version):131self.set_macro("VCInstallDir", self.vsbase + r"\Setup\VC", "productdir")132self.set_macro("VSInstallDir", self.vsbase + r"\Setup\VS", "productdir")133self.set_macro("FrameworkDir", NET_BASE, "installroot")134try:135if version >= 8.0:136self.set_macro("FrameworkSDKDir", NET_BASE,137"sdkinstallrootv2.0")138else:139raise KeyError("sdkinstallrootv2.0")140except KeyError:141raise DistutilsPlatformError(142"""Python was built with Visual Studio 2008;143extensions must be built with a compiler than can generate compatible binaries.144Visual Studio 2008 was not found on this system. If you have Cygwin installed,145you can try compiling with MingW32, by passing "-c mingw32" to setup.py.""")146147if version >= 9.0:148self.set_macro("FrameworkVersion", self.vsbase, "clr version")149self.set_macro("WindowsSdkDir", WINSDK_BASE, "currentinstallfolder")150else:151p = r"Software\Microsoft\NET Framework Setup\Product"152for base in HKEYS:153try:154h = RegOpenKeyEx(base, p)155except RegError:156continue157key = RegEnumKey(h, 0)158d = Reg.get_value(base, r"%s\%s" % (p, key))159self.macros["$(FrameworkVersion)"] = d["version"]160161def sub(self, s):162for k, v in self.macros.items():163s = s.replace(k, v)164return s165166def get_build_version():167"""Return the version of MSVC that was used to build Python.168169For Python 2.3 and up, the version number is included in170sys.version. For earlier versions, assume the compiler is MSVC 6.171"""172prefix = "MSC v."173i = sys.version.find(prefix)174if i == -1:175return 6176i = i + len(prefix)177s, rest = sys.version[i:].split(" ", 1)178majorVersion = int(s[:-2]) - 6179if majorVersion >= 13:180# v13 was skipped and should be v14181majorVersion += 1182minorVersion = int(s[2:3]) / 10.0183# I don't think paths are affected by minor version in version 6184if majorVersion == 6:185minorVersion = 0186if majorVersion >= 6:187return majorVersion + minorVersion188# else we don't know what version of the compiler this is189return None190191def normalize_and_reduce_paths(paths):192"""Return a list of normalized paths with duplicates removed.193194The current order of paths is maintained.195"""196# Paths are normalized so things like: /a and /a/ aren't both preserved.197reduced_paths = []198for p in paths:199np = os.path.normpath(p)200# XXX(nnorwitz): O(n**2), if reduced_paths gets long perhaps use a set.201if np not in reduced_paths:202reduced_paths.append(np)203return reduced_paths204205def removeDuplicates(variable):206"""Remove duplicate values of an environment variable.207"""208oldList = variable.split(os.pathsep)209newList = []210for i in oldList:211if i not in newList:212newList.append(i)213newVariable = os.pathsep.join(newList)214return newVariable215216def find_vcvarsall(version):217"""Find the vcvarsall.bat file218219At first it tries to find the productdir of VS 2008 in the registry. If220that fails it falls back to the VS90COMNTOOLS env var.221"""222vsbase = VS_BASE % version223try:224productdir = Reg.get_value(r"%s\Setup\VC" % vsbase,225"productdir")226except KeyError:227log.debug("Unable to find productdir in registry")228productdir = None229230if not productdir or not os.path.isdir(productdir):231toolskey = "VS%0.f0COMNTOOLS" % version232toolsdir = os.environ.get(toolskey, None)233234if toolsdir and os.path.isdir(toolsdir):235productdir = os.path.join(toolsdir, os.pardir, os.pardir, "VC")236productdir = os.path.abspath(productdir)237if not os.path.isdir(productdir):238log.debug("%s is not a valid directory" % productdir)239return None240else:241log.debug("Env var %s is not set or invalid" % toolskey)242if not productdir:243log.debug("No productdir found")244return None245vcvarsall = os.path.join(productdir, "vcvarsall.bat")246if os.path.isfile(vcvarsall):247return vcvarsall248log.debug("Unable to find vcvarsall.bat")249return None250251def query_vcvarsall(version, arch="x86"):252"""Launch vcvarsall.bat and read the settings from its environment253"""254vcvarsall = find_vcvarsall(version)255interesting = {"include", "lib", "libpath", "path"}256result = {}257258if vcvarsall is None:259raise DistutilsPlatformError("Unable to find vcvarsall.bat")260log.debug("Calling 'vcvarsall.bat %s' (version=%s)", arch, version)261popen = subprocess.Popen('"%s" %s & set' % (vcvarsall, arch),262stdout=subprocess.PIPE,263stderr=subprocess.PIPE)264try:265stdout, stderr = popen.communicate()266if popen.wait() != 0:267raise DistutilsPlatformError(stderr.decode("mbcs"))268269stdout = stdout.decode("mbcs")270for line in stdout.split("\n"):271line = Reg.convert_mbcs(line)272if '=' not in line:273continue274line = line.strip()275key, value = line.split('=', 1)276key = key.lower()277if key in interesting:278if value.endswith(os.pathsep):279value = value[:-1]280result[key] = removeDuplicates(value)281282finally:283popen.stdout.close()284popen.stderr.close()285286if len(result) != len(interesting):287raise ValueError(str(list(result.keys())))288289return result290291# More globals292VERSION = get_build_version()293# MACROS = MacroExpander(VERSION)294295class MSVCCompiler(CCompiler) :296"""Concrete class that implements an interface to Microsoft Visual C++,297as defined by the CCompiler abstract class."""298299compiler_type = 'msvc'300301# Just set this so CCompiler's constructor doesn't barf. We currently302# don't use the 'set_executables()' bureaucracy provided by CCompiler,303# as it really isn't necessary for this sort of single-compiler class.304# Would be nice to have a consistent interface with UnixCCompiler,305# though, so it's worth thinking about.306executables = {}307308# Private class data (need to distinguish C from C++ source for compiler)309_c_extensions = ['.c']310_cpp_extensions = ['.cc', '.cpp', '.cxx']311_rc_extensions = ['.rc']312_mc_extensions = ['.mc']313314# Needed for the filename generation methods provided by the315# base class, CCompiler.316src_extensions = (_c_extensions + _cpp_extensions +317_rc_extensions + _mc_extensions)318res_extension = '.res'319obj_extension = '.obj'320static_lib_extension = '.lib'321shared_lib_extension = '.dll'322static_lib_format = shared_lib_format = '%s%s'323exe_extension = '.exe'324325def __init__(self, verbose=0, dry_run=0, force=0):326super().__init__(verbose, dry_run, force)327self.__version = VERSION328self.__root = r"Software\Microsoft\VisualStudio"329# self.__macros = MACROS330self.__paths = []331# target platform (.plat_name is consistent with 'bdist')332self.plat_name = None333self.__arch = None # deprecated name334self.initialized = False335336def initialize(self, plat_name=None):337# multi-init means we would need to check platform same each time...338assert not self.initialized, "don't init multiple times"339if self.__version < 8.0:340raise DistutilsPlatformError("VC %0.1f is not supported by this module" % self.__version)341if plat_name is None:342plat_name = get_platform()343# sanity check for platforms to prevent obscure errors later.344ok_plats = 'win32', 'win-amd64'345if plat_name not in ok_plats:346raise DistutilsPlatformError("--plat-name must be one of %s" %347(ok_plats,))348349if "DISTUTILS_USE_SDK" in os.environ and "MSSdk" in os.environ and self.find_exe("cl.exe"):350# Assume that the SDK set up everything alright; don't try to be351# smarter352self.cc = "cl.exe"353self.linker = "link.exe"354self.lib = "lib.exe"355self.rc = "rc.exe"356self.mc = "mc.exe"357else:358# On x86, 'vcvars32.bat amd64' creates an env that doesn't work;359# to cross compile, you use 'x86_amd64'.360# On AMD64, 'vcvars32.bat amd64' is a native build env; to cross361# compile use 'x86' (ie, it runs the x86 compiler directly)362if plat_name == get_platform() or plat_name == 'win32':363# native build or cross-compile to win32364plat_spec = PLAT_TO_VCVARS[plat_name]365else:366# cross compile from win32 -> some 64bit367plat_spec = PLAT_TO_VCVARS[get_platform()] + '_' + \368PLAT_TO_VCVARS[plat_name]369370vc_env = query_vcvarsall(VERSION, plat_spec)371372self.__paths = vc_env['path'].split(os.pathsep)373os.environ['lib'] = vc_env['lib']374os.environ['include'] = vc_env['include']375376if len(self.__paths) == 0:377raise DistutilsPlatformError("Python was built with %s, "378"and extensions need to be built with the same "379"version of the compiler, but it isn't installed."380% self.__product)381382self.cc = self.find_exe("cl.exe")383self.linker = self.find_exe("link.exe")384self.lib = self.find_exe("lib.exe")385self.rc = self.find_exe("rc.exe") # resource compiler386self.mc = self.find_exe("mc.exe") # message compiler387#self.set_path_env_var('lib')388#self.set_path_env_var('include')389390# extend the MSVC path with the current path391try:392for p in os.environ['path'].split(';'):393self.__paths.append(p)394except KeyError:395pass396self.__paths = normalize_and_reduce_paths(self.__paths)397os.environ['path'] = ";".join(self.__paths)398399self.preprocess_options = None400if self.__arch == "x86":401self.compile_options = [ '/nologo', '/O2', '/MD', '/W3',402'/DNDEBUG']403self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3',404'/Z7', '/D_DEBUG']405else:406# Win64407self.compile_options = [ '/nologo', '/O2', '/MD', '/W3', '/GS-' ,408'/DNDEBUG']409self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/GS-',410'/Z7', '/D_DEBUG']411412self.ldflags_shared = ['/DLL', '/nologo', '/INCREMENTAL:NO']413if self.__version >= 7:414self.ldflags_shared_debug = [415'/DLL', '/nologo', '/INCREMENTAL:no', '/DEBUG'416]417self.ldflags_static = [ '/nologo']418419self.initialized = True420421# -- Worker methods ------------------------------------------------422423def object_filenames(self,424source_filenames,425strip_dir=0,426output_dir=''):427# Copied from ccompiler.py, extended to return .res as 'object'-file428# for .rc input file429if output_dir is None: output_dir = ''430obj_names = []431for src_name in source_filenames:432(base, ext) = os.path.splitext (src_name)433base = os.path.splitdrive(base)[1] # Chop off the drive434base = base[os.path.isabs(base):] # If abs, chop off leading /435if ext not in self.src_extensions:436# Better to raise an exception instead of silently continuing437# and later complain about sources and targets having438# different lengths439raise CompileError ("Don't know how to compile %s" % src_name)440if strip_dir:441base = os.path.basename (base)442if ext in self._rc_extensions:443obj_names.append (os.path.join (output_dir,444base + self.res_extension))445elif ext in self._mc_extensions:446obj_names.append (os.path.join (output_dir,447base + self.res_extension))448else:449obj_names.append (os.path.join (output_dir,450base + self.obj_extension))451return obj_names452453454def compile(self, sources,455output_dir=None, macros=None, include_dirs=None, debug=0,456extra_preargs=None, extra_postargs=None, depends=None):457458if not self.initialized:459self.initialize()460compile_info = self._setup_compile(output_dir, macros, include_dirs,461sources, depends, extra_postargs)462macros, objects, extra_postargs, pp_opts, build = compile_info463464compile_opts = extra_preargs or []465compile_opts.append ('/c')466if debug:467compile_opts.extend(self.compile_options_debug)468else:469compile_opts.extend(self.compile_options)470471for obj in objects:472try:473src, ext = build[obj]474except KeyError:475continue476if debug:477# pass the full pathname to MSVC in debug mode,478# this allows the debugger to find the source file479# without asking the user to browse for it480src = os.path.abspath(src)481482if ext in self._c_extensions:483input_opt = "/Tc" + src484elif ext in self._cpp_extensions:485input_opt = "/Tp" + src486elif ext in self._rc_extensions:487# compile .RC to .RES file488input_opt = src489output_opt = "/fo" + obj490try:491self.spawn([self.rc] + pp_opts +492[output_opt] + [input_opt])493except DistutilsExecError as msg:494raise CompileError(msg)495continue496elif ext in self._mc_extensions:497# Compile .MC to .RC file to .RES file.498# * '-h dir' specifies the directory for the499# generated include file500# * '-r dir' specifies the target directory of the501# generated RC file and the binary message resource502# it includes503#504# For now (since there are no options to change this),505# we use the source-directory for the include file and506# the build directory for the RC file and message507# resources. This works at least for win32all.508h_dir = os.path.dirname(src)509rc_dir = os.path.dirname(obj)510try:511# first compile .MC to .RC and .H file512self.spawn([self.mc] +513['-h', h_dir, '-r', rc_dir] + [src])514base, _ = os.path.splitext (os.path.basename (src))515rc_file = os.path.join (rc_dir, base + '.rc')516# then compile .RC to .RES file517self.spawn([self.rc] +518["/fo" + obj] + [rc_file])519520except DistutilsExecError as msg:521raise CompileError(msg)522continue523else:524# how to handle this file?525raise CompileError("Don't know how to compile %s to %s"526% (src, obj))527528output_opt = "/Fo" + obj529try:530self.spawn([self.cc] + compile_opts + pp_opts +531[input_opt, output_opt] +532extra_postargs)533except DistutilsExecError as msg:534raise CompileError(msg)535536return objects537538539def create_static_lib(self,540objects,541output_libname,542output_dir=None,543debug=0,544target_lang=None):545546if not self.initialized:547self.initialize()548(objects, output_dir) = self._fix_object_args(objects, output_dir)549output_filename = self.library_filename(output_libname,550output_dir=output_dir)551552if self._need_link(objects, output_filename):553lib_args = objects + ['/OUT:' + output_filename]554if debug:555pass # XXX what goes here?556try:557self.spawn([self.lib] + lib_args)558except DistutilsExecError as msg:559raise LibError(msg)560else:561log.debug("skipping %s (up-to-date)", output_filename)562563564def link(self,565target_desc,566objects,567output_filename,568output_dir=None,569libraries=None,570library_dirs=None,571runtime_library_dirs=None,572export_symbols=None,573debug=0,574extra_preargs=None,575extra_postargs=None,576build_temp=None,577target_lang=None):578579if not self.initialized:580self.initialize()581(objects, output_dir) = self._fix_object_args(objects, output_dir)582fixed_args = self._fix_lib_args(libraries, library_dirs,583runtime_library_dirs)584(libraries, library_dirs, runtime_library_dirs) = fixed_args585586if runtime_library_dirs:587self.warn ("I don't know what to do with 'runtime_library_dirs': "588+ str (runtime_library_dirs))589590lib_opts = gen_lib_options(self,591library_dirs, runtime_library_dirs,592libraries)593if output_dir is not None:594output_filename = os.path.join(output_dir, output_filename)595596if self._need_link(objects, output_filename):597if target_desc == CCompiler.EXECUTABLE:598if debug:599ldflags = self.ldflags_shared_debug[1:]600else:601ldflags = self.ldflags_shared[1:]602else:603if debug:604ldflags = self.ldflags_shared_debug605else:606ldflags = self.ldflags_shared607608export_opts = []609for sym in (export_symbols or []):610export_opts.append("/EXPORT:" + sym)611612ld_args = (ldflags + lib_opts + export_opts +613objects + ['/OUT:' + output_filename])614615# The MSVC linker generates .lib and .exp files, which cannot be616# suppressed by any linker switches. The .lib files may even be617# needed! Make sure they are generated in the temporary build618# directory. Since they have different names for debug and release619# builds, they can go into the same directory.620build_temp = os.path.dirname(objects[0])621if export_symbols is not None:622(dll_name, dll_ext) = os.path.splitext(623os.path.basename(output_filename))624implib_file = os.path.join(625build_temp,626self.library_filename(dll_name))627ld_args.append ('/IMPLIB:' + implib_file)628629self.manifest_setup_ldargs(output_filename, build_temp, ld_args)630631if extra_preargs:632ld_args[:0] = extra_preargs633if extra_postargs:634ld_args.extend(extra_postargs)635636self.mkpath(os.path.dirname(output_filename))637try:638self.spawn([self.linker] + ld_args)639except DistutilsExecError as msg:640raise LinkError(msg)641642# embed the manifest643# XXX - this is somewhat fragile - if mt.exe fails, distutils644# will still consider the DLL up-to-date, but it will not have a645# manifest. Maybe we should link to a temp file? OTOH, that646# implies a build environment error that shouldn't go undetected.647mfinfo = self.manifest_get_embed_info(target_desc, ld_args)648if mfinfo is not None:649mffilename, mfid = mfinfo650out_arg = '-outputresource:%s;%s' % (output_filename, mfid)651try:652self.spawn(['mt.exe', '-nologo', '-manifest',653mffilename, out_arg])654except DistutilsExecError as msg:655raise LinkError(msg)656else:657log.debug("skipping %s (up-to-date)", output_filename)658659def manifest_setup_ldargs(self, output_filename, build_temp, ld_args):660# If we need a manifest at all, an embedded manifest is recommended.661# See MSDN article titled662# "How to: Embed a Manifest Inside a C/C++ Application"663# (currently at http://msdn2.microsoft.com/en-us/library/ms235591(VS.80).aspx)664# Ask the linker to generate the manifest in the temp dir, so665# we can check it, and possibly embed it, later.666temp_manifest = os.path.join(667build_temp,668os.path.basename(output_filename) + ".manifest")669ld_args.append('/MANIFESTFILE:' + temp_manifest)670671def manifest_get_embed_info(self, target_desc, ld_args):672# If a manifest should be embedded, return a tuple of673# (manifest_filename, resource_id). Returns None if no manifest674# should be embedded. See http://bugs.python.org/issue7833 for why675# we want to avoid any manifest for extension modules if we can)676for arg in ld_args:677if arg.startswith("/MANIFESTFILE:"):678temp_manifest = arg.split(":", 1)[1]679break680else:681# no /MANIFESTFILE so nothing to do.682return None683if target_desc == CCompiler.EXECUTABLE:684# by default, executables always get the manifest with the685# CRT referenced.686mfid = 1687else:688# Extension modules try and avoid any manifest if possible.689mfid = 2690temp_manifest = self._remove_visual_c_ref(temp_manifest)691if temp_manifest is None:692return None693return temp_manifest, mfid694695def _remove_visual_c_ref(self, manifest_file):696try:697# Remove references to the Visual C runtime, so they will698# fall through to the Visual C dependency of Python.exe.699# This way, when installed for a restricted user (e.g.700# runtimes are not in WinSxS folder, but in Python's own701# folder), the runtimes do not need to be in every folder702# with .pyd's.703# Returns either the filename of the modified manifest or704# None if no manifest should be embedded.705manifest_f = open(manifest_file)706try:707manifest_buf = manifest_f.read()708finally:709manifest_f.close()710pattern = re.compile(711r"""<assemblyIdentity.*?name=("|')Microsoft\."""\712r"""VC\d{2}\.CRT("|').*?(/>|</assemblyIdentity>)""",713re.DOTALL)714manifest_buf = re.sub(pattern, "", manifest_buf)715pattern = r"<dependentAssembly>\s*</dependentAssembly>"716manifest_buf = re.sub(pattern, "", manifest_buf)717# Now see if any other assemblies are referenced - if not, we718# don't want a manifest embedded.719pattern = re.compile(720r"""<assemblyIdentity.*?name=(?:"|')(.+?)(?:"|')"""721r""".*?(?:/>|</assemblyIdentity>)""", re.DOTALL)722if re.search(pattern, manifest_buf) is None:723return None724725manifest_f = open(manifest_file, 'w')726try:727manifest_f.write(manifest_buf)728return manifest_file729finally:730manifest_f.close()731except OSError:732pass733734# -- Miscellaneous methods -----------------------------------------735# These are all used by the 'gen_lib_options() function, in736# ccompiler.py.737738def library_dir_option(self, dir):739return "/LIBPATH:" + dir740741def runtime_library_dir_option(self, dir):742raise DistutilsPlatformError(743"don't know how to set runtime library search path for MSVC++")744745def library_option(self, lib):746return self.library_filename(lib)747748749def find_library_file(self, dirs, lib, debug=0):750# Prefer a debugging library if found (and requested), but deal751# with it if we don't have one.752if debug:753try_names = [lib + "_d", lib]754else:755try_names = [lib]756for dir in dirs:757for name in try_names:758libfile = os.path.join(dir, self.library_filename (name))759if os.path.exists(libfile):760return libfile761else:762# Oops, didn't find it in *any* of 'dirs'763return None764765# Helper methods for using the MSVC registry settings766767def find_exe(self, exe):768"""Return path to an MSVC executable program.769770Tries to find the program in several places: first, one of the771MSVC program search paths from the registry; next, the directories772in the PATH environment variable. If any of those work, return an773absolute path that is known to exist. If none of them work, just774return the original program name, 'exe'.775"""776for p in self.__paths:777fn = os.path.join(os.path.abspath(p), exe)778if os.path.isfile(fn):779return fn780781# didn't find it; try existing path782for p in os.environ['Path'].split(';'):783fn = os.path.join(os.path.abspath(p),exe)784if os.path.isfile(fn):785return fn786787return exe788789790