diff options
author | JGoutin <ginnungagap@free.fr> | 2016-03-03 18:00:58 +0000 |
---|---|---|
committer | JGoutin <ginnungagap@free.fr> | 2016-03-03 18:00:58 +0000 |
commit | 72db3cf5ff66ddab3f6e14880f5545d759938ae6 (patch) | |
tree | 9db0e76b41891a6d19e060ff44c8466e8ef8ffbf /setuptools | |
parent | 75282e5568039e3dae52ee41ada35baf56f0567c (diff) | |
download | python-setuptools-git-72db3cf5ff66ddab3f6e14880f5545d759938ae6.tar.gz |
* Move non registry parts from RegistryInfo to SystemInfo.
* Rename many variable to use the same names as in MSVC .bat files. This really help to compare the Python script with original sources.
* Split compute_env to EnvironmentInfo class.
* Continue to add support for MSVC14.
* Some more little fixes.
Diffstat (limited to 'setuptools')
-rw-r--r-- | setuptools/msvc9_support.py | 775 |
1 files changed, 509 insertions, 266 deletions
diff --git a/setuptools/msvc9_support.py b/setuptools/msvc9_support.py index a956a5b0..8835c22d 100644 --- a/setuptools/msvc9_support.py +++ b/setuptools/msvc9_support.py @@ -4,6 +4,7 @@ This module improve support for Microsoft Visual C++ compilers. (Windows Only) import os import itertools import distutils.errors +import winreg try: # Distutil file for MSVC++ 9.0 and upper @@ -30,16 +31,16 @@ def patch_for_specialized_compiler(): Known supported compilers: -------------------------- - Microsoft Visual C++ 9.0: - Microsoft Visual C++ Compiler for Python 2.7 (x86, amd64); - Microsoft Windows SDK 7.0 (x86, x64, ia64); - Microsoft Windows SDK 6.1 (x86, x64, ia64) + Microsoft Visual C++ 9.0: + Microsoft Visual C++ Compiler for Python 2.7 (x86, amd64); + Microsoft Windows SDK 7.0 (x86, x64, ia64); + Microsoft Windows SDK 6.1 (x86, x64, ia64) - Microsoft Visual C++ 10.0: - Microsoft Windows SDK 7.1 (x86, x64, ia64) + Microsoft Visual C++ 10.0: + Microsoft Windows SDK 7.1 (x86, x64, ia64) - Microsoft Visual C++ 14.0: - Microsoft Visual C++ Build Tools 2015 (x86, x64, arm) + Microsoft Visual C++ 14.0: + Microsoft Visual C++ Build Tools 2015 (x86, x64, arm) """ if 'distutils' not in globals(): # The module isn't available to be patched @@ -74,8 +75,8 @@ def msvc9_find_vcvarsall(version): Known supported compilers ------------------------- - Microsoft Visual C++ 9.0: - Microsoft Visual C++ Compiler for Python 2.7 (x86, amd64) + Microsoft Visual C++ 9.0: + Microsoft Visual C++ Compiler for Python 2.7 (x86, amd64) Parameters ---------- @@ -115,13 +116,13 @@ def msvc9_query_vcvarsall(ver, arch='x86', *args, **kwargs): Known supported compilers ------------------------- - Microsoft Visual C++ 9.0: - Microsoft Visual C++ Compiler for Python 2.7 (x86, amd64); - Microsoft Windows SDK 7.0 (x86, x64, ia64); - Microsoft Windows SDK 6.1 (x86, x64, ia64) + Microsoft Visual C++ 9.0: + Microsoft Visual C++ Compiler for Python 2.7 (x86, amd64); + Microsoft Windows SDK 7.0 (x86, x64, ia64); + Microsoft Windows SDK 6.1 (x86, x64, ia64) - Microsoft Visual C++ 10.0: - Microsoft Windows SDK 7.1 (x86, x64, ia64) + Microsoft Visual C++ 10.0: + Microsoft Windows SDK 7.1 (x86, x64, ia64) Parameters ---------- @@ -138,15 +139,15 @@ def msvc9_query_vcvarsall(ver, arch='x86', *args, **kwargs): try: return unpatched['msvc9_query_vcvarsall'](ver, arch, *args, **kwargs) except distutils.errors.DistutilsPlatformError: - # Error if Vcvarsall.bat is missing + # Pass error if Vcvarsall.bat is missing pass except ValueError: - # Error if environment not set after executing vcvarsall.bat + # Pass error if environment not set after executing vcvarsall.bat pass - # If vcvarsall.bat fail, try to set environment directly + # If error, try to set environment directly try: - return _compute_env(ver, arch) + return EnvironmentInfo(arch, ver).return_env() except distutils.errors.DistutilsPlatformError as exc: _augment_exception(exc, ver, arch) raise @@ -159,8 +160,8 @@ def msvc14_get_vc_env(plat_spec): Known supported compilers ------------------------- - Microsoft Visual C++ 14.0: - Microsoft Visual C++ Build Tools 2015 (x86, x64, arm) + Microsoft Visual C++ 14.0: + Microsoft Visual C++ Build Tools 2015 (x86, x64, arm) Parameters ---------- @@ -171,21 +172,22 @@ def msvc14_get_vc_env(plat_spec): ------ environment: dict """ + # Try to get environement from vcvarsall.bat (Classical way) try: return unpatched['msv14_get_vc_env'](plat_spec) except distutils.errors.DistutilsPlatformError: - # Error if Vcvarsall.bat is missing + # Pass error Vcvarsall.bat is missing pass - # If vcvarsall.bat fail, try to set environment directly + # If error, try to set environment directly try: - return _compute_env(version, plat_spec) + return EnvironmentInfo(plat_spec, vcvermin=14.0).return_env() except distutils.errors.DistutilsPlatformError as exc: - _augment_exception(exc, version, plat_spec) + _augment_exception(exc, 14.0) raise -def _augment_exception(exc, version, arch): +def _augment_exception(exc, version, arch=''): """ Add details to the exception message to help guide the user as to what action will resolve it. @@ -193,32 +195,33 @@ def _augment_exception(exc, version, arch): # Error if MSVC++ directory not found or environment not set message = exc.args[0] - if "vcvarsall.bat" in message: + if "vcvarsall" in message.lower() or "visual c" in message.lower(): # Special error message if MSVC++ not installed message = 'Microsoft Visual C++ %0.1f is required (%s).' %\ (version, message) - if int(version) == 9: + msdownload = r'www.microsoft.com/download/details.aspx?id=%d' + if version == 9.0: if arch.lower().find('ia64') > -1: # For VC++ 9.0, if IA64 support is needed, redirect user # to Windows SDK 7.0 message += ' Get it with "Microsoft Windows SDK 7.0": ' - message += r'www.microsoft.com/download/details.aspx?id=3138' + message += msdownload % 3138 else: # For VC++ 9.0 redirect user to Vc++ for Python 2.7 : # This redirection link is maintained by Microsoft. # Contact vspython@microsoft.com if it needs updating. message += r' Get it from http://aka.ms/vcpython27' - elif int(version) == 10: + elif version == 10.0: # For VC++ 10.0 Redirect user to Windows SDK 7.1 message += ' Get it with "Microsoft Windows SDK 7.1": ' - message += r'www.microsoft.com/download/details.aspx?id=8279' + message += msdownload % 8279 exc.args[0] = message class PlatformInfo: """ - Find architecture informations and system paths. + Current and Target Architectures informations. Parameters ---------- @@ -226,9 +229,6 @@ class PlatformInfo: Target architecture. """ current_cpu = os.environ['processor_architecture'].lower() - win_dir = os.environ['WinDir'] - program_files = os.environ['ProgramFiles'] - program_files_x86 = os.environ.get('ProgramFiles(x86)', program_files) def __init__(self, arch): self.arch = arch.lower() @@ -241,9 +241,9 @@ class PlatformInfo: return self.target_cpu == 'x86' def current_is_x86(self): - return self.current_cpu != 'x86' + return self.current_cpu == 'x86' - def ccpu_dir(self, hidex86=False, x64=False): + def current_dir(self, hidex86=False, x64=False): """ Current platform specific subfolder. @@ -256,7 +256,8 @@ class PlatformInfo: Return ------ - subfolder: str (starting with'\') + subfolder: str + "\target" """ return ( '' if (self.current_cpu == 'x86' and hidex86) else @@ -264,7 +265,7 @@ class PlatformInfo: r'\%s' % self.current_cpu ) - def tcpu_dir(self, hidex86=False, x64=False): + def target_dir(self, hidex86=False, x64=False): """ Target platform specific subfolder. @@ -277,7 +278,8 @@ class PlatformInfo: Return ------ - subfolder: str (starting with'\') + subfolder: str + "\current" """ return ( '' if (self.target_cpu == 'x86' and hidex86) else @@ -285,9 +287,9 @@ class PlatformInfo: r'\%s' % self.target_cpu ) - def tools_extra(self, forcex86=False): + def cross_dir(self, forcex86=False): """ - Platform specific subfolder for Visual C++ Tools. + Cross platform specific subfolder. Parameters ---------- @@ -297,9 +299,11 @@ class PlatformInfo: Return ------ - subfolder: str (starting with'\') + subfolder: str + "\current" if target architecture is current architecture, + "\current_target" if not. """ - path = self.tcpu_dir(True) + path = self.target_dir(True) if self.target_cpu != self.current_cpu: current = 'x86' if forcex86 else self.current_cpu path = path.replace('\\', '\\%s_' % current) @@ -308,24 +312,25 @@ class PlatformInfo: class RegistryInfo: """ - Find Microsoft Visual C++ compiler related paths using registry or - default paths. + Microsoft Visual Studio related registry informations. Parameters ---------- - platform_info: platform_info - "platform_info" instance. - version: float - Required Microsoft Visual C++ version. + platform_info: PlatformInfo + "PlatformInfo" instance. """ - def __init__(self, platform_info, version): + HKEYS = (winreg.HKEY_USERS, + winreg.HKEY_CURRENT_USER, + winreg.HKEY_LOCAL_MACHINE, + winreg.HKEY_CLASSES_ROOT) + + def __init__(self, platform_info): self.pi = platform_info - self.version = version @property def microsoft(self): """ - Microsoft registry path. + Microsoft software registry key. """ return os.path.join( 'Software', @@ -336,292 +341,530 @@ class RegistryInfo: @property def sxs(self): """ - Visual Studio SxS registry path. + Microsoft Visual Studio SxS registry key. """ return os.path.join(self.microsoft, r'VisualStudio\SxS') @property def vc(self): """ - Visual C++ registry path. + Microsoft Visual C++ registry key. """ return os.path.join(self.sxs, 'VC7') @property def vs(self): """ - Visual Studio registry path. + Microsoft Visual Studio registry key. """ return os.path.join(self.sxs, 'VS7') @property def vc_for_python(self): """ - Visual C++ for Python. + Microsoft Visual C++ for Python registry key. """ - path = r'DevDiv\VCForPython\%0.1f' % self.version + path = r'DevDiv\VCForPython' return os.path.join(self.microsoft, path) @property def windows_sdk(self): """ - Windows/Platform SDK registry path. + Microsoft Windows/Platform SDK registry key. """ return os.path.join(self.microsoft, r'Microsoft SDKs\Windows') - def find_visual_studio(self): + def lookup(self, key, name): + """ + Look for values in registry. + + Parameters + ---------- + key: str + Registry key path where look. + name: str + Value name to find. + + Return + ------ + str: value + """ + for hkey in self.HKEYS: + try: + bkey = winreg.OpenKey(hkey, key, 0, winreg.KEY_READ) + except FileNotFoundError: + continue + try: + return winreg.QueryValueEx(bkey, name)[0] + except FileNotFoundError: + pass + + +class SystemInfo: + """ + Microsoft Windows and Visual Studio related system inormations. + + Parameters + ---------- + registry_info: RegistryInfo + "RegistryInfo" instance. + vcver: float + Required Microsoft Visual C++ version. + """ + WinDir = os.environ['WinDir'] + ProgramFiles = os.environ['ProgramFiles'] + ProgramFilesx86 = os.environ.get('ProgramFiles(x86)', ProgramFiles) + + def __init__(self, registry_info, vcver=None): + self.ri = registry_info + if vcver: + self.vcver = vcver + else: + try: + self.vcver = self.find_availables_vcver()[-1] + except IndexError: + err = 'No Microsoft Visual C++ version found' + raise distutils.errors.DistutilsPlatformError(err) + + def find_availables_vcver(self): + """ + Find all availables Microsoft Visual C++ versions. + """ + vckeys = (self.ri.vc, self.ri.vc_for_python) + vsvers = [] + for hkey in self.ri.HKEYS: + for key in vckeys: + try: + bkey = winreg.OpenKey(hkey, key, 0, winreg.KEY_READ) + except FileNotFoundError: + continue + subkeys, values, _ = winreg.QueryInfoKey(bkey) + for i in range(values): + try: + ver = float(winreg.EnumValue(bkey, i)[0]) + if ver not in vsvers: + vsvers.append(ver) + except ValueError: + pass + for i in range(subkeys): + try: + ver = float(winreg.EnumKey(bkey, i)) + if ver not in vsvers: + vsvers.append(ver) + except ValueError: + pass + return sorted(vsvers) + + @property + def VSInstallDir(self): """ - Find Microsoft Visual Studio directory. + Microsoft Visual Studio directory. """ # Default path - name = 'Microsoft Visual Studio %0.1f' % self.version - default = os.path.join(self.pi.program_files_x86, name) + name = 'Microsoft Visual Studio %0.1f' % self.vcver + default = os.path.join(self.ProgramFilesx86, name) # Try to get path from registry, if fail use default path - return self._lookup(self.vs, '%0.1f' % self.version) or default + return self.ri.lookup(self.ri.vs, '%0.1f' % self.vcver) or default - def find_visual_c(self): + @property + def VCInstallDir(self): """ - Find Microsoft Visual C++ directory. + Microsoft Visual C++ directory. """ # Default path - default = r'Microsoft Visual Studio %0.1f\VC' % self.version - guess_vc = os.path.join(self.pi.program_files_x86, default) + default = r'Microsoft Visual Studio %0.1f\VC' % self.vcver + guess_vc = os.path.join(self.ProgramFilesx86, default) # Try to get "VC++ for Python" path from registry as default path - python_vc = self._lookup(self.vc_for_python, 'installdir') + path = os.path.join(self.ri.vc_for_python, '%0.1f' % self.vcver) + python_vc = self.ri.lookup(path, 'installdir') default_vc = os.path.join(python_vc, 'VC') if python_vc else guess_vc # Try to get path from registry, if fail use default path - result = self._lookup(self.vc, '%0.1f' % self.version) or default_vc + result = self.ri.lookup(self.ri.vc, '%0.1f' % self.vcver) or default_vc if not os.path.isdir(result): - msg = 'vcvarsall.bat and Visual C++ directory not found' + msg = 'Microsoft Visual C++ directory not found' raise distutils.errors.DistutilsPlatformError(msg) return result - def find_windows_sdk(self): + @property + def WindowsSdkDir(self): """ - Find Microsoft Windows SDK directory. + Microsoft Windows SDK directory. """ - WindowsSdkDir = '' - if self.version == 9.0: - WindowsSdkVer = ('7.0', '6.1', '6.0a') - elif self.version == 10.0: - WindowsSdkVer = ('7.1', '7.0a') + sdkdir = '' + if self.vcver == 9.0: + sdkver = ('7.0', '6.1', '6.0a') + elif self.vcver == 10.0: + sdkver = ('7.1', '7.0a') + elif self.vcver == 14.0: + sdkver = ('10.0', '8.1', '8.1a') else: - WindowsSdkVer = () - for ver in WindowsSdkVer: + sdkver = () + for ver in sdkver: # Try to get it from registry - loc = os.path.join(self.windows_sdk, 'v%s' % ver) - WindowsSdkDir = self._lookup(loc, 'installationfolder') - if WindowsSdkDir: + loc = os.path.join(self.ri.windows_sdk, 'v%s' % ver) + sdkdir = self.ri.lookup(loc, 'installationfolder') + if sdkdir: break - if not WindowsSdkDir or not os.path.isdir(WindowsSdkDir): + if not sdkdir or not os.path.isdir(sdkdir): # Try to get "VC++ for Python" version from registry - install_base = self._lookup(self.vc_for_python, 'installdir') + path = os.path.join(self.ri.vc_for_python, '%0.1f' % self.vcver) + install_base = self.ri.lookup(path, 'installdir') if install_base: - WindowsSdkDir = os.path.join(install_base, 'WinSDK') - if not WindowsSdkDir or not os.path.isdir(WindowsSdkDir): - # If fail, use default path - for ver in WindowsSdkVer: + 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 sdkver: + intver = ver[:ver.rfind('.')] + path = r'Microsoft SDKs\Windows Kits\%s' % (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 sdkver: path = r'Microsoft SDKs\Windows\v%s' % ver - d = os.path.join(self.pi.program_files, path) + d = os.path.join(self.ProgramFiles, path) if os.path.isdir(d): - WindowsSdkDir = d - if not WindowsSdkDir: + sdkdir = d + if not sdkdir: # If fail, use Platform SDK - WindowsSdkDir = os.path.join(self.find_visual_c(), 'PlatformSDK') - return WindowsSdkDir - - def find_dot_net_versions(self): - """ - Find Microsoft .NET Framework Versions. - """ - if self.version == 10.0: - v4 = self._lookup(self.vc, 'frameworkver32') or '' - if v4.lower()[:2] != 'v4': - v4 = None - # default to last v4 version - v4 = v4 or 'v4.0.30319' - FrameworkVer = (v4, 'v3.5') - elif self.version == 9.0: - FrameworkVer = ('v3.5', 'v2.0.50727') - elif self.version == 8.0: - FrameworkVer = ('v3.0', 'v2.0.50727') - return FrameworkVer + sdkdir = os.path.join(self.VCInstallDir, 'PlatformSDK') + return sdkdir - def find_dot_net_32(self): + @property + def FrameworkDir32(self): """ - Find Microsoft .NET Framework 32bit directory. + Microsoft .NET Framework 32bit directory. """ # Default path - guess_fw = os.path.join(self.pi.win_dir, r'Microsoft.NET\Framework') + guess_fw = os.path.join(self.WinDir, r'Microsoft.NET\Framework') # Try to get path from registry, if fail use default path - return self._lookup(self.vc, 'frameworkdir32') or guess_fw + return self.ri.lookup(self.ri.vc, 'frameworkdir32') or guess_fw - def find_dot_net_64(self): + @property + def FrameworkDir64(self): """ - Find Microsoft .NET Framework 64bit directory. + Microsoft .NET Framework 64bit directory. """ # Default path - guess_fw = os.path.join(self.pi.win_dir, r'Microsoft.NET\Framework64') + guess_fw = os.path.join(self.WinDir, r'Microsoft.NET\Framework64') # Try to get path from registry, if fail use default path - return self._lookup(self.vc, 'frameworkdir64') or guess_fw + return self.ri.lookup(self.ri.vc, 'frameworkdir64') or guess_fw - def _lookup(self, base, key): - try: - return msvc9compiler.Reg.get_value(base, key) - except KeyError: - pass + @property + def FrameworkVersion32(self): + """ + Microsoft .NET Framework 32bit versions. + """ + return self._find_dot_net_versions(32) + + @property + def FrameworkVersion64(self): + """ + Microsoft .NET Framework 64bit versions. + """ + return self._find_dot_net_versions(64) + def _find_dot_net_versions(self, bits=32): + """ + Find Microsoft .NET Framework versions. -def _compute_env(version, arch): + Parameters + ---------- + bits: int + Platform number of bits: 32 or 64. + """ + # Find actual .NET version + ver = self.ri.lookup(self.ri.vc, 'frameworkver%d' % bits) or '' + + # Set .NET versions for specified MSVC++ version + if self.vcver >= 14.0: + frameworkver = (ver, 'v4.0') + elif self.vcver == 10.0: + if ver.lower()[:2] != 'v4': + ver = '' + ver = ver or 'v4.0.30319' + frameworkver = (ver, 'v3.5') + elif self.vcver == 9.0: + frameworkver = ('v3.5', 'v2.0.50727') + elif self.vcver == 8.0: + frameworkver = ('v3.0', 'v2.0.50727') + return frameworkver + + +class EnvironmentInfo: """ Return environment variables for specified Microsoft Visual C++ version - and platform. + and platform : Lib, Include, Path and libpath. - Microsoft Visual C++ known compatibles versions - ----------------------------------------------- - 9.0, 10.0, 14.0 - """ - pi = PlatformInfo(arch) - reg = RegistryInfo(pi, version) - - # Set Microsoft Visual Studio Tools - paths = [r'Common7\IDE', r'Common7\Tools'] - if version >= 14.0: - paths.append(r'Common7\IDE\CommonExtensions\Microsoft\TestWindow') - paths.append(r'Team Tools\Performance Tools') - paths.append(r'Team Tools\Performance Tools' + pi.ccpu_dir(True, True)) - VSTools = [os.path.join(reg.find_visual_studio(), path) for path in paths] - - # Set Microsoft Visual C++ & Microsoft Foundation Class Includes - VCIncludes = [os.path.join(reg.find_visual_c(), 'Include'), - os.path.join(reg.find_visual_c(), 'ATLMFC\Include')] - - # Set Microsoft Visual C++ & Microsoft Foundation Class Libraries - paths = ['Lib' + pi.tcpu_dir(True), r'ATLMFC\Lib' + pi.tcpu_dir(True)] - if version >= 14.0: - paths.append(r'Lib\store' + pi.tcpu_dir(True)) - VCLibraries = [os.path.join(reg.find_visual_c(), path) for path in paths] - - # Set Microsoft Visual C++ store references Libraries - if version >= 14.0: - path = r'Lib\store\references' - VCStoreRefs = [os.path.join(reg.find_visual_c(), path)] - else: - VCStoreRefs = [] - - # Set Microsoft Visual C++ Tools - path = 'Bin' + pi.tools_extra(False if version >= 14.0 else True) - VCTools = [ - os.path.join(reg.find_visual_c(), 'VCPackages'), - os.path.join(reg.find_visual_c(), path), - ] - if pi.tools_extra() and version >= 14.0: - path = 'Bin' + pi.ccpu_dir(True) - VCTools.append(os.path.join(reg.find_visual_c(), path)) - else: - VCTools.append(os.path.join(reg.find_visual_c(), 'Bin')) - - # Set Microsoft Windows SDK Libraries - path = 'Lib' + pi.tcpu_dir(True, True) - OSLibraries = [os.path.join(reg.find_windows_sdk(), path)] - - # Set Microsoft Windows SDK Include - OSIncludes = [ - os.path.join(reg.find_windows_sdk(), 'Include'), - os.path.join(reg.find_windows_sdk(), r'Include\gl'), - ] - - # Set Microsoft Windows SDK Tools - SdkTools = [os.path.join(reg.find_windows_sdk(), 'Bin')] - if not pi.target_is_x86(): - path = 'Bin' + pi.tcpu_dir(True, True) - SdkTools.append(os.path.join(reg.find_windows_sdk(), path)) - if version == 10.0: - path = r'Bin\NETFX 4.0 Tools' + pi.tcpu_dir(True, True) - SdkTools.append(os.path.join(reg.find_windows_sdk(), path)) - - # Set Microsoft Windows SDK Setup - SdkSetup = [os.path.join(reg.find_windows_sdk(), 'Setup')] - - # Set Microsoft .NET Framework Tools - roots = [reg.find_dot_net_32()] - include_64_framework = not pi.target_is_x86() and not pi.current_is_x86() - roots += [reg.find_dot_net_64()] if include_64_framework else [] - - FxTools = [ - os.path.join(root, ver) - for root, ver in itertools.product(roots, reg.find_dot_net_versions()) - ] - - # Set Microsoft Visual Studio Team System Database - VsTDb = [os.path.join(reg.find_visual_studio(), r'VSTSDB\Deploy')] - - # Set Microsoft Build Engine - path = r'\MSBuild\%0.1f\bin%s' % (version, pi.ccpu_dir(True)) - MSBuild = [ - os.path.join(pi.program_files_x86, path), - os.path.join(pi.program_files, path) - ] - - # Set Microsoft HTML Help Workshop - path = 'HTML Help Workshop' - HTMLWork = [ - os.path.join(pi.program_files_x86, path), - os.path.join(pi.program_files, path) - ] - - # Return environment - return dict( - include=_build_paths('include', [VCIncludes, OSIncludes]), - lib=_build_paths('lib', [VCLibraries, OSLibraries, FxTools]), - libpath=_build_paths('libpath', [VCLibraries, FxTools, VCStoreRefs]), - path=_build_paths('path', [VCTools, VSTools, VsTDb, SdkTools, SdkSetup, - FxTools, MSBuild, HTMLWork]), - ) - - -def _build_paths(name, spec_path_lists): - """ - 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. + This function is compatible with Microsoft Visual C++ 9.0 to 14.0. + + Parameters + ---------- + arch: str + Target architecture. + vcver: float + Required Microsoft Visual C++ version. If not set, autodetect the last + version. + vcvermin: float + Minimum Microsoft Visual C++ version. """ - # flatten spec_path_lists - spec_paths = itertools.chain.from_iterable(spec_path_lists) - env_paths = os.environ.get(name, '').split(os.pathsep) - paths = itertools.chain(spec_paths, env_paths) - extant_paths = list(filter(os.path.isdir, paths)) - if not extant_paths: - msg = "%s environment variable is empty" % name.upper() - raise distutils.errors.DistutilsPlatformError(msg) - unique_paths = _unique_everseen(extant_paths) - return os.pathsep.join(unique_paths) - - -# from Python docs -def _unique_everseen(iterable, key=None): - "List unique elements, preserving order. Remember all elements ever seen." - # unique_everseen('AAAABBBCCDAABBB') --> A B C D - # unique_everseen('ABBCcAD', str.lower) --> A B C D - seen = set() - seen_add = seen.add - filterfalse = six.moves.filterfalse - if key is None: - for element in filterfalse(seen.__contains__, iterable): - seen_add(element) - yield element - else: - for element in iterable: - k = key(element) - if k not in seen: - seen_add(k) + def __init__(self, arch, vcver=None, vcvermin=None): + self.pi = PlatformInfo(arch) + self.ri = RegistryInfo(self.pi) + self.si = SystemInfo(self.ri, vcver) + + if self.vcver < vcvermin: + err = 'No suitable Microsoft Visual C++ version found' + raise distutils.errors.DistutilsPlatformError(err) + + @property + def vcver(self): + """ + Microsoft Visual C++ version. + """ + return self.si.vcver + + @property + def VSTools(self): + """ + Microsoft Visual Studio Tools + """ + paths = [r'Common7\IDE', r'Common7\Tools'] + if self.vcver >= 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 += [r'Team Tools\Performance Tools%s' % 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 [os.path.join(self.si.VCInstallDir, 'Include'), + os.path.join(self.si.VCInstallDir, 'ATLMFC\Include')] + + @property + def VCLibraries(self): + """ + Microsoft Visual C++ & Microsoft Foundation Class Libraries + """ + arch_subdir = self.pi.target_dir(hidex86=True) + paths = ['Lib%s' % arch_subdir, r'ATLMFC\Lib%s' % arch_subdir] + if self.vcver >= 14.0: + paths += [r'Lib\store%s' % arch_subdir] + return [os.path.join(self.si.VCInstallDir, path) for path in paths] + + @property + def VCStoreRefs(self): + """ + Microsoft Visual C++ store references Libraries + """ + path = os.path.join(self.si.VCInstallDir, r'Lib\store\references') + return [path] if self.vcver >= 14.0 else [] + + @property + def VCTools(self): + """ + Microsoft Visual C++ Tools + """ + forcex86 = True if self.vcver <= 10.0 else False + arch_subdir = self.pi.cross_dir(forcex86) + tools = [ + os.path.join(self.si.VCInstallDir, 'VCPackages'), + os.path.join(self.si.VCInstallDir, 'Bin%s' % arch_subdir), + ] + if self.pi.cross_dir() and self.vcver >= 14.0: + path = 'Bin%s' % self.pi.current_dir(hidex86=True) + tools += [os.path.join(self.si.VCInstallDir, path)] + else: + tools += [os.path.join(self.si.VCInstallDir, 'Bin')] + return tools + + @property + def OSLibraries(self): + """ + Microsoft Windows SDK Libraries + """ + arch_subdir = self.pi.target_dir(hidex86=True, x64=True) + return [os.path.join(self.si.WindowsSdkDir, 'Bin%s' % arch_subdir)] + + @property + def OSIncludes(self): + """ + Microsoft Windows SDK Include + """ + return [ + os.path.join(self.si.WindowsSdkDir, 'Include'), + os.path.join(self.si.WindowsSdkDir, r'Include\gl'), + ] + + @property + def SdkTools(self): + """ + Microsoft Windows SDK Tools + """ + if self.vcver <= 10: + arch_subdir = self.pi.target_dir(hidex86=True, x64=True) + else: + arch_subdir = self.pi.target_dir(x64=True) + tools = [os.path.join(self.si.WindowsSdkDir, 'Bin')] + if not self.pi.target_is_x86(): + path = 'Bin%s' % arch_subdir + tools += [os.path.join(self.si.WindowsSdkDir, path)] + if self.vcver == 10.0: + path = r'Bin\NETFX 4.0 Tools%s' % arch_subdir + tools += [os.path.join(self.si.WindowsSdkDir, path)] + return tools + + @property + def SdkSetup(self): + """ + Microsoft Windows SDK Setup + """ + return [os.path.join(self.si.WindowsSdkDir, 'Setup')] + + @property + def FxTools(self): + """ + Microsoft .NET Framework Tools + """ + pi = self.pi + si = self.si + if self.vcver <= 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 VsTDb(self): + """ + Microsoft Visual Studio Team System Database + """ + return [os.path.join(self.si.VSInstallDir, r'VSTSDB\Deploy')] + + @property + def MSBuild(self): + """ + Microsoft Build Engine + """ + arch_subdir = self.pi.current_dir(hidex86=True) + path = r'\MSBuild\%0.1f\bin%s' % (self.vcver, arch_subdir) + return [ + os.path.join(self.si.ProgramFilesx86, path), + os.path.join(self.si.ProgramFiles, path) + ] + + @property + def HTMLWs(self): + """ + Microsoft HTML Help Workshop + """ + return [ + os.path.join(self.si.ProgramFilesx86, 'HTML Help Workshop'), + os.path.join(self.si.ProgramFiles, 'HTML Help Workshop') + ] + + @property + def VCRuntimeRedist(self): + """ + Microsoft Visual C++ runtime redistribuable dll + """ + arch_subdir = self.pi.target_dir(x64=True) + vcruntime = 'redist%s\\Microsoft.VC%d0.CRT\\vcruntime%d0.dll' + vcruntime = vcruntime % (arch_subdir, self.vcver, self.vcver) + return os.path.join(self.si.VCInstallDir, vcruntime) + + def return_env(self): + """ + Return environment dict. + """ + env = dict( + include=self._build_paths('include', + [self.VCIncludes, + self.OSIncludes]), + lib=self._build_paths('lib', + [self.VCLibraries, + self.OSLibraries, + self.FxTools]), + libpath=self._build_paths('libpath', + [self.VCLibraries, + self.FxTools, + self.VCStoreRefs]), + path=self._build_paths('path', + [self.VCTools, + self.VSTools, + self.VsTDb, + self.SdkTools, + self.SdkSetup, + self.FxTools, + self.MSBuild, + self.HTMLWs]), + ) + if self.vcver >= 14 and os.path.isfile(self.VCRuntimeRedist): + env['py_vcruntime_redist'] = self.VCRuntimeRedist + return env + + def _build_paths(self, name, spec_path_lists): + """ + 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. + """ + # flatten spec_path_lists + spec_paths = itertools.chain.from_iterable(spec_path_lists) + env_paths = os.environ.get(name, '').split(os.pathsep) + paths = itertools.chain(spec_paths, env_paths) + extant_paths = list(filter(os.path.isdir, paths)) + if not extant_paths: + msg = "%s environment variable is empty" % name.upper() + raise distutils.errors.DistutilsPlatformError(msg) + unique_paths = self._unique_everseen(extant_paths) + return os.pathsep.join(unique_paths) + + # from Python docs + def _unique_everseen(self, iterable, key=None): + """ + List unique elements, preserving order. + Remember all elements ever seen. + """ + # unique_everseen('AAAABBBCCDAABBB') --> A B C D + # unique_everseen('ABBCcAD', str.lower) --> A B C D + seen = set() + seen_add = seen.add + filterfalse = six.moves.filterfalse + if key is None: + for element in filterfalse(seen.__contains__, iterable): + seen_add(element) yield element + else: + for element in iterable: + k = key(element) + if k not in seen: + seen_add(k) + yield element |