2 Improved support for Microsoft Visual C++ compilers.
4 Known supported compilers:
5 --------------------------
6 Microsoft Visual C++ 14.X:
7 Microsoft Visual C++ Build Tools 2015 (x86, x64, arm)
8 Microsoft Visual Studio Build Tools 2017 (x86, x64, arm, arm64)
9 Microsoft Visual Studio Build Tools 2019 (x86, x64, arm, arm64)
11 This may also support compilers shipped with compatible Visual Studio versions.
16 from os
import listdir
, pathsep
17 from os
.path
import join
, isfile
, isdir
, dirname
23 import distutils
.errors
24 from setuptools
.extern
.packaging
.version
import LegacyVersion
25 from setuptools
.extern
.more_itertools
import unique_everseen
27 from .monkey
import get_unpatched
29 if platform
.system() == 'Windows':
31 from os
import environ
33 # Mock winreg and environ so the module can be imported on this platform.
37 HKEY_CURRENT_USER
= None
38 HKEY_LOCAL_MACHINE
= None
39 HKEY_CLASSES_ROOT
= None
44 def _msvc14_find_vc2015():
45 """Python 3.8 "distutils/_msvccompiler.py" backport"""
48 winreg
.HKEY_LOCAL_MACHINE
,
49 r
"Software\Microsoft\VisualStudio\SxS\VC7",
51 winreg
.KEY_READ | winreg
.KEY_WOW64_32KEY
59 for i
in itertools
.count():
61 v
, vc_dir
, vt
= winreg
.EnumValue(key
, i
)
64 if v
and vt
== winreg
.REG_SZ
and isdir(vc_dir
):
66 version
= int(float(v
))
67 except (ValueError, TypeError):
69 if version
>= 14 and version
> best_version
:
70 best_version
, best_dir
= version
, vc_dir
71 return best_version
, best_dir
74 def _msvc14_find_vc2017():
75 """Python 3.8 "distutils/_msvccompiler.py" backport
77 Returns "15, path" based on the result of invoking vswhere.exe
78 If no install is found, returns "None, None"
80 The version is returned to avoid unnecessarily changing the function
81 result. It may be ignored when the path is not None.
83 If vswhere.exe is not available, by definition, VS 2017 is not
86 root
= environ
.get("ProgramFiles(x86)") or environ
.get("ProgramFiles")
91 path
= subprocess
.check_output([
92 join(root
, "Microsoft Visual Studio", "Installer", "vswhere.exe"),
96 "-requires", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64",
97 "-requires", "Microsoft.VisualStudio.Workload.WDExpress",
98 "-property", "installationPath",
100 ]).decode(encoding
="mbcs", errors
="strict").strip()
101 except (subprocess
.CalledProcessError
, OSError, UnicodeDecodeError):
104 path
= join(path
, "VC", "Auxiliary", "Build")
111 PLAT_SPEC_TO_RUNTIME
= {
119 def _msvc14_find_vcvarsall(plat_spec
):
120 """Python 3.8 "distutils/_msvccompiler.py" backport"""
121 _
, best_dir
= _msvc14_find_vc2017()
124 if plat_spec
in PLAT_SPEC_TO_RUNTIME
:
125 vcruntime_plat
= PLAT_SPEC_TO_RUNTIME
[plat_spec
]
127 vcruntime_plat
= 'x64' if 'amd64' in plat_spec
else 'x86'
130 vcredist
= join(best_dir
, "..", "..", "redist", "MSVC", "**",
131 vcruntime_plat
, "Microsoft.VC14*.CRT",
135 vcruntime
= glob
.glob(vcredist
, recursive
=True)[-1]
136 except (ImportError, OSError, LookupError):
140 best_version
, best_dir
= _msvc14_find_vc2015()
142 vcruntime
= join(best_dir
, 'redist', vcruntime_plat
,
143 "Microsoft.VC140.CRT", "vcruntime140.dll")
148 vcvarsall
= join(best_dir
, "vcvarsall.bat")
149 if not isfile(vcvarsall
):
152 if not vcruntime
or not isfile(vcruntime
):
155 return vcvarsall
, vcruntime
158 def _msvc14_get_vc_env(plat_spec
):
159 """Python 3.8 "distutils/_msvccompiler.py" backport"""
160 if "DISTUTILS_USE_SDK" in environ
:
163 for key
, value
in environ
.items()
166 vcvarsall
, vcruntime
= _msvc14_find_vcvarsall(plat_spec
)
168 raise distutils
.errors
.DistutilsPlatformError(
169 "Unable to find vcvarsall.bat"
173 out
= subprocess
.check_output(
174 'cmd /u /c "{}" {} && set'.format(vcvarsall
, plat_spec
),
175 stderr
=subprocess
.STDOUT
,
176 ).decode('utf-16le', errors
='replace')
177 except subprocess
.CalledProcessError
as exc
:
178 raise distutils
.errors
.DistutilsPlatformError(
179 "Error executing {}".format(exc
.cmd
)
185 (line
.partition('=') for line
in out
.splitlines())
190 env
['py_vcruntime_redist'] = vcruntime
194 def msvc14_get_vc_env(plat_spec
):
196 Patched "distutils._msvccompiler._get_vc_env" for support extra
197 Microsoft Visual C++ 14.X compilers.
199 Set environment without use of "vcvarsall.bat".
212 # Always use backport from CPython 3.8
214 return _msvc14_get_vc_env(plat_spec
)
215 except distutils
.errors
.DistutilsPlatformError
as exc
:
216 _augment_exception(exc
, 14.0)
220 def msvc14_gen_lib_options(*args
, **kwargs
):
222 Patched "distutils._msvccompiler.gen_lib_options" for fix
223 compatibility between "numpy.distutils" and "distutils._msvccompiler"
226 if "numpy.distutils" in sys
.modules
:
228 if LegacyVersion(np
.__version
__) < LegacyVersion('1.11.2'):
229 return np
.distutils
.ccompiler
.gen_lib_options(*args
, **kwargs
)
230 return get_unpatched(msvc14_gen_lib_options
)(*args
, **kwargs
)
233 def _augment_exception(exc
, version
, arch
=''):
235 Add details to the exception message to help guide the user
236 as to what action will resolve it.
238 # Error if MSVC++ directory not found or environment not set
239 message
= exc
.args
[0]
241 if "vcvarsall" in message
.lower() or "visual c" in message
.lower():
242 # Special error message if MSVC++ not installed
243 tmpl
= 'Microsoft Visual C++ {version:0.1f} or greater is required.'
244 message
= tmpl
.format(**locals())
245 msdownload
= 'www.microsoft.com/download/details.aspx?id=%d'
247 if arch
.lower().find('ia64') > -1:
248 # For VC++ 9.0, if IA64 support is needed, redirect user
249 # to Windows SDK 7.0.
250 # Note: No download link available from Microsoft.
251 message
+= ' Get it with "Microsoft Windows SDK 7.0"'
253 # For VC++ 9.0 redirect user to Vc++ for Python 2.7 :
254 # This redirection link is maintained by Microsoft.
255 # Contact vspython@microsoft.com if it needs updating.
256 message
+= ' Get it from http://aka.ms/vcpython27'
257 elif version
== 10.0:
258 # For VC++ 10.0 Redirect user to Windows SDK 7.1
259 message
+= ' Get it with "Microsoft Windows SDK 7.1": '
260 message
+= msdownload
% 8279
261 elif version
>= 14.0:
262 # For VC++ 14.X Redirect user to latest Visual C++ Build Tools
263 message
+= (' Get it with "Microsoft C++ Build Tools": '
264 r
'https://visualstudio.microsoft.com'
265 r
'/visual-cpp-build-tools/')
267 exc
.args
= (message
, )
272 Current and Target Architectures information.
279 current_cpu
= environ
.get('processor_architecture', '').lower()
281 def __init__(self
, arch
):
282 self
.arch
= arch
.lower().replace('x64', 'amd64')
285 def target_cpu(self
):
287 Return Target CPU architecture.
294 return self
.arch
[self
.arch
.find('_') + 1:]
296 def target_is_x86(self
):
298 Return True if target CPU is x86 32 bits..
305 return self
.target_cpu
== 'x86'
307 def current_is_x86(self
):
309 Return True if current CPU is x86 32 bits..
316 return self
.current_cpu
== 'x86'
318 def current_dir(self
, hidex86
=False, x64
=False):
320 Current platform specific subfolder.
325 return '' and not '\x86' if architecture is x86.
327 return '\x64' and not '\amd64' if architecture is amd64.
332 subfolder: '\target', or '' (see hidex86 parameter)
335 '' if (self
.current_cpu
== 'x86' and hidex86
) else
336 r
'\x64' if (self
.current_cpu
== 'amd64' and x64
) else
337 r
'\%s' % self
.current_cpu
340 def target_dir(self
, hidex86
=False, x64
=False):
342 Target platform specific subfolder.
347 return '' and not '\x86' if architecture is x86.
349 return '\x64' and not '\amd64' if architecture is amd64.
354 subfolder: '\current', or '' (see hidex86 parameter)
357 '' if (self
.target_cpu
== 'x86' and hidex86
) else
358 r
'\x64' if (self
.target_cpu
== 'amd64' and x64
) else
359 r
'\%s' % self
.target_cpu
362 def cross_dir(self
, forcex86
=False):
364 Cross platform specific subfolder.
369 Use 'x86' as current architecture even if current architecture is
375 subfolder: '' if target architecture is current architecture,
376 '\current_target' if not.
378 current
= 'x86' if forcex86
else self
.current_cpu
380 '' if self
.target_cpu
== current
else
381 self
.target_dir().replace('\\', '\\%s_' % current
)
387 Microsoft Visual Studio related registry information.
391 platform_info: PlatformInfo
392 "PlatformInfo" instance.
394 HKEYS
= (winreg
.HKEY_USERS
,
395 winreg
.HKEY_CURRENT_USER
,
396 winreg
.HKEY_LOCAL_MACHINE
,
397 winreg
.HKEY_CLASSES_ROOT
)
399 def __init__(self
, platform_info
):
400 self
.pi
= platform_info
403 def visualstudio(self
):
405 Microsoft Visual Studio root registry key.
412 return 'VisualStudio'
417 Microsoft Visual Studio SxS registry key.
424 return join(self
.visualstudio
, 'SxS')
429 Microsoft Visual C++ VC7 registry key.
436 return join(self
.sxs
, 'VC7')
441 Microsoft Visual Studio VS7 registry key.
448 return join(self
.sxs
, 'VS7')
451 def vc_for_python(self
):
453 Microsoft Visual C++ for Python registry key.
460 return r
'DevDiv\VCForPython'
463 def microsoft_sdk(self
):
465 Microsoft SDK registry key.
472 return 'Microsoft SDKs'
475 def windows_sdk(self
):
477 Microsoft Windows/Platform SDK registry key.
484 return join(self
.microsoft_sdk
, 'Windows')
489 Microsoft .NET Framework SDK registry key.
496 return join(self
.microsoft_sdk
, 'NETFXSDK')
499 def windows_kits_roots(self
):
501 Microsoft Windows Kits Roots registry key.
508 return r
'Windows Kits\Installed Roots'
510 def microsoft(self
, key
, x86
=False):
512 Return key in Microsoft software registry.
517 Registry key path where look.
519 Force x86 software registry.
526 node64
= '' if self
.pi
.current_is_x86() or x86
else 'Wow6432Node'
527 return join('Software', node64
, 'Microsoft', key
)
529 def lookup(self
, key
, name
):
531 Look for values in registry in Microsoft software registry.
536 Registry key path where look.
545 key_read
= winreg
.KEY_READ
546 openkey
= winreg
.OpenKey
547 closekey
= winreg
.CloseKey
549 for hkey
in self
.HKEYS
:
552 bkey
= openkey(hkey
, ms(key
), 0, key_read
)
553 except (OSError, IOError):
554 if not self
.pi
.current_is_x86():
556 bkey
= openkey(hkey
, ms(key
, True), 0, key_read
)
557 except (OSError, IOError):
562 return winreg
.QueryValueEx(bkey
, name
)[0]
563 except (OSError, IOError):
572 Microsoft Windows and Visual Studio related system information.
576 registry_info: RegistryInfo
577 "RegistryInfo" instance.
579 Required Microsoft Visual C++ version.
582 # Variables and properties in this class use originals CamelCase variables
583 # names from Microsoft source files for more easy comparison.
584 WinDir
= environ
.get('WinDir', '')
585 ProgramFiles
= environ
.get('ProgramFiles', '')
586 ProgramFilesx86
= environ
.get('ProgramFiles(x86)', ProgramFiles
)
588 def __init__(self
, registry_info
, vc_ver
=None):
589 self
.ri
= registry_info
592 self
.known_vs_paths
= self
.find_programdata_vs_vers()
594 # Except for VS15+, VC version is aligned with VS version
595 self
.vs_ver
= self
.vc_ver
= (
596 vc_ver
or self
._find
_latest
_available
_vs
_ver
())
598 def _find_latest_available_vs_ver(self
):
600 Find the latest VC version
607 reg_vc_vers
= self
.find_reg_vs_vers()
609 if not (reg_vc_vers
or self
.known_vs_paths
):
610 raise distutils
.errors
.DistutilsPlatformError(
611 'No Microsoft Visual C++ version found')
613 vc_vers
= set(reg_vc_vers
)
614 vc_vers
.update(self
.known_vs_paths
)
615 return sorted(vc_vers
)[-1]
617 def find_reg_vs_vers(self
):
619 Find Microsoft Visual Studio versions available in registry.
626 ms
= self
.ri
.microsoft
627 vckeys
= (self
.ri
.vc
, self
.ri
.vc_for_python
, self
.ri
.vs
)
629 for hkey
, key
in itertools
.product(self
.ri
.HKEYS
, vckeys
):
631 bkey
= winreg
.OpenKey(hkey
, ms(key
), 0, winreg
.KEY_READ
)
632 except (OSError, IOError):
635 subkeys
, values
, _
= winreg
.QueryInfoKey(bkey
)
636 for i
in range(values
):
637 with contextlib
.suppress(ValueError):
638 ver
= float(winreg
.EnumValue(bkey
, i
)[0])
639 if ver
not in vs_vers
:
641 for i
in range(subkeys
):
642 with contextlib
.suppress(ValueError):
643 ver
= float(winreg
.EnumKey(bkey
, i
))
644 if ver
not in vs_vers
:
646 return sorted(vs_vers
)
648 def find_programdata_vs_vers(self
):
650 Find Visual studio 2017+ versions from information in
651 "C:\ProgramData\Microsoft\VisualStudio\Packages\_Instances".
656 float version as key, path as value.
660 r
'C:\ProgramData\Microsoft\VisualStudio\Packages\_Instances'
663 hashed_names
= listdir(instances_dir
)
665 except (OSError, IOError):
666 # Directory not exists with all Visual Studio versions
669 for name
in hashed_names
:
671 # Get VS installation path from "state.json" file
672 state_path
= join(instances_dir
, name
, 'state.json')
673 with
open(state_path
, 'rt', encoding
='utf-8') as state_file
:
674 state
= json
.load(state_file
)
675 vs_path
= state
['installationPath']
677 # Raises OSError if this VS installation does not contain VC
678 listdir(join(vs_path
, r
'VC\Tools\MSVC'))
680 # Store version and path
681 vs_versions
[self
._as
_float
_version
(
682 state
['installationVersion'])] = vs_path
684 except (OSError, IOError, KeyError):
685 # Skip if "state.json" file is missing or bad format
691 def _as_float_version(version
):
693 Return a string version as a simplified float version (major.minor)
705 return float('.'.join(version
.split('.')[:2]))
708 def VSInstallDir(self
):
710 Microsoft Visual Studio directory.
718 default
= join(self
.ProgramFilesx86
,
719 'Microsoft Visual Studio %0.1f' % self
.vs_ver
)
721 # Try to get path from registry, if fail use default path
722 return self
.ri
.lookup(self
.ri
.vs
, '%0.1f' % self
.vs_ver
) or default
725 def VCInstallDir(self
):
727 Microsoft Visual C++ directory.
734 path
= self
._guess
_vc
() or self
._guess
_vc
_legacy
()
737 msg
= 'Microsoft Visual C++ directory not found'
738 raise distutils
.errors
.DistutilsPlatformError(msg
)
744 Locate Visual C++ for VS2017+.
751 if self
.vs_ver
<= 14.0:
755 # First search in known VS paths
756 vs_dir
= self
.known_vs_paths
[self
.vs_ver
]
758 # Else, search with path from registry
759 vs_dir
= self
.VSInstallDir
761 guess_vc
= join(vs_dir
, r
'VC\Tools\MSVC')
763 # Subdir with VC exact version as name
765 # Update the VC version with real one instead of VS version
766 vc_ver
= listdir(guess_vc
)[-1]
767 self
.vc_ver
= self
._as
_float
_version
(vc_ver
)
768 return join(guess_vc
, vc_ver
)
769 except (OSError, IOError, IndexError):
772 def _guess_vc_legacy(self
):
774 Locate Visual C++ for versions prior to 2017.
781 default
= join(self
.ProgramFilesx86
,
782 r
'Microsoft Visual Studio %0.1f\VC' % self
.vs_ver
)
784 # Try to get "VC++ for Python" path from registry as default path
785 reg_path
= join(self
.ri
.vc_for_python
, '%0.1f' % self
.vs_ver
)
786 python_vc
= self
.ri
.lookup(reg_path
, 'installdir')
787 default_vc
= join(python_vc
, 'VC') if python_vc
else default
789 # Try to get path from registry, if fail use default path
790 return self
.ri
.lookup(self
.ri
.vc
, '%0.1f' % self
.vs_ver
) or default_vc
793 def WindowsSdkVersion(self
):
795 Microsoft Windows SDK versions for specified MSVC++ version.
802 if self
.vs_ver
<= 9.0:
803 return '7.0', '6.1', '6.0a'
804 elif self
.vs_ver
== 10.0:
806 elif self
.vs_ver
== 11.0:
808 elif self
.vs_ver
== 12.0:
810 elif self
.vs_ver
>= 14.0:
814 def WindowsSdkLastVersion(self
):
816 Microsoft Windows SDK last version.
823 return self
._use
_last
_dir
_name
(join(self
.WindowsSdkDir
, 'lib'))
825 @property # noqa: C901
826 def WindowsSdkDir(self
): # noqa: C901 # is too complex (12) # FIXME
828 Microsoft Windows SDK directory.
836 for ver
in self
.WindowsSdkVersion
:
837 # Try to get it from registry
838 loc
= join(self
.ri
.windows_sdk
, 'v%s' % ver
)
839 sdkdir
= self
.ri
.lookup(loc
, 'installationfolder')
842 if not sdkdir
or not isdir(sdkdir
):
843 # Try to get "VC++ for Python" version from registry
844 path
= join(self
.ri
.vc_for_python
, '%0.1f' % self
.vc_ver
)
845 install_base
= self
.ri
.lookup(path
, 'installdir')
847 sdkdir
= join(install_base
, 'WinSDK')
848 if not sdkdir
or not isdir(sdkdir
):
849 # If fail, use default new path
850 for ver
in self
.WindowsSdkVersion
:
851 intver
= ver
[:ver
.rfind('.')]
852 path
= r
'Microsoft SDKs\Windows Kits\%s' % intver
853 d
= join(self
.ProgramFiles
, path
)
856 if not sdkdir
or not isdir(sdkdir
):
857 # If fail, use default old path
858 for ver
in self
.WindowsSdkVersion
:
859 path
= r
'Microsoft SDKs\Windows\v%s' % ver
860 d
= join(self
.ProgramFiles
, path
)
864 # If fail, use Platform SDK
865 sdkdir
= join(self
.VCInstallDir
, 'PlatformSDK')
869 def WindowsSDKExecutablePath(self
):
871 Microsoft Windows SDK executable directory.
878 # Find WinSDK NetFx Tools registry dir name
879 if self
.vs_ver
<= 11.0:
884 hidex86
= True if self
.vs_ver
<= 12.0 else False
885 arch
= self
.pi
.current_dir(x64
=True, hidex86
=hidex86
)
886 fx
= 'WinSDK-NetFx%dTools%s' % (netfxver
, arch
.replace('\\', '-'))
888 # list all possibles registry paths
890 if self
.vs_ver
>= 14.0:
891 for ver
in self
.NetFxSdkVersion
:
892 regpaths
+= [join(self
.ri
.netfx_sdk
, ver
, fx
)]
894 for ver
in self
.WindowsSdkVersion
:
895 regpaths
+= [join(self
.ri
.windows_sdk
, 'v%sA' % ver
, fx
)]
897 # Return installation folder from the more recent path
898 for path
in regpaths
:
899 execpath
= self
.ri
.lookup(path
, 'installationfolder')
904 def FSharpInstallDir(self
):
906 Microsoft Visual F# directory.
913 path
= join(self
.ri
.visualstudio
, r
'%0.1f\Setup\F#' % self
.vs_ver
)
914 return self
.ri
.lookup(path
, 'productdir') or ''
917 def UniversalCRTSdkDir(self
):
919 Microsoft Universal CRT SDK directory.
926 # Set Kit Roots versions for specified MSVC++ version
927 vers
= ('10', '81') if self
.vs_ver
>= 14.0 else ()
929 # Find path of the more recent Kit
931 sdkdir
= self
.ri
.lookup(self
.ri
.windows_kits_roots
,
937 def UniversalCRTSdkLastVersion(self
):
939 Microsoft Universal C Runtime SDK last version.
946 return self
._use
_last
_dir
_name
(join(self
.UniversalCRTSdkDir
, 'lib'))
949 def NetFxSdkVersion(self
):
951 Microsoft .NET Framework SDK versions.
958 # Set FxSdk versions for specified VS version
959 return (('4.7.2', '4.7.1', '4.7',
960 '4.6.2', '4.6.1', '4.6',
961 '4.5.2', '4.5.1', '4.5')
962 if self
.vs_ver
>= 14.0 else ())
965 def NetFxSdkDir(self
):
967 Microsoft .NET Framework SDK directory.
975 for ver
in self
.NetFxSdkVersion
:
976 loc
= join(self
.ri
.netfx_sdk
, ver
)
977 sdkdir
= self
.ri
.lookup(loc
, 'kitsinstallationfolder')
983 def FrameworkDir32(self
):
985 Microsoft .NET Framework 32bit directory.
993 guess_fw
= join(self
.WinDir
, r
'Microsoft.NET\Framework')
995 # Try to get path from registry, if fail use default path
996 return self
.ri
.lookup(self
.ri
.vc
, 'frameworkdir32') or guess_fw
999 def FrameworkDir64(self
):
1001 Microsoft .NET Framework 64bit directory.
1009 guess_fw
= join(self
.WinDir
, r
'Microsoft.NET\Framework64')
1011 # Try to get path from registry, if fail use default path
1012 return self
.ri
.lookup(self
.ri
.vc
, 'frameworkdir64') or guess_fw
1015 def FrameworkVersion32(self
):
1017 Microsoft .NET Framework 32bit versions.
1024 return self
._find
_dot
_net
_versions
(32)
1027 def FrameworkVersion64(self
):
1029 Microsoft .NET Framework 64bit versions.
1036 return self
._find
_dot
_net
_versions
(64)
1038 def _find_dot_net_versions(self
, bits
):
1040 Find Microsoft .NET Framework versions.
1045 Platform number of bits: 32 or 64.
1052 # Find actual .NET version in registry
1053 reg_ver
= self
.ri
.lookup(self
.ri
.vc
, 'frameworkver%d' % bits
)
1054 dot_net_dir
= getattr(self
, 'FrameworkDir%d' % bits
)
1055 ver
= reg_ver
or self
._use
_last
_dir
_name
(dot_net_dir
, 'v') or ''
1057 # Set .NET versions for specified MSVC++ version
1058 if self
.vs_ver
>= 12.0:
1060 elif self
.vs_ver
>= 10.0:
1061 return 'v4.0.30319' if ver
.lower()[:2] != 'v4' else ver
, 'v3.5'
1062 elif self
.vs_ver
== 9.0:
1063 return 'v3.5', 'v2.0.50727'
1064 elif self
.vs_ver
== 8.0:
1065 return 'v3.0', 'v2.0.50727'
1068 def _use_last_dir_name(path
, prefix
=''):
1070 Return name of the last dir in path or '' if no dir found.
1075 Use dirs in this path
1077 Use only dirs starting by this prefix
1086 for dir_name
in reversed(listdir(path
))
1087 if isdir(join(path
, dir_name
)) and
1088 dir_name
.startswith(prefix
)
1090 return next(matching_dirs
, None) or ''
1093 class EnvironmentInfo
:
1095 Return environment variables for specified Microsoft Visual C++ version
1096 and platform : Lib, Include, Path and libpath.
1098 This function is compatible with Microsoft Visual C++ 9.0 to 14.X.
1100 Script created by analysing Microsoft environment configuration files like
1101 "vcvars[...].bat", "SetEnv.Cmd", "vcbuildtools.bat", ...
1106 Target architecture.
1108 Required Microsoft Visual C++ version. If not set, autodetect the last
1111 Minimum Microsoft Visual C++ version.
1114 # Variables and properties in this class use originals CamelCase variables
1115 # names from Microsoft source files for more easy comparison.
1117 def __init__(self
, arch
, vc_ver
=None, vc_min_ver
=0):
1118 self
.pi
= PlatformInfo(arch
)
1119 self
.ri
= RegistryInfo(self
.pi
)
1120 self
.si
= SystemInfo(self
.ri
, vc_ver
)
1122 if self
.vc_ver
< vc_min_ver
:
1123 err
= 'No suitable Microsoft Visual C++ version found'
1124 raise distutils
.errors
.DistutilsPlatformError(err
)
1129 Microsoft Visual Studio.
1136 return self
.si
.vs_ver
1141 Microsoft Visual C++ version.
1148 return self
.si
.vc_ver
1153 Microsoft Visual Studio Tools.
1160 paths
= [r
'Common7\IDE', r
'Common7\Tools']
1162 if self
.vs_ver
>= 14.0:
1163 arch_subdir
= self
.pi
.current_dir(hidex86
=True, x64
=True)
1164 paths
+= [r
'Common7\IDE\CommonExtensions\Microsoft\TestWindow']
1165 paths
+= [r
'Team Tools\Performance Tools']
1166 paths
+= [r
'Team Tools\Performance Tools%s' % arch_subdir
]
1168 return [join(self
.si
.VSInstallDir
, path
) for path
in paths
]
1171 def VCIncludes(self
):
1173 Microsoft Visual C++ & Microsoft Foundation Class Includes.
1180 return [join(self
.si
.VCInstallDir
, 'Include'),
1181 join(self
.si
.VCInstallDir
, r
'ATLMFC\Include')]
1184 def VCLibraries(self
):
1186 Microsoft Visual C++ & Microsoft Foundation Class Libraries.
1193 if self
.vs_ver
>= 15.0:
1194 arch_subdir
= self
.pi
.target_dir(x64
=True)
1196 arch_subdir
= self
.pi
.target_dir(hidex86
=True)
1197 paths
= ['Lib%s' % arch_subdir
, r
'ATLMFC\Lib%s' % arch_subdir
]
1199 if self
.vs_ver
>= 14.0:
1200 paths
+= [r
'Lib\store%s' % arch_subdir
]
1202 return [join(self
.si
.VCInstallDir
, path
) for path
in paths
]
1205 def VCStoreRefs(self
):
1207 Microsoft Visual C++ store references Libraries.
1214 if self
.vs_ver
< 14.0:
1216 return [join(self
.si
.VCInstallDir
, r
'Lib\store\references')]
1221 Microsoft Visual C++ Tools.
1229 tools
= [join(si
.VCInstallDir
, 'VCPackages')]
1231 forcex86
= True if self
.vs_ver
<= 10.0 else False
1232 arch_subdir
= self
.pi
.cross_dir(forcex86
)
1234 tools
+= [join(si
.VCInstallDir
, 'Bin%s' % arch_subdir
)]
1236 if self
.vs_ver
== 14.0:
1237 path
= 'Bin%s' % self
.pi
.current_dir(hidex86
=True)
1238 tools
+= [join(si
.VCInstallDir
, path
)]
1240 elif self
.vs_ver
>= 15.0:
1241 host_dir
= (r
'bin\HostX86%s' if self
.pi
.current_is_x86() else
1244 si
.VCInstallDir
, host_dir
% self
.pi
.target_dir(x64
=True))]
1246 if self
.pi
.current_cpu
!= self
.pi
.target_cpu
:
1248 si
.VCInstallDir
, host_dir
% self
.pi
.current_dir(x64
=True))]
1251 tools
+= [join(si
.VCInstallDir
, 'Bin')]
1256 def OSLibraries(self
):
1258 Microsoft Windows SDK Libraries.
1265 if self
.vs_ver
<= 10.0:
1266 arch_subdir
= self
.pi
.target_dir(hidex86
=True, x64
=True)
1267 return [join(self
.si
.WindowsSdkDir
, 'Lib%s' % arch_subdir
)]
1270 arch_subdir
= self
.pi
.target_dir(x64
=True)
1271 lib
= join(self
.si
.WindowsSdkDir
, 'lib')
1272 libver
= self
._sdk
_subdir
1273 return [join(lib
, '%sum%s' % (libver
, arch_subdir
))]
1276 def OSIncludes(self
):
1278 Microsoft Windows SDK Include.
1285 include
= join(self
.si
.WindowsSdkDir
, 'include')
1287 if self
.vs_ver
<= 10.0:
1288 return [include
, join(include
, 'gl')]
1291 if self
.vs_ver
>= 14.0:
1292 sdkver
= self
._sdk
_subdir
1295 return [join(include
, '%sshared' % sdkver
),
1296 join(include
, '%sum' % sdkver
),
1297 join(include
, '%swinrt' % sdkver
)]
1300 def OSLibpath(self
):
1302 Microsoft Windows SDK Libraries Paths.
1309 ref
= join(self
.si
.WindowsSdkDir
, 'References')
1312 if self
.vs_ver
<= 9.0:
1313 libpath
+= self
.OSLibraries
1315 if self
.vs_ver
>= 11.0:
1316 libpath
+= [join(ref
, r
'CommonConfiguration\Neutral')]
1318 if self
.vs_ver
>= 14.0:
1321 join(self
.si
.WindowsSdkDir
, 'UnionMetadata'),
1323 ref
, 'Windows.Foundation.UniversalApiContract', '1.0.0.0'),
1324 join(ref
, 'Windows.Foundation.FoundationContract', '1.0.0.0'),
1326 ref
, 'Windows.Networking.Connectivity.WwanContract',
1329 self
.si
.WindowsSdkDir
, 'ExtensionSDKs', 'Microsoft.VCLibs',
1330 '%0.1f' % self
.vs_ver
, 'References', 'CommonConfiguration',
1338 Microsoft Windows SDK Tools.
1345 return list(self
._sdk
_tools
())
1347 def _sdk_tools(self
):
1349 Microsoft Windows SDK Tools paths generator.
1356 if self
.vs_ver
< 15.0:
1357 bin_dir
= 'Bin' if self
.vs_ver
<= 11.0 else r
'Bin\x86'
1358 yield join(self
.si
.WindowsSdkDir
, bin_dir
)
1360 if not self
.pi
.current_is_x86():
1361 arch_subdir
= self
.pi
.current_dir(x64
=True)
1362 path
= 'Bin%s' % arch_subdir
1363 yield join(self
.si
.WindowsSdkDir
, path
)
1365 if self
.vs_ver
in (10.0, 11.0):
1366 if self
.pi
.target_is_x86():
1369 arch_subdir
= self
.pi
.current_dir(hidex86
=True, x64
=True)
1370 path
= r
'Bin\NETFX 4.0 Tools%s' % arch_subdir
1371 yield join(self
.si
.WindowsSdkDir
, path
)
1373 elif self
.vs_ver
>= 15.0:
1374 path
= join(self
.si
.WindowsSdkDir
, 'Bin')
1375 arch_subdir
= self
.pi
.current_dir(x64
=True)
1376 sdkver
= self
.si
.WindowsSdkLastVersion
1377 yield join(path
, '%s%s' % (sdkver
, arch_subdir
))
1379 if self
.si
.WindowsSDKExecutablePath
:
1380 yield self
.si
.WindowsSDKExecutablePath
1383 def _sdk_subdir(self
):
1385 Microsoft Windows SDK version subdir.
1392 ucrtver
= self
.si
.WindowsSdkLastVersion
1393 return ('%s\\' % ucrtver
) if ucrtver
else ''
1398 Microsoft Windows SDK Setup.
1405 if self
.vs_ver
> 9.0:
1408 return [join(self
.si
.WindowsSdkDir
, 'Setup')]
1413 Microsoft .NET Framework Tools.
1423 if self
.vs_ver
<= 10.0:
1425 include64
= not pi
.target_is_x86() and not pi
.current_is_x86()
1427 include32
= pi
.target_is_x86() or pi
.current_is_x86()
1428 include64
= pi
.current_cpu
== 'amd64' or pi
.target_cpu
== 'amd64'
1432 tools
+= [join(si
.FrameworkDir32
, ver
)
1433 for ver
in si
.FrameworkVersion32
]
1435 tools
+= [join(si
.FrameworkDir64
, ver
)
1436 for ver
in si
.FrameworkVersion64
]
1440 def NetFxSDKLibraries(self
):
1442 Microsoft .Net Framework SDK Libraries.
1449 if self
.vs_ver
< 14.0 or not self
.si
.NetFxSdkDir
:
1452 arch_subdir
= self
.pi
.target_dir(x64
=True)
1453 return [join(self
.si
.NetFxSdkDir
, r
'lib\um%s' % arch_subdir
)]
1456 def NetFxSDKIncludes(self
):
1458 Microsoft .Net Framework SDK Includes.
1465 if self
.vs_ver
< 14.0 or not self
.si
.NetFxSdkDir
:
1468 return [join(self
.si
.NetFxSdkDir
, r
'include\um')]
1473 Microsoft Visual Studio Team System Database.
1480 return [join(self
.si
.VSInstallDir
, r
'VSTSDB\Deploy')]
1485 Microsoft Build Engine.
1492 if self
.vs_ver
< 12.0:
1494 elif self
.vs_ver
< 15.0:
1495 base_path
= self
.si
.ProgramFilesx86
1496 arch_subdir
= self
.pi
.current_dir(hidex86
=True)
1498 base_path
= self
.si
.VSInstallDir
1501 path
= r
'MSBuild\%0.1f\bin%s' % (self
.vs_ver
, arch_subdir
)
1502 build
= [join(base_path
, path
)]
1504 if self
.vs_ver
>= 15.0:
1505 # Add Roslyn C# & Visual Basic Compiler
1506 build
+= [join(base_path
, path
, 'Roslyn')]
1511 def HTMLHelpWorkshop(self
):
1513 Microsoft HTML Help Workshop.
1520 if self
.vs_ver
< 11.0:
1523 return [join(self
.si
.ProgramFilesx86
, 'HTML Help Workshop')]
1526 def UCRTLibraries(self
):
1528 Microsoft Universal C Runtime SDK Libraries.
1535 if self
.vs_ver
< 14.0:
1538 arch_subdir
= self
.pi
.target_dir(x64
=True)
1539 lib
= join(self
.si
.UniversalCRTSdkDir
, 'lib')
1540 ucrtver
= self
._ucrt
_subdir
1541 return [join(lib
, '%sucrt%s' % (ucrtver
, arch_subdir
))]
1544 def UCRTIncludes(self
):
1546 Microsoft Universal C Runtime SDK Include.
1553 if self
.vs_ver
< 14.0:
1556 include
= join(self
.si
.UniversalCRTSdkDir
, 'include')
1557 return [join(include
, '%sucrt' % self
._ucrt
_subdir
)]
1560 def _ucrt_subdir(self
):
1562 Microsoft Universal C Runtime SDK version subdir.
1569 ucrtver
= self
.si
.UniversalCRTSdkLastVersion
1570 return ('%s\\' % ucrtver
) if ucrtver
else ''
1575 Microsoft Visual F#.
1582 if 11.0 > self
.vs_ver
> 12.0:
1585 return [self
.si
.FSharpInstallDir
]
1588 def VCRuntimeRedist(self
):
1590 Microsoft Visual C++ runtime redistributable dll.
1597 vcruntime
= 'vcruntime%d0.dll' % self
.vc_ver
1598 arch_subdir
= self
.pi
.target_dir(x64
=True).strip('\\')
1600 # Installation prefixes candidates
1602 tools_path
= self
.si
.VCInstallDir
1603 redist_path
= dirname(tools_path
.replace(r
'\Tools', r
'\Redist'))
1604 if isdir(redist_path
):
1605 # Redist version may not be exactly the same as tools
1606 redist_path
= join(redist_path
, listdir(redist_path
)[-1])
1607 prefixes
+= [redist_path
, join(redist_path
, 'onecore')]
1609 prefixes
+= [join(tools_path
, 'redist')] # VS14 legacy path
1612 crt_dirs
= ('Microsoft.VC%d.CRT' % (self
.vc_ver
* 10),
1613 # Sometime store in directory with VS version instead of VC
1614 'Microsoft.VC%d.CRT' % (int(self
.vs_ver
) * 10))
1617 for prefix
, crt_dir
in itertools
.product(prefixes
, crt_dirs
):
1618 path
= join(prefix
, arch_subdir
, crt_dir
, vcruntime
)
1622 def return_env(self
, exists
=True):
1624 Return environment dict.
1629 It True, only return existing paths.
1637 include
=self
._build
_paths
('include',
1641 self
.NetFxSDKIncludes
],
1643 lib
=self
._build
_paths
('lib',
1648 self
.NetFxSDKLibraries
],
1650 libpath
=self
._build
_paths
('libpath',
1656 path
=self
._build
_paths
('path',
1664 self
.HTMLHelpWorkshop
,
1668 if self
.vs_ver
>= 14 and isfile(self
.VCRuntimeRedist
):
1669 env
['py_vcruntime_redist'] = self
.VCRuntimeRedist
1672 def _build_paths(self
, name
, spec_path_lists
, exists
):
1674 Given an environment variable name and specified paths,
1675 return a pathsep-separated string of paths containing
1676 unique, extant, directories from those paths and from
1677 the environment variable. Raise an error if no paths
1683 Environment variable name
1684 spec_path_lists: list of str
1687 It True, only return existing paths.
1692 Pathsep-separated paths
1694 # flatten spec_path_lists
1695 spec_paths
= itertools
.chain
.from_iterable(spec_path_lists
)
1696 env_paths
= environ
.get(name
, '').split(pathsep
)
1697 paths
= itertools
.chain(spec_paths
, env_paths
)
1698 extant_paths
= list(filter(isdir
, paths
)) if exists
else paths
1699 if not extant_paths
:
1700 msg
= "%s environment variable is empty" % name
.upper()
1701 raise distutils
.errors
.DistutilsPlatformError(msg
)
1702 unique_paths
= unique_everseen(extant_paths
)
1703 return pathsep
.join(unique_paths
)