jpayne@69: """ jpayne@69: Environment info about Microsoft Compilers. jpayne@69: jpayne@69: >>> getfixture('windows_only') jpayne@69: >>> ei = EnvironmentInfo('amd64') jpayne@69: """ jpayne@69: jpayne@69: from __future__ import annotations jpayne@69: jpayne@69: import contextlib jpayne@69: import itertools jpayne@69: import json jpayne@69: import os jpayne@69: import os.path jpayne@69: import platform jpayne@69: from typing import TYPE_CHECKING jpayne@69: jpayne@69: from more_itertools import unique_everseen jpayne@69: jpayne@69: import distutils.errors jpayne@69: jpayne@69: # https://github.com/python/mypy/issues/8166 jpayne@69: if not TYPE_CHECKING and platform.system() == 'Windows': jpayne@69: import winreg jpayne@69: from os import environ jpayne@69: else: jpayne@69: # Mock winreg and environ so the module can be imported on this platform. jpayne@69: jpayne@69: class winreg: jpayne@69: HKEY_USERS = None jpayne@69: HKEY_CURRENT_USER = None jpayne@69: HKEY_LOCAL_MACHINE = None jpayne@69: HKEY_CLASSES_ROOT = None jpayne@69: jpayne@69: environ: dict[str, str] = dict() jpayne@69: jpayne@69: jpayne@69: class PlatformInfo: jpayne@69: """ jpayne@69: Current and Target Architectures information. jpayne@69: jpayne@69: Parameters jpayne@69: ---------- jpayne@69: arch: str jpayne@69: Target architecture. jpayne@69: """ jpayne@69: jpayne@69: current_cpu = environ.get('processor_architecture', '').lower() jpayne@69: jpayne@69: def __init__(self, arch): jpayne@69: self.arch = arch.lower().replace('x64', 'amd64') jpayne@69: jpayne@69: @property jpayne@69: def target_cpu(self): jpayne@69: """ jpayne@69: Return Target CPU architecture. jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: str jpayne@69: Target CPU jpayne@69: """ jpayne@69: return self.arch[self.arch.find('_') + 1 :] jpayne@69: jpayne@69: def target_is_x86(self): jpayne@69: """ jpayne@69: Return True if target CPU is x86 32 bits.. jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: bool jpayne@69: CPU is x86 32 bits jpayne@69: """ jpayne@69: return self.target_cpu == 'x86' jpayne@69: jpayne@69: def current_is_x86(self): jpayne@69: """ jpayne@69: Return True if current CPU is x86 32 bits.. jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: bool jpayne@69: CPU is x86 32 bits jpayne@69: """ jpayne@69: return self.current_cpu == 'x86' jpayne@69: jpayne@69: def current_dir(self, hidex86=False, x64=False): jpayne@69: """ jpayne@69: Current platform specific subfolder. jpayne@69: jpayne@69: Parameters jpayne@69: ---------- jpayne@69: hidex86: bool jpayne@69: return '' and not '\x86' if architecture is x86. jpayne@69: x64: bool jpayne@69: return '\x64' and not '\amd64' if architecture is amd64. jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: str jpayne@69: subfolder: '\target', or '' (see hidex86 parameter) jpayne@69: """ jpayne@69: return ( jpayne@69: '' jpayne@69: if (self.current_cpu == 'x86' and hidex86) jpayne@69: else r'\x64' jpayne@69: if (self.current_cpu == 'amd64' and x64) jpayne@69: else r'\%s' % self.current_cpu jpayne@69: ) jpayne@69: jpayne@69: def target_dir(self, hidex86=False, x64=False): jpayne@69: r""" jpayne@69: Target platform specific subfolder. jpayne@69: jpayne@69: Parameters jpayne@69: ---------- jpayne@69: hidex86: bool jpayne@69: return '' and not '\x86' if architecture is x86. jpayne@69: x64: bool jpayne@69: return '\x64' and not '\amd64' if architecture is amd64. jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: str jpayne@69: subfolder: '\current', or '' (see hidex86 parameter) jpayne@69: """ jpayne@69: return ( jpayne@69: '' jpayne@69: if (self.target_cpu == 'x86' and hidex86) jpayne@69: else r'\x64' jpayne@69: if (self.target_cpu == 'amd64' and x64) jpayne@69: else r'\%s' % self.target_cpu jpayne@69: ) jpayne@69: jpayne@69: def cross_dir(self, forcex86=False): jpayne@69: r""" jpayne@69: Cross platform specific subfolder. jpayne@69: jpayne@69: Parameters jpayne@69: ---------- jpayne@69: forcex86: bool jpayne@69: Use 'x86' as current architecture even if current architecture is jpayne@69: not x86. jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: str jpayne@69: subfolder: '' if target architecture is current architecture, jpayne@69: '\current_target' if not. jpayne@69: """ jpayne@69: current = 'x86' if forcex86 else self.current_cpu jpayne@69: return ( jpayne@69: '' jpayne@69: if self.target_cpu == current jpayne@69: else self.target_dir().replace('\\', '\\%s_' % current) jpayne@69: ) jpayne@69: jpayne@69: jpayne@69: class RegistryInfo: jpayne@69: """ jpayne@69: Microsoft Visual Studio related registry information. jpayne@69: jpayne@69: Parameters jpayne@69: ---------- jpayne@69: platform_info: PlatformInfo jpayne@69: "PlatformInfo" instance. jpayne@69: """ jpayne@69: jpayne@69: HKEYS = ( jpayne@69: winreg.HKEY_USERS, jpayne@69: winreg.HKEY_CURRENT_USER, jpayne@69: winreg.HKEY_LOCAL_MACHINE, jpayne@69: winreg.HKEY_CLASSES_ROOT, jpayne@69: ) jpayne@69: jpayne@69: def __init__(self, platform_info): jpayne@69: self.pi = platform_info jpayne@69: jpayne@69: @property jpayne@69: def visualstudio(self): jpayne@69: """ jpayne@69: Microsoft Visual Studio root registry key. jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: str jpayne@69: Registry key jpayne@69: """ jpayne@69: return 'VisualStudio' jpayne@69: jpayne@69: @property jpayne@69: def sxs(self): jpayne@69: """ jpayne@69: Microsoft Visual Studio SxS registry key. jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: str jpayne@69: Registry key jpayne@69: """ jpayne@69: return os.path.join(self.visualstudio, 'SxS') jpayne@69: jpayne@69: @property jpayne@69: def vc(self): jpayne@69: """ jpayne@69: Microsoft Visual C++ VC7 registry key. jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: str jpayne@69: Registry key jpayne@69: """ jpayne@69: return os.path.join(self.sxs, 'VC7') jpayne@69: jpayne@69: @property jpayne@69: def vs(self): jpayne@69: """ jpayne@69: Microsoft Visual Studio VS7 registry key. jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: str jpayne@69: Registry key jpayne@69: """ jpayne@69: return os.path.join(self.sxs, 'VS7') jpayne@69: jpayne@69: @property jpayne@69: def vc_for_python(self): jpayne@69: """ jpayne@69: Microsoft Visual C++ for Python registry key. jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: str jpayne@69: Registry key jpayne@69: """ jpayne@69: return r'DevDiv\VCForPython' jpayne@69: jpayne@69: @property jpayne@69: def microsoft_sdk(self): jpayne@69: """ jpayne@69: Microsoft SDK registry key. jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: str jpayne@69: Registry key jpayne@69: """ jpayne@69: return 'Microsoft SDKs' jpayne@69: jpayne@69: @property jpayne@69: def windows_sdk(self): jpayne@69: """ jpayne@69: Microsoft Windows/Platform SDK registry key. jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: str jpayne@69: Registry key jpayne@69: """ jpayne@69: return os.path.join(self.microsoft_sdk, 'Windows') jpayne@69: jpayne@69: @property jpayne@69: def netfx_sdk(self): jpayne@69: """ jpayne@69: Microsoft .NET Framework SDK registry key. jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: str jpayne@69: Registry key jpayne@69: """ jpayne@69: return os.path.join(self.microsoft_sdk, 'NETFXSDK') jpayne@69: jpayne@69: @property jpayne@69: def windows_kits_roots(self): jpayne@69: """ jpayne@69: Microsoft Windows Kits Roots registry key. jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: str jpayne@69: Registry key jpayne@69: """ jpayne@69: return r'Windows Kits\Installed Roots' jpayne@69: jpayne@69: def microsoft(self, key, x86=False): jpayne@69: """ jpayne@69: Return key in Microsoft software registry. jpayne@69: jpayne@69: Parameters jpayne@69: ---------- jpayne@69: key: str jpayne@69: Registry key path where look. jpayne@69: x86: str jpayne@69: Force x86 software registry. jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: str jpayne@69: Registry key jpayne@69: """ jpayne@69: node64 = '' if self.pi.current_is_x86() or x86 else 'Wow6432Node' jpayne@69: return os.path.join('Software', node64, 'Microsoft', key) jpayne@69: jpayne@69: def lookup(self, key, name): jpayne@69: """ jpayne@69: Look for values in registry in Microsoft software registry. jpayne@69: jpayne@69: Parameters jpayne@69: ---------- jpayne@69: key: str jpayne@69: Registry key path where look. jpayne@69: name: str jpayne@69: Value name to find. jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: str jpayne@69: value jpayne@69: """ jpayne@69: key_read = winreg.KEY_READ jpayne@69: openkey = winreg.OpenKey jpayne@69: closekey = winreg.CloseKey jpayne@69: ms = self.microsoft jpayne@69: for hkey in self.HKEYS: jpayne@69: bkey = None jpayne@69: try: jpayne@69: bkey = openkey(hkey, ms(key), 0, key_read) jpayne@69: except OSError: jpayne@69: if not self.pi.current_is_x86(): jpayne@69: try: jpayne@69: bkey = openkey(hkey, ms(key, True), 0, key_read) jpayne@69: except OSError: jpayne@69: continue jpayne@69: else: jpayne@69: continue jpayne@69: try: jpayne@69: return winreg.QueryValueEx(bkey, name)[0] jpayne@69: except OSError: jpayne@69: pass jpayne@69: finally: jpayne@69: if bkey: jpayne@69: closekey(bkey) jpayne@69: return None jpayne@69: jpayne@69: jpayne@69: class SystemInfo: jpayne@69: """ jpayne@69: Microsoft Windows and Visual Studio related system information. jpayne@69: jpayne@69: Parameters jpayne@69: ---------- jpayne@69: registry_info: RegistryInfo jpayne@69: "RegistryInfo" instance. jpayne@69: vc_ver: float jpayne@69: Required Microsoft Visual C++ version. jpayne@69: """ jpayne@69: jpayne@69: # Variables and properties in this class use originals CamelCase variables jpayne@69: # names from Microsoft source files for more easy comparison. jpayne@69: WinDir = environ.get('WinDir', '') jpayne@69: ProgramFiles = environ.get('ProgramFiles', '') jpayne@69: ProgramFilesx86 = environ.get('ProgramFiles(x86)', ProgramFiles) jpayne@69: jpayne@69: def __init__(self, registry_info, vc_ver=None): jpayne@69: self.ri = registry_info jpayne@69: self.pi = self.ri.pi jpayne@69: jpayne@69: self.known_vs_paths = self.find_programdata_vs_vers() jpayne@69: jpayne@69: # Except for VS15+, VC version is aligned with VS version jpayne@69: self.vs_ver = self.vc_ver = vc_ver or self._find_latest_available_vs_ver() jpayne@69: jpayne@69: def _find_latest_available_vs_ver(self): jpayne@69: """ jpayne@69: Find the latest VC version jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: float jpayne@69: version jpayne@69: """ jpayne@69: reg_vc_vers = self.find_reg_vs_vers() jpayne@69: jpayne@69: if not (reg_vc_vers or self.known_vs_paths): jpayne@69: raise distutils.errors.DistutilsPlatformError( jpayne@69: 'No Microsoft Visual C++ version found' jpayne@69: ) jpayne@69: jpayne@69: vc_vers = set(reg_vc_vers) jpayne@69: vc_vers.update(self.known_vs_paths) jpayne@69: return sorted(vc_vers)[-1] jpayne@69: jpayne@69: def find_reg_vs_vers(self): jpayne@69: """ jpayne@69: Find Microsoft Visual Studio versions available in registry. jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: list of float jpayne@69: Versions jpayne@69: """ jpayne@69: ms = self.ri.microsoft jpayne@69: vckeys = (self.ri.vc, self.ri.vc_for_python, self.ri.vs) jpayne@69: vs_vers = [] jpayne@69: for hkey, key in itertools.product(self.ri.HKEYS, vckeys): jpayne@69: try: jpayne@69: bkey = winreg.OpenKey(hkey, ms(key), 0, winreg.KEY_READ) jpayne@69: except OSError: jpayne@69: continue jpayne@69: with bkey: jpayne@69: subkeys, values, _ = winreg.QueryInfoKey(bkey) jpayne@69: for i in range(values): jpayne@69: with contextlib.suppress(ValueError): jpayne@69: ver = float(winreg.EnumValue(bkey, i)[0]) jpayne@69: if ver not in vs_vers: jpayne@69: vs_vers.append(ver) jpayne@69: for i in range(subkeys): jpayne@69: with contextlib.suppress(ValueError): jpayne@69: ver = float(winreg.EnumKey(bkey, i)) jpayne@69: if ver not in vs_vers: jpayne@69: vs_vers.append(ver) jpayne@69: return sorted(vs_vers) jpayne@69: jpayne@69: def find_programdata_vs_vers(self): jpayne@69: r""" jpayne@69: Find Visual studio 2017+ versions from information in jpayne@69: "C:\ProgramData\Microsoft\VisualStudio\Packages\_Instances". jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: dict jpayne@69: float version as key, path as value. jpayne@69: """ jpayne@69: vs_versions = {} jpayne@69: instances_dir = r'C:\ProgramData\Microsoft\VisualStudio\Packages\_Instances' jpayne@69: jpayne@69: try: jpayne@69: hashed_names = os.listdir(instances_dir) jpayne@69: jpayne@69: except OSError: jpayne@69: # Directory not exists with all Visual Studio versions jpayne@69: return vs_versions jpayne@69: jpayne@69: for name in hashed_names: jpayne@69: try: jpayne@69: # Get VS installation path from "state.json" file jpayne@69: state_path = os.path.join(instances_dir, name, 'state.json') jpayne@69: with open(state_path, 'rt', encoding='utf-8') as state_file: jpayne@69: state = json.load(state_file) jpayne@69: vs_path = state['installationPath'] jpayne@69: jpayne@69: # Raises OSError if this VS installation does not contain VC jpayne@69: os.listdir(os.path.join(vs_path, r'VC\Tools\MSVC')) jpayne@69: jpayne@69: # Store version and path jpayne@69: vs_versions[self._as_float_version(state['installationVersion'])] = ( jpayne@69: vs_path jpayne@69: ) jpayne@69: jpayne@69: except (OSError, KeyError): jpayne@69: # Skip if "state.json" file is missing or bad format jpayne@69: continue jpayne@69: jpayne@69: return vs_versions jpayne@69: jpayne@69: @staticmethod jpayne@69: def _as_float_version(version): jpayne@69: """ jpayne@69: Return a string version as a simplified float version (major.minor) jpayne@69: jpayne@69: Parameters jpayne@69: ---------- jpayne@69: version: str jpayne@69: Version. jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: float jpayne@69: version jpayne@69: """ jpayne@69: return float('.'.join(version.split('.')[:2])) jpayne@69: jpayne@69: @property jpayne@69: def VSInstallDir(self): jpayne@69: """ jpayne@69: Microsoft Visual Studio directory. jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: str jpayne@69: path jpayne@69: """ jpayne@69: # Default path jpayne@69: default = os.path.join( jpayne@69: self.ProgramFilesx86, 'Microsoft Visual Studio %0.1f' % self.vs_ver jpayne@69: ) jpayne@69: jpayne@69: # Try to get path from registry, if fail use default path jpayne@69: return self.ri.lookup(self.ri.vs, '%0.1f' % self.vs_ver) or default jpayne@69: jpayne@69: @property jpayne@69: def VCInstallDir(self): jpayne@69: """ jpayne@69: Microsoft Visual C++ directory. jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: str jpayne@69: path jpayne@69: """ jpayne@69: path = self._guess_vc() or self._guess_vc_legacy() jpayne@69: jpayne@69: if not os.path.isdir(path): jpayne@69: msg = 'Microsoft Visual C++ directory not found' jpayne@69: raise distutils.errors.DistutilsPlatformError(msg) jpayne@69: jpayne@69: return path jpayne@69: jpayne@69: def _guess_vc(self): jpayne@69: """ jpayne@69: Locate Visual C++ for VS2017+. jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: str jpayne@69: path jpayne@69: """ jpayne@69: if self.vs_ver <= 14.0: jpayne@69: return '' jpayne@69: jpayne@69: try: jpayne@69: # First search in known VS paths jpayne@69: vs_dir = self.known_vs_paths[self.vs_ver] jpayne@69: except KeyError: jpayne@69: # Else, search with path from registry jpayne@69: vs_dir = self.VSInstallDir jpayne@69: jpayne@69: guess_vc = os.path.join(vs_dir, r'VC\Tools\MSVC') jpayne@69: jpayne@69: # Subdir with VC exact version as name jpayne@69: try: jpayne@69: # Update the VC version with real one instead of VS version jpayne@69: vc_ver = os.listdir(guess_vc)[-1] jpayne@69: self.vc_ver = self._as_float_version(vc_ver) jpayne@69: return os.path.join(guess_vc, vc_ver) jpayne@69: except (OSError, IndexError): jpayne@69: return '' jpayne@69: jpayne@69: def _guess_vc_legacy(self): jpayne@69: """ jpayne@69: Locate Visual C++ for versions prior to 2017. jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: str jpayne@69: path jpayne@69: """ jpayne@69: default = os.path.join( jpayne@69: self.ProgramFilesx86, r'Microsoft Visual Studio %0.1f\VC' % self.vs_ver jpayne@69: ) jpayne@69: jpayne@69: # Try to get "VC++ for Python" path from registry as default path jpayne@69: reg_path = os.path.join(self.ri.vc_for_python, '%0.1f' % self.vs_ver) jpayne@69: python_vc = self.ri.lookup(reg_path, 'installdir') jpayne@69: default_vc = os.path.join(python_vc, 'VC') if python_vc else default jpayne@69: jpayne@69: # Try to get path from registry, if fail use default path jpayne@69: return self.ri.lookup(self.ri.vc, '%0.1f' % self.vs_ver) or default_vc jpayne@69: jpayne@69: @property jpayne@69: def WindowsSdkVersion(self): jpayne@69: """ jpayne@69: Microsoft Windows SDK versions for specified MSVC++ version. jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: tuple of str jpayne@69: versions jpayne@69: """ jpayne@69: if self.vs_ver <= 9.0: jpayne@69: return '7.0', '6.1', '6.0a' jpayne@69: elif self.vs_ver == 10.0: jpayne@69: return '7.1', '7.0a' jpayne@69: elif self.vs_ver == 11.0: jpayne@69: return '8.0', '8.0a' jpayne@69: elif self.vs_ver == 12.0: jpayne@69: return '8.1', '8.1a' jpayne@69: elif self.vs_ver >= 14.0: jpayne@69: return '10.0', '8.1' jpayne@69: return None jpayne@69: jpayne@69: @property jpayne@69: def WindowsSdkLastVersion(self): jpayne@69: """ jpayne@69: Microsoft Windows SDK last version. jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: str jpayne@69: version jpayne@69: """ jpayne@69: return self._use_last_dir_name(os.path.join(self.WindowsSdkDir, 'lib')) jpayne@69: jpayne@69: @property jpayne@69: def WindowsSdkDir(self): # noqa: C901 # is too complex (12) # FIXME jpayne@69: """ jpayne@69: Microsoft Windows SDK directory. jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: str jpayne@69: path jpayne@69: """ jpayne@69: sdkdir = '' jpayne@69: for ver in self.WindowsSdkVersion: jpayne@69: # Try to get it from registry jpayne@69: loc = os.path.join(self.ri.windows_sdk, 'v%s' % ver) jpayne@69: sdkdir = self.ri.lookup(loc, 'installationfolder') jpayne@69: if sdkdir: jpayne@69: break jpayne@69: if not sdkdir or not os.path.isdir(sdkdir): jpayne@69: # Try to get "VC++ for Python" version from registry jpayne@69: path = os.path.join(self.ri.vc_for_python, '%0.1f' % self.vc_ver) jpayne@69: install_base = self.ri.lookup(path, 'installdir') jpayne@69: if install_base: jpayne@69: sdkdir = os.path.join(install_base, 'WinSDK') jpayne@69: if not sdkdir or not os.path.isdir(sdkdir): jpayne@69: # If fail, use default new path jpayne@69: for ver in self.WindowsSdkVersion: jpayne@69: intver = ver[: ver.rfind('.')] jpayne@69: path = r'Microsoft SDKs\Windows Kits\%s' % intver jpayne@69: d = os.path.join(self.ProgramFiles, path) jpayne@69: if os.path.isdir(d): jpayne@69: sdkdir = d jpayne@69: if not sdkdir or not os.path.isdir(sdkdir): jpayne@69: # If fail, use default old path jpayne@69: for ver in self.WindowsSdkVersion: jpayne@69: path = r'Microsoft SDKs\Windows\v%s' % ver jpayne@69: d = os.path.join(self.ProgramFiles, path) jpayne@69: if os.path.isdir(d): jpayne@69: sdkdir = d jpayne@69: if not sdkdir: jpayne@69: # If fail, use Platform SDK jpayne@69: sdkdir = os.path.join(self.VCInstallDir, 'PlatformSDK') jpayne@69: return sdkdir jpayne@69: jpayne@69: @property jpayne@69: def WindowsSDKExecutablePath(self): jpayne@69: """ jpayne@69: Microsoft Windows SDK executable directory. jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: str jpayne@69: path jpayne@69: """ jpayne@69: # Find WinSDK NetFx Tools registry dir name jpayne@69: if self.vs_ver <= 11.0: jpayne@69: netfxver = 35 jpayne@69: arch = '' jpayne@69: else: jpayne@69: netfxver = 40 jpayne@69: hidex86 = True if self.vs_ver <= 12.0 else False jpayne@69: arch = self.pi.current_dir(x64=True, hidex86=hidex86) jpayne@69: fx = 'WinSDK-NetFx%dTools%s' % (netfxver, arch.replace('\\', '-')) jpayne@69: jpayne@69: # list all possibles registry paths jpayne@69: regpaths = [] jpayne@69: if self.vs_ver >= 14.0: jpayne@69: for ver in self.NetFxSdkVersion: jpayne@69: regpaths += [os.path.join(self.ri.netfx_sdk, ver, fx)] jpayne@69: jpayne@69: for ver in self.WindowsSdkVersion: jpayne@69: regpaths += [os.path.join(self.ri.windows_sdk, 'v%sA' % ver, fx)] jpayne@69: jpayne@69: # Return installation folder from the more recent path jpayne@69: for path in regpaths: jpayne@69: execpath = self.ri.lookup(path, 'installationfolder') jpayne@69: if execpath: jpayne@69: return execpath jpayne@69: jpayne@69: return None jpayne@69: jpayne@69: @property jpayne@69: def FSharpInstallDir(self): jpayne@69: """ jpayne@69: Microsoft Visual F# directory. jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: str jpayne@69: path jpayne@69: """ jpayne@69: path = os.path.join(self.ri.visualstudio, r'%0.1f\Setup\F#' % self.vs_ver) jpayne@69: return self.ri.lookup(path, 'productdir') or '' jpayne@69: jpayne@69: @property jpayne@69: def UniversalCRTSdkDir(self): jpayne@69: """ jpayne@69: Microsoft Universal CRT SDK directory. jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: str jpayne@69: path jpayne@69: """ jpayne@69: # Set Kit Roots versions for specified MSVC++ version jpayne@69: vers = ('10', '81') if self.vs_ver >= 14.0 else () jpayne@69: jpayne@69: # Find path of the more recent Kit jpayne@69: for ver in vers: jpayne@69: sdkdir = self.ri.lookup(self.ri.windows_kits_roots, 'kitsroot%s' % ver) jpayne@69: if sdkdir: jpayne@69: return sdkdir or '' jpayne@69: jpayne@69: return None jpayne@69: jpayne@69: @property jpayne@69: def UniversalCRTSdkLastVersion(self): jpayne@69: """ jpayne@69: Microsoft Universal C Runtime SDK last version. jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: str jpayne@69: version jpayne@69: """ jpayne@69: return self._use_last_dir_name(os.path.join(self.UniversalCRTSdkDir, 'lib')) jpayne@69: jpayne@69: @property jpayne@69: def NetFxSdkVersion(self): jpayne@69: """ jpayne@69: Microsoft .NET Framework SDK versions. jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: tuple of str jpayne@69: versions jpayne@69: """ jpayne@69: # Set FxSdk versions for specified VS version jpayne@69: return ( jpayne@69: ('4.7.2', '4.7.1', '4.7', '4.6.2', '4.6.1', '4.6', '4.5.2', '4.5.1', '4.5') jpayne@69: if self.vs_ver >= 14.0 jpayne@69: else () jpayne@69: ) jpayne@69: jpayne@69: @property jpayne@69: def NetFxSdkDir(self): jpayne@69: """ jpayne@69: Microsoft .NET Framework SDK directory. jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: str jpayne@69: path jpayne@69: """ jpayne@69: sdkdir = '' jpayne@69: for ver in self.NetFxSdkVersion: jpayne@69: loc = os.path.join(self.ri.netfx_sdk, ver) jpayne@69: sdkdir = self.ri.lookup(loc, 'kitsinstallationfolder') jpayne@69: if sdkdir: jpayne@69: break jpayne@69: return sdkdir jpayne@69: jpayne@69: @property jpayne@69: def FrameworkDir32(self): jpayne@69: """ jpayne@69: Microsoft .NET Framework 32bit directory. jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: str jpayne@69: path jpayne@69: """ jpayne@69: # Default path jpayne@69: guess_fw = os.path.join(self.WinDir, r'Microsoft.NET\Framework') jpayne@69: jpayne@69: # Try to get path from registry, if fail use default path jpayne@69: return self.ri.lookup(self.ri.vc, 'frameworkdir32') or guess_fw jpayne@69: jpayne@69: @property jpayne@69: def FrameworkDir64(self): jpayne@69: """ jpayne@69: Microsoft .NET Framework 64bit directory. jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: str jpayne@69: path jpayne@69: """ jpayne@69: # Default path jpayne@69: guess_fw = os.path.join(self.WinDir, r'Microsoft.NET\Framework64') jpayne@69: jpayne@69: # Try to get path from registry, if fail use default path jpayne@69: return self.ri.lookup(self.ri.vc, 'frameworkdir64') or guess_fw jpayne@69: jpayne@69: @property jpayne@69: def FrameworkVersion32(self): jpayne@69: """ jpayne@69: Microsoft .NET Framework 32bit versions. jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: tuple of str jpayne@69: versions jpayne@69: """ jpayne@69: return self._find_dot_net_versions(32) jpayne@69: jpayne@69: @property jpayne@69: def FrameworkVersion64(self): jpayne@69: """ jpayne@69: Microsoft .NET Framework 64bit versions. jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: tuple of str jpayne@69: versions jpayne@69: """ jpayne@69: return self._find_dot_net_versions(64) jpayne@69: jpayne@69: def _find_dot_net_versions(self, bits): jpayne@69: """ jpayne@69: Find Microsoft .NET Framework versions. jpayne@69: jpayne@69: Parameters jpayne@69: ---------- jpayne@69: bits: int jpayne@69: Platform number of bits: 32 or 64. jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: tuple of str jpayne@69: versions jpayne@69: """ jpayne@69: # Find actual .NET version in registry jpayne@69: reg_ver = self.ri.lookup(self.ri.vc, 'frameworkver%d' % bits) jpayne@69: dot_net_dir = getattr(self, 'FrameworkDir%d' % bits) jpayne@69: ver = reg_ver or self._use_last_dir_name(dot_net_dir, 'v') or '' jpayne@69: jpayne@69: # Set .NET versions for specified MSVC++ version jpayne@69: if self.vs_ver >= 12.0: jpayne@69: return ver, 'v4.0' jpayne@69: elif self.vs_ver >= 10.0: jpayne@69: return 'v4.0.30319' if ver.lower()[:2] != 'v4' else ver, 'v3.5' jpayne@69: elif self.vs_ver == 9.0: jpayne@69: return 'v3.5', 'v2.0.50727' jpayne@69: elif self.vs_ver == 8.0: jpayne@69: return 'v3.0', 'v2.0.50727' jpayne@69: return None jpayne@69: jpayne@69: @staticmethod jpayne@69: def _use_last_dir_name(path, prefix=''): jpayne@69: """ jpayne@69: Return name of the last dir in path or '' if no dir found. jpayne@69: jpayne@69: Parameters jpayne@69: ---------- jpayne@69: path: str jpayne@69: Use dirs in this path jpayne@69: prefix: str jpayne@69: Use only dirs starting by this prefix jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: str jpayne@69: name jpayne@69: """ jpayne@69: matching_dirs = ( jpayne@69: dir_name jpayne@69: for dir_name in reversed(os.listdir(path)) jpayne@69: if os.path.isdir(os.path.join(path, dir_name)) jpayne@69: and dir_name.startswith(prefix) jpayne@69: ) jpayne@69: return next(matching_dirs, None) or '' jpayne@69: jpayne@69: jpayne@69: class EnvironmentInfo: jpayne@69: """ jpayne@69: Return environment variables for specified Microsoft Visual C++ version jpayne@69: and platform : Lib, Include, Path and libpath. jpayne@69: jpayne@69: This function is compatible with Microsoft Visual C++ 9.0 to 14.X. jpayne@69: jpayne@69: Script created by analysing Microsoft environment configuration files like jpayne@69: "vcvars[...].bat", "SetEnv.Cmd", "vcbuildtools.bat", ... jpayne@69: jpayne@69: Parameters jpayne@69: ---------- jpayne@69: arch: str jpayne@69: Target architecture. jpayne@69: vc_ver: float jpayne@69: Required Microsoft Visual C++ version. If not set, autodetect the last jpayne@69: version. jpayne@69: vc_min_ver: float jpayne@69: Minimum Microsoft Visual C++ version. jpayne@69: """ jpayne@69: jpayne@69: # Variables and properties in this class use originals CamelCase variables jpayne@69: # names from Microsoft source files for more easy comparison. jpayne@69: jpayne@69: def __init__(self, arch, vc_ver=None, vc_min_ver=0): jpayne@69: self.pi = PlatformInfo(arch) jpayne@69: self.ri = RegistryInfo(self.pi) jpayne@69: self.si = SystemInfo(self.ri, vc_ver) jpayne@69: jpayne@69: if self.vc_ver < vc_min_ver: jpayne@69: err = 'No suitable Microsoft Visual C++ version found' jpayne@69: raise distutils.errors.DistutilsPlatformError(err) jpayne@69: jpayne@69: @property jpayne@69: def vs_ver(self): jpayne@69: """ jpayne@69: Microsoft Visual Studio. jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: float jpayne@69: version jpayne@69: """ jpayne@69: return self.si.vs_ver jpayne@69: jpayne@69: @property jpayne@69: def vc_ver(self): jpayne@69: """ jpayne@69: Microsoft Visual C++ version. jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: float jpayne@69: version jpayne@69: """ jpayne@69: return self.si.vc_ver jpayne@69: jpayne@69: @property jpayne@69: def VSTools(self): jpayne@69: """ jpayne@69: Microsoft Visual Studio Tools. jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: list of str jpayne@69: paths jpayne@69: """ jpayne@69: paths = [r'Common7\IDE', r'Common7\Tools'] jpayne@69: jpayne@69: if self.vs_ver >= 14.0: jpayne@69: arch_subdir = self.pi.current_dir(hidex86=True, x64=True) jpayne@69: paths += [r'Common7\IDE\CommonExtensions\Microsoft\TestWindow'] jpayne@69: paths += [r'Team Tools\Performance Tools'] jpayne@69: paths += [r'Team Tools\Performance Tools%s' % arch_subdir] jpayne@69: jpayne@69: return [os.path.join(self.si.VSInstallDir, path) for path in paths] jpayne@69: jpayne@69: @property jpayne@69: def VCIncludes(self): jpayne@69: """ jpayne@69: Microsoft Visual C++ & Microsoft Foundation Class Includes. jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: list of str jpayne@69: paths jpayne@69: """ jpayne@69: return [ jpayne@69: os.path.join(self.si.VCInstallDir, 'Include'), jpayne@69: os.path.join(self.si.VCInstallDir, r'ATLMFC\Include'), jpayne@69: ] jpayne@69: jpayne@69: @property jpayne@69: def VCLibraries(self): jpayne@69: """ jpayne@69: Microsoft Visual C++ & Microsoft Foundation Class Libraries. jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: list of str jpayne@69: paths jpayne@69: """ jpayne@69: if self.vs_ver >= 15.0: jpayne@69: arch_subdir = self.pi.target_dir(x64=True) jpayne@69: else: jpayne@69: arch_subdir = self.pi.target_dir(hidex86=True) jpayne@69: paths = ['Lib%s' % arch_subdir, r'ATLMFC\Lib%s' % arch_subdir] jpayne@69: jpayne@69: if self.vs_ver >= 14.0: jpayne@69: paths += [r'Lib\store%s' % arch_subdir] jpayne@69: jpayne@69: return [os.path.join(self.si.VCInstallDir, path) for path in paths] jpayne@69: jpayne@69: @property jpayne@69: def VCStoreRefs(self): jpayne@69: """ jpayne@69: Microsoft Visual C++ store references Libraries. jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: list of str jpayne@69: paths jpayne@69: """ jpayne@69: if self.vs_ver < 14.0: jpayne@69: return [] jpayne@69: return [os.path.join(self.si.VCInstallDir, r'Lib\store\references')] jpayne@69: jpayne@69: @property jpayne@69: def VCTools(self): jpayne@69: """ jpayne@69: Microsoft Visual C++ Tools. jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: list of str jpayne@69: paths jpayne@69: """ jpayne@69: si = self.si jpayne@69: tools = [os.path.join(si.VCInstallDir, 'VCPackages')] jpayne@69: jpayne@69: forcex86 = True if self.vs_ver <= 10.0 else False jpayne@69: arch_subdir = self.pi.cross_dir(forcex86) jpayne@69: if arch_subdir: jpayne@69: tools += [os.path.join(si.VCInstallDir, 'Bin%s' % arch_subdir)] jpayne@69: jpayne@69: if self.vs_ver == 14.0: jpayne@69: path = 'Bin%s' % self.pi.current_dir(hidex86=True) jpayne@69: tools += [os.path.join(si.VCInstallDir, path)] jpayne@69: jpayne@69: elif self.vs_ver >= 15.0: jpayne@69: host_dir = ( jpayne@69: r'bin\HostX86%s' if self.pi.current_is_x86() else r'bin\HostX64%s' jpayne@69: ) jpayne@69: tools += [ jpayne@69: os.path.join(si.VCInstallDir, host_dir % self.pi.target_dir(x64=True)) jpayne@69: ] jpayne@69: jpayne@69: if self.pi.current_cpu != self.pi.target_cpu: jpayne@69: tools += [ jpayne@69: os.path.join( jpayne@69: si.VCInstallDir, host_dir % self.pi.current_dir(x64=True) jpayne@69: ) jpayne@69: ] jpayne@69: jpayne@69: else: jpayne@69: tools += [os.path.join(si.VCInstallDir, 'Bin')] jpayne@69: jpayne@69: return tools jpayne@69: jpayne@69: @property jpayne@69: def OSLibraries(self): jpayne@69: """ jpayne@69: Microsoft Windows SDK Libraries. jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: list of str jpayne@69: paths jpayne@69: """ jpayne@69: if self.vs_ver <= 10.0: jpayne@69: arch_subdir = self.pi.target_dir(hidex86=True, x64=True) jpayne@69: return [os.path.join(self.si.WindowsSdkDir, 'Lib%s' % arch_subdir)] jpayne@69: jpayne@69: else: jpayne@69: arch_subdir = self.pi.target_dir(x64=True) jpayne@69: lib = os.path.join(self.si.WindowsSdkDir, 'lib') jpayne@69: libver = self._sdk_subdir jpayne@69: return [os.path.join(lib, '%sum%s' % (libver, arch_subdir))] jpayne@69: jpayne@69: @property jpayne@69: def OSIncludes(self): jpayne@69: """ jpayne@69: Microsoft Windows SDK Include. jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: list of str jpayne@69: paths jpayne@69: """ jpayne@69: include = os.path.join(self.si.WindowsSdkDir, 'include') jpayne@69: jpayne@69: if self.vs_ver <= 10.0: jpayne@69: return [include, os.path.join(include, 'gl')] jpayne@69: jpayne@69: else: jpayne@69: if self.vs_ver >= 14.0: jpayne@69: sdkver = self._sdk_subdir jpayne@69: else: jpayne@69: sdkver = '' jpayne@69: return [ jpayne@69: os.path.join(include, '%sshared' % sdkver), jpayne@69: os.path.join(include, '%sum' % sdkver), jpayne@69: os.path.join(include, '%swinrt' % sdkver), jpayne@69: ] jpayne@69: jpayne@69: @property jpayne@69: def OSLibpath(self): jpayne@69: """ jpayne@69: Microsoft Windows SDK Libraries Paths. jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: list of str jpayne@69: paths jpayne@69: """ jpayne@69: ref = os.path.join(self.si.WindowsSdkDir, 'References') jpayne@69: libpath = [] jpayne@69: jpayne@69: if self.vs_ver <= 9.0: jpayne@69: libpath += self.OSLibraries jpayne@69: jpayne@69: if self.vs_ver >= 11.0: jpayne@69: libpath += [os.path.join(ref, r'CommonConfiguration\Neutral')] jpayne@69: jpayne@69: if self.vs_ver >= 14.0: jpayne@69: libpath += [ jpayne@69: ref, jpayne@69: os.path.join(self.si.WindowsSdkDir, 'UnionMetadata'), jpayne@69: os.path.join(ref, 'Windows.Foundation.UniversalApiContract', '1.0.0.0'), jpayne@69: os.path.join(ref, 'Windows.Foundation.FoundationContract', '1.0.0.0'), jpayne@69: os.path.join( jpayne@69: ref, 'Windows.Networking.Connectivity.WwanContract', '1.0.0.0' jpayne@69: ), jpayne@69: os.path.join( jpayne@69: self.si.WindowsSdkDir, jpayne@69: 'ExtensionSDKs', jpayne@69: 'Microsoft.VCLibs', jpayne@69: '%0.1f' % self.vs_ver, jpayne@69: 'References', jpayne@69: 'CommonConfiguration', jpayne@69: 'neutral', jpayne@69: ), jpayne@69: ] jpayne@69: return libpath jpayne@69: jpayne@69: @property jpayne@69: def SdkTools(self): jpayne@69: """ jpayne@69: Microsoft Windows SDK Tools. jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: list of str jpayne@69: paths jpayne@69: """ jpayne@69: return list(self._sdk_tools()) jpayne@69: jpayne@69: def _sdk_tools(self): jpayne@69: """ jpayne@69: Microsoft Windows SDK Tools paths generator. jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: generator of str jpayne@69: paths jpayne@69: """ jpayne@69: if self.vs_ver < 15.0: jpayne@69: bin_dir = 'Bin' if self.vs_ver <= 11.0 else r'Bin\x86' jpayne@69: yield os.path.join(self.si.WindowsSdkDir, bin_dir) jpayne@69: jpayne@69: if not self.pi.current_is_x86(): jpayne@69: arch_subdir = self.pi.current_dir(x64=True) jpayne@69: path = 'Bin%s' % arch_subdir jpayne@69: yield os.path.join(self.si.WindowsSdkDir, path) jpayne@69: jpayne@69: if self.vs_ver in (10.0, 11.0): jpayne@69: if self.pi.target_is_x86(): jpayne@69: arch_subdir = '' jpayne@69: else: jpayne@69: arch_subdir = self.pi.current_dir(hidex86=True, x64=True) jpayne@69: path = r'Bin\NETFX 4.0 Tools%s' % arch_subdir jpayne@69: yield os.path.join(self.si.WindowsSdkDir, path) jpayne@69: jpayne@69: elif self.vs_ver >= 15.0: jpayne@69: path = os.path.join(self.si.WindowsSdkDir, 'Bin') jpayne@69: arch_subdir = self.pi.current_dir(x64=True) jpayne@69: sdkver = self.si.WindowsSdkLastVersion jpayne@69: yield os.path.join(path, '%s%s' % (sdkver, arch_subdir)) jpayne@69: jpayne@69: if self.si.WindowsSDKExecutablePath: jpayne@69: yield self.si.WindowsSDKExecutablePath jpayne@69: jpayne@69: @property jpayne@69: def _sdk_subdir(self): jpayne@69: """ jpayne@69: Microsoft Windows SDK version subdir. jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: str jpayne@69: subdir jpayne@69: """ jpayne@69: ucrtver = self.si.WindowsSdkLastVersion jpayne@69: return ('%s\\' % ucrtver) if ucrtver else '' jpayne@69: jpayne@69: @property jpayne@69: def SdkSetup(self): jpayne@69: """ jpayne@69: Microsoft Windows SDK Setup. jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: list of str jpayne@69: paths jpayne@69: """ jpayne@69: if self.vs_ver > 9.0: jpayne@69: return [] jpayne@69: jpayne@69: return [os.path.join(self.si.WindowsSdkDir, 'Setup')] jpayne@69: jpayne@69: @property jpayne@69: def FxTools(self): jpayne@69: """ jpayne@69: Microsoft .NET Framework Tools. jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: list of str jpayne@69: paths jpayne@69: """ jpayne@69: pi = self.pi jpayne@69: si = self.si jpayne@69: jpayne@69: if self.vs_ver <= 10.0: jpayne@69: include32 = True jpayne@69: include64 = not pi.target_is_x86() and not pi.current_is_x86() jpayne@69: else: jpayne@69: include32 = pi.target_is_x86() or pi.current_is_x86() jpayne@69: include64 = pi.current_cpu == 'amd64' or pi.target_cpu == 'amd64' jpayne@69: jpayne@69: tools = [] jpayne@69: if include32: jpayne@69: tools += [ jpayne@69: os.path.join(si.FrameworkDir32, ver) for ver in si.FrameworkVersion32 jpayne@69: ] jpayne@69: if include64: jpayne@69: tools += [ jpayne@69: os.path.join(si.FrameworkDir64, ver) for ver in si.FrameworkVersion64 jpayne@69: ] jpayne@69: return tools jpayne@69: jpayne@69: @property jpayne@69: def NetFxSDKLibraries(self): jpayne@69: """ jpayne@69: Microsoft .Net Framework SDK Libraries. jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: list of str jpayne@69: paths jpayne@69: """ jpayne@69: if self.vs_ver < 14.0 or not self.si.NetFxSdkDir: jpayne@69: return [] jpayne@69: jpayne@69: arch_subdir = self.pi.target_dir(x64=True) jpayne@69: return [os.path.join(self.si.NetFxSdkDir, r'lib\um%s' % arch_subdir)] jpayne@69: jpayne@69: @property jpayne@69: def NetFxSDKIncludes(self): jpayne@69: """ jpayne@69: Microsoft .Net Framework SDK Includes. jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: list of str jpayne@69: paths jpayne@69: """ jpayne@69: if self.vs_ver < 14.0 or not self.si.NetFxSdkDir: jpayne@69: return [] jpayne@69: jpayne@69: return [os.path.join(self.si.NetFxSdkDir, r'include\um')] jpayne@69: jpayne@69: @property jpayne@69: def VsTDb(self): jpayne@69: """ jpayne@69: Microsoft Visual Studio Team System Database. jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: list of str jpayne@69: paths jpayne@69: """ jpayne@69: return [os.path.join(self.si.VSInstallDir, r'VSTSDB\Deploy')] jpayne@69: jpayne@69: @property jpayne@69: def MSBuild(self): jpayne@69: """ jpayne@69: Microsoft Build Engine. jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: list of str jpayne@69: paths jpayne@69: """ jpayne@69: if self.vs_ver < 12.0: jpayne@69: return [] jpayne@69: elif self.vs_ver < 15.0: jpayne@69: base_path = self.si.ProgramFilesx86 jpayne@69: arch_subdir = self.pi.current_dir(hidex86=True) jpayne@69: else: jpayne@69: base_path = self.si.VSInstallDir jpayne@69: arch_subdir = '' jpayne@69: jpayne@69: path = r'MSBuild\%0.1f\bin%s' % (self.vs_ver, arch_subdir) jpayne@69: build = [os.path.join(base_path, path)] jpayne@69: jpayne@69: if self.vs_ver >= 15.0: jpayne@69: # Add Roslyn C# & Visual Basic Compiler jpayne@69: build += [os.path.join(base_path, path, 'Roslyn')] jpayne@69: jpayne@69: return build jpayne@69: jpayne@69: @property jpayne@69: def HTMLHelpWorkshop(self): jpayne@69: """ jpayne@69: Microsoft HTML Help Workshop. jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: list of str jpayne@69: paths jpayne@69: """ jpayne@69: if self.vs_ver < 11.0: jpayne@69: return [] jpayne@69: jpayne@69: return [os.path.join(self.si.ProgramFilesx86, 'HTML Help Workshop')] jpayne@69: jpayne@69: @property jpayne@69: def UCRTLibraries(self): jpayne@69: """ jpayne@69: Microsoft Universal C Runtime SDK Libraries. jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: list of str jpayne@69: paths jpayne@69: """ jpayne@69: if self.vs_ver < 14.0: jpayne@69: return [] jpayne@69: jpayne@69: arch_subdir = self.pi.target_dir(x64=True) jpayne@69: lib = os.path.join(self.si.UniversalCRTSdkDir, 'lib') jpayne@69: ucrtver = self._ucrt_subdir jpayne@69: return [os.path.join(lib, '%sucrt%s' % (ucrtver, arch_subdir))] jpayne@69: jpayne@69: @property jpayne@69: def UCRTIncludes(self): jpayne@69: """ jpayne@69: Microsoft Universal C Runtime SDK Include. jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: list of str jpayne@69: paths jpayne@69: """ jpayne@69: if self.vs_ver < 14.0: jpayne@69: return [] jpayne@69: jpayne@69: include = os.path.join(self.si.UniversalCRTSdkDir, 'include') jpayne@69: return [os.path.join(include, '%sucrt' % self._ucrt_subdir)] jpayne@69: jpayne@69: @property jpayne@69: def _ucrt_subdir(self): jpayne@69: """ jpayne@69: Microsoft Universal C Runtime SDK version subdir. jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: str jpayne@69: subdir jpayne@69: """ jpayne@69: ucrtver = self.si.UniversalCRTSdkLastVersion jpayne@69: return ('%s\\' % ucrtver) if ucrtver else '' jpayne@69: jpayne@69: @property jpayne@69: def FSharp(self): jpayne@69: """ jpayne@69: Microsoft Visual F#. jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: list of str jpayne@69: paths jpayne@69: """ jpayne@69: if 11.0 > self.vs_ver > 12.0: jpayne@69: return [] jpayne@69: jpayne@69: return [self.si.FSharpInstallDir] jpayne@69: jpayne@69: @property jpayne@69: def VCRuntimeRedist(self) -> str | None: jpayne@69: """ jpayne@69: Microsoft Visual C++ runtime redistributable dll. jpayne@69: jpayne@69: Returns the first suitable path found or None. jpayne@69: """ jpayne@69: vcruntime = 'vcruntime%d0.dll' % self.vc_ver jpayne@69: arch_subdir = self.pi.target_dir(x64=True).strip('\\') jpayne@69: jpayne@69: # Installation prefixes candidates jpayne@69: prefixes = [] jpayne@69: tools_path = self.si.VCInstallDir jpayne@69: redist_path = os.path.dirname(tools_path.replace(r'\Tools', r'\Redist')) jpayne@69: if os.path.isdir(redist_path): jpayne@69: # Redist version may not be exactly the same as tools jpayne@69: redist_path = os.path.join(redist_path, os.listdir(redist_path)[-1]) jpayne@69: prefixes += [redist_path, os.path.join(redist_path, 'onecore')] jpayne@69: jpayne@69: prefixes += [os.path.join(tools_path, 'redist')] # VS14 legacy path jpayne@69: jpayne@69: # CRT directory jpayne@69: crt_dirs = ( jpayne@69: 'Microsoft.VC%d.CRT' % (self.vc_ver * 10), jpayne@69: # Sometime store in directory with VS version instead of VC jpayne@69: 'Microsoft.VC%d.CRT' % (int(self.vs_ver) * 10), jpayne@69: ) jpayne@69: jpayne@69: # vcruntime path jpayne@69: candidate_paths = ( jpayne@69: os.path.join(prefix, arch_subdir, crt_dir, vcruntime) jpayne@69: for (prefix, crt_dir) in itertools.product(prefixes, crt_dirs) jpayne@69: ) jpayne@69: return next(filter(os.path.isfile, candidate_paths), None) # type: ignore[arg-type] #python/mypy#12682 jpayne@69: jpayne@69: def return_env(self, exists=True): jpayne@69: """ jpayne@69: Return environment dict. jpayne@69: jpayne@69: Parameters jpayne@69: ---------- jpayne@69: exists: bool jpayne@69: It True, only return existing paths. jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: dict jpayne@69: environment jpayne@69: """ jpayne@69: env = dict( jpayne@69: include=self._build_paths( jpayne@69: 'include', jpayne@69: [ jpayne@69: self.VCIncludes, jpayne@69: self.OSIncludes, jpayne@69: self.UCRTIncludes, jpayne@69: self.NetFxSDKIncludes, jpayne@69: ], jpayne@69: exists, jpayne@69: ), jpayne@69: lib=self._build_paths( jpayne@69: 'lib', jpayne@69: [ jpayne@69: self.VCLibraries, jpayne@69: self.OSLibraries, jpayne@69: self.FxTools, jpayne@69: self.UCRTLibraries, jpayne@69: self.NetFxSDKLibraries, jpayne@69: ], jpayne@69: exists, jpayne@69: ), jpayne@69: libpath=self._build_paths( jpayne@69: 'libpath', jpayne@69: [self.VCLibraries, self.FxTools, self.VCStoreRefs, self.OSLibpath], jpayne@69: exists, jpayne@69: ), jpayne@69: path=self._build_paths( jpayne@69: 'path', jpayne@69: [ jpayne@69: self.VCTools, jpayne@69: self.VSTools, jpayne@69: self.VsTDb, jpayne@69: self.SdkTools, jpayne@69: self.SdkSetup, jpayne@69: self.FxTools, jpayne@69: self.MSBuild, jpayne@69: self.HTMLHelpWorkshop, jpayne@69: self.FSharp, jpayne@69: ], jpayne@69: exists, jpayne@69: ), jpayne@69: ) jpayne@69: if self.vs_ver >= 14 and self.VCRuntimeRedist: jpayne@69: env['py_vcruntime_redist'] = self.VCRuntimeRedist jpayne@69: return env jpayne@69: jpayne@69: def _build_paths(self, name, spec_path_lists, exists): jpayne@69: """ jpayne@69: Given an environment variable name and specified paths, jpayne@69: return a pathsep-separated string of paths containing jpayne@69: unique, extant, directories from those paths and from jpayne@69: the environment variable. Raise an error if no paths jpayne@69: are resolved. jpayne@69: jpayne@69: Parameters jpayne@69: ---------- jpayne@69: name: str jpayne@69: Environment variable name jpayne@69: spec_path_lists: list of str jpayne@69: Paths jpayne@69: exists: bool jpayne@69: It True, only return existing paths. jpayne@69: jpayne@69: Return jpayne@69: ------ jpayne@69: str jpayne@69: Pathsep-separated paths jpayne@69: """ jpayne@69: # flatten spec_path_lists jpayne@69: spec_paths = itertools.chain.from_iterable(spec_path_lists) jpayne@69: env_paths = environ.get(name, '').split(os.pathsep) jpayne@69: paths = itertools.chain(spec_paths, env_paths) jpayne@69: extant_paths = list(filter(os.path.isdir, paths)) if exists else paths jpayne@69: if not extant_paths: jpayne@69: msg = "%s environment variable is empty" % name.upper() jpayne@69: raise distutils.errors.DistutilsPlatformError(msg) jpayne@69: unique_paths = unique_everseen(extant_paths) jpayne@69: return os.pathsep.join(unique_paths)