Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/setuptools/msvc.py: 27%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""
2Improved support for Microsoft Visual C++ compilers.
4Known supported compilers:
5--------------------------
6Microsoft Visual C++ 9.0:
7 Microsoft Visual C++ Compiler for Python 2.7 (x86, amd64)
8 Microsoft Windows SDK 6.1 (x86, x64, ia64)
9 Microsoft Windows SDK 7.0 (x86, x64, ia64)
11Microsoft Visual C++ 10.0:
12 Microsoft Windows SDK 7.1 (x86, x64, ia64)
14Microsoft Visual C++ 14.X:
15 Microsoft Visual C++ Build Tools 2015 (x86, x64, arm)
16 Microsoft Visual Studio Build Tools 2017 (x86, x64, arm, arm64)
17 Microsoft Visual Studio Build Tools 2019 (x86, x64, arm, arm64)
19This may also support compilers shipped with compatible Visual Studio versions.
20"""
22import json
23from io import open
24from os import listdir, pathsep
25from os.path import join, isfile, isdir, dirname
26import sys
27import platform
28import itertools
29import distutils.errors
30from setuptools.extern.packaging.version import LegacyVersion
32from setuptools.extern.six.moves import filterfalse
34from .monkey import get_unpatched
36if platform.system() == 'Windows':
37 from setuptools.extern.six.moves import winreg
38 from os import environ
39else:
40 # Mock winreg and environ so the module can be imported on this platform.
42 class winreg:
43 HKEY_USERS = None
44 HKEY_CURRENT_USER = None
45 HKEY_LOCAL_MACHINE = None
46 HKEY_CLASSES_ROOT = None
48 environ = dict()
50_msvc9_suppress_errors = (
51 # msvc9compiler isn't available on some platforms
52 ImportError,
54 # msvc9compiler raises DistutilsPlatformError in some
55 # environments. See #1118.
56 distutils.errors.DistutilsPlatformError,
57)
59try:
60 from distutils.msvc9compiler import Reg
61except _msvc9_suppress_errors:
62 pass
65def msvc9_find_vcvarsall(version):
66 """
67 Patched "distutils.msvc9compiler.find_vcvarsall" to use the standalone
68 compiler build for Python
69 (VCForPython / Microsoft Visual C++ Compiler for Python 2.7).
71 Fall back to original behavior when the standalone compiler is not
72 available.
74 Redirect the path of "vcvarsall.bat".
76 Parameters
77 ----------
78 version: float
79 Required Microsoft Visual C++ version.
81 Return
82 ------
83 str
84 vcvarsall.bat path
85 """
86 vc_base = r'Software\%sMicrosoft\DevDiv\VCForPython\%0.1f'
87 key = vc_base % ('', version)
88 try:
89 # Per-user installs register the compiler path here
90 productdir = Reg.get_value(key, "installdir")
91 except KeyError:
92 try:
93 # All-user installs on a 64-bit system register here
94 key = vc_base % ('Wow6432Node\\', version)
95 productdir = Reg.get_value(key, "installdir")
96 except KeyError:
97 productdir = None
99 if productdir:
100 vcvarsall = join(productdir, "vcvarsall.bat")
101 if isfile(vcvarsall):
102 return vcvarsall
104 return get_unpatched(msvc9_find_vcvarsall)(version)
107def msvc9_query_vcvarsall(ver, arch='x86', *args, **kwargs):
108 """
109 Patched "distutils.msvc9compiler.query_vcvarsall" for support extra
110 Microsoft Visual C++ 9.0 and 10.0 compilers.
112 Set environment without use of "vcvarsall.bat".
114 Parameters
115 ----------
116 ver: float
117 Required Microsoft Visual C++ version.
118 arch: str
119 Target architecture.
121 Return
122 ------
123 dict
124 environment
125 """
126 # Try to get environment from vcvarsall.bat (Classical way)
127 try:
128 orig = get_unpatched(msvc9_query_vcvarsall)
129 return orig(ver, arch, *args, **kwargs)
130 except distutils.errors.DistutilsPlatformError:
131 # Pass error if Vcvarsall.bat is missing
132 pass
133 except ValueError:
134 # Pass error if environment not set after executing vcvarsall.bat
135 pass
137 # If error, try to set environment directly
138 try:
139 return EnvironmentInfo(arch, ver).return_env()
140 except distutils.errors.DistutilsPlatformError as exc:
141 _augment_exception(exc, ver, arch)
142 raise
145def msvc14_get_vc_env(plat_spec):
146 """
147 Patched "distutils._msvccompiler._get_vc_env" for support extra
148 Microsoft Visual C++ 14.X compilers.
150 Set environment without use of "vcvarsall.bat".
152 Parameters
153 ----------
154 plat_spec: str
155 Target architecture.
157 Return
158 ------
159 dict
160 environment
161 """
162 # Try to get environment from vcvarsall.bat (Classical way)
163 try:
164 return get_unpatched(msvc14_get_vc_env)(plat_spec)
165 except distutils.errors.DistutilsPlatformError:
166 # Pass error Vcvarsall.bat is missing
167 pass
169 # If error, try to set environment directly
170 try:
171 return EnvironmentInfo(plat_spec, vc_min_ver=14.0).return_env()
172 except distutils.errors.DistutilsPlatformError as exc:
173 _augment_exception(exc, 14.0)
174 raise
177def msvc14_gen_lib_options(*args, **kwargs):
178 """
179 Patched "distutils._msvccompiler.gen_lib_options" for fix
180 compatibility between "numpy.distutils" and "distutils._msvccompiler"
181 (for Numpy < 1.11.2)
182 """
183 if "numpy.distutils" in sys.modules:
184 import numpy as np
185 if LegacyVersion(np.__version__) < LegacyVersion('1.11.2'):
186 return np.distutils.ccompiler.gen_lib_options(*args, **kwargs)
187 return get_unpatched(msvc14_gen_lib_options)(*args, **kwargs)
190def _augment_exception(exc, version, arch=''):
191 """
192 Add details to the exception message to help guide the user
193 as to what action will resolve it.
194 """
195 # Error if MSVC++ directory not found or environment not set
196 message = exc.args[0]
198 if "vcvarsall" in message.lower() or "visual c" in message.lower():
199 # Special error message if MSVC++ not installed
200 tmpl = 'Microsoft Visual C++ {version:0.1f} is required.'
201 message = tmpl.format(**locals())
202 msdownload = 'www.microsoft.com/download/details.aspx?id=%d'
203 if version == 9.0:
204 if arch.lower().find('ia64') > -1:
205 # For VC++ 9.0, if IA64 support is needed, redirect user
206 # to Windows SDK 7.0.
207 # Note: No download link available from Microsoft.
208 message += ' Get it with "Microsoft Windows SDK 7.0"'
209 else:
210 # For VC++ 9.0 redirect user to Vc++ for Python 2.7 :
211 # This redirection link is maintained by Microsoft.
212 # Contact vspython@microsoft.com if it needs updating.
213 message += ' Get it from http://aka.ms/vcpython27'
214 elif version == 10.0:
215 # For VC++ 10.0 Redirect user to Windows SDK 7.1
216 message += ' Get it with "Microsoft Windows SDK 7.1": '
217 message += msdownload % 8279
218 elif version >= 14.0:
219 # For VC++ 14.X Redirect user to latest Visual C++ Build Tools
220 message += (' Get it with "Build Tools for Visual Studio": '
221 r'https://visualstudio.microsoft.com/downloads/')
223 exc.args = (message, )
226class PlatformInfo:
227 """
228 Current and Target Architectures information.
230 Parameters
231 ----------
232 arch: str
233 Target architecture.
234 """
235 current_cpu = environ.get('processor_architecture', '').lower()
237 def __init__(self, arch):
238 self.arch = arch.lower().replace('x64', 'amd64')
240 @property
241 def target_cpu(self):
242 """
243 Return Target CPU architecture.
245 Return
246 ------
247 str
248 Target CPU
249 """
250 return self.arch[self.arch.find('_') + 1:]
252 def target_is_x86(self):
253 """
254 Return True if target CPU is x86 32 bits..
256 Return
257 ------
258 bool
259 CPU is x86 32 bits
260 """
261 return self.target_cpu == 'x86'
263 def current_is_x86(self):
264 """
265 Return True if current CPU is x86 32 bits..
267 Return
268 ------
269 bool
270 CPU is x86 32 bits
271 """
272 return self.current_cpu == 'x86'
274 def current_dir(self, hidex86=False, x64=False):
275 """
276 Current platform specific subfolder.
278 Parameters
279 ----------
280 hidex86: bool
281 return '' and not '\x86' if architecture is x86.
282 x64: bool
283 return '\x64' and not '\amd64' if architecture is amd64.
285 Return
286 ------
287 str
288 subfolder: '\target', or '' (see hidex86 parameter)
289 """
290 return (
291 '' if (self.current_cpu == 'x86' and hidex86) else
292 r'\x64' if (self.current_cpu == 'amd64' and x64) else
293 r'\%s' % self.current_cpu
294 )
296 def target_dir(self, hidex86=False, x64=False):
297 r"""
298 Target platform specific subfolder.
300 Parameters
301 ----------
302 hidex86: bool
303 return '' and not '\x86' if architecture is x86.
304 x64: bool
305 return '\x64' and not '\amd64' if architecture is amd64.
307 Return
308 ------
309 str
310 subfolder: '\current', or '' (see hidex86 parameter)
311 """
312 return (
313 '' if (self.target_cpu == 'x86' and hidex86) else
314 r'\x64' if (self.target_cpu == 'amd64' and x64) else
315 r'\%s' % self.target_cpu
316 )
318 def cross_dir(self, forcex86=False):
319 r"""
320 Cross platform specific subfolder.
322 Parameters
323 ----------
324 forcex86: bool
325 Use 'x86' as current architecture even if current architecture is
326 not x86.
328 Return
329 ------
330 str
331 subfolder: '' if target architecture is current architecture,
332 '\current_target' if not.
333 """
334 current = 'x86' if forcex86 else self.current_cpu
335 return (
336 '' if self.target_cpu == current else
337 self.target_dir().replace('\\', '\\%s_' % current)
338 )
341class RegistryInfo:
342 """
343 Microsoft Visual Studio related registry information.
345 Parameters
346 ----------
347 platform_info: PlatformInfo
348 "PlatformInfo" instance.
349 """
350 HKEYS = (winreg.HKEY_USERS,
351 winreg.HKEY_CURRENT_USER,
352 winreg.HKEY_LOCAL_MACHINE,
353 winreg.HKEY_CLASSES_ROOT)
355 def __init__(self, platform_info):
356 self.pi = platform_info
358 @property
359 def visualstudio(self):
360 """
361 Microsoft Visual Studio root registry key.
363 Return
364 ------
365 str
366 Registry key
367 """
368 return 'VisualStudio'
370 @property
371 def sxs(self):
372 """
373 Microsoft Visual Studio SxS registry key.
375 Return
376 ------
377 str
378 Registry key
379 """
380 return join(self.visualstudio, 'SxS')
382 @property
383 def vc(self):
384 """
385 Microsoft Visual C++ VC7 registry key.
387 Return
388 ------
389 str
390 Registry key
391 """
392 return join(self.sxs, 'VC7')
394 @property
395 def vs(self):
396 """
397 Microsoft Visual Studio VS7 registry key.
399 Return
400 ------
401 str
402 Registry key
403 """
404 return join(self.sxs, 'VS7')
406 @property
407 def vc_for_python(self):
408 """
409 Microsoft Visual C++ for Python registry key.
411 Return
412 ------
413 str
414 Registry key
415 """
416 return r'DevDiv\VCForPython'
418 @property
419 def microsoft_sdk(self):
420 """
421 Microsoft SDK registry key.
423 Return
424 ------
425 str
426 Registry key
427 """
428 return 'Microsoft SDKs'
430 @property
431 def windows_sdk(self):
432 """
433 Microsoft Windows/Platform SDK registry key.
435 Return
436 ------
437 str
438 Registry key
439 """
440 return join(self.microsoft_sdk, 'Windows')
442 @property
443 def netfx_sdk(self):
444 """
445 Microsoft .NET Framework SDK registry key.
447 Return
448 ------
449 str
450 Registry key
451 """
452 return join(self.microsoft_sdk, 'NETFXSDK')
454 @property
455 def windows_kits_roots(self):
456 """
457 Microsoft Windows Kits Roots registry key.
459 Return
460 ------
461 str
462 Registry key
463 """
464 return r'Windows Kits\Installed Roots'
466 def microsoft(self, key, x86=False):
467 """
468 Return key in Microsoft software registry.
470 Parameters
471 ----------
472 key: str
473 Registry key path where look.
474 x86: str
475 Force x86 software registry.
477 Return
478 ------
479 str
480 Registry key
481 """
482 node64 = '' if self.pi.current_is_x86() or x86 else 'Wow6432Node'
483 return join('Software', node64, 'Microsoft', key)
485 def lookup(self, key, name):
486 """
487 Look for values in registry in Microsoft software registry.
489 Parameters
490 ----------
491 key: str
492 Registry key path where look.
493 name: str
494 Value name to find.
496 Return
497 ------
498 str
499 value
500 """
501 key_read = winreg.KEY_READ
502 openkey = winreg.OpenKey
503 ms = self.microsoft
504 for hkey in self.HKEYS:
505 try:
506 bkey = openkey(hkey, ms(key), 0, key_read)
507 except (OSError, IOError):
508 if not self.pi.current_is_x86():
509 try:
510 bkey = openkey(hkey, ms(key, True), 0, key_read)
511 except (OSError, IOError):
512 continue
513 else:
514 continue
515 try:
516 return winreg.QueryValueEx(bkey, name)[0]
517 except (OSError, IOError):
518 pass
521class SystemInfo:
522 """
523 Microsoft Windows and Visual Studio related system information.
525 Parameters
526 ----------
527 registry_info: RegistryInfo
528 "RegistryInfo" instance.
529 vc_ver: float
530 Required Microsoft Visual C++ version.
531 """
533 # Variables and properties in this class use originals CamelCase variables
534 # names from Microsoft source files for more easy comparison.
535 WinDir = environ.get('WinDir', '')
536 ProgramFiles = environ.get('ProgramFiles', '')
537 ProgramFilesx86 = environ.get('ProgramFiles(x86)', ProgramFiles)
539 def __init__(self, registry_info, vc_ver=None):
540 self.ri = registry_info
541 self.pi = self.ri.pi
543 self.known_vs_paths = self.find_programdata_vs_vers()
545 # Except for VS15+, VC version is aligned with VS version
546 self.vs_ver = self.vc_ver = (
547 vc_ver or self._find_latest_available_vs_ver())
549 def _find_latest_available_vs_ver(self):
550 """
551 Find the latest VC version
553 Return
554 ------
555 float
556 version
557 """
558 reg_vc_vers = self.find_reg_vs_vers()
560 if not (reg_vc_vers or self.known_vs_paths):
561 raise distutils.errors.DistutilsPlatformError(
562 'No Microsoft Visual C++ version found')
564 vc_vers = set(reg_vc_vers)
565 vc_vers.update(self.known_vs_paths)
566 return sorted(vc_vers)[-1]
568 def find_reg_vs_vers(self):
569 """
570 Find Microsoft Visual Studio versions available in registry.
572 Return
573 ------
574 list of float
575 Versions
576 """
577 ms = self.ri.microsoft
578 vckeys = (self.ri.vc, self.ri.vc_for_python, self.ri.vs)
579 vs_vers = []
580 for hkey in self.ri.HKEYS:
581 for key in vckeys:
582 try:
583 bkey = winreg.OpenKey(hkey, ms(key), 0, winreg.KEY_READ)
584 except (OSError, IOError):
585 continue
586 subkeys, values, _ = winreg.QueryInfoKey(bkey)
587 for i in range(values):
588 try:
589 ver = float(winreg.EnumValue(bkey, i)[0])
590 if ver not in vs_vers:
591 vs_vers.append(ver)
592 except ValueError:
593 pass
594 for i in range(subkeys):
595 try:
596 ver = float(winreg.EnumKey(bkey, i))
597 if ver not in vs_vers:
598 vs_vers.append(ver)
599 except ValueError:
600 pass
601 return sorted(vs_vers)
603 def find_programdata_vs_vers(self):
604 r"""
605 Find Visual studio 2017+ versions from information in
606 "C:\ProgramData\Microsoft\VisualStudio\Packages\_Instances".
608 Return
609 ------
610 dict
611 float version as key, path as value.
612 """
613 vs_versions = {}
614 instances_dir = \
615 r'C:\ProgramData\Microsoft\VisualStudio\Packages\_Instances'
617 try:
618 hashed_names = listdir(instances_dir)
620 except (OSError, IOError):
621 # Directory not exists with all Visual Studio versions
622 return vs_versions
624 for name in hashed_names:
625 try:
626 # Get VS installation path from "state.json" file
627 state_path = join(instances_dir, name, 'state.json')
628 with open(state_path, 'rt', encoding='utf-8') as state_file:
629 state = json.load(state_file)
630 vs_path = state['installationPath']
632 # Raises OSError if this VS installation does not contain VC
633 listdir(join(vs_path, r'VC\Tools\MSVC'))
635 # Store version and path
636 vs_versions[self._as_float_version(
637 state['installationVersion'])] = vs_path
639 except (OSError, IOError, KeyError):
640 # Skip if "state.json" file is missing or bad format
641 continue
643 return vs_versions
645 @staticmethod
646 def _as_float_version(version):
647 """
648 Return a string version as a simplified float version (major.minor)
650 Parameters
651 ----------
652 version: str
653 Version.
655 Return
656 ------
657 float
658 version
659 """
660 return float('.'.join(version.split('.')[:2]))
662 @property
663 def VSInstallDir(self):
664 """
665 Microsoft Visual Studio directory.
667 Return
668 ------
669 str
670 path
671 """
672 # Default path
673 default = join(self.ProgramFilesx86,
674 'Microsoft Visual Studio %0.1f' % self.vs_ver)
676 # Try to get path from registry, if fail use default path
677 return self.ri.lookup(self.ri.vs, '%0.1f' % self.vs_ver) or default
679 @property
680 def VCInstallDir(self):
681 """
682 Microsoft Visual C++ directory.
684 Return
685 ------
686 str
687 path
688 """
689 path = self._guess_vc() or self._guess_vc_legacy()
691 if not isdir(path):
692 msg = 'Microsoft Visual C++ directory not found'
693 raise distutils.errors.DistutilsPlatformError(msg)
695 return path
697 def _guess_vc(self):
698 """
699 Locate Visual C++ for VS2017+.
701 Return
702 ------
703 str
704 path
705 """
706 if self.vs_ver <= 14.0:
707 return ''
709 try:
710 # First search in known VS paths
711 vs_dir = self.known_vs_paths[self.vs_ver]
712 except KeyError:
713 # Else, search with path from registry
714 vs_dir = self.VSInstallDir
716 guess_vc = join(vs_dir, r'VC\Tools\MSVC')
718 # Subdir with VC exact version as name
719 try:
720 # Update the VC version with real one instead of VS version
721 vc_ver = listdir(guess_vc)[-1]
722 self.vc_ver = self._as_float_version(vc_ver)
723 return join(guess_vc, vc_ver)
724 except (OSError, IOError, IndexError):
725 return ''
727 def _guess_vc_legacy(self):
728 """
729 Locate Visual C++ for versions prior to 2017.
731 Return
732 ------
733 str
734 path
735 """
736 default = join(self.ProgramFilesx86,
737 r'Microsoft Visual Studio %0.1f\VC' % self.vs_ver)
739 # Try to get "VC++ for Python" path from registry as default path
740 reg_path = join(self.ri.vc_for_python, '%0.1f' % self.vs_ver)
741 python_vc = self.ri.lookup(reg_path, 'installdir')
742 default_vc = join(python_vc, 'VC') if python_vc else default
744 # Try to get path from registry, if fail use default path
745 return self.ri.lookup(self.ri.vc, '%0.1f' % self.vs_ver) or default_vc
747 @property
748 def WindowsSdkVersion(self):
749 """
750 Microsoft Windows SDK versions for specified MSVC++ version.
752 Return
753 ------
754 tuple of str
755 versions
756 """
757 if self.vs_ver <= 9.0:
758 return '7.0', '6.1', '6.0a'
759 elif self.vs_ver == 10.0:
760 return '7.1', '7.0a'
761 elif self.vs_ver == 11.0:
762 return '8.0', '8.0a'
763 elif self.vs_ver == 12.0:
764 return '8.1', '8.1a'
765 elif self.vs_ver >= 14.0:
766 return '10.0', '8.1'
768 @property
769 def WindowsSdkLastVersion(self):
770 """
771 Microsoft Windows SDK last version.
773 Return
774 ------
775 str
776 version
777 """
778 return self._use_last_dir_name(join(self.WindowsSdkDir, 'lib'))
780 @property
781 def WindowsSdkDir(self):
782 """
783 Microsoft Windows SDK directory.
785 Return
786 ------
787 str
788 path
789 """
790 sdkdir = ''
791 for ver in self.WindowsSdkVersion:
792 # Try to get it from registry
793 loc = join(self.ri.windows_sdk, 'v%s' % ver)
794 sdkdir = self.ri.lookup(loc, 'installationfolder')
795 if sdkdir:
796 break
797 if not sdkdir or not isdir(sdkdir):
798 # Try to get "VC++ for Python" version from registry
799 path = join(self.ri.vc_for_python, '%0.1f' % self.vc_ver)
800 install_base = self.ri.lookup(path, 'installdir')
801 if install_base:
802 sdkdir = join(install_base, 'WinSDK')
803 if not sdkdir or not isdir(sdkdir):
804 # If fail, use default new path
805 for ver in self.WindowsSdkVersion:
806 intver = ver[:ver.rfind('.')]
807 path = r'Microsoft SDKs\Windows Kits\%s' % intver
808 d = join(self.ProgramFiles, path)
809 if isdir(d):
810 sdkdir = d
811 if not sdkdir or not isdir(sdkdir):
812 # If fail, use default old path
813 for ver in self.WindowsSdkVersion:
814 path = r'Microsoft SDKs\Windows\v%s' % ver
815 d = join(self.ProgramFiles, path)
816 if isdir(d):
817 sdkdir = d
818 if not sdkdir:
819 # If fail, use Platform SDK
820 sdkdir = join(self.VCInstallDir, 'PlatformSDK')
821 return sdkdir
823 @property
824 def WindowsSDKExecutablePath(self):
825 """
826 Microsoft Windows SDK executable directory.
828 Return
829 ------
830 str
831 path
832 """
833 # Find WinSDK NetFx Tools registry dir name
834 if self.vs_ver <= 11.0:
835 netfxver = 35
836 arch = ''
837 else:
838 netfxver = 40
839 hidex86 = True if self.vs_ver <= 12.0 else False
840 arch = self.pi.current_dir(x64=True, hidex86=hidex86)
841 fx = 'WinSDK-NetFx%dTools%s' % (netfxver, arch.replace('\\', '-'))
843 # list all possibles registry paths
844 regpaths = []
845 if self.vs_ver >= 14.0:
846 for ver in self.NetFxSdkVersion:
847 regpaths += [join(self.ri.netfx_sdk, ver, fx)]
849 for ver in self.WindowsSdkVersion:
850 regpaths += [join(self.ri.windows_sdk, 'v%sA' % ver, fx)]
852 # Return installation folder from the more recent path
853 for path in regpaths:
854 execpath = self.ri.lookup(path, 'installationfolder')
855 if execpath:
856 return execpath
858 @property
859 def FSharpInstallDir(self):
860 """
861 Microsoft Visual F# directory.
863 Return
864 ------
865 str
866 path
867 """
868 path = join(self.ri.visualstudio, r'%0.1f\Setup\F#' % self.vs_ver)
869 return self.ri.lookup(path, 'productdir') or ''
871 @property
872 def UniversalCRTSdkDir(self):
873 """
874 Microsoft Universal CRT SDK directory.
876 Return
877 ------
878 str
879 path
880 """
881 # Set Kit Roots versions for specified MSVC++ version
882 vers = ('10', '81') if self.vs_ver >= 14.0 else ()
884 # Find path of the more recent Kit
885 for ver in vers:
886 sdkdir = self.ri.lookup(self.ri.windows_kits_roots,
887 'kitsroot%s' % ver)
888 if sdkdir:
889 return sdkdir or ''
891 @property
892 def UniversalCRTSdkLastVersion(self):
893 """
894 Microsoft Universal C Runtime SDK last version.
896 Return
897 ------
898 str
899 version
900 """
901 return self._use_last_dir_name(join(self.UniversalCRTSdkDir, 'lib'))
903 @property
904 def NetFxSdkVersion(self):
905 """
906 Microsoft .NET Framework SDK versions.
908 Return
909 ------
910 tuple of str
911 versions
912 """
913 # Set FxSdk versions for specified VS version
914 return (('4.7.2', '4.7.1', '4.7',
915 '4.6.2', '4.6.1', '4.6',
916 '4.5.2', '4.5.1', '4.5')
917 if self.vs_ver >= 14.0 else ())
919 @property
920 def NetFxSdkDir(self):
921 """
922 Microsoft .NET Framework SDK directory.
924 Return
925 ------
926 str
927 path
928 """
929 sdkdir = ''
930 for ver in self.NetFxSdkVersion:
931 loc = join(self.ri.netfx_sdk, ver)
932 sdkdir = self.ri.lookup(loc, 'kitsinstallationfolder')
933 if sdkdir:
934 break
935 return sdkdir
937 @property
938 def FrameworkDir32(self):
939 """
940 Microsoft .NET Framework 32bit directory.
942 Return
943 ------
944 str
945 path
946 """
947 # Default path
948 guess_fw = join(self.WinDir, r'Microsoft.NET\Framework')
950 # Try to get path from registry, if fail use default path
951 return self.ri.lookup(self.ri.vc, 'frameworkdir32') or guess_fw
953 @property
954 def FrameworkDir64(self):
955 """
956 Microsoft .NET Framework 64bit directory.
958 Return
959 ------
960 str
961 path
962 """
963 # Default path
964 guess_fw = join(self.WinDir, r'Microsoft.NET\Framework64')
966 # Try to get path from registry, if fail use default path
967 return self.ri.lookup(self.ri.vc, 'frameworkdir64') or guess_fw
969 @property
970 def FrameworkVersion32(self):
971 """
972 Microsoft .NET Framework 32bit versions.
974 Return
975 ------
976 tuple of str
977 versions
978 """
979 return self._find_dot_net_versions(32)
981 @property
982 def FrameworkVersion64(self):
983 """
984 Microsoft .NET Framework 64bit versions.
986 Return
987 ------
988 tuple of str
989 versions
990 """
991 return self._find_dot_net_versions(64)
993 def _find_dot_net_versions(self, bits):
994 """
995 Find Microsoft .NET Framework versions.
997 Parameters
998 ----------
999 bits: int
1000 Platform number of bits: 32 or 64.
1002 Return
1003 ------
1004 tuple of str
1005 versions
1006 """
1007 # Find actual .NET version in registry
1008 reg_ver = self.ri.lookup(self.ri.vc, 'frameworkver%d' % bits)
1009 dot_net_dir = getattr(self, 'FrameworkDir%d' % bits)
1010 ver = reg_ver or self._use_last_dir_name(dot_net_dir, 'v') or ''
1012 # Set .NET versions for specified MSVC++ version
1013 if self.vs_ver >= 12.0:
1014 return ver, 'v4.0'
1015 elif self.vs_ver >= 10.0:
1016 return 'v4.0.30319' if ver.lower()[:2] != 'v4' else ver, 'v3.5'
1017 elif self.vs_ver == 9.0:
1018 return 'v3.5', 'v2.0.50727'
1019 elif self.vs_ver == 8.0:
1020 return 'v3.0', 'v2.0.50727'
1022 @staticmethod
1023 def _use_last_dir_name(path, prefix=''):
1024 """
1025 Return name of the last dir in path or '' if no dir found.
1027 Parameters
1028 ----------
1029 path: str
1030 Use dirs in this path
1031 prefix: str
1032 Use only dirs starting by this prefix
1034 Return
1035 ------
1036 str
1037 name
1038 """
1039 matching_dirs = (
1040 dir_name
1041 for dir_name in reversed(listdir(path))
1042 if isdir(join(path, dir_name)) and
1043 dir_name.startswith(prefix)
1044 )
1045 return next(matching_dirs, None) or ''
1048class EnvironmentInfo:
1049 """
1050 Return environment variables for specified Microsoft Visual C++ version
1051 and platform : Lib, Include, Path and libpath.
1053 This function is compatible with Microsoft Visual C++ 9.0 to 14.X.
1055 Script created by analysing Microsoft environment configuration files like
1056 "vcvars[...].bat", "SetEnv.Cmd", "vcbuildtools.bat", ...
1058 Parameters
1059 ----------
1060 arch: str
1061 Target architecture.
1062 vc_ver: float
1063 Required Microsoft Visual C++ version. If not set, autodetect the last
1064 version.
1065 vc_min_ver: float
1066 Minimum Microsoft Visual C++ version.
1067 """
1069 # Variables and properties in this class use originals CamelCase variables
1070 # names from Microsoft source files for more easy comparison.
1072 def __init__(self, arch, vc_ver=None, vc_min_ver=0):
1073 self.pi = PlatformInfo(arch)
1074 self.ri = RegistryInfo(self.pi)
1075 self.si = SystemInfo(self.ri, vc_ver)
1077 if self.vc_ver < vc_min_ver:
1078 err = 'No suitable Microsoft Visual C++ version found'
1079 raise distutils.errors.DistutilsPlatformError(err)
1081 @property
1082 def vs_ver(self):
1083 """
1084 Microsoft Visual Studio.
1086 Return
1087 ------
1088 float
1089 version
1090 """
1091 return self.si.vs_ver
1093 @property
1094 def vc_ver(self):
1095 """
1096 Microsoft Visual C++ version.
1098 Return
1099 ------
1100 float
1101 version
1102 """
1103 return self.si.vc_ver
1105 @property
1106 def VSTools(self):
1107 """
1108 Microsoft Visual Studio Tools.
1110 Return
1111 ------
1112 list of str
1113 paths
1114 """
1115 paths = [r'Common7\IDE', r'Common7\Tools']
1117 if self.vs_ver >= 14.0:
1118 arch_subdir = self.pi.current_dir(hidex86=True, x64=True)
1119 paths += [r'Common7\IDE\CommonExtensions\Microsoft\TestWindow']
1120 paths += [r'Team Tools\Performance Tools']
1121 paths += [r'Team Tools\Performance Tools%s' % arch_subdir]
1123 return [join(self.si.VSInstallDir, path) for path in paths]
1125 @property
1126 def VCIncludes(self):
1127 """
1128 Microsoft Visual C++ & Microsoft Foundation Class Includes.
1130 Return
1131 ------
1132 list of str
1133 paths
1134 """
1135 return [join(self.si.VCInstallDir, 'Include'),
1136 join(self.si.VCInstallDir, r'ATLMFC\Include')]
1138 @property
1139 def VCLibraries(self):
1140 """
1141 Microsoft Visual C++ & Microsoft Foundation Class Libraries.
1143 Return
1144 ------
1145 list of str
1146 paths
1147 """
1148 if self.vs_ver >= 15.0:
1149 arch_subdir = self.pi.target_dir(x64=True)
1150 else:
1151 arch_subdir = self.pi.target_dir(hidex86=True)
1152 paths = ['Lib%s' % arch_subdir, r'ATLMFC\Lib%s' % arch_subdir]
1154 if self.vs_ver >= 14.0:
1155 paths += [r'Lib\store%s' % arch_subdir]
1157 return [join(self.si.VCInstallDir, path) for path in paths]
1159 @property
1160 def VCStoreRefs(self):
1161 """
1162 Microsoft Visual C++ store references Libraries.
1164 Return
1165 ------
1166 list of str
1167 paths
1168 """
1169 if self.vs_ver < 14.0:
1170 return []
1171 return [join(self.si.VCInstallDir, r'Lib\store\references')]
1173 @property
1174 def VCTools(self):
1175 """
1176 Microsoft Visual C++ Tools.
1178 Return
1179 ------
1180 list of str
1181 paths
1182 """
1183 si = self.si
1184 tools = [join(si.VCInstallDir, 'VCPackages')]
1186 forcex86 = True if self.vs_ver <= 10.0 else False
1187 arch_subdir = self.pi.cross_dir(forcex86)
1188 if arch_subdir:
1189 tools += [join(si.VCInstallDir, 'Bin%s' % arch_subdir)]
1191 if self.vs_ver == 14.0:
1192 path = 'Bin%s' % self.pi.current_dir(hidex86=True)
1193 tools += [join(si.VCInstallDir, path)]
1195 elif self.vs_ver >= 15.0:
1196 host_dir = (r'bin\HostX86%s' if self.pi.current_is_x86() else
1197 r'bin\HostX64%s')
1198 tools += [join(
1199 si.VCInstallDir, host_dir % self.pi.target_dir(x64=True))]
1201 if self.pi.current_cpu != self.pi.target_cpu:
1202 tools += [join(
1203 si.VCInstallDir, host_dir % self.pi.current_dir(x64=True))]
1205 else:
1206 tools += [join(si.VCInstallDir, 'Bin')]
1208 return tools
1210 @property
1211 def OSLibraries(self):
1212 """
1213 Microsoft Windows SDK Libraries.
1215 Return
1216 ------
1217 list of str
1218 paths
1219 """
1220 if self.vs_ver <= 10.0:
1221 arch_subdir = self.pi.target_dir(hidex86=True, x64=True)
1222 return [join(self.si.WindowsSdkDir, 'Lib%s' % arch_subdir)]
1224 else:
1225 arch_subdir = self.pi.target_dir(x64=True)
1226 lib = join(self.si.WindowsSdkDir, 'lib')
1227 libver = self._sdk_subdir
1228 return [join(lib, '%sum%s' % (libver , arch_subdir))]
1230 @property
1231 def OSIncludes(self):
1232 """
1233 Microsoft Windows SDK Include.
1235 Return
1236 ------
1237 list of str
1238 paths
1239 """
1240 include = join(self.si.WindowsSdkDir, 'include')
1242 if self.vs_ver <= 10.0:
1243 return [include, join(include, 'gl')]
1245 else:
1246 if self.vs_ver >= 14.0:
1247 sdkver = self._sdk_subdir
1248 else:
1249 sdkver = ''
1250 return [join(include, '%sshared' % sdkver),
1251 join(include, '%sum' % sdkver),
1252 join(include, '%swinrt' % sdkver)]
1254 @property
1255 def OSLibpath(self):
1256 """
1257 Microsoft Windows SDK Libraries Paths.
1259 Return
1260 ------
1261 list of str
1262 paths
1263 """
1264 ref = join(self.si.WindowsSdkDir, 'References')
1265 libpath = []
1267 if self.vs_ver <= 9.0:
1268 libpath += self.OSLibraries
1270 if self.vs_ver >= 11.0:
1271 libpath += [join(ref, r'CommonConfiguration\Neutral')]
1273 if self.vs_ver >= 14.0:
1274 libpath += [
1275 ref,
1276 join(self.si.WindowsSdkDir, 'UnionMetadata'),
1277 join(ref, 'Windows.Foundation.UniversalApiContract', '1.0.0.0'),
1278 join(ref, 'Windows.Foundation.FoundationContract', '1.0.0.0'),
1279 join(ref,'Windows.Networking.Connectivity.WwanContract',
1280 '1.0.0.0'),
1281 join(self.si.WindowsSdkDir, 'ExtensionSDKs', 'Microsoft.VCLibs',
1282 '%0.1f' % self.vs_ver, 'References', 'CommonConfiguration',
1283 'neutral'),
1284 ]
1285 return libpath
1287 @property
1288 def SdkTools(self):
1289 """
1290 Microsoft Windows SDK Tools.
1292 Return
1293 ------
1294 list of str
1295 paths
1296 """
1297 return list(self._sdk_tools())
1299 def _sdk_tools(self):
1300 """
1301 Microsoft Windows SDK Tools paths generator.
1303 Return
1304 ------
1305 generator of str
1306 paths
1307 """
1308 if self.vs_ver < 15.0:
1309 bin_dir = 'Bin' if self.vs_ver <= 11.0 else r'Bin\x86'
1310 yield join(self.si.WindowsSdkDir, bin_dir)
1312 if not self.pi.current_is_x86():
1313 arch_subdir = self.pi.current_dir(x64=True)
1314 path = 'Bin%s' % arch_subdir
1315 yield join(self.si.WindowsSdkDir, path)
1317 if self.vs_ver in (10.0, 11.0):
1318 if self.pi.target_is_x86():
1319 arch_subdir = ''
1320 else:
1321 arch_subdir = self.pi.current_dir(hidex86=True, x64=True)
1322 path = r'Bin\NETFX 4.0 Tools%s' % arch_subdir
1323 yield join(self.si.WindowsSdkDir, path)
1325 elif self.vs_ver >= 15.0:
1326 path = join(self.si.WindowsSdkDir, 'Bin')
1327 arch_subdir = self.pi.current_dir(x64=True)
1328 sdkver = self.si.WindowsSdkLastVersion
1329 yield join(path, '%s%s' % (sdkver, arch_subdir))
1331 if self.si.WindowsSDKExecutablePath:
1332 yield self.si.WindowsSDKExecutablePath
1334 @property
1335 def _sdk_subdir(self):
1336 """
1337 Microsoft Windows SDK version subdir.
1339 Return
1340 ------
1341 str
1342 subdir
1343 """
1344 ucrtver = self.si.WindowsSdkLastVersion
1345 return ('%s\\' % ucrtver) if ucrtver else ''
1347 @property
1348 def SdkSetup(self):
1349 """
1350 Microsoft Windows SDK Setup.
1352 Return
1353 ------
1354 list of str
1355 paths
1356 """
1357 if self.vs_ver > 9.0:
1358 return []
1360 return [join(self.si.WindowsSdkDir, 'Setup')]
1362 @property
1363 def FxTools(self):
1364 """
1365 Microsoft .NET Framework Tools.
1367 Return
1368 ------
1369 list of str
1370 paths
1371 """
1372 pi = self.pi
1373 si = self.si
1375 if self.vs_ver <= 10.0:
1376 include32 = True
1377 include64 = not pi.target_is_x86() and not pi.current_is_x86()
1378 else:
1379 include32 = pi.target_is_x86() or pi.current_is_x86()
1380 include64 = pi.current_cpu == 'amd64' or pi.target_cpu == 'amd64'
1382 tools = []
1383 if include32:
1384 tools += [join(si.FrameworkDir32, ver)
1385 for ver in si.FrameworkVersion32]
1386 if include64:
1387 tools += [join(si.FrameworkDir64, ver)
1388 for ver in si.FrameworkVersion64]
1389 return tools
1391 @property
1392 def NetFxSDKLibraries(self):
1393 """
1394 Microsoft .Net Framework SDK Libraries.
1396 Return
1397 ------
1398 list of str
1399 paths
1400 """
1401 if self.vs_ver < 14.0 or not self.si.NetFxSdkDir:
1402 return []
1404 arch_subdir = self.pi.target_dir(x64=True)
1405 return [join(self.si.NetFxSdkDir, r'lib\um%s' % arch_subdir)]
1407 @property
1408 def NetFxSDKIncludes(self):
1409 """
1410 Microsoft .Net Framework SDK Includes.
1412 Return
1413 ------
1414 list of str
1415 paths
1416 """
1417 if self.vs_ver < 14.0 or not self.si.NetFxSdkDir:
1418 return []
1420 return [join(self.si.NetFxSdkDir, r'include\um')]
1422 @property
1423 def VsTDb(self):
1424 """
1425 Microsoft Visual Studio Team System Database.
1427 Return
1428 ------
1429 list of str
1430 paths
1431 """
1432 return [join(self.si.VSInstallDir, r'VSTSDB\Deploy')]
1434 @property
1435 def MSBuild(self):
1436 """
1437 Microsoft Build Engine.
1439 Return
1440 ------
1441 list of str
1442 paths
1443 """
1444 if self.vs_ver < 12.0:
1445 return []
1446 elif self.vs_ver < 15.0:
1447 base_path = self.si.ProgramFilesx86
1448 arch_subdir = self.pi.current_dir(hidex86=True)
1449 else:
1450 base_path = self.si.VSInstallDir
1451 arch_subdir = ''
1453 path = r'MSBuild\%0.1f\bin%s' % (self.vs_ver, arch_subdir)
1454 build = [join(base_path, path)]
1456 if self.vs_ver >= 15.0:
1457 # Add Roslyn C# & Visual Basic Compiler
1458 build += [join(base_path, path, 'Roslyn')]
1460 return build
1462 @property
1463 def HTMLHelpWorkshop(self):
1464 """
1465 Microsoft HTML Help Workshop.
1467 Return
1468 ------
1469 list of str
1470 paths
1471 """
1472 if self.vs_ver < 11.0:
1473 return []
1475 return [join(self.si.ProgramFilesx86, 'HTML Help Workshop')]
1477 @property
1478 def UCRTLibraries(self):
1479 """
1480 Microsoft Universal C Runtime SDK Libraries.
1482 Return
1483 ------
1484 list of str
1485 paths
1486 """
1487 if self.vs_ver < 14.0:
1488 return []
1490 arch_subdir = self.pi.target_dir(x64=True)
1491 lib = join(self.si.UniversalCRTSdkDir, 'lib')
1492 ucrtver = self._ucrt_subdir
1493 return [join(lib, '%sucrt%s' % (ucrtver, arch_subdir))]
1495 @property
1496 def UCRTIncludes(self):
1497 """
1498 Microsoft Universal C Runtime SDK Include.
1500 Return
1501 ------
1502 list of str
1503 paths
1504 """
1505 if self.vs_ver < 14.0:
1506 return []
1508 include = join(self.si.UniversalCRTSdkDir, 'include')
1509 return [join(include, '%sucrt' % self._ucrt_subdir)]
1511 @property
1512 def _ucrt_subdir(self):
1513 """
1514 Microsoft Universal C Runtime SDK version subdir.
1516 Return
1517 ------
1518 str
1519 subdir
1520 """
1521 ucrtver = self.si.UniversalCRTSdkLastVersion
1522 return ('%s\\' % ucrtver) if ucrtver else ''
1524 @property
1525 def FSharp(self):
1526 """
1527 Microsoft Visual F#.
1529 Return
1530 ------
1531 list of str
1532 paths
1533 """
1534 if 11.0 > self.vs_ver > 12.0:
1535 return []
1537 return [self.si.FSharpInstallDir]
1539 @property
1540 def VCRuntimeRedist(self):
1541 """
1542 Microsoft Visual C++ runtime redistributable dll.
1544 Return
1545 ------
1546 str
1547 path
1548 """
1549 vcruntime = 'vcruntime%d0.dll' % self.vc_ver
1550 arch_subdir = self.pi.target_dir(x64=True).strip('\\')
1552 # Installation prefixes candidates
1553 prefixes = []
1554 tools_path = self.si.VCInstallDir
1555 redist_path = dirname(tools_path.replace(r'\Tools', r'\Redist'))
1556 if isdir(redist_path):
1557 # Redist version may not be exactly the same as tools
1558 redist_path = join(redist_path, listdir(redist_path)[-1])
1559 prefixes += [redist_path, join(redist_path, 'onecore')]
1561 prefixes += [join(tools_path, 'redist')] # VS14 legacy path
1563 # CRT directory
1564 crt_dirs = ('Microsoft.VC%d.CRT' % (self.vc_ver * 10),
1565 # Sometime store in directory with VS version instead of VC
1566 'Microsoft.VC%d.CRT' % (int(self.vs_ver) * 10))
1568 # vcruntime path
1569 for prefix, crt_dir in itertools.product(prefixes, crt_dirs):
1570 path = join(prefix, arch_subdir, crt_dir, vcruntime)
1571 if isfile(path):
1572 return path
1574 def return_env(self, exists=True):
1575 """
1576 Return environment dict.
1578 Parameters
1579 ----------
1580 exists: bool
1581 It True, only return existing paths.
1583 Return
1584 ------
1585 dict
1586 environment
1587 """
1588 env = dict(
1589 include=self._build_paths('include',
1590 [self.VCIncludes,
1591 self.OSIncludes,
1592 self.UCRTIncludes,
1593 self.NetFxSDKIncludes],
1594 exists),
1595 lib=self._build_paths('lib',
1596 [self.VCLibraries,
1597 self.OSLibraries,
1598 self.FxTools,
1599 self.UCRTLibraries,
1600 self.NetFxSDKLibraries],
1601 exists),
1602 libpath=self._build_paths('libpath',
1603 [self.VCLibraries,
1604 self.FxTools,
1605 self.VCStoreRefs,
1606 self.OSLibpath],
1607 exists),
1608 path=self._build_paths('path',
1609 [self.VCTools,
1610 self.VSTools,
1611 self.VsTDb,
1612 self.SdkTools,
1613 self.SdkSetup,
1614 self.FxTools,
1615 self.MSBuild,
1616 self.HTMLHelpWorkshop,
1617 self.FSharp],
1618 exists),
1619 )
1620 if self.vs_ver >= 14 and isfile(self.VCRuntimeRedist):
1621 env['py_vcruntime_redist'] = self.VCRuntimeRedist
1622 return env
1624 def _build_paths(self, name, spec_path_lists, exists):
1625 """
1626 Given an environment variable name and specified paths,
1627 return a pathsep-separated string of paths containing
1628 unique, extant, directories from those paths and from
1629 the environment variable. Raise an error if no paths
1630 are resolved.
1632 Parameters
1633 ----------
1634 name: str
1635 Environment variable name
1636 spec_path_lists: list of str
1637 Paths
1638 exists: bool
1639 It True, only return existing paths.
1641 Return
1642 ------
1643 str
1644 Pathsep-separated paths
1645 """
1646 # flatten spec_path_lists
1647 spec_paths = itertools.chain.from_iterable(spec_path_lists)
1648 env_paths = environ.get(name, '').split(pathsep)
1649 paths = itertools.chain(spec_paths, env_paths)
1650 extant_paths = list(filter(isdir, paths)) if exists else paths
1651 if not extant_paths:
1652 msg = "%s environment variable is empty" % name.upper()
1653 raise distutils.errors.DistutilsPlatformError(msg)
1654 unique_paths = self._unique_everseen(extant_paths)
1655 return pathsep.join(unique_paths)
1657 # from Python docs
1658 @staticmethod
1659 def _unique_everseen(iterable, key=None):
1660 """
1661 List unique elements, preserving order.
1662 Remember all elements ever seen.
1664 _unique_everseen('AAAABBBCCDAABBB') --> A B C D
1666 _unique_everseen('ABBCcAD', str.lower) --> A B C D
1667 """
1668 seen = set()
1669 seen_add = seen.add
1670 if key is None:
1671 for element in filterfalse(seen.__contains__, iterable):
1672 seen_add(element)
1673 yield element
1674 else:
1675 for element in iterable:
1676 k = key(element)
1677 if k not in seen:
1678 seen_add(k)
1679 yield element