Path: blob/main/Tools/c-analyzer/distutils/msvccompiler.py
12 views
"""distutils.msvccompiler12Contains MSVCCompiler, an implementation of the abstract CCompiler class3for the Microsoft Visual Studio.4"""56# Written by Perry Stoll7# hacked by Robin Becker and Thomas Heller to do a better job of8# finding DevStudio (through the registry)910import sys, os11from distutils.errors import DistutilsPlatformError12from distutils.ccompiler import CCompiler13from distutils import log1415_can_read_reg = False16try:17import winreg1819_can_read_reg = True20hkey_mod = winreg2122RegOpenKeyEx = winreg.OpenKeyEx23RegEnumKey = winreg.EnumKey24RegEnumValue = winreg.EnumValue25RegError = winreg.error2627except ImportError:28try:29import win32api30import win32con31_can_read_reg = True32hkey_mod = win32con3334RegOpenKeyEx = win32api.RegOpenKeyEx35RegEnumKey = win32api.RegEnumKey36RegEnumValue = win32api.RegEnumValue37RegError = win32api.error38except ImportError:39log.info("Warning: Can't read registry to find the "40"necessary compiler setting\n"41"Make sure that Python modules winreg, "42"win32api or win32con are installed.")4344if _can_read_reg:45HKEYS = (hkey_mod.HKEY_USERS,46hkey_mod.HKEY_CURRENT_USER,47hkey_mod.HKEY_LOCAL_MACHINE,48hkey_mod.HKEY_CLASSES_ROOT)4950def read_keys(base, key):51"""Return list of registry keys."""52try:53handle = RegOpenKeyEx(base, key)54except RegError:55return None56L = []57i = 058while True:59try:60k = RegEnumKey(handle, i)61except RegError:62break63L.append(k)64i += 165return L6667def read_values(base, key):68"""Return dict of registry keys and values.6970All names are converted to lowercase.71"""72try:73handle = RegOpenKeyEx(base, key)74except RegError:75return None76d = {}77i = 078while True:79try:80name, value, type = RegEnumValue(handle, i)81except RegError:82break83name = name.lower()84d[convert_mbcs(name)] = convert_mbcs(value)85i += 186return d8788def convert_mbcs(s):89dec = getattr(s, "decode", None)90if dec is not None:91try:92s = dec("mbcs")93except UnicodeError:94pass95return s9697class MacroExpander:98def __init__(self, version):99self.macros = {}100self.load_macros(version)101102def set_macro(self, macro, path, key):103for base in HKEYS:104d = read_values(base, path)105if d:106self.macros["$(%s)" % macro] = d[key]107break108109def load_macros(self, version):110vsbase = r"Software\Microsoft\VisualStudio\%0.1f" % version111self.set_macro("VCInstallDir", vsbase + r"\Setup\VC", "productdir")112self.set_macro("VSInstallDir", vsbase + r"\Setup\VS", "productdir")113net = r"Software\Microsoft\.NETFramework"114self.set_macro("FrameworkDir", net, "installroot")115try:116if version > 7.0:117self.set_macro("FrameworkSDKDir", net, "sdkinstallrootv1.1")118else:119self.set_macro("FrameworkSDKDir", net, "sdkinstallroot")120except KeyError as exc: #121raise DistutilsPlatformError(122"""Python was built with Visual Studio 2003;123extensions must be built with a compiler than can generate compatible binaries.124Visual Studio 2003 was not found on this system. If you have Cygwin installed,125you can try compiling with MingW32, by passing "-c mingw32" to setup.py.""")126127p = r"Software\Microsoft\NET Framework Setup\Product"128for base in HKEYS:129try:130h = RegOpenKeyEx(base, p)131except RegError:132continue133key = RegEnumKey(h, 0)134d = read_values(base, r"%s\%s" % (p, key))135self.macros["$(FrameworkVersion)"] = d["version"]136137def sub(self, s):138for k, v in self.macros.items():139s = s.replace(k, v)140return s141142def get_build_version():143"""Return the version of MSVC that was used to build Python.144145For Python 2.3 and up, the version number is included in146sys.version. For earlier versions, assume the compiler is MSVC 6.147"""148prefix = "MSC v."149i = sys.version.find(prefix)150if i == -1:151return 6152i = i + len(prefix)153s, rest = sys.version[i:].split(" ", 1)154majorVersion = int(s[:-2]) - 6155if majorVersion >= 13:156# v13 was skipped and should be v14157majorVersion += 1158minorVersion = int(s[2:3]) / 10.0159# I don't think paths are affected by minor version in version 6160if majorVersion == 6:161minorVersion = 0162if majorVersion >= 6:163return majorVersion + minorVersion164# else we don't know what version of the compiler this is165return None166167def get_build_architecture():168"""Return the processor architecture.169170Possible results are "Intel" or "AMD64".171"""172173prefix = " bit ("174i = sys.version.find(prefix)175if i == -1:176return "Intel"177j = sys.version.find(")", i)178return sys.version[i+len(prefix):j]179180def normalize_and_reduce_paths(paths):181"""Return a list of normalized paths with duplicates removed.182183The current order of paths is maintained.184"""185# Paths are normalized so things like: /a and /a/ aren't both preserved.186reduced_paths = []187for p in paths:188np = os.path.normpath(p)189# XXX(nnorwitz): O(n**2), if reduced_paths gets long perhaps use a set.190if np not in reduced_paths:191reduced_paths.append(np)192return reduced_paths193194195class MSVCCompiler(CCompiler) :196"""Concrete class that implements an interface to Microsoft Visual C++,197as defined by the CCompiler abstract class."""198199compiler_type = 'msvc'200201# Just set this so CCompiler's constructor doesn't barf. We currently202# don't use the 'set_executables()' bureaucracy provided by CCompiler,203# as it really isn't necessary for this sort of single-compiler class.204# Would be nice to have a consistent interface with UnixCCompiler,205# though, so it's worth thinking about.206executables = {}207208# Private class data (need to distinguish C from C++ source for compiler)209_c_extensions = ['.c']210_cpp_extensions = ['.cc', '.cpp', '.cxx']211_rc_extensions = ['.rc']212_mc_extensions = ['.mc']213214# Needed for the filename generation methods provided by the215# base class, CCompiler.216src_extensions = (_c_extensions + _cpp_extensions +217_rc_extensions + _mc_extensions)218res_extension = '.res'219obj_extension = '.obj'220static_lib_extension = '.lib'221shared_lib_extension = '.dll'222static_lib_format = shared_lib_format = '%s%s'223exe_extension = '.exe'224225def __init__(self, verbose=0, dry_run=0, force=0):226CCompiler.__init__ (self, verbose, dry_run, force)227self.__version = get_build_version()228self.__arch = get_build_architecture()229if self.__arch == "Intel":230# x86231if self.__version >= 7:232self.__root = r"Software\Microsoft\VisualStudio"233self.__macros = MacroExpander(self.__version)234else:235self.__root = r"Software\Microsoft\Devstudio"236self.__product = "Visual Studio version %s" % self.__version237else:238# Win64. Assume this was built with the platform SDK239self.__product = "Microsoft SDK compiler %s" % (self.__version + 6)240241self.initialized = False242243244# -- Miscellaneous methods -----------------------------------------245246# Helper methods for using the MSVC registry settings247248def find_exe(self, exe):249"""Return path to an MSVC executable program.250251Tries to find the program in several places: first, one of the252MSVC program search paths from the registry; next, the directories253in the PATH environment variable. If any of those work, return an254absolute path that is known to exist. If none of them work, just255return the original program name, 'exe'.256"""257for p in self.__paths:258fn = os.path.join(os.path.abspath(p), exe)259if os.path.isfile(fn):260return fn261262# didn't find it; try existing path263for p in os.environ['Path'].split(';'):264fn = os.path.join(os.path.abspath(p),exe)265if os.path.isfile(fn):266return fn267268return exe269270def get_msvc_paths(self, path, platform='x86'):271"""Get a list of devstudio directories (include, lib or path).272273Return a list of strings. The list will be empty if unable to274access the registry or appropriate registry keys not found.275"""276if not _can_read_reg:277return []278279path = path + " dirs"280if self.__version >= 7:281key = (r"%s\%0.1f\VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories"282% (self.__root, self.__version))283else:284key = (r"%s\6.0\Build System\Components\Platforms"285r"\Win32 (%s)\Directories" % (self.__root, platform))286287for base in HKEYS:288d = read_values(base, key)289if d:290if self.__version >= 7:291return self.__macros.sub(d[path]).split(";")292else:293return d[path].split(";")294# MSVC 6 seems to create the registry entries we need only when295# the GUI is run.296if self.__version == 6:297for base in HKEYS:298if read_values(base, r"%s\6.0" % self.__root) is not None:299self.warn("It seems you have Visual Studio 6 installed, "300"but the expected registry settings are not present.\n"301"You must at least run the Visual Studio GUI once "302"so that these entries are created.")303break304return []305306def set_path_env_var(self, name):307"""Set environment variable 'name' to an MSVC path type value.308309This is equivalent to a SET command prior to execution of spawned310commands.311"""312313if name == "lib":314p = self.get_msvc_paths("library")315else:316p = self.get_msvc_paths(name)317if p:318os.environ[name] = ';'.join(p)319320321if get_build_version() >= 8.0:322log.debug("Importing new compiler from distutils.msvc9compiler")323OldMSVCCompiler = MSVCCompiler324from distutils.msvc9compiler import MSVCCompiler325# get_build_architecture not really relevant now we support cross-compile326from distutils.msvc9compiler import MacroExpander327328329