codekingpro/portable-devtools
114k
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, TypedDict, overload17 18from more_itertools import unique_everseen19 20from ._path import StrPath21from .compat import py31022 23import distutils.errors24 25if TYPE_CHECKING:26 from typing_extensions import LiteralString, NotRequired27 28# https://github.com/python/mypy/issues/816629if not TYPE_CHECKING and platform.system() == 'Windows':30 import winreg31 from os import environ32else:33 # Mock winreg and environ so the module can be imported on this platform.34 35 class winreg:36 HKEY_USERS = None37 HKEY_CURRENT_USER = None38 HKEY_LOCAL_MACHINE = None39 HKEY_CLASSES_ROOT = None40 41 environ: dict[str, str] = dict()42 43 44class PlatformInfo:45 """46 Current and Target Architectures information.47 48 Parameters49 ----------50 arch: str51 Target architecture.52 """53 54 current_cpu = environ.get('processor_architecture', '').lower()55 56 def __init__(self, arch: str) -> None:57 self.arch = arch.lower().replace('x64', 'amd64')58 59 @property60 def target_cpu(self) -> str:61 """62 Return Target CPU architecture.63 64 Return65 ------66 str67 Target CPU68 """69 return self.arch[self.arch.find('_') + 1 :]70 71 def target_is_x86(self) -> bool:72 """73 Return True if target CPU is x86 32 bits..74 75 Return76 ------77 bool78 CPU is x86 32 bits79 """80 return self.target_cpu == 'x86'81 82 def current_is_x86(self) -> bool:83 """84 Return True if current CPU is x86 32 bits..85 86 Return87 ------88 bool89 CPU is x86 32 bits90 """91 return self.current_cpu == 'x86'92 93 def current_dir(self, hidex86=False, x64=False) -> str:94 """95 Current platform specific subfolder.96 97 Parameters98 ----------99 hidex86: bool100 return '' and not '\x86' if architecture is x86.101 x64: bool102 return '\x64' and not '\amd64' if architecture is amd64.103 104 Return105 ------106 str107 subfolder: '\target', or '' (see hidex86 parameter)108 """109 return (110 ''111 if (self.current_cpu == 'x86' and hidex86)112 else r'\x64'113 if (self.current_cpu == 'amd64' and x64)114 else rf'\{self.current_cpu}'115 )116 117 def target_dir(self, hidex86=False, x64=False) -> str:118 r"""119 Target platform specific subfolder.120 121 Parameters122 ----------123 hidex86: bool124 return '' and not '\x86' if architecture is x86.125 x64: bool126 return '\x64' and not '\amd64' if architecture is amd64.127 128 Return129 ------130 str131 subfolder: '\current', or '' (see hidex86 parameter)132 """133 return (134 ''135 if (self.target_cpu == 'x86' and hidex86)136 else r'\x64'137 if (self.target_cpu == 'amd64' and x64)138 else rf'\{self.target_cpu}'139 )140 141 def cross_dir(self, forcex86=False) -> str:142 r"""143 Cross platform specific subfolder.144 145 Parameters146 ----------147 forcex86: bool148 Use 'x86' as current architecture even if current architecture is149 not x86.150 151 Return152 ------153 str154 subfolder: '' if target architecture is current architecture,155 '\current_target' if not.156 """157 current = 'x86' if forcex86 else self.current_cpu158 return (159 ''160 if self.target_cpu == current161 else self.target_dir().replace('\\', f'\\{current}_')162 )163 164 165class RegistryInfo:166 """167 Microsoft Visual Studio related registry information.168 169 Parameters170 ----------171 platform_info: PlatformInfo172 "PlatformInfo" instance.173 """174 175 HKEYS = (176 winreg.HKEY_USERS,177 winreg.HKEY_CURRENT_USER,178 winreg.HKEY_LOCAL_MACHINE,179 winreg.HKEY_CLASSES_ROOT,180 )181 182 def __init__(self, platform_info: PlatformInfo) -> None:183 self.pi = platform_info184 185 @property186 def visualstudio(self) -> LiteralString:187 """188 Microsoft Visual Studio root registry key.189 190 Return191 ------192 str193 Registry key194 """195 return 'VisualStudio'196 197 @property198 def sxs(self) -> LiteralString:199 """200 Microsoft Visual Studio SxS registry key.201 202 Return203 ------204 str205 Registry key206 """207 return os.path.join(self.visualstudio, 'SxS')208 209 @property210 def vc(self) -> LiteralString:211 """212 Microsoft Visual C++ VC7 registry key.213 214 Return215 ------216 str217 Registry key218 """219 return os.path.join(self.sxs, 'VC7')220 221 @property222 def vs(self) -> LiteralString:223 """224 Microsoft Visual Studio VS7 registry key.225 226 Return227 ------228 str229 Registry key230 """231 return os.path.join(self.sxs, 'VS7')232 233 @property234 def vc_for_python(self) -> LiteralString:235 """236 Microsoft Visual C++ for Python registry key.237 238 Return239 ------240 str241 Registry key242 """243 return r'DevDiv\VCForPython'244 245 @property246 def microsoft_sdk(self) -> LiteralString:247 """248 Microsoft SDK registry key.249 250 Return251 ------252 str253 Registry key254 """255 return 'Microsoft SDKs'256 257 @property258 def windows_sdk(self) -> LiteralString:259 """260 Microsoft Windows/Platform SDK registry key.261 262 Return263 ------264 str265 Registry key266 """267 return os.path.join(self.microsoft_sdk, 'Windows')268 269 @property270 def netfx_sdk(self) -> LiteralString:271 """272 Microsoft .NET Framework SDK registry key.273 274 Return275 ------276 str277 Registry key278 """279 return os.path.join(self.microsoft_sdk, 'NETFXSDK')280 281 @property282 def windows_kits_roots(self) -> LiteralString:283 """284 Microsoft Windows Kits Roots registry key.285 286 Return287 ------288 str289 Registry key290 """291 return r'Windows Kits\Installed Roots'292 293 @overload294 def microsoft(self, key: LiteralString, x86: bool = False) -> LiteralString: ...295 @overload296 def microsoft(self, key: str, x86: bool = False) -> str: ... # type: ignore[misc]297 def microsoft(self, key: str, x86: bool = False) -> str:298 """299 Return key in Microsoft software registry.300 301 Parameters302 ----------303 key: str304 Registry key path where look.305 x86: bool306 Force x86 software registry.307 308 Return309 ------310 str311 Registry key312 """313 node64 = '' if self.pi.current_is_x86() or x86 else 'Wow6432Node'314 return os.path.join('Software', node64, 'Microsoft', key)315 316 def lookup(self, key: str, name: str) -> str | None:317 """318 Look for values in registry in Microsoft software registry.319 320 Parameters321 ----------322 key: str323 Registry key path where look.324 name: str325 Value name to find.326 327 Return328 ------329 str | None330 value331 """332 key_read = winreg.KEY_READ333 openkey = winreg.OpenKey334 closekey = winreg.CloseKey335 ms = self.microsoft336 for hkey in self.HKEYS:337 bkey = None338 try:339 bkey = openkey(hkey, ms(key), 0, key_read)340 except OSError:341 if not self.pi.current_is_x86():342 try:343 bkey = openkey(hkey, ms(key, True), 0, key_read)344 except OSError:345 continue346 else:347 continue348 try:349 return winreg.QueryValueEx(bkey, name)[0]350 except OSError:351 pass352 finally:353 if bkey:354 closekey(bkey)355 return None356 357 358class SystemInfo:359 """360 Microsoft Windows and Visual Studio related system information.361 362 Parameters363 ----------364 registry_info: RegistryInfo365 "RegistryInfo" instance.366 vc_ver: float367 Required Microsoft Visual C++ version.368 """369 370 # Variables and properties in this class use originals CamelCase variables371 # names from Microsoft source files for more easy comparison.372 WinDir = environ.get('WinDir', '')373 ProgramFiles = environ.get('ProgramFiles', '')374 ProgramFilesx86 = environ.get('ProgramFiles(x86)', ProgramFiles)375 376 def __init__(377 self, registry_info: RegistryInfo, vc_ver: float | None = None378 ) -> None:379 self.ri = registry_info380 self.pi = self.ri.pi381 382 self.known_vs_paths = self.find_programdata_vs_vers()383 384 # Except for VS15+, VC version is aligned with VS version385 self.vs_ver = self.vc_ver = vc_ver or self._find_latest_available_vs_ver()386 387 def _find_latest_available_vs_ver(self):388 """389 Find the latest VC version390 391 Return392 ------393 float394 version395 """396 reg_vc_vers = self.find_reg_vs_vers()397 398 if not (reg_vc_vers or self.known_vs_paths):399 raise distutils.errors.DistutilsPlatformError(400 'No Microsoft Visual C++ version found'401 )402 403 vc_vers = set(reg_vc_vers)404 vc_vers.update(self.known_vs_paths)405 return max(vc_vers)406 407 def find_reg_vs_vers(self) -> list[float]:408 """409 Find Microsoft Visual Studio versions available in registry.410 411 Return412 ------413 list of float414 Versions415 """416 ms = self.ri.microsoft417 vckeys = (self.ri.vc, self.ri.vc_for_python, self.ri.vs)418 vs_vers = []419 for hkey, key in itertools.product(self.ri.HKEYS, vckeys):420 try:421 bkey = winreg.OpenKey(hkey, ms(key), 0, winreg.KEY_READ)422 except OSError:423 continue424 with bkey:425 subkeys, values, _ = winreg.QueryInfoKey(bkey)426 for i in range(values):427 with contextlib.suppress(ValueError):428 ver = float(winreg.EnumValue(bkey, i)[0])429 if ver not in vs_vers:430 vs_vers.append(ver)431 for i in range(subkeys):432 with contextlib.suppress(ValueError):433 ver = float(winreg.EnumKey(bkey, i))434 if ver not in vs_vers:435 vs_vers.append(ver)436 return sorted(vs_vers)437 438 def find_programdata_vs_vers(self) -> dict[float, str]:439 r"""440 Find Visual studio 2017+ versions from information in441 "C:\ProgramData\Microsoft\VisualStudio\Packages\_Instances".442 443 Return444 ------445 dict446 float version as key, path as value.447 """448 vs_versions: dict[float, str] = {}449 instances_dir = r'C:\ProgramData\Microsoft\VisualStudio\Packages\_Instances'450 451 try:452 hashed_names = os.listdir(instances_dir)453 454 except OSError:455 # Directory not exists with all Visual Studio versions456 return vs_versions457 458 for name in hashed_names:459 try:460 # Get VS installation path from "state.json" file461 state_path = os.path.join(instances_dir, name, 'state.json')462 with open(state_path, 'rt', encoding='utf-8') as state_file:463 state = json.load(state_file)464 vs_path = state['installationPath']465 466 # Raises OSError if this VS installation does not contain VC467 os.listdir(os.path.join(vs_path, r'VC\Tools\MSVC'))468 469 # Store version and path470 vs_versions[self._as_float_version(state['installationVersion'])] = (471 vs_path472 )473 474 except (OSError, KeyError):475 # Skip if "state.json" file is missing or bad format476 continue477 478 return vs_versions479 480 @staticmethod481 def _as_float_version(version):482 """483 Return a string version as a simplified float version (major.minor)484 485 Parameters486 ----------487 version: str488 Version.489 490 Return491 ------492 float493 version494 """495 return float('.'.join(version.split('.')[:2]))496 497 @property498 def VSInstallDir(self) -> str:499 """500 Microsoft Visual Studio directory.501 502 Return503 ------504 str505 path506 """507 # Default path508 default = os.path.join(509 self.ProgramFilesx86, f'Microsoft Visual Studio {self.vs_ver:0.1f}'510 )511 512 # Try to get path from registry, if fail use default path513 return self.ri.lookup(self.ri.vs, f'{self.vs_ver:0.1f}') or default514 515 @property516 def VCInstallDir(self) -> str:517 """518 Microsoft Visual C++ directory.519 520 Return521 ------522 str523 path524 """525 path = self._guess_vc() or self._guess_vc_legacy()526 527 if not os.path.isdir(path):528 msg = 'Microsoft Visual C++ directory not found'529 raise distutils.errors.DistutilsPlatformError(msg)530 531 return path532 533 def _guess_vc(self):534 """535 Locate Visual C++ for VS2017+.536 537 Return538 ------539 str540 path541 """542 if self.vs_ver <= 14.0:543 return ''544 545 try:546 # First search in known VS paths547 vs_dir = self.known_vs_paths[self.vs_ver]548 except KeyError:549 # Else, search with path from registry550 vs_dir = self.VSInstallDir551 552 guess_vc = os.path.join(vs_dir, r'VC\Tools\MSVC')553 554 # Subdir with VC exact version as name555 try:556 # Update the VC version with real one instead of VS version557 vc_ver = os.listdir(guess_vc)[-1]558 self.vc_ver = self._as_float_version(vc_ver)559 return os.path.join(guess_vc, vc_ver)560 except (OSError, IndexError):561 return ''562 563 def _guess_vc_legacy(self):564 """565 Locate Visual C++ for versions prior to 2017.566 567 Return568 ------569 str570 path571 """572 default = os.path.join(573 self.ProgramFilesx86,574 rf'Microsoft Visual Studio {self.vs_ver:0.1f}\VC',575 )576 577 # Try to get "VC++ for Python" path from registry as default path578 reg_path = os.path.join(self.ri.vc_for_python, f'{self.vs_ver:0.1f}')579 python_vc = self.ri.lookup(reg_path, 'installdir')580 default_vc = os.path.join(python_vc, 'VC') if python_vc else default581 582 # Try to get path from registry, if fail use default path583 return self.ri.lookup(self.ri.vc, f'{self.vs_ver:0.1f}') or default_vc584 585 @property586 def WindowsSdkVersion(self) -> tuple[LiteralString, ...]:587 """588 Microsoft Windows SDK versions for specified MSVC++ version.589 590 Return591 ------592 tuple of str593 versions594 """595 if self.vs_ver <= 9.0:596 return '7.0', '6.1', '6.0a'597 elif self.vs_ver == 10.0:598 return '7.1', '7.0a'599 elif self.vs_ver == 11.0:600 return '8.0', '8.0a'601 elif self.vs_ver == 12.0:602 return '8.1', '8.1a'603 elif self.vs_ver >= 14.0:604 return '10.0', '8.1'605 return ()606 607 @property608 def WindowsSdkLastVersion(self) -> str:609 """610 Microsoft Windows SDK last version.611 612 Return613 ------614 str615 version616 """617 return self._use_last_dir_name(os.path.join(self.WindowsSdkDir, 'lib'))618 619 @property620 def WindowsSdkDir(self) -> str: # noqa: C901 # is too complex (12) # FIXME621 """622 Microsoft Windows SDK directory.623 624 Return625 ------626 str627 path628 """629 sdkdir: str | None = ''630 for ver in self.WindowsSdkVersion:631 # Try to get it from registry632 loc = os.path.join(self.ri.windows_sdk, f'v{ver}')633 sdkdir = self.ri.lookup(loc, 'installationfolder')634 if sdkdir:635 break636 if not sdkdir or not os.path.isdir(sdkdir):637 # Try to get "VC++ for Python" version from registry638 path = os.path.join(self.ri.vc_for_python, f'{self.vc_ver:0.1f}')639 install_base = self.ri.lookup(path, 'installdir')640 if install_base:641 sdkdir = os.path.join(install_base, 'WinSDK')642 if not sdkdir or not os.path.isdir(sdkdir):643 # If fail, use default new path644 for ver in self.WindowsSdkVersion:645 intver = ver[: ver.rfind('.')]646 path = rf'Microsoft SDKs\Windows Kits\{intver}'647 d = os.path.join(self.ProgramFiles, path)648 if os.path.isdir(d):649 sdkdir = d650 if not sdkdir or not os.path.isdir(sdkdir):651 # If fail, use default old path652 for ver in self.WindowsSdkVersion:653 path = rf'Microsoft SDKs\Windows\v{ver}'654 d = os.path.join(self.ProgramFiles, path)655 if os.path.isdir(d):656 sdkdir = d657 if not sdkdir:658 # If fail, use Platform SDK659 sdkdir = os.path.join(self.VCInstallDir, 'PlatformSDK')660 return sdkdir661 662 @property663 def WindowsSDKExecutablePath(self) -> str | None:664 """665 Microsoft Windows SDK executable directory.666 667 Return668 ------669 str | None670 path671 """672 # Find WinSDK NetFx Tools registry dir name673 if self.vs_ver <= 11.0:674 netfxver = 35675 arch = ''676 else:677 netfxver = 40678 hidex86 = True if self.vs_ver <= 12.0 else False679 arch = self.pi.current_dir(x64=True, hidex86=hidex86).replace('\\', '-')680 fx = f'WinSDK-NetFx{netfxver}Tools{arch}'681 682 # list all possibles registry paths683 regpaths = []684 if self.vs_ver >= 14.0:685 for ver in self.NetFxSdkVersion:686 regpaths += [os.path.join(self.ri.netfx_sdk, ver, fx)]687 688 for ver in self.WindowsSdkVersion:689 regpaths += [os.path.join(self.ri.windows_sdk, f'v{ver}A', fx)]690 691 # Return installation folder from the more recent path692 for path in regpaths:693 execpath = self.ri.lookup(path, 'installationfolder')694 if execpath:695 return execpath696 697 return None698 699 @property700 def FSharpInstallDir(self) -> str:701 """702 Microsoft Visual F# directory.703 704 Return705 ------706 str707 path708 """709 path = os.path.join(self.ri.visualstudio, rf'{self.vs_ver:0.1f}\Setup\F#')710 return self.ri.lookup(path, 'productdir') or ''711 712 @property713 def UniversalCRTSdkDir(self) -> str | None:714 """715 Microsoft Universal CRT SDK directory.716 717 Return718 ------719 str | None720 path721 """722 # Set Kit Roots versions for specified MSVC++ version723 vers = ('10', '81') if self.vs_ver >= 14.0 else ()724 725 # Find path of the more recent Kit726 for ver in vers:727 sdkdir = self.ri.lookup(self.ri.windows_kits_roots, f'kitsroot{ver}')728 if sdkdir:729 return sdkdir730 731 return None732 733 @property734 def UniversalCRTSdkLastVersion(self) -> str:735 """736 Microsoft Universal C Runtime SDK last version.737 738 Return739 ------740 str741 version742 """743 try:744 return self._use_last_dir_name(os.path.join(self.UniversalCRTSdkDir, 'lib')) # type: ignore[arg-type] # Expected TypeError745 except TypeError as ex:746 py310.add_note(ex, "Cannot find UniversalCRTSdkDir")747 raise748 749 @property750 def NetFxSdkVersion(self) -> tuple[LiteralString, ...]:751 """752 Microsoft .NET Framework SDK versions.753 754 Return755 ------756 tuple of str757 versions758 """759 # Set FxSdk versions for specified VS version760 return (761 ('4.7.2', '4.7.1', '4.7', '4.6.2', '4.6.1', '4.6', '4.5.2', '4.5.1', '4.5')762 if self.vs_ver >= 14.0763 else ()764 )765 766 @property767 def NetFxSdkDir(self) -> str | None:768 """769 Microsoft .NET Framework SDK directory.770 771 Return772 ------773 str | None774 path775 """776 sdkdir: str | None = ''777 for ver in self.NetFxSdkVersion:778 loc = os.path.join(self.ri.netfx_sdk, ver)779 sdkdir = self.ri.lookup(loc, 'kitsinstallationfolder')780 if sdkdir:781 break782 return sdkdir783 784 @property785 def FrameworkDir32(self) -> str:786 """787 Microsoft .NET Framework 32bit directory.788 789 Return790 ------791 str792 path793 """794 # Default path795 guess_fw = os.path.join(self.WinDir, r'Microsoft.NET\Framework')796 797 # Try to get path from registry, if fail use default path798 return self.ri.lookup(self.ri.vc, 'frameworkdir32') or guess_fw799 800 @property801 def FrameworkDir64(self) -> str:802 """803 Microsoft .NET Framework 64bit directory.804 805 Return806 ------807 str808 path809 """810 # Default path811 guess_fw = os.path.join(self.WinDir, r'Microsoft.NET\Framework64')812 813 # Try to get path from registry, if fail use default path814 return self.ri.lookup(self.ri.vc, 'frameworkdir64') or guess_fw815 816 @property817 def FrameworkVersion32(self) -> tuple[str, ...]:818 """819 Microsoft .NET Framework 32bit versions.820 821 Return822 ------823 tuple of str824 versions825 """826 return self._find_dot_net_versions(32)827 828 @property829 def FrameworkVersion64(self) -> tuple[str, ...]:830 """831 Microsoft .NET Framework 64bit versions.832 833 Return834 ------835 tuple of str836 versions837 """838 return self._find_dot_net_versions(64)839 840 def _find_dot_net_versions(self, bits) -> tuple[str, ...]:841 """842 Find Microsoft .NET Framework versions.843 844 Parameters845 ----------846 bits: int847 Platform number of bits: 32 or 64.848 849 Return850 ------851 tuple of str852 versions853 """854 # Find actual .NET version in registry855 reg_ver = self.ri.lookup(self.ri.vc, f'frameworkver{bits}')856 dot_net_dir = getattr(self, f'FrameworkDir{bits}')857 ver = reg_ver or self._use_last_dir_name(dot_net_dir, 'v') or ''858 859 # Set .NET versions for specified MSVC++ version860 if self.vs_ver >= 12.0:861 return ver, 'v4.0'862 elif self.vs_ver >= 10.0:863 return 'v4.0.30319' if ver.lower()[:2] != 'v4' else ver, 'v3.5'864 elif self.vs_ver == 9.0:865 return 'v3.5', 'v2.0.50727'866 elif self.vs_ver == 8.0:867 return 'v3.0', 'v2.0.50727'868 return ()869 870 @staticmethod871 def _use_last_dir_name(path: StrPath, prefix: str = '') -> str:872 """873 Return name of the last dir in path or '' if no dir found.874 875 Parameters876 ----------877 path: StrPath878 Use dirs in this path879 prefix: str880 Use only dirs starting by this prefix881 882 Return883 ------884 str885 name886 """887 matching_dirs = (888 dir_name889 for dir_name in reversed(os.listdir(path))890 if os.path.isdir(os.path.join(path, dir_name))891 and dir_name.startswith(prefix)892 )893 return next(matching_dirs, '')894 895 896class _EnvironmentDict(TypedDict):897 include: str898 lib: str899 libpath: str900 path: str901 py_vcruntime_redist: NotRequired[str | None]902 903 904class EnvironmentInfo:905 """906 Return environment variables for specified Microsoft Visual C++ version907 and platform : Lib, Include, Path and libpath.908 909 This function is compatible with Microsoft Visual C++ 9.0 to 14.X.910 911 Script created by analysing Microsoft environment configuration files like912 "vcvars[...].bat", "SetEnv.Cmd", "vcbuildtools.bat", ...913 914 Parameters915 ----------916 arch: str917 Target architecture.918 vc_ver: float919 Required Microsoft Visual C++ version. If not set, autodetect the last920 version.921 vc_min_ver: float922 Minimum Microsoft Visual C++ version.923 """924 925 # Variables and properties in this class use originals CamelCase variables926 # names from Microsoft source files for more easy comparison.927 928 def __init__(self, arch, vc_ver=None, vc_min_ver=0) -> None:929 self.pi = PlatformInfo(arch)930 self.ri = RegistryInfo(self.pi)931 self.si = SystemInfo(self.ri, vc_ver)932 933 if self.vc_ver < vc_min_ver:934 err = 'No suitable Microsoft Visual C++ version found'935 raise distutils.errors.DistutilsPlatformError(err)936 937 @property938 def vs_ver(self):939 """940 Microsoft Visual Studio.941 942 Return943 ------944 float945 version946 """947 return self.si.vs_ver948 949 @property950 def vc_ver(self):951 """952 Microsoft Visual C++ version.953 954 Return955 ------956 float957 version958 """959 return self.si.vc_ver960 961 @property962 def VSTools(self):963 """964 Microsoft Visual Studio Tools.965 966 Return967 ------968 list of str969 paths970 """971 paths = [r'Common7\IDE', r'Common7\Tools']972 973 if self.vs_ver >= 14.0:974 arch_subdir = self.pi.current_dir(hidex86=True, x64=True)975 paths += [r'Common7\IDE\CommonExtensions\Microsoft\TestWindow']976 paths += [r'Team Tools\Performance Tools']977 paths += [rf'Team Tools\Performance Tools{arch_subdir}']978 979 return [os.path.join(self.si.VSInstallDir, path) for path in paths]980 981 @property982 def VCIncludes(self):983 """984 Microsoft Visual C++ & Microsoft Foundation Class Includes.985 986 Return987 ------988 list of str989 paths990 """991 return [992 os.path.join(self.si.VCInstallDir, 'Include'),993 os.path.join(self.si.VCInstallDir, r'ATLMFC\Include'),994 ]995 996 @property997 def VCLibraries(self):998 """999 Microsoft Visual C++ & Microsoft Foundation Class Libraries.1000 1001 Return1002 ------1003 list of str1004 paths1005 """1006 if self.vs_ver >= 15.0:1007 arch_subdir = self.pi.target_dir(x64=True)1008 else:1009 arch_subdir = self.pi.target_dir(hidex86=True)1010 paths = [f'Lib{arch_subdir}', rf'ATLMFC\Lib{arch_subdir}']1011 1012 if self.vs_ver >= 14.0:1013 paths += [rf'Lib\store{arch_subdir}']1014 1015 return [os.path.join(self.si.VCInstallDir, path) for path in paths]1016 1017 @property1018 def VCStoreRefs(self):1019 """1020 Microsoft Visual C++ store references Libraries.1021 1022 Return1023 ------1024 list of str1025 paths1026 """1027 if self.vs_ver < 14.0:1028 return []1029 return [os.path.join(self.si.VCInstallDir, r'Lib\store\references')]1030 1031 @property1032 def VCTools(self):1033 """1034 Microsoft Visual C++ Tools.1035 1036 Return1037 ------1038 list of str1039 paths1040 1041 When host CPU is ARM, the tools should be found for ARM.1042 1043 >>> getfixture('windows_only')1044 >>> mp = getfixture('monkeypatch')1045 >>> mp.setattr(PlatformInfo, 'current_cpu', 'arm64')1046 >>> ei = EnvironmentInfo(arch='irrelevant')1047 >>> paths = ei.VCTools1048 >>> any('HostARM64' in path for path in paths)1049 True1050 """1051 si = self.si1052 tools = [os.path.join(si.VCInstallDir, 'VCPackages')]1053 1054 forcex86 = True if self.vs_ver <= 10.0 else False1055 arch_subdir = self.pi.cross_dir(forcex86)1056 if arch_subdir:1057 tools += [os.path.join(si.VCInstallDir, f'Bin{arch_subdir}')]1058 1059 if self.vs_ver == 14.0:1060 path = f'Bin{self.pi.current_dir(hidex86=True)}'1061 tools += [os.path.join(si.VCInstallDir, path)]1062 1063 elif self.vs_ver >= 15.0:1064 host_id = self.pi.current_cpu.replace('amd64', 'x64').upper()1065 host_dir = os.path.join('bin', f'Host{host_id}%s')1066 tools += [1067 os.path.join(si.VCInstallDir, host_dir % self.pi.target_dir(x64=True))1068 ]1069 1070 if self.pi.current_cpu != self.pi.target_cpu:1071 tools += [1072 os.path.join(1073 si.VCInstallDir, host_dir % self.pi.current_dir(x64=True)1074 )1075 ]1076 1077 else:1078 tools += [os.path.join(si.VCInstallDir, 'Bin')]1079 1080 return tools1081 1082 @property1083 def OSLibraries(self):1084 """1085 Microsoft Windows SDK Libraries.1086 1087 Return1088 ------1089 list of str1090 paths1091 """1092 if self.vs_ver <= 10.0:1093 arch_subdir = self.pi.target_dir(hidex86=True, x64=True)1094 return [os.path.join(self.si.WindowsSdkDir, f'Lib{arch_subdir}')]1095 1096 else:1097 arch_subdir = self.pi.target_dir(x64=True)1098 lib = os.path.join(self.si.WindowsSdkDir, 'lib')1099 libver = self._sdk_subdir1100 return [os.path.join(lib, f'{libver}um{arch_subdir}')]1101 1102 @property1103 def OSIncludes(self):1104 """1105 Microsoft Windows SDK Include.1106 1107 Return1108 ------1109 list of str1110 paths1111 """1112 include = os.path.join(self.si.WindowsSdkDir, 'include')1113 1114 if self.vs_ver <= 10.0:1115 return [include, os.path.join(include, 'gl')]1116 1117 else:1118 if self.vs_ver >= 14.0:1119 sdkver = self._sdk_subdir1120 else:1121 sdkver = ''1122 return [1123 os.path.join(include, f'{sdkver}shared'),1124 os.path.join(include, f'{sdkver}um'),1125 os.path.join(include, f'{sdkver}winrt'),1126 ]1127 1128 @property1129 def OSLibpath(self):1130 """1131 Microsoft Windows SDK Libraries Paths.1132 1133 Return1134 ------1135 list of str1136 paths1137 """1138 ref = os.path.join(self.si.WindowsSdkDir, 'References')1139 libpath = []1140 1141 if self.vs_ver <= 9.0:1142 libpath += self.OSLibraries1143 1144 if self.vs_ver >= 11.0:1145 libpath += [os.path.join(ref, r'CommonConfiguration\Neutral')]1146 1147 if self.vs_ver >= 14.0:1148 libpath += [1149 ref,1150 os.path.join(self.si.WindowsSdkDir, 'UnionMetadata'),1151 os.path.join(ref, 'Windows.Foundation.UniversalApiContract', '1.0.0.0'),1152 os.path.join(ref, 'Windows.Foundation.FoundationContract', '1.0.0.0'),1153 os.path.join(1154 ref, 'Windows.Networking.Connectivity.WwanContract', '1.0.0.0'1155 ),1156 os.path.join(1157 self.si.WindowsSdkDir,1158 'ExtensionSDKs',1159 'Microsoft.VCLibs',1160 f'{self.vs_ver:0.1f}',1161 'References',1162 'CommonConfiguration',1163 'neutral',1164 ),1165 ]1166 return libpath1167 1168 @property1169 def SdkTools(self):1170 """1171 Microsoft Windows SDK Tools.1172 1173 Return1174 ------1175 list of str1176 paths1177 """1178 return list(self._sdk_tools())1179 1180 def _sdk_tools(self):1181 """1182 Microsoft Windows SDK Tools paths generator.1183 1184 Return1185 ------1186 generator of str1187 paths1188 """1189 if self.vs_ver < 15.0:1190 bin_dir = 'Bin' if self.vs_ver <= 11.0 else r'Bin\x86'1191 yield os.path.join(self.si.WindowsSdkDir, bin_dir)1192 1193 if not self.pi.current_is_x86():1194 arch_subdir = self.pi.current_dir(x64=True)1195 path = f'Bin{arch_subdir}'1196 yield os.path.join(self.si.WindowsSdkDir, path)1197 1198 if self.vs_ver in (10.0, 11.0):1199 if self.pi.target_is_x86():1200 arch_subdir = ''