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