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