Aluode/PerceptionLabPortable
0
1"""2Environment info about Microsoft Compilers.3 4>>> getfixture('windows_only')5>>> ei = EnvironmentInfo('amd64')6"""7 8from __future__ import annotations9 10import contextlib11import itertools12import json13import os14import os.path15import platform16from typing import TYPE_CHECKING, TypedDict17 18from more_itertools import unique_everseen19 20import distutils.errors21 22if TYPE_CHECKING:23 from typing_extensions import LiteralString, NotRequired24 25# https://github.com/python/mypy/issues/816626if not TYPE_CHECKING and platform.system() == 'Windows':27 import winreg28 from os import environ29else:30 # Mock winreg and environ so the module can be imported on this platform.31 32 class winreg:33 HKEY_USERS = None34 HKEY_CURRENT_USER = None35 HKEY_LOCAL_MACHINE = None36 HKEY_CLASSES_ROOT = None37 38 environ: dict[str, str] = dict()39 40 41class PlatformInfo:42 """43 Current and Target Architectures information.44 45 Parameters46 ----------47 arch: str48 Target architecture.49 """50 51 current_cpu = environ.get('processor_architecture', '').lower()52 53 def __init__(self, arch) -> None:54 self.arch = arch.lower().replace('x64', 'amd64')55 56 @property57 def target_cpu(self):58 """59 Return Target CPU architecture.60 61 Return62 ------63 str64 Target CPU65 """66 return self.arch[self.arch.find('_') + 1 :]67 68 def target_is_x86(self):69 """70 Return True if target CPU is x86 32 bits..71 72 Return73 ------74 bool75 CPU is x86 32 bits76 """77 return self.target_cpu == 'x86'78 79 def current_is_x86(self):80 """81 Return True if current CPU is x86 32 bits..82 83 Return84 ------85 bool86 CPU is x86 32 bits87 """88 return self.current_cpu == 'x86'89 90 def current_dir(self, hidex86=False, x64=False) -> str:91 """92 Current platform specific subfolder.93 94 Parameters95 ----------96 hidex86: bool97 return '' and not '\x86' if architecture is x86.98 x64: bool99 return '\x64' and not '\amd64' if architecture is amd64.100 101 Return102 ------103 str104 subfolder: '\target', or '' (see hidex86 parameter)105 """106 return (107 ''108 if (self.current_cpu == 'x86' and hidex86)109 else r'\x64'110 if (self.current_cpu == 'amd64' and x64)111 else rf'\{self.current_cpu}'112 )113 114 def target_dir(self, hidex86=False, x64=False) -> str:115 r"""116 Target platform specific subfolder.117 118 Parameters119 ----------120 hidex86: bool121 return '' and not '\x86' if architecture is x86.122 x64: bool123 return '\x64' and not '\amd64' if architecture is amd64.124 125 Return126 ------127 str128 subfolder: '\current', or '' (see hidex86 parameter)129 """130 return (131 ''132 if (self.target_cpu == 'x86' and hidex86)133 else r'\x64'134 if (self.target_cpu == 'amd64' and x64)135 else rf'\{self.target_cpu}'136 )137 138 def cross_dir(self, forcex86=False):139 r"""140 Cross platform specific subfolder.141 142 Parameters143 ----------144 forcex86: bool145 Use 'x86' as current architecture even if current architecture is146 not x86.147 148 Return149 ------150 str151 subfolder: '' if target architecture is current architecture,152 '\current_target' if not.153 """154 current = 'x86' if forcex86 else self.current_cpu155 return (156 ''157 if self.target_cpu == current158 else self.target_dir().replace('\\', f'\\{current}_')159 )160 161 162class RegistryInfo:163 """164 Microsoft Visual Studio related registry information.165 166 Parameters167 ----------168 platform_info: PlatformInfo169 "PlatformInfo" instance.170 """171 172 HKEYS = (173 winreg.HKEY_USERS,174 winreg.HKEY_CURRENT_USER,175 winreg.HKEY_LOCAL_MACHINE,176 winreg.HKEY_CLASSES_ROOT,177 )178 179 def __init__(self, platform_info) -> None:180 self.pi = platform_info181 182 @property183 def visualstudio(self) -> str:184 """185 Microsoft Visual Studio root registry key.186 187 Return188 ------189 str190 Registry key191 """192 return 'VisualStudio'193 194 @property195 def sxs(self):196 """197 Microsoft Visual Studio SxS registry key.198 199 Return200 ------201 str202 Registry key203 """204 return os.path.join(self.visualstudio, 'SxS')205 206 @property207 def vc(self):208 """209 Microsoft Visual C++ VC7 registry key.210 211 Return212 ------213 str214 Registry key215 """216 return os.path.join(self.sxs, 'VC7')217 218 @property219 def vs(self):220 """221 Microsoft Visual Studio VS7 registry key.222 223 Return224 ------225 str226 Registry key227 """228 return os.path.join(self.sxs, 'VS7')229 230 @property231 def vc_for_python(self) -> str:232 """233 Microsoft Visual C++ for Python registry key.234 235 Return236 ------237 str238 Registry key239 """240 return r'DevDiv\VCForPython'241 242 @property243 def microsoft_sdk(self) -> str:244 """245 Microsoft SDK registry key.246 247 Return248 ------249 str250 Registry key251 """252 return 'Microsoft SDKs'253 254 @property255 def windows_sdk(self):256 """257 Microsoft Windows/Platform SDK registry key.258 259 Return260 ------261 str262 Registry key263 """264 return os.path.join(self.microsoft_sdk, 'Windows')265 266 @property267 def netfx_sdk(self):268 """269 Microsoft .NET Framework SDK registry key.270 271 Return272 ------273 str274 Registry key275 """276 return os.path.join(self.microsoft_sdk, 'NETFXSDK')277 278 @property279 def windows_kits_roots(self) -> str:280 """281 Microsoft Windows Kits Roots registry key.282 283 Return284 ------285 str286 Registry key287 """288 return r'Windows Kits\Installed Roots'289 290 def microsoft(self, key, x86=False):291 """292 Return key in Microsoft software registry.293 294 Parameters295 ----------296 key: str297 Registry key path where look.298 x86: str299 Force x86 software registry.300 301 Return302 ------303 str304 Registry key305 """306 node64 = '' if self.pi.current_is_x86() or x86 else 'Wow6432Node'307 return os.path.join('Software', node64, 'Microsoft', key)308 309 def lookup(self, key, name):310 """311 Look for values in registry in Microsoft software registry.312 313 Parameters314 ----------315 key: str316 Registry key path where look.317 name: str318 Value name to find.319 320 Return321 ------322 str323 value324 """325 key_read = winreg.KEY_READ326 openkey = winreg.OpenKey327 closekey = winreg.CloseKey328 ms = self.microsoft329 for hkey in self.HKEYS:330 bkey = None331 try:332 bkey = openkey(hkey, ms(key), 0, key_read)333 except OSError:334 if not self.pi.current_is_x86():335 try:336 bkey = openkey(hkey, ms(key, True), 0, key_read)337 except OSError:338 continue339 else:340 continue341 try:342 return winreg.QueryValueEx(bkey, name)[0]343 except OSError:344 pass345 finally:346 if bkey:347 closekey(bkey)348 return None349 350 351class SystemInfo:352 """353 Microsoft Windows and Visual Studio related system information.354 355 Parameters356 ----------357 registry_info: RegistryInfo358 "RegistryInfo" instance.359 vc_ver: float360 Required Microsoft Visual C++ version.361 """362 363 # Variables and properties in this class use originals CamelCase variables364 # names from Microsoft source files for more easy comparison.365 WinDir = environ.get('WinDir', '')366 ProgramFiles = environ.get('ProgramFiles', '')367 ProgramFilesx86 = environ.get('ProgramFiles(x86)', ProgramFiles)368 369 def __init__(self, registry_info, vc_ver=None) -> None:370 self.ri = registry_info371 self.pi = self.ri.pi372 373 self.known_vs_paths = self.find_programdata_vs_vers()374 375 # Except for VS15+, VC version is aligned with VS version376 self.vs_ver = self.vc_ver = vc_ver or self._find_latest_available_vs_ver()377 378 def _find_latest_available_vs_ver(self):379 """380 Find the latest VC version381 382 Return383 ------384 float385 version386 """387 reg_vc_vers = self.find_reg_vs_vers()388 389 if not (reg_vc_vers or self.known_vs_paths):390 raise distutils.errors.DistutilsPlatformError(391 'No Microsoft Visual C++ version found'392 )393 394 vc_vers = set(reg_vc_vers)395 vc_vers.update(self.known_vs_paths)396 return sorted(vc_vers)[-1]397 398 def find_reg_vs_vers(self):399 """400 Find Microsoft Visual Studio versions available in registry.401 402 Return403 ------404 list of float405 Versions406 """407 ms = self.ri.microsoft408 vckeys = (self.ri.vc, self.ri.vc_for_python, self.ri.vs)409 vs_vers = []410 for hkey, key in itertools.product(self.ri.HKEYS, vckeys):411 try:412 bkey = winreg.OpenKey(hkey, ms(key), 0, winreg.KEY_READ)413 except OSError:414 continue415 with bkey:416 subkeys, values, _ = winreg.QueryInfoKey(bkey)417 for i in range(values):418 with contextlib.suppress(ValueError):419 ver = float(winreg.EnumValue(bkey, i)[0])420 if ver not in vs_vers:421 vs_vers.append(ver)422 for i in range(subkeys):423 with contextlib.suppress(ValueError):424 ver = float(winreg.EnumKey(bkey, i))425 if ver not in vs_vers:426 vs_vers.append(ver)427 return sorted(vs_vers)428 429 def find_programdata_vs_vers(self) -> dict[float, str]:430 r"""431 Find Visual studio 2017+ versions from information in432 "C:\ProgramData\Microsoft\VisualStudio\Packages\_Instances".433 434 Return435 ------436 dict437 float version as key, path as value.438 """439 vs_versions: dict[float, str] = {}440 instances_dir = r'C:\ProgramData\Microsoft\VisualStudio\Packages\_Instances'441 442 try:443 hashed_names = os.listdir(instances_dir)444 445 except OSError:446 # Directory not exists with all Visual Studio versions447 return vs_versions448 449 for name in hashed_names:450 try:451 # Get VS installation path from "state.json" file452 state_path = os.path.join(instances_dir, name, 'state.json')453 with open(state_path, 'rt', encoding='utf-8') as state_file:454 state = json.load(state_file)455 vs_path = state['installationPath']456 457 # Raises OSError if this VS installation does not contain VC458 os.listdir(os.path.join(vs_path, r'VC\Tools\MSVC'))459 460 # Store version and path461 vs_versions[self._as_float_version(state['installationVersion'])] = (462 vs_path463 )464 465 except (OSError, KeyError):466 # Skip if "state.json" file is missing or bad format467 continue468 469 return vs_versions470 471 @staticmethod472 def _as_float_version(version):473 """474 Return a string version as a simplified float version (major.minor)475 476 Parameters477 ----------478 version: str479 Version.480 481 Return482 ------483 float484 version485 """486 return float('.'.join(version.split('.')[:2]))487 488 @property489 def VSInstallDir(self):490 """491 Microsoft Visual Studio directory.492 493 Return494 ------495 str496 path497 """498 # Default path499 default = os.path.join(500 self.ProgramFilesx86, f'Microsoft Visual Studio {self.vs_ver:0.1f}'501 )502 503 # Try to get path from registry, if fail use default path504 return self.ri.lookup(self.ri.vs, f'{self.vs_ver:0.1f}') or default505 506 @property507 def VCInstallDir(self):508 """509 Microsoft Visual C++ directory.510 511 Return512 ------513 str514 path515 """516 path = self._guess_vc() or self._guess_vc_legacy()517 518 if not os.path.isdir(path):519 msg = 'Microsoft Visual C++ directory not found'520 raise distutils.errors.DistutilsPlatformError(msg)521 522 return path523 524 def _guess_vc(self):525 """526 Locate Visual C++ for VS2017+.527 528 Return529 ------530 str531 path532 """533 if self.vs_ver <= 14.0:534 return ''535 536 try:537 # First search in known VS paths538 vs_dir = self.known_vs_paths[self.vs_ver]539 except KeyError:540 # Else, search with path from registry541 vs_dir = self.VSInstallDir542 543 guess_vc = os.path.join(vs_dir, r'VC\Tools\MSVC')544 545 # Subdir with VC exact version as name546 try:547 # Update the VC version with real one instead of VS version548 vc_ver = os.listdir(guess_vc)[-1]549 self.vc_ver = self._as_float_version(vc_ver)550 return os.path.join(guess_vc, vc_ver)551 except (OSError, IndexError):552 return ''553 554 def _guess_vc_legacy(self):555 """556 Locate Visual C++ for versions prior to 2017.557 558 Return559 ------560 str561 path562 """563 default = os.path.join(564 self.ProgramFilesx86,565 rf'Microsoft Visual Studio {self.vs_ver:0.1f}\VC',566 )567 568 # Try to get "VC++ for Python" path from registry as default path569 reg_path = os.path.join(self.ri.vc_for_python, f'{self.vs_ver:0.1f}')570 python_vc = self.ri.lookup(reg_path, 'installdir')571 default_vc = os.path.join(python_vc, 'VC') if python_vc else default572 573 # Try to get path from registry, if fail use default path574 return self.ri.lookup(self.ri.vc, f'{self.vs_ver:0.1f}') or default_vc575 576 @property577 def WindowsSdkVersion(self) -> tuple[LiteralString, ...]:578 """579 Microsoft Windows SDK versions for specified MSVC++ version.580 581 Return582 ------583 tuple of str584 versions585 """586 if self.vs_ver <= 9.0:587 return '7.0', '6.1', '6.0a'588 elif self.vs_ver == 10.0:589 return '7.1', '7.0a'590 elif self.vs_ver == 11.0:591 return '8.0', '8.0a'592 elif self.vs_ver == 12.0:593 return '8.1', '8.1a'594 elif self.vs_ver >= 14.0:595 return '10.0', '8.1'596 return ()597 598 @property599 def WindowsSdkLastVersion(self):600 """601 Microsoft Windows SDK last version.602 603 Return604 ------605 str606 version607 """608 return self._use_last_dir_name(os.path.join(self.WindowsSdkDir, 'lib'))609 610 @property611 def WindowsSdkDir(self) -> str | None: # noqa: C901 # is too complex (12) # FIXME612 """613 Microsoft Windows SDK directory.614 615 Return616 ------617 str618 path619 """620 sdkdir: str | None = ''621 for ver in self.WindowsSdkVersion:622 # Try to get it from registry623 loc = os.path.join(self.ri.windows_sdk, f'v{ver}')624 sdkdir = self.ri.lookup(loc, 'installationfolder')625 if sdkdir:626 break627 if not sdkdir or not os.path.isdir(sdkdir):628 # Try to get "VC++ for Python" version from registry629 path = os.path.join(self.ri.vc_for_python, f'{self.vc_ver:0.1f}')630 install_base = self.ri.lookup(path, 'installdir')631 if install_base:632 sdkdir = os.path.join(install_base, 'WinSDK')633 if not sdkdir or not os.path.isdir(sdkdir):634 # If fail, use default new path635 for ver in self.WindowsSdkVersion:636 intver = ver[: ver.rfind('.')]637 path = rf'Microsoft SDKs\Windows Kits\{intver}'638 d = os.path.join(self.ProgramFiles, path)639 if os.path.isdir(d):640 sdkdir = d641 if not sdkdir or not os.path.isdir(sdkdir):642 # If fail, use default old path643 for ver in self.WindowsSdkVersion:644 path = rf'Microsoft SDKs\Windows\v{ver}'645 d = os.path.join(self.ProgramFiles, path)646 if os.path.isdir(d):647 sdkdir = d648 if not sdkdir:649 # If fail, use Platform SDK650 sdkdir = os.path.join(self.VCInstallDir, 'PlatformSDK')651 return sdkdir652 653 @property654 def WindowsSDKExecutablePath(self):655 """656 Microsoft Windows SDK executable directory.657 658 Return659 ------660 str661 path662 """663 # Find WinSDK NetFx Tools registry dir name664 if self.vs_ver <= 11.0:665 netfxver = 35666 arch = ''667 else:668 netfxver = 40669 hidex86 = True if self.vs_ver <= 12.0 else False670 arch = self.pi.current_dir(x64=True, hidex86=hidex86).replace('\\', '-')671 fx = f'WinSDK-NetFx{netfxver}Tools{arch}'672 673 # list all possibles registry paths674 regpaths = []675 if self.vs_ver >= 14.0:676 for ver in self.NetFxSdkVersion:677 regpaths += [os.path.join(self.ri.netfx_sdk, ver, fx)]678 679 for ver in self.WindowsSdkVersion:680 regpaths += [os.path.join(self.ri.windows_sdk, f'v{ver}A', fx)]681 682 # Return installation folder from the more recent path683 for path in regpaths:684 execpath = self.ri.lookup(path, 'installationfolder')685 if execpath:686 return execpath687 688 return None689 690 @property691 def FSharpInstallDir(self):692 """693 Microsoft Visual F# directory.694 695 Return696 ------697 str698 path699 """700 path = os.path.join(self.ri.visualstudio, rf'{self.vs_ver:0.1f}\Setup\F#')701 return self.ri.lookup(path, 'productdir') or ''702 703 @property704 def UniversalCRTSdkDir(self):705 """706 Microsoft Universal CRT SDK directory.707 708 Return709 ------710 str711 path712 """713 # Set Kit Roots versions for specified MSVC++ version714 vers = ('10', '81') if self.vs_ver >= 14.0 else ()715 716 # Find path of the more recent Kit717 for ver in vers:718 sdkdir = self.ri.lookup(self.ri.windows_kits_roots, f'kitsroot{ver}')719 if sdkdir:720 return sdkdir or ''721 722 return None723 724 @property725 def UniversalCRTSdkLastVersion(self):726 """727 Microsoft Universal C Runtime SDK last version.728 729 Return730 ------731 str732 version733 """734 return self._use_last_dir_name(os.path.join(self.UniversalCRTSdkDir, 'lib'))735 736 @property737 def NetFxSdkVersion(self):738 """739 Microsoft .NET Framework SDK versions.740 741 Return742 ------743 tuple of str744 versions745 """746 # Set FxSdk versions for specified VS version747 return (748 ('4.7.2', '4.7.1', '4.7', '4.6.2', '4.6.1', '4.6', '4.5.2', '4.5.1', '4.5')749 if self.vs_ver >= 14.0750 else ()751 )752 753 @property754 def NetFxSdkDir(self):755 """756 Microsoft .NET Framework SDK directory.757 758 Return759 ------760 str761 path762 """763 sdkdir = ''764 for ver in self.NetFxSdkVersion:765 loc = os.path.join(self.ri.netfx_sdk, ver)766 sdkdir = self.ri.lookup(loc, 'kitsinstallationfolder')767 if sdkdir:768 break769 return sdkdir770 771 @property772 def FrameworkDir32(self):773 """774 Microsoft .NET Framework 32bit directory.775 776 Return777 ------778 str779 path780 """781 # Default path782 guess_fw = os.path.join(self.WinDir, r'Microsoft.NET\Framework')783 784 # Try to get path from registry, if fail use default path785 return self.ri.lookup(self.ri.vc, 'frameworkdir32') or guess_fw786 787 @property788 def FrameworkDir64(self):789 """790 Microsoft .NET Framework 64bit directory.791 792 Return793 ------794 str795 path796 """797 # Default path798 guess_fw = os.path.join(self.WinDir, r'Microsoft.NET\Framework64')799 800 # Try to get path from registry, if fail use default path801 return self.ri.lookup(self.ri.vc, 'frameworkdir64') or guess_fw802 803 @property804 def FrameworkVersion32(self) -> tuple[str, ...]:805 """806 Microsoft .NET Framework 32bit versions.807 808 Return809 ------810 tuple of str811 versions812 """813 return self._find_dot_net_versions(32)814 815 @property816 def FrameworkVersion64(self) -> tuple[str, ...]:817 """818 Microsoft .NET Framework 64bit versions.819 820 Return821 ------822 tuple of str823 versions824 """825 return self._find_dot_net_versions(64)826 827 def _find_dot_net_versions(self, bits) -> tuple[str, ...]:828 """829 Find Microsoft .NET Framework versions.830 831 Parameters832 ----------833 bits: int834 Platform number of bits: 32 or 64.835 836 Return837 ------838 tuple of str839 versions840 """841 # Find actual .NET version in registry842 reg_ver = self.ri.lookup(self.ri.vc, f'frameworkver{bits}')843 dot_net_dir = getattr(self, f'FrameworkDir{bits}')844 ver = reg_ver or self._use_last_dir_name(dot_net_dir, 'v') or ''845 846 # Set .NET versions for specified MSVC++ version847 if self.vs_ver >= 12.0:848 return ver, 'v4.0'849 elif self.vs_ver >= 10.0:850 return 'v4.0.30319' if ver.lower()[:2] != 'v4' else ver, 'v3.5'851 elif self.vs_ver == 9.0:852 return 'v3.5', 'v2.0.50727'853 elif self.vs_ver == 8.0:854 return 'v3.0', 'v2.0.50727'855 return ()856 857 @staticmethod858 def _use_last_dir_name(path, prefix=''):859 """860 Return name of the last dir in path or '' if no dir found.861 862 Parameters863 ----------864 path: str865 Use dirs in this path866 prefix: str867 Use only dirs starting by this prefix868 869 Return870 ------871 str872 name873 """874 matching_dirs = (875 dir_name876 for dir_name in reversed(os.listdir(path))877 if os.path.isdir(os.path.join(path, dir_name))878 and dir_name.startswith(prefix)879 )880 return next(matching_dirs, None) or ''881 882 883class _EnvironmentDict(TypedDict):884 include: str885 lib: str886 libpath: str887 path: str888 py_vcruntime_redist: NotRequired[str | None]889 890 891class EnvironmentInfo:892 """893 Return environment variables for specified Microsoft Visual C++ version894 and platform : Lib, Include, Path and libpath.895 896 This function is compatible with Microsoft Visual C++ 9.0 to 14.X.897 898 Script created by analysing Microsoft environment configuration files like899 "vcvars[...].bat", "SetEnv.Cmd", "vcbuildtools.bat", ...900 901 Parameters902 ----------903 arch: str904 Target architecture.905 vc_ver: float906 Required Microsoft Visual C++ version. If not set, autodetect the last907 version.908 vc_min_ver: float909 Minimum Microsoft Visual C++ version.910 """911 912 # Variables and properties in this class use originals CamelCase variables913 # names from Microsoft source files for more easy comparison.914 915 def __init__(self, arch, vc_ver=None, vc_min_ver=0) -> None:916 self.pi = PlatformInfo(arch)917 self.ri = RegistryInfo(self.pi)918 self.si = SystemInfo(self.ri, vc_ver)919 920 if self.vc_ver < vc_min_ver:921 err = 'No suitable Microsoft Visual C++ version found'922 raise distutils.errors.DistutilsPlatformError(err)923 924 @property925 def vs_ver(self):926 """927 Microsoft Visual Studio.928 929 Return930 ------931 float932 version933 """934 return self.si.vs_ver935 936 @property937 def vc_ver(self):938 """939 Microsoft Visual C++ version.940 941 Return942 ------943 float944 version945 """946 return self.si.vc_ver947 948 @property949 def VSTools(self):950 """951 Microsoft Visual Studio Tools.952 953 Return954 ------955 list of str956 paths957 """958 paths = [r'Common7\IDE', r'Common7\Tools']959 960 if self.vs_ver >= 14.0:961 arch_subdir = self.pi.current_dir(hidex86=True, x64=True)962 paths += [r'Common7\IDE\CommonExtensions\Microsoft\TestWindow']963 paths += [r'Team Tools\Performance Tools']964 paths += [rf'Team Tools\Performance Tools{arch_subdir}']965 966 return [os.path.join(self.si.VSInstallDir, path) for path in paths]967 968 @property969 def VCIncludes(self):970 """971 Microsoft Visual C++ & Microsoft Foundation Class Includes.972 973 Return974 ------975 list of str976 paths977 """978 return [979 os.path.join(self.si.VCInstallDir, 'Include'),980 os.path.join(self.si.VCInstallDir, r'ATLMFC\Include'),981 ]982 983 @property984 def VCLibraries(self):985 """986 Microsoft Visual C++ & Microsoft Foundation Class Libraries.987 988 Return989 ------990 list of str991 paths992 """993 if self.vs_ver >= 15.0:994 arch_subdir = self.pi.target_dir(x64=True)995 else:996 arch_subdir = self.pi.target_dir(hidex86=True)997 paths = [f'Lib{arch_subdir}', rf'ATLMFC\Lib{arch_subdir}']998 999 if self.vs_ver >= 14.0:1000 paths += [rf'Lib\store{arch_subdir}']1001 1002 return [os.path.join(self.si.VCInstallDir, path) for path in paths]1003 1004 @property1005 def VCStoreRefs(self):1006 """1007 Microsoft Visual C++ store references Libraries.1008 1009 Return1010 ------1011 list of str1012 paths1013 """1014 if self.vs_ver < 14.0:1015 return []1016 return [os.path.join(self.si.VCInstallDir, r'Lib\store\references')]1017 1018 @property1019 def VCTools(self):1020 """1021 Microsoft Visual C++ Tools.1022 1023 Return1024 ------1025 list of str1026 paths1027 1028 When host CPU is ARM, the tools should be found for ARM.1029 1030 >>> getfixture('windows_only')1031 >>> mp = getfixture('monkeypatch')1032 >>> mp.setattr(PlatformInfo, 'current_cpu', 'arm64')1033 >>> ei = EnvironmentInfo(arch='irrelevant')1034 >>> paths = ei.VCTools1035 >>> any('HostARM64' in path for path in paths)1036 True1037 """1038 si = self.si1039 tools = [os.path.join(si.VCInstallDir, 'VCPackages')]1040 1041 forcex86 = True if self.vs_ver <= 10.0 else False1042 arch_subdir = self.pi.cross_dir(forcex86)1043 if arch_subdir:1044 tools += [os.path.join(si.VCInstallDir, f'Bin{arch_subdir}')]1045 1046 if self.vs_ver == 14.0:1047 path = f'Bin{self.pi.current_dir(hidex86=True)}'1048 tools += [os.path.join(si.VCInstallDir, path)]1049 1050 elif self.vs_ver >= 15.0:1051 host_id = self.pi.current_cpu.replace('amd64', 'x64').upper()1052 host_dir = os.path.join('bin', f'Host{host_id}%s')1053 tools += [1054 os.path.join(si.VCInstallDir, host_dir % self.pi.target_dir(x64=True))1055 ]1056 1057 if self.pi.current_cpu != self.pi.target_cpu:1058 tools += [1059 os.path.join(1060 si.VCInstallDir, host_dir % self.pi.current_dir(x64=True)1061 )1062 ]1063 1064 else:1065 tools += [os.path.join(si.VCInstallDir, 'Bin')]1066 1067 return tools1068 1069 @property1070 def OSLibraries(self):1071 """1072 Microsoft Windows SDK Libraries.1073 1074 Return1075 ------1076 list of str1077 paths1078 """1079 if self.vs_ver <= 10.0:1080 arch_subdir = self.pi.target_dir(hidex86=True, x64=True)1081 return [os.path.join(self.si.WindowsSdkDir, f'Lib{arch_subdir}')]1082 1083 else:1084 arch_subdir = self.pi.target_dir(x64=True)1085 lib = os.path.join(self.si.WindowsSdkDir, 'lib')1086 libver = self._sdk_subdir1087 return [os.path.join(lib, f'{libver}um{arch_subdir}')]1088 1089 @property1090 def OSIncludes(self):1091 """1092 Microsoft Windows SDK Include.1093 1094 Return1095 ------1096 list of str1097 paths1098 """1099 include = os.path.join(self.si.WindowsSdkDir, 'include')1100 1101 if self.vs_ver <= 10.0:1102 return [include, os.path.join(include, 'gl')]1103 1104 else:1105 if self.vs_ver >= 14.0:1106 sdkver = self._sdk_subdir1107 else:1108 sdkver = ''1109 return [1110 os.path.join(include, f'{sdkver}shared'),1111 os.path.join(include, f'{sdkver}um'),1112 os.path.join(include, f'{sdkver}winrt'),1113 ]1114 1115 @property1116 def OSLibpath(self):1117 """1118 Microsoft Windows SDK Libraries Paths.1119 1120 Return1121 ------1122 list of str1123 paths1124 """1125 ref = os.path.join(self.si.WindowsSdkDir, 'References')1126 libpath = []1127 1128 if self.vs_ver <= 9.0:1129 libpath += self.OSLibraries1130 1131 if self.vs_ver >= 11.0:1132 libpath += [os.path.join(ref, r'CommonConfiguration\Neutral')]1133 1134 if self.vs_ver >= 14.0:1135 libpath += [1136 ref,1137 os.path.join(self.si.WindowsSdkDir, 'UnionMetadata'),1138 os.path.join(ref, 'Windows.Foundation.UniversalApiContract', '1.0.0.0'),1139 os.path.join(ref, 'Windows.Foundation.FoundationContract', '1.0.0.0'),1140 os.path.join(1141 ref, 'Windows.Networking.Connectivity.WwanContract', '1.0.0.0'1142 ),1143 os.path.join(1144 self.si.WindowsSdkDir,1145 'ExtensionSDKs',1146 'Microsoft.VCLibs',1147 f'{self.vs_ver:0.1f}',1148 'References',1149 'CommonConfiguration',1150 'neutral',1151 ),1152 ]1153 return libpath1154 1155 @property1156 def SdkTools(self):1157 """1158 Microsoft Windows SDK Tools.1159 1160 Return1161 ------1162 list of str1163 paths1164 """1165 return list(self._sdk_tools())1166 1167 def _sdk_tools(self):1168 """1169 Microsoft Windows SDK Tools paths generator.1170 1171 Return1172 ------1173 generator of str1174 paths1175 """1176 if self.vs_ver < 15.0:1177 bin_dir = 'Bin' if self.vs_ver <= 11.0 else r'Bin\x86'1178 yield os.path.join(self.si.WindowsSdkDir, bin_dir)1179 1180 if not self.pi.current_is_x86():1181 arch_subdir = self.pi.current_dir(x64=True)1182 path = f'Bin{arch_subdir}'1183 yield os.path.join(self.si.WindowsSdkDir, path)1184 1185 if self.vs_ver in (10.0, 11.0):1186 if self.pi.target_is_x86():1187 arch_subdir = ''1188 else:1189 arch_subdir = self.pi.current_dir(hidex86=True, x64=True)1190 path = rf'Bin\NETFX 4.0 Tools{arch_subdir}'1191 yield os.path.join(self.si.WindowsSdkDir, path)1192 1193 elif self.vs_ver >= 15.0:1194 path = os.path.join(self.si.WindowsSdkDir, 'Bin')1195 arch_subdir = self.pi.current_dir(x64=True)1196 sdkver = self.si.WindowsSdkLastVersion1197 yield os.path.join(path, f'{sdkver}{arch_subdir}')1198 1199 if self.si.WindowsSDKExecutablePath:1200 yield self.si.WindowsSDKExecutablePath