comparison CSP2/CSP2_env/env-d9b9114564458d9d-741b3de822f2aaca6c6caa4325c4afce/lib/python3.8/site-packages/setuptools/msvc.py @ 69:33d812a61356

planemo upload commit 2e9511a184a1ca667c7be0c6321a36dc4e3d116d
author jpayne
date Tue, 18 Mar 2025 17:55:14 -0400
parents
children
comparison
equal deleted inserted replaced
67:0e9998148a16 69:33d812a61356
1 """
2 Environment info about Microsoft Compilers.
3
4 >>> getfixture('windows_only')
5 >>> ei = EnvironmentInfo('amd64')
6 """
7
8 from __future__ import annotations
9
10 import contextlib
11 import itertools
12 import json
13 import os
14 import os.path
15 import platform
16 from typing import TYPE_CHECKING
17
18 from more_itertools import unique_everseen
19
20 import distutils.errors
21
22 # https://github.com/python/mypy/issues/8166
23 if not TYPE_CHECKING and platform.system() == 'Windows':
24 import winreg
25 from os import environ
26 else:
27 # Mock winreg and environ so the module can be imported on this platform.
28
29 class winreg:
30 HKEY_USERS = None
31 HKEY_CURRENT_USER = None
32 HKEY_LOCAL_MACHINE = None
33 HKEY_CLASSES_ROOT = None
34
35 environ: dict[str, str] = dict()
36
37
38 class PlatformInfo:
39 """
40 Current and Target Architectures information.
41
42 Parameters
43 ----------
44 arch: str
45 Target architecture.
46 """
47
48 current_cpu = environ.get('processor_architecture', '').lower()
49
50 def __init__(self, arch):
51 self.arch = arch.lower().replace('x64', 'amd64')
52
53 @property
54 def target_cpu(self):
55 """
56 Return Target CPU architecture.
57
58 Return
59 ------
60 str
61 Target CPU
62 """
63 return self.arch[self.arch.find('_') + 1 :]
64
65 def target_is_x86(self):
66 """
67 Return True if target CPU is x86 32 bits..
68
69 Return
70 ------
71 bool
72 CPU is x86 32 bits
73 """
74 return self.target_cpu == 'x86'
75
76 def current_is_x86(self):
77 """
78 Return True if current CPU is x86 32 bits..
79
80 Return
81 ------
82 bool
83 CPU is x86 32 bits
84 """
85 return self.current_cpu == 'x86'
86
87 def current_dir(self, hidex86=False, x64=False):
88 """
89 Current platform specific subfolder.
90
91 Parameters
92 ----------
93 hidex86: bool
94 return '' and not '\x86' if architecture is x86.
95 x64: bool
96 return '\x64' and not '\amd64' if architecture is amd64.
97
98 Return
99 ------
100 str
101 subfolder: '\target', or '' (see hidex86 parameter)
102 """
103 return (
104 ''
105 if (self.current_cpu == 'x86' and hidex86)
106 else r'\x64'
107 if (self.current_cpu == 'amd64' and x64)
108 else r'\%s' % self.current_cpu
109 )
110
111 def target_dir(self, hidex86=False, x64=False):
112 r"""
113 Target platform specific subfolder.
114
115 Parameters
116 ----------
117 hidex86: bool
118 return '' and not '\x86' if architecture is x86.
119 x64: bool
120 return '\x64' and not '\amd64' if architecture is amd64.
121
122 Return
123 ------
124 str
125 subfolder: '\current', or '' (see hidex86 parameter)
126 """
127 return (
128 ''
129 if (self.target_cpu == 'x86' and hidex86)
130 else r'\x64'
131 if (self.target_cpu == 'amd64' and x64)
132 else r'\%s' % self.target_cpu
133 )
134
135 def cross_dir(self, forcex86=False):
136 r"""
137 Cross platform specific subfolder.
138
139 Parameters
140 ----------
141 forcex86: bool
142 Use 'x86' as current architecture even if current architecture is
143 not x86.
144
145 Return
146 ------
147 str
148 subfolder: '' if target architecture is current architecture,
149 '\current_target' if not.
150 """
151 current = 'x86' if forcex86 else self.current_cpu
152 return (
153 ''
154 if self.target_cpu == current
155 else self.target_dir().replace('\\', '\\%s_' % current)
156 )
157
158
159 class RegistryInfo:
160 """
161 Microsoft Visual Studio related registry information.
162
163 Parameters
164 ----------
165 platform_info: PlatformInfo
166 "PlatformInfo" instance.
167 """
168
169 HKEYS = (
170 winreg.HKEY_USERS,
171 winreg.HKEY_CURRENT_USER,
172 winreg.HKEY_LOCAL_MACHINE,
173 winreg.HKEY_CLASSES_ROOT,
174 )
175
176 def __init__(self, platform_info):
177 self.pi = platform_info
178
179 @property
180 def visualstudio(self):
181 """
182 Microsoft Visual Studio root registry key.
183
184 Return
185 ------
186 str
187 Registry key
188 """
189 return 'VisualStudio'
190
191 @property
192 def sxs(self):
193 """
194 Microsoft Visual Studio SxS registry key.
195
196 Return
197 ------
198 str
199 Registry key
200 """
201 return os.path.join(self.visualstudio, 'SxS')
202
203 @property
204 def vc(self):
205 """
206 Microsoft Visual C++ VC7 registry key.
207
208 Return
209 ------
210 str
211 Registry key
212 """
213 return os.path.join(self.sxs, 'VC7')
214
215 @property
216 def vs(self):
217 """
218 Microsoft Visual Studio VS7 registry key.
219
220 Return
221 ------
222 str
223 Registry key
224 """
225 return os.path.join(self.sxs, 'VS7')
226
227 @property
228 def vc_for_python(self):
229 """
230 Microsoft Visual C++ for Python registry key.
231
232 Return
233 ------
234 str
235 Registry key
236 """
237 return r'DevDiv\VCForPython'
238
239 @property
240 def microsoft_sdk(self):
241 """
242 Microsoft SDK registry key.
243
244 Return
245 ------
246 str
247 Registry key
248 """
249 return 'Microsoft SDKs'
250
251 @property
252 def windows_sdk(self):
253 """
254 Microsoft Windows/Platform SDK registry key.
255
256 Return
257 ------
258 str
259 Registry key
260 """
261 return os.path.join(self.microsoft_sdk, 'Windows')
262
263 @property
264 def netfx_sdk(self):
265 """
266 Microsoft .NET Framework SDK registry key.
267
268 Return
269 ------
270 str
271 Registry key
272 """
273 return os.path.join(self.microsoft_sdk, 'NETFXSDK')
274
275 @property
276 def windows_kits_roots(self):
277 """
278 Microsoft Windows Kits Roots registry key.
279
280 Return
281 ------
282 str
283 Registry key
284 """
285 return r'Windows Kits\Installed Roots'
286
287 def microsoft(self, key, x86=False):
288 """
289 Return key in Microsoft software registry.
290
291 Parameters
292 ----------
293 key: str
294 Registry key path where look.
295 x86: str
296 Force x86 software registry.
297
298 Return
299 ------
300 str
301 Registry key
302 """
303 node64 = '' if self.pi.current_is_x86() or x86 else 'Wow6432Node'
304 return os.path.join('Software', node64, 'Microsoft', key)
305
306 def lookup(self, key, name):
307 """
308 Look for values in registry in Microsoft software registry.
309
310 Parameters
311 ----------
312 key: str
313 Registry key path where look.
314 name: str
315 Value name to find.
316
317 Return
318 ------
319 str
320 value
321 """
322 key_read = winreg.KEY_READ
323 openkey = winreg.OpenKey
324 closekey = winreg.CloseKey
325 ms = self.microsoft
326 for hkey in self.HKEYS:
327 bkey = None
328 try:
329 bkey = openkey(hkey, ms(key), 0, key_read)
330 except OSError:
331 if not self.pi.current_is_x86():
332 try:
333 bkey = openkey(hkey, ms(key, True), 0, key_read)
334 except OSError:
335 continue
336 else:
337 continue
338 try:
339 return winreg.QueryValueEx(bkey, name)[0]
340 except OSError:
341 pass
342 finally:
343 if bkey:
344 closekey(bkey)
345 return None
346
347
348 class SystemInfo:
349 """
350 Microsoft Windows and Visual Studio related system information.
351
352 Parameters
353 ----------
354 registry_info: RegistryInfo
355 "RegistryInfo" instance.
356 vc_ver: float
357 Required Microsoft Visual C++ version.
358 """
359
360 # Variables and properties in this class use originals CamelCase variables
361 # names from Microsoft source files for more easy comparison.
362 WinDir = environ.get('WinDir', '')
363 ProgramFiles = environ.get('ProgramFiles', '')
364 ProgramFilesx86 = environ.get('ProgramFiles(x86)', ProgramFiles)
365
366 def __init__(self, registry_info, vc_ver=None):
367 self.ri = registry_info
368 self.pi = self.ri.pi
369
370 self.known_vs_paths = self.find_programdata_vs_vers()
371
372 # Except for VS15+, VC version is aligned with VS version
373 self.vs_ver = self.vc_ver = vc_ver or self._find_latest_available_vs_ver()
374
375 def _find_latest_available_vs_ver(self):
376 """
377 Find the latest VC version
378
379 Return
380 ------
381 float
382 version
383 """
384 reg_vc_vers = self.find_reg_vs_vers()
385
386 if not (reg_vc_vers or self.known_vs_paths):
387 raise distutils.errors.DistutilsPlatformError(
388 'No Microsoft Visual C++ version found'
389 )
390
391 vc_vers = set(reg_vc_vers)
392 vc_vers.update(self.known_vs_paths)
393 return sorted(vc_vers)[-1]
394
395 def find_reg_vs_vers(self):
396 """
397 Find Microsoft Visual Studio versions available in registry.
398
399 Return
400 ------
401 list of float
402 Versions
403 """
404 ms = self.ri.microsoft
405 vckeys = (self.ri.vc, self.ri.vc_for_python, self.ri.vs)
406 vs_vers = []
407 for hkey, key in itertools.product(self.ri.HKEYS, vckeys):
408 try:
409 bkey = winreg.OpenKey(hkey, ms(key), 0, winreg.KEY_READ)
410 except OSError:
411 continue
412 with bkey:
413 subkeys, values, _ = winreg.QueryInfoKey(bkey)
414 for i in range(values):
415 with contextlib.suppress(ValueError):
416 ver = float(winreg.EnumValue(bkey, i)[0])
417 if ver not in vs_vers:
418 vs_vers.append(ver)
419 for i in range(subkeys):
420 with contextlib.suppress(ValueError):
421 ver = float(winreg.EnumKey(bkey, i))
422 if ver not in vs_vers:
423 vs_vers.append(ver)
424 return sorted(vs_vers)
425
426 def find_programdata_vs_vers(self):
427 r"""
428 Find Visual studio 2017+ versions from information in
429 "C:\ProgramData\Microsoft\VisualStudio\Packages\_Instances".
430
431 Return
432 ------
433 dict
434 float version as key, path as value.
435 """
436 vs_versions = {}
437 instances_dir = r'C:\ProgramData\Microsoft\VisualStudio\Packages\_Instances'
438
439 try:
440 hashed_names = os.listdir(instances_dir)
441
442 except OSError:
443 # Directory not exists with all Visual Studio versions
444 return vs_versions
445
446 for name in hashed_names:
447 try:
448 # Get VS installation path from "state.json" file
449 state_path = os.path.join(instances_dir, name, 'state.json')
450 with open(state_path, 'rt', encoding='utf-8') as state_file:
451 state = json.load(state_file)
452 vs_path = state['installationPath']
453
454 # Raises OSError if this VS installation does not contain VC
455 os.listdir(os.path.join(vs_path, r'VC\Tools\MSVC'))
456
457 # Store version and path
458 vs_versions[self._as_float_version(state['installationVersion'])] = (
459 vs_path
460 )
461
462 except (OSError, KeyError):
463 # Skip if "state.json" file is missing or bad format
464 continue
465
466 return vs_versions
467
468 @staticmethod
469 def _as_float_version(version):
470 """
471 Return a string version as a simplified float version (major.minor)
472
473 Parameters
474 ----------
475 version: str
476 Version.
477
478 Return
479 ------
480 float
481 version
482 """
483 return float('.'.join(version.split('.')[:2]))
484
485 @property
486 def VSInstallDir(self):
487 """
488 Microsoft Visual Studio directory.
489
490 Return
491 ------
492 str
493 path
494 """
495 # Default path
496 default = os.path.join(
497 self.ProgramFilesx86, 'Microsoft Visual Studio %0.1f' % self.vs_ver
498 )
499
500 # Try to get path from registry, if fail use default path
501 return self.ri.lookup(self.ri.vs, '%0.1f' % self.vs_ver) or default
502
503 @property
504 def VCInstallDir(self):
505 """
506 Microsoft Visual C++ directory.
507
508 Return
509 ------
510 str
511 path
512 """
513 path = self._guess_vc() or self._guess_vc_legacy()
514
515 if not os.path.isdir(path):
516 msg = 'Microsoft Visual C++ directory not found'
517 raise distutils.errors.DistutilsPlatformError(msg)
518
519 return path
520
521 def _guess_vc(self):
522 """
523 Locate Visual C++ for VS2017+.
524
525 Return
526 ------
527 str
528 path
529 """
530 if self.vs_ver <= 14.0:
531 return ''
532
533 try:
534 # First search in known VS paths
535 vs_dir = self.known_vs_paths[self.vs_ver]
536 except KeyError:
537 # Else, search with path from registry
538 vs_dir = self.VSInstallDir
539
540 guess_vc = os.path.join(vs_dir, r'VC\Tools\MSVC')
541
542 # Subdir with VC exact version as name
543 try:
544 # Update the VC version with real one instead of VS version
545 vc_ver = os.listdir(guess_vc)[-1]
546 self.vc_ver = self._as_float_version(vc_ver)
547 return os.path.join(guess_vc, vc_ver)
548 except (OSError, IndexError):
549 return ''
550
551 def _guess_vc_legacy(self):
552 """
553 Locate Visual C++ for versions prior to 2017.
554
555 Return
556 ------
557 str
558 path
559 """
560 default = os.path.join(
561 self.ProgramFilesx86, r'Microsoft Visual Studio %0.1f\VC' % self.vs_ver
562 )
563
564 # Try to get "VC++ for Python" path from registry as default path
565 reg_path = os.path.join(self.ri.vc_for_python, '%0.1f' % self.vs_ver)
566 python_vc = self.ri.lookup(reg_path, 'installdir')
567 default_vc = os.path.join(python_vc, 'VC') if python_vc else default
568
569 # Try to get path from registry, if fail use default path
570 return self.ri.lookup(self.ri.vc, '%0.1f' % self.vs_ver) or default_vc
571
572 @property
573 def WindowsSdkVersion(self):
574 """
575 Microsoft Windows SDK versions for specified MSVC++ version.
576
577 Return
578 ------
579 tuple of str
580 versions
581 """
582 if self.vs_ver <= 9.0:
583 return '7.0', '6.1', '6.0a'
584 elif self.vs_ver == 10.0:
585 return '7.1', '7.0a'
586 elif self.vs_ver == 11.0:
587 return '8.0', '8.0a'
588 elif self.vs_ver == 12.0:
589 return '8.1', '8.1a'
590 elif self.vs_ver >= 14.0:
591 return '10.0', '8.1'
592 return None
593
594 @property
595 def WindowsSdkLastVersion(self):
596 """
597 Microsoft Windows SDK last version.
598
599 Return
600 ------
601 str
602 version
603 """
604 return self._use_last_dir_name(os.path.join(self.WindowsSdkDir, 'lib'))
605
606 @property
607 def WindowsSdkDir(self): # noqa: C901 # is too complex (12) # FIXME
608 """
609 Microsoft Windows SDK directory.
610
611 Return
612 ------
613 str
614 path
615 """
616 sdkdir = ''
617 for ver in self.WindowsSdkVersion:
618 # Try to get it from registry
619 loc = os.path.join(self.ri.windows_sdk, 'v%s' % ver)
620 sdkdir = self.ri.lookup(loc, 'installationfolder')
621 if sdkdir:
622 break
623 if not sdkdir or not os.path.isdir(sdkdir):
624 # Try to get "VC++ for Python" version from registry
625 path = os.path.join(self.ri.vc_for_python, '%0.1f' % self.vc_ver)
626 install_base = self.ri.lookup(path, 'installdir')
627 if install_base:
628 sdkdir = os.path.join(install_base, 'WinSDK')
629 if not sdkdir or not os.path.isdir(sdkdir):
630 # If fail, use default new path
631 for ver in self.WindowsSdkVersion:
632 intver = ver[: ver.rfind('.')]
633 path = r'Microsoft SDKs\Windows Kits\%s' % intver
634 d = os.path.join(self.ProgramFiles, path)
635 if os.path.isdir(d):
636 sdkdir = d
637 if not sdkdir or not os.path.isdir(sdkdir):
638 # If fail, use default old path
639 for ver in self.WindowsSdkVersion:
640 path = r'Microsoft SDKs\Windows\v%s' % ver
641 d = os.path.join(self.ProgramFiles, path)
642 if os.path.isdir(d):
643 sdkdir = d
644 if not sdkdir:
645 # If fail, use Platform SDK
646 sdkdir = os.path.join(self.VCInstallDir, 'PlatformSDK')
647 return sdkdir
648
649 @property
650 def WindowsSDKExecutablePath(self):
651 """
652 Microsoft Windows SDK executable directory.
653
654 Return
655 ------
656 str
657 path
658 """
659 # Find WinSDK NetFx Tools registry dir name
660 if self.vs_ver <= 11.0:
661 netfxver = 35
662 arch = ''
663 else:
664 netfxver = 40
665 hidex86 = True if self.vs_ver <= 12.0 else False
666 arch = self.pi.current_dir(x64=True, hidex86=hidex86)
667 fx = 'WinSDK-NetFx%dTools%s' % (netfxver, arch.replace('\\', '-'))
668
669 # list all possibles registry paths
670 regpaths = []
671 if self.vs_ver >= 14.0:
672 for ver in self.NetFxSdkVersion:
673 regpaths += [os.path.join(self.ri.netfx_sdk, ver, fx)]
674
675 for ver in self.WindowsSdkVersion:
676 regpaths += [os.path.join(self.ri.windows_sdk, 'v%sA' % ver, fx)]
677
678 # Return installation folder from the more recent path
679 for path in regpaths:
680 execpath = self.ri.lookup(path, 'installationfolder')
681 if execpath:
682 return execpath
683
684 return None
685
686 @property
687 def FSharpInstallDir(self):
688 """
689 Microsoft Visual F# directory.
690
691 Return
692 ------
693 str
694 path
695 """
696 path = os.path.join(self.ri.visualstudio, r'%0.1f\Setup\F#' % self.vs_ver)
697 return self.ri.lookup(path, 'productdir') or ''
698
699 @property
700 def UniversalCRTSdkDir(self):
701 """
702 Microsoft Universal CRT SDK directory.
703
704 Return
705 ------
706 str
707 path
708 """
709 # Set Kit Roots versions for specified MSVC++ version
710 vers = ('10', '81') if self.vs_ver >= 14.0 else ()
711
712 # Find path of the more recent Kit
713 for ver in vers:
714 sdkdir = self.ri.lookup(self.ri.windows_kits_roots, 'kitsroot%s' % ver)
715 if sdkdir:
716 return sdkdir or ''
717
718 return None
719
720 @property
721 def UniversalCRTSdkLastVersion(self):
722 """
723 Microsoft Universal C Runtime SDK last version.
724
725 Return
726 ------
727 str
728 version
729 """
730 return self._use_last_dir_name(os.path.join(self.UniversalCRTSdkDir, 'lib'))
731
732 @property
733 def NetFxSdkVersion(self):
734 """
735 Microsoft .NET Framework SDK versions.
736
737 Return
738 ------
739 tuple of str
740 versions
741 """
742 # Set FxSdk versions for specified VS version
743 return (
744 ('4.7.2', '4.7.1', '4.7', '4.6.2', '4.6.1', '4.6', '4.5.2', '4.5.1', '4.5')
745 if self.vs_ver >= 14.0
746 else ()
747 )
748
749 @property
750 def NetFxSdkDir(self):
751 """
752 Microsoft .NET Framework SDK directory.
753
754 Return
755 ------
756 str
757 path
758 """
759 sdkdir = ''
760 for ver in self.NetFxSdkVersion:
761 loc = os.path.join(self.ri.netfx_sdk, ver)
762 sdkdir = self.ri.lookup(loc, 'kitsinstallationfolder')
763 if sdkdir:
764 break
765 return sdkdir
766
767 @property
768 def FrameworkDir32(self):
769 """
770 Microsoft .NET Framework 32bit directory.
771
772 Return
773 ------
774 str
775 path
776 """
777 # Default path
778 guess_fw = os.path.join(self.WinDir, r'Microsoft.NET\Framework')
779
780 # Try to get path from registry, if fail use default path
781 return self.ri.lookup(self.ri.vc, 'frameworkdir32') or guess_fw
782
783 @property
784 def FrameworkDir64(self):
785 """
786 Microsoft .NET Framework 64bit directory.
787
788 Return
789 ------
790 str
791 path
792 """
793 # Default path
794 guess_fw = os.path.join(self.WinDir, r'Microsoft.NET\Framework64')
795
796 # Try to get path from registry, if fail use default path
797 return self.ri.lookup(self.ri.vc, 'frameworkdir64') or guess_fw
798
799 @property
800 def FrameworkVersion32(self):
801 """
802 Microsoft .NET Framework 32bit versions.
803
804 Return
805 ------
806 tuple of str
807 versions
808 """
809 return self._find_dot_net_versions(32)
810
811 @property
812 def FrameworkVersion64(self):
813 """
814 Microsoft .NET Framework 64bit versions.
815
816 Return
817 ------
818 tuple of str
819 versions
820 """
821 return self._find_dot_net_versions(64)
822
823 def _find_dot_net_versions(self, bits):
824 """
825 Find Microsoft .NET Framework versions.
826
827 Parameters
828 ----------
829 bits: int
830 Platform number of bits: 32 or 64.
831
832 Return
833 ------
834 tuple of str
835 versions
836 """
837 # Find actual .NET version in registry
838 reg_ver = self.ri.lookup(self.ri.vc, 'frameworkver%d' % bits)
839 dot_net_dir = getattr(self, 'FrameworkDir%d' % bits)
840 ver = reg_ver or self._use_last_dir_name(dot_net_dir, 'v') or ''
841
842 # Set .NET versions for specified MSVC++ version
843 if self.vs_ver >= 12.0:
844 return ver, 'v4.0'
845 elif self.vs_ver >= 10.0:
846 return 'v4.0.30319' if ver.lower()[:2] != 'v4' else ver, 'v3.5'
847 elif self.vs_ver == 9.0:
848 return 'v3.5', 'v2.0.50727'
849 elif self.vs_ver == 8.0:
850 return 'v3.0', 'v2.0.50727'
851 return None
852
853 @staticmethod
854 def _use_last_dir_name(path, prefix=''):
855 """
856 Return name of the last dir in path or '' if no dir found.
857
858 Parameters
859 ----------
860 path: str
861 Use dirs in this path
862 prefix: str
863 Use only dirs starting by this prefix
864
865 Return
866 ------
867 str
868 name
869 """
870 matching_dirs = (
871 dir_name
872 for dir_name in reversed(os.listdir(path))
873 if os.path.isdir(os.path.join(path, dir_name))
874 and dir_name.startswith(prefix)
875 )
876 return next(matching_dirs, None) or ''
877
878
879 class EnvironmentInfo:
880 """
881 Return environment variables for specified Microsoft Visual C++ version
882 and platform : Lib, Include, Path and libpath.
883
884 This function is compatible with Microsoft Visual C++ 9.0 to 14.X.
885
886 Script created by analysing Microsoft environment configuration files like
887 "vcvars[...].bat", "SetEnv.Cmd", "vcbuildtools.bat", ...
888
889 Parameters
890 ----------
891 arch: str
892 Target architecture.
893 vc_ver: float
894 Required Microsoft Visual C++ version. If not set, autodetect the last
895 version.
896 vc_min_ver: float
897 Minimum Microsoft Visual C++ version.
898 """
899
900 # Variables and properties in this class use originals CamelCase variables
901 # names from Microsoft source files for more easy comparison.
902
903 def __init__(self, arch, vc_ver=None, vc_min_ver=0):
904 self.pi = PlatformInfo(arch)
905 self.ri = RegistryInfo(self.pi)
906 self.si = SystemInfo(self.ri, vc_ver)
907
908 if self.vc_ver < vc_min_ver:
909 err = 'No suitable Microsoft Visual C++ version found'
910 raise distutils.errors.DistutilsPlatformError(err)
911
912 @property
913 def vs_ver(self):
914 """
915 Microsoft Visual Studio.
916
917 Return
918 ------
919 float
920 version
921 """
922 return self.si.vs_ver
923
924 @property
925 def vc_ver(self):
926 """
927 Microsoft Visual C++ version.
928
929 Return
930 ------
931 float
932 version
933 """
934 return self.si.vc_ver
935
936 @property
937 def VSTools(self):
938 """
939 Microsoft Visual Studio Tools.
940
941 Return
942 ------
943 list of str
944 paths
945 """
946 paths = [r'Common7\IDE', r'Common7\Tools']
947
948 if self.vs_ver >= 14.0:
949 arch_subdir = self.pi.current_dir(hidex86=True, x64=True)
950 paths += [r'Common7\IDE\CommonExtensions\Microsoft\TestWindow']
951 paths += [r'Team Tools\Performance Tools']
952 paths += [r'Team Tools\Performance Tools%s' % arch_subdir]
953
954 return [os.path.join(self.si.VSInstallDir, path) for path in paths]
955
956 @property
957 def VCIncludes(self):
958 """
959 Microsoft Visual C++ & Microsoft Foundation Class Includes.
960
961 Return
962 ------
963 list of str
964 paths
965 """
966 return [
967 os.path.join(self.si.VCInstallDir, 'Include'),
968 os.path.join(self.si.VCInstallDir, r'ATLMFC\Include'),
969 ]
970
971 @property
972 def VCLibraries(self):
973 """
974 Microsoft Visual C++ & Microsoft Foundation Class Libraries.
975
976 Return
977 ------
978 list of str
979 paths
980 """
981 if self.vs_ver >= 15.0:
982 arch_subdir = self.pi.target_dir(x64=True)
983 else:
984 arch_subdir = self.pi.target_dir(hidex86=True)
985 paths = ['Lib%s' % arch_subdir, r'ATLMFC\Lib%s' % arch_subdir]
986
987 if self.vs_ver >= 14.0:
988 paths += [r'Lib\store%s' % arch_subdir]
989
990 return [os.path.join(self.si.VCInstallDir, path) for path in paths]
991
992 @property
993 def VCStoreRefs(self):
994 """
995 Microsoft Visual C++ store references Libraries.
996
997 Return
998 ------
999 list of str
1000 paths
1001 """
1002 if self.vs_ver < 14.0:
1003 return []
1004 return [os.path.join(self.si.VCInstallDir, r'Lib\store\references')]
1005
1006 @property
1007 def VCTools(self):
1008 """
1009 Microsoft Visual C++ Tools.
1010
1011 Return
1012 ------
1013 list of str
1014 paths
1015 """
1016 si = self.si
1017 tools = [os.path.join(si.VCInstallDir, 'VCPackages')]
1018
1019 forcex86 = True if self.vs_ver <= 10.0 else False
1020 arch_subdir = self.pi.cross_dir(forcex86)
1021 if arch_subdir:
1022 tools += [os.path.join(si.VCInstallDir, 'Bin%s' % arch_subdir)]
1023
1024 if self.vs_ver == 14.0:
1025 path = 'Bin%s' % self.pi.current_dir(hidex86=True)
1026 tools += [os.path.join(si.VCInstallDir, path)]
1027
1028 elif self.vs_ver >= 15.0:
1029 host_dir = (
1030 r'bin\HostX86%s' if self.pi.current_is_x86() else r'bin\HostX64%s'
1031 )
1032 tools += [
1033 os.path.join(si.VCInstallDir, host_dir % self.pi.target_dir(x64=True))
1034 ]
1035
1036 if self.pi.current_cpu != self.pi.target_cpu:
1037 tools += [
1038 os.path.join(
1039 si.VCInstallDir, host_dir % self.pi.current_dir(x64=True)
1040 )
1041 ]
1042
1043 else:
1044 tools += [os.path.join(si.VCInstallDir, 'Bin')]
1045
1046 return tools
1047
1048 @property
1049 def OSLibraries(self):
1050 """
1051 Microsoft Windows SDK Libraries.
1052
1053 Return
1054 ------
1055 list of str
1056 paths
1057 """
1058 if self.vs_ver <= 10.0:
1059 arch_subdir = self.pi.target_dir(hidex86=True, x64=True)
1060 return [os.path.join(self.si.WindowsSdkDir, 'Lib%s' % arch_subdir)]
1061
1062 else:
1063 arch_subdir = self.pi.target_dir(x64=True)
1064 lib = os.path.join(self.si.WindowsSdkDir, 'lib')
1065 libver = self._sdk_subdir
1066 return [os.path.join(lib, '%sum%s' % (libver, arch_subdir))]
1067
1068 @property
1069 def OSIncludes(self):
1070 """
1071 Microsoft Windows SDK Include.
1072
1073 Return
1074 ------
1075 list of str
1076 paths
1077 """
1078 include = os.path.join(self.si.WindowsSdkDir, 'include')
1079
1080 if self.vs_ver <= 10.0:
1081 return [include, os.path.join(include, 'gl')]
1082
1083 else:
1084 if self.vs_ver >= 14.0:
1085 sdkver = self._sdk_subdir
1086 else:
1087 sdkver = ''
1088 return [
1089 os.path.join(include, '%sshared' % sdkver),
1090 os.path.join(include, '%sum' % sdkver),
1091 os.path.join(include, '%swinrt' % sdkver),
1092 ]
1093
1094 @property
1095 def OSLibpath(self):
1096 """
1097 Microsoft Windows SDK Libraries Paths.
1098
1099 Return
1100 ------
1101 list of str
1102 paths
1103 """
1104 ref = os.path.join(self.si.WindowsSdkDir, 'References')
1105 libpath = []
1106
1107 if self.vs_ver <= 9.0:
1108 libpath += self.OSLibraries
1109
1110 if self.vs_ver >= 11.0:
1111 libpath += [os.path.join(ref, r'CommonConfiguration\Neutral')]
1112
1113 if self.vs_ver >= 14.0:
1114 libpath += [
1115 ref,
1116 os.path.join(self.si.WindowsSdkDir, 'UnionMetadata'),
1117 os.path.join(ref, 'Windows.Foundation.UniversalApiContract', '1.0.0.0'),
1118 os.path.join(ref, 'Windows.Foundation.FoundationContract', '1.0.0.0'),
1119 os.path.join(
1120 ref, 'Windows.Networking.Connectivity.WwanContract', '1.0.0.0'
1121 ),
1122 os.path.join(
1123 self.si.WindowsSdkDir,
1124 'ExtensionSDKs',
1125 'Microsoft.VCLibs',
1126 '%0.1f' % self.vs_ver,
1127 'References',
1128 'CommonConfiguration',
1129 'neutral',
1130 ),
1131 ]
1132 return libpath
1133
1134 @property
1135 def SdkTools(self):
1136 """
1137 Microsoft Windows SDK Tools.
1138
1139 Return
1140 ------
1141 list of str
1142 paths
1143 """
1144 return list(self._sdk_tools())
1145
1146 def _sdk_tools(self):
1147 """
1148 Microsoft Windows SDK Tools paths generator.
1149
1150 Return
1151 ------
1152 generator of str
1153 paths
1154 """
1155 if self.vs_ver < 15.0:
1156 bin_dir = 'Bin' if self.vs_ver <= 11.0 else r'Bin\x86'
1157 yield os.path.join(self.si.WindowsSdkDir, bin_dir)
1158
1159 if not self.pi.current_is_x86():
1160 arch_subdir = self.pi.current_dir(x64=True)
1161 path = 'Bin%s' % arch_subdir
1162 yield os.path.join(self.si.WindowsSdkDir, path)
1163
1164 if self.vs_ver in (10.0, 11.0):
1165 if self.pi.target_is_x86():
1166 arch_subdir = ''
1167 else:
1168 arch_subdir = self.pi.current_dir(hidex86=True, x64=True)
1169 path = r'Bin\NETFX 4.0 Tools%s' % arch_subdir
1170 yield os.path.join(self.si.WindowsSdkDir, path)
1171
1172 elif self.vs_ver >= 15.0:
1173 path = os.path.join(self.si.WindowsSdkDir, 'Bin')
1174 arch_subdir = self.pi.current_dir(x64=True)
1175 sdkver = self.si.WindowsSdkLastVersion
1176 yield os.path.join(path, '%s%s' % (sdkver, arch_subdir))
1177
1178 if self.si.WindowsSDKExecutablePath:
1179 yield self.si.WindowsSDKExecutablePath
1180
1181 @property
1182 def _sdk_subdir(self):
1183 """
1184 Microsoft Windows SDK version subdir.
1185
1186 Return
1187 ------
1188 str
1189 subdir
1190 """
1191 ucrtver = self.si.WindowsSdkLastVersion
1192 return ('%s\\' % ucrtver) if ucrtver else ''
1193
1194 @property
1195 def SdkSetup(self):
1196 """
1197 Microsoft Windows SDK Setup.
1198
1199 Return
1200 ------
1201 list of str
1202 paths
1203 """
1204 if self.vs_ver > 9.0:
1205 return []
1206
1207 return [os.path.join(self.si.WindowsSdkDir, 'Setup')]
1208
1209 @property
1210 def FxTools(self):
1211 """
1212 Microsoft .NET Framework Tools.
1213
1214 Return
1215 ------
1216 list of str
1217 paths
1218 """
1219 pi = self.pi
1220 si = self.si
1221
1222 if self.vs_ver <= 10.0:
1223 include32 = True
1224 include64 = not pi.target_is_x86() and not pi.current_is_x86()
1225 else:
1226 include32 = pi.target_is_x86() or pi.current_is_x86()
1227 include64 = pi.current_cpu == 'amd64' or pi.target_cpu == 'amd64'
1228
1229 tools = []
1230 if include32:
1231 tools += [
1232 os.path.join(si.FrameworkDir32, ver) for ver in si.FrameworkVersion32
1233 ]
1234 if include64:
1235 tools += [
1236 os.path.join(si.FrameworkDir64, ver) for ver in si.FrameworkVersion64
1237 ]
1238 return tools
1239
1240 @property
1241 def NetFxSDKLibraries(self):
1242 """
1243 Microsoft .Net Framework SDK Libraries.
1244
1245 Return
1246 ------
1247 list of str
1248 paths
1249 """
1250 if self.vs_ver < 14.0 or not self.si.NetFxSdkDir:
1251 return []
1252
1253 arch_subdir = self.pi.target_dir(x64=True)
1254 return [os.path.join(self.si.NetFxSdkDir, r'lib\um%s' % arch_subdir)]
1255
1256 @property
1257 def NetFxSDKIncludes(self):
1258 """
1259 Microsoft .Net Framework SDK Includes.
1260
1261 Return
1262 ------
1263 list of str
1264 paths
1265 """
1266 if self.vs_ver < 14.0 or not self.si.NetFxSdkDir:
1267 return []
1268
1269 return [os.path.join(self.si.NetFxSdkDir, r'include\um')]
1270
1271 @property
1272 def VsTDb(self):
1273 """
1274 Microsoft Visual Studio Team System Database.
1275
1276 Return
1277 ------
1278 list of str
1279 paths
1280 """
1281 return [os.path.join(self.si.VSInstallDir, r'VSTSDB\Deploy')]
1282
1283 @property
1284 def MSBuild(self):
1285 """
1286 Microsoft Build Engine.
1287
1288 Return
1289 ------
1290 list of str
1291 paths
1292 """
1293 if self.vs_ver < 12.0:
1294 return []
1295 elif self.vs_ver < 15.0:
1296 base_path = self.si.ProgramFilesx86
1297 arch_subdir = self.pi.current_dir(hidex86=True)
1298 else:
1299 base_path = self.si.VSInstallDir
1300 arch_subdir = ''
1301
1302 path = r'MSBuild\%0.1f\bin%s' % (self.vs_ver, arch_subdir)
1303 build = [os.path.join(base_path, path)]
1304
1305 if self.vs_ver >= 15.0:
1306 # Add Roslyn C# & Visual Basic Compiler
1307 build += [os.path.join(base_path, path, 'Roslyn')]
1308
1309 return build
1310
1311 @property
1312 def HTMLHelpWorkshop(self):
1313 """
1314 Microsoft HTML Help Workshop.
1315
1316 Return
1317 ------
1318 list of str
1319 paths
1320 """
1321 if self.vs_ver < 11.0:
1322 return []
1323
1324 return [os.path.join(self.si.ProgramFilesx86, 'HTML Help Workshop')]
1325
1326 @property
1327 def UCRTLibraries(self):
1328 """
1329 Microsoft Universal C Runtime SDK Libraries.
1330
1331 Return
1332 ------
1333 list of str
1334 paths
1335 """
1336 if self.vs_ver < 14.0:
1337 return []
1338
1339 arch_subdir = self.pi.target_dir(x64=True)
1340 lib = os.path.join(self.si.UniversalCRTSdkDir, 'lib')
1341 ucrtver = self._ucrt_subdir
1342 return [os.path.join(lib, '%sucrt%s' % (ucrtver, arch_subdir))]
1343
1344 @property
1345 def UCRTIncludes(self):
1346 """
1347 Microsoft Universal C Runtime SDK Include.
1348
1349 Return
1350 ------
1351 list of str
1352 paths
1353 """
1354 if self.vs_ver < 14.0:
1355 return []
1356
1357 include = os.path.join(self.si.UniversalCRTSdkDir, 'include')
1358 return [os.path.join(include, '%sucrt' % self._ucrt_subdir)]
1359
1360 @property
1361 def _ucrt_subdir(self):
1362 """
1363 Microsoft Universal C Runtime SDK version subdir.
1364
1365 Return
1366 ------
1367 str
1368 subdir
1369 """
1370 ucrtver = self.si.UniversalCRTSdkLastVersion
1371 return ('%s\\' % ucrtver) if ucrtver else ''
1372
1373 @property
1374 def FSharp(self):
1375 """
1376 Microsoft Visual F#.
1377
1378 Return
1379 ------
1380 list of str
1381 paths
1382 """
1383 if 11.0 > self.vs_ver > 12.0:
1384 return []
1385
1386 return [self.si.FSharpInstallDir]
1387
1388 @property
1389 def VCRuntimeRedist(self) -> str | None:
1390 """
1391 Microsoft Visual C++ runtime redistributable dll.
1392
1393 Returns the first suitable path found or None.
1394 """
1395 vcruntime = 'vcruntime%d0.dll' % self.vc_ver
1396 arch_subdir = self.pi.target_dir(x64=True).strip('\\')
1397
1398 # Installation prefixes candidates
1399 prefixes = []
1400 tools_path = self.si.VCInstallDir
1401 redist_path = os.path.dirname(tools_path.replace(r'\Tools', r'\Redist'))
1402 if os.path.isdir(redist_path):
1403 # Redist version may not be exactly the same as tools
1404 redist_path = os.path.join(redist_path, os.listdir(redist_path)[-1])
1405 prefixes += [redist_path, os.path.join(redist_path, 'onecore')]
1406
1407 prefixes += [os.path.join(tools_path, 'redist')] # VS14 legacy path
1408
1409 # CRT directory
1410 crt_dirs = (
1411 'Microsoft.VC%d.CRT' % (self.vc_ver * 10),
1412 # Sometime store in directory with VS version instead of VC
1413 'Microsoft.VC%d.CRT' % (int(self.vs_ver) * 10),
1414 )
1415
1416 # vcruntime path
1417 candidate_paths = (
1418 os.path.join(prefix, arch_subdir, crt_dir, vcruntime)
1419 for (prefix, crt_dir) in itertools.product(prefixes, crt_dirs)
1420 )
1421 return next(filter(os.path.isfile, candidate_paths), None) # type: ignore[arg-type] #python/mypy#12682
1422
1423 def return_env(self, exists=True):
1424 """
1425 Return environment dict.
1426
1427 Parameters
1428 ----------
1429 exists: bool
1430 It True, only return existing paths.
1431
1432 Return
1433 ------
1434 dict
1435 environment
1436 """
1437 env = dict(
1438 include=self._build_paths(
1439 'include',
1440 [
1441 self.VCIncludes,
1442 self.OSIncludes,
1443 self.UCRTIncludes,
1444 self.NetFxSDKIncludes,
1445 ],
1446 exists,
1447 ),
1448 lib=self._build_paths(
1449 'lib',
1450 [
1451 self.VCLibraries,
1452 self.OSLibraries,
1453 self.FxTools,
1454 self.UCRTLibraries,
1455 self.NetFxSDKLibraries,
1456 ],
1457 exists,
1458 ),
1459 libpath=self._build_paths(
1460 'libpath',
1461 [self.VCLibraries, self.FxTools, self.VCStoreRefs, self.OSLibpath],
1462 exists,
1463 ),
1464 path=self._build_paths(
1465 'path',
1466 [
1467 self.VCTools,
1468 self.VSTools,
1469 self.VsTDb,
1470 self.SdkTools,
1471 self.SdkSetup,
1472 self.FxTools,
1473 self.MSBuild,
1474 self.HTMLHelpWorkshop,
1475 self.FSharp,
1476 ],
1477 exists,
1478 ),
1479 )
1480 if self.vs_ver >= 14 and self.VCRuntimeRedist:
1481 env['py_vcruntime_redist'] = self.VCRuntimeRedist
1482 return env
1483
1484 def _build_paths(self, name, spec_path_lists, exists):
1485 """
1486 Given an environment variable name and specified paths,
1487 return a pathsep-separated string of paths containing
1488 unique, extant, directories from those paths and from
1489 the environment variable. Raise an error if no paths
1490 are resolved.
1491
1492 Parameters
1493 ----------
1494 name: str
1495 Environment variable name
1496 spec_path_lists: list of str
1497 Paths
1498 exists: bool
1499 It True, only return existing paths.
1500
1501 Return
1502 ------
1503 str
1504 Pathsep-separated paths
1505 """
1506 # flatten spec_path_lists
1507 spec_paths = itertools.chain.from_iterable(spec_path_lists)
1508 env_paths = environ.get(name, '').split(os.pathsep)
1509 paths = itertools.chain(spec_paths, env_paths)
1510 extant_paths = list(filter(os.path.isdir, paths)) if exists else paths
1511 if not extant_paths:
1512 msg = "%s environment variable is empty" % name.upper()
1513 raise distutils.errors.DistutilsPlatformError(msg)
1514 unique_paths = unique_everseen(extant_paths)
1515 return os.pathsep.join(unique_paths)