mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 05:31:20 +00:00 
			
		
		
		
	
		
			
	
	
		
			204 lines
		
	
	
	
		
			6.4 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
		
		
			
		
	
	
			204 lines
		
	
	
	
		
			6.4 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
|   | """distutils._msvccompiler
 | ||
|  | 
 | ||
|  | Contains MSVCCompiler, an implementation of the abstract CCompiler class | ||
|  | for Microsoft Visual Studio 2015. | ||
|  | 
 | ||
|  | The module is compatible with VS 2015 and later. You can find legacy support | ||
|  | for older versions in distutils.msvc9compiler and distutils.msvccompiler. | ||
|  | """
 | ||
|  | 
 | ||
|  | # Written by Perry Stoll | ||
|  | # hacked by Robin Becker and Thomas Heller to do a better job of | ||
|  | #   finding DevStudio (through the registry) | ||
|  | # ported to VS 2005 and VS 2008 by Christian Heimes | ||
|  | # ported to VS 2015 by Steve Dower | ||
|  | 
 | ||
|  | import os | ||
|  | import subprocess | ||
|  | import winreg | ||
|  | 
 | ||
|  | from distutils.errors import DistutilsPlatformError | ||
|  | from distutils.ccompiler import CCompiler | ||
|  | from distutils import log | ||
|  | 
 | ||
|  | from itertools import count | ||
|  | 
 | ||
|  | def _find_vc2015(): | ||
|  |     try: | ||
|  |         key = winreg.OpenKeyEx( | ||
|  |             winreg.HKEY_LOCAL_MACHINE, | ||
|  |             r"Software\Microsoft\VisualStudio\SxS\VC7", | ||
|  |             access=winreg.KEY_READ | winreg.KEY_WOW64_32KEY | ||
|  |         ) | ||
|  |     except OSError: | ||
|  |         log.debug("Visual C++ is not registered") | ||
|  |         return None, None | ||
|  | 
 | ||
|  |     best_version = 0 | ||
|  |     best_dir = None | ||
|  |     with key: | ||
|  |         for i in count(): | ||
|  |             try: | ||
|  |                 v, vc_dir, vt = winreg.EnumValue(key, i) | ||
|  |             except OSError: | ||
|  |                 break | ||
|  |             if v and vt == winreg.REG_SZ and os.path.isdir(vc_dir): | ||
|  |                 try: | ||
|  |                     version = int(float(v)) | ||
|  |                 except (ValueError, TypeError): | ||
|  |                     continue | ||
|  |                 if version >= 14 and version > best_version: | ||
|  |                     best_version, best_dir = version, vc_dir | ||
|  |     return best_version, best_dir | ||
|  | 
 | ||
|  | def _find_vc2017(): | ||
|  |     """Returns "15, path" based on the result of invoking vswhere.exe
 | ||
|  |     If no install is found, returns "None, None" | ||
|  | 
 | ||
|  |     The version is returned to avoid unnecessarily changing the function | ||
|  |     result. It may be ignored when the path is not None. | ||
|  | 
 | ||
|  |     If vswhere.exe is not available, by definition, VS 2017 is not | ||
|  |     installed. | ||
|  |     """
 | ||
|  |     root = os.environ.get("ProgramFiles(x86)") or os.environ.get("ProgramFiles") | ||
|  |     if not root: | ||
|  |         return None, None | ||
|  | 
 | ||
|  |     try: | ||
|  |         path = subprocess.check_output([ | ||
|  |             os.path.join(root, "Microsoft Visual Studio", "Installer", "vswhere.exe"), | ||
|  |             "-latest", | ||
|  |             "-prerelease", | ||
|  |             "-requires", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", | ||
|  |             "-property", "installationPath", | ||
|  |             "-products", "*", | ||
|  |         ], encoding="mbcs", errors="strict").strip() | ||
|  |     except (subprocess.CalledProcessError, OSError, UnicodeDecodeError): | ||
|  |         return None, None | ||
|  | 
 | ||
|  |     path = os.path.join(path, "VC", "Auxiliary", "Build") | ||
|  |     if os.path.isdir(path): | ||
|  |         return 15, path | ||
|  | 
 | ||
|  |     return None, None | ||
|  | 
 | ||
|  | PLAT_SPEC_TO_RUNTIME = { | ||
|  |     'x86' : 'x86', | ||
|  |     'x86_amd64' : 'x64', | ||
|  |     'x86_arm' : 'arm', | ||
|  |     'x86_arm64' : 'arm64' | ||
|  | } | ||
|  | 
 | ||
|  | def _find_vcvarsall(plat_spec): | ||
|  |     # bpo-38597: Removed vcruntime return value | ||
|  |     _, best_dir = _find_vc2017() | ||
|  | 
 | ||
|  |     if not best_dir: | ||
|  |         best_version, best_dir = _find_vc2015() | ||
|  | 
 | ||
|  |     if not best_dir: | ||
|  |         log.debug("No suitable Visual C++ version found") | ||
|  |         return None, None | ||
|  | 
 | ||
|  |     vcvarsall = os.path.join(best_dir, "vcvarsall.bat") | ||
|  |     if not os.path.isfile(vcvarsall): | ||
|  |         log.debug("%s cannot be found", vcvarsall) | ||
|  |         return None, None | ||
|  | 
 | ||
|  |     return vcvarsall, None | ||
|  | 
 | ||
|  | def _get_vc_env(plat_spec): | ||
|  |     if os.getenv("DISTUTILS_USE_SDK"): | ||
|  |         return { | ||
|  |             key.lower(): value | ||
|  |             for key, value in os.environ.items() | ||
|  |         } | ||
|  | 
 | ||
|  |     vcvarsall, _ = _find_vcvarsall(plat_spec) | ||
|  |     if not vcvarsall: | ||
|  |         raise DistutilsPlatformError("Unable to find vcvarsall.bat") | ||
|  | 
 | ||
|  |     try: | ||
|  |         out = subprocess.check_output( | ||
|  |             'cmd /u /c "{}" {} && set'.format(vcvarsall, plat_spec), | ||
|  |             stderr=subprocess.STDOUT, | ||
|  |         ).decode('utf-16le', errors='replace') | ||
|  |     except subprocess.CalledProcessError as exc: | ||
|  |         log.error(exc.output) | ||
|  |         raise DistutilsPlatformError("Error executing {}" | ||
|  |                 .format(exc.cmd)) | ||
|  | 
 | ||
|  |     env = { | ||
|  |         key.lower(): value | ||
|  |         for key, _, value in | ||
|  |         (line.partition('=') for line in out.splitlines()) | ||
|  |         if key and value | ||
|  |     } | ||
|  | 
 | ||
|  |     return env | ||
|  | 
 | ||
|  | def _find_exe(exe, paths=None): | ||
|  |     """Return path to an MSVC executable program.
 | ||
|  | 
 | ||
|  |     Tries to find the program in several places: first, one of the | ||
|  |     MSVC program search paths from the registry; next, the directories | ||
|  |     in the PATH environment variable.  If any of those work, return an | ||
|  |     absolute path that is known to exist.  If none of them work, just | ||
|  |     return the original program name, 'exe'. | ||
|  |     """
 | ||
|  |     if not paths: | ||
|  |         paths = os.getenv('path').split(os.pathsep) | ||
|  |     for p in paths: | ||
|  |         fn = os.path.join(os.path.abspath(p), exe) | ||
|  |         if os.path.isfile(fn): | ||
|  |             return fn | ||
|  |     return exe | ||
|  | 
 | ||
|  | # A map keyed by get_platform() return values to values accepted by | ||
|  | # 'vcvarsall.bat'. Always cross-compile from x86 to work with the | ||
|  | # lighter-weight MSVC installs that do not include native 64-bit tools. | ||
|  | PLAT_TO_VCVARS = { | ||
|  |     'win32' : 'x86', | ||
|  |     'win-amd64' : 'x86_amd64', | ||
|  |     'win-arm32' : 'x86_arm', | ||
|  |     'win-arm64' : 'x86_arm64' | ||
|  | } | ||
|  | 
 | ||
|  | class MSVCCompiler(CCompiler) : | ||
|  |     """Concrete class that implements an interface to Microsoft Visual C++,
 | ||
|  |        as defined by the CCompiler abstract class."""
 | ||
|  | 
 | ||
|  |     compiler_type = 'msvc' | ||
|  | 
 | ||
|  |     # Just set this so CCompiler's constructor doesn't barf.  We currently | ||
|  |     # don't use the 'set_executables()' bureaucracy provided by CCompiler, | ||
|  |     # as it really isn't necessary for this sort of single-compiler class. | ||
|  |     # Would be nice to have a consistent interface with UnixCCompiler, | ||
|  |     # though, so it's worth thinking about. | ||
|  |     executables = {} | ||
|  | 
 | ||
|  |     # Private class data (need to distinguish C from C++ source for compiler) | ||
|  |     _c_extensions = ['.c'] | ||
|  |     _cpp_extensions = ['.cc', '.cpp', '.cxx'] | ||
|  |     _rc_extensions = ['.rc'] | ||
|  |     _mc_extensions = ['.mc'] | ||
|  | 
 | ||
|  |     # Needed for the filename generation methods provided by the | ||
|  |     # base class, CCompiler. | ||
|  |     src_extensions = (_c_extensions + _cpp_extensions + | ||
|  |                       _rc_extensions + _mc_extensions) | ||
|  |     res_extension = '.res' | ||
|  |     obj_extension = '.obj' | ||
|  |     static_lib_extension = '.lib' | ||
|  |     shared_lib_extension = '.dll' | ||
|  |     static_lib_format = shared_lib_format = '%s%s' | ||
|  |     exe_extension = '.exe' | ||
|  | 
 | ||
|  | 
 | ||
|  |     def __init__(self, verbose=0, dry_run=0, force=0): | ||
|  |         CCompiler.__init__ (self, verbose, dry_run, force) | ||
|  |         # target platform (.plat_name is consistent with 'bdist') | ||
|  |         self.plat_name = None | ||
|  |         self.initialized = False |