comparison env/lib/python3.9/site-packages/setuptools/msvc.py @ 0:4f3585e2f14b draft default tip

"planemo upload commit 60cee0fc7c0cda8592644e1aad72851dec82c959"
author shellac
date Mon, 22 Mar 2021 18:12:50 +0000
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:4f3585e2f14b
1 """
2 Improved support for Microsoft Visual C++ compilers.
3
4 Known supported compilers:
5 --------------------------
6 Microsoft 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)
10
11 Microsoft Visual C++ 10.0:
12 Microsoft Windows SDK 7.1 (x86, x64, ia64)
13
14 Microsoft 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)
18
19 This may also support compilers shipped with compatible Visual Studio versions.
20 """
21
22 import json
23 from io import open
24 from os import listdir, pathsep
25 from os.path import join, isfile, isdir, dirname
26 import sys
27 import contextlib
28 import platform
29 import itertools
30 import subprocess
31 import distutils.errors
32 from setuptools.extern.packaging.version import LegacyVersion
33
34 from .monkey import get_unpatched
35
36 if platform.system() == 'Windows':
37 import winreg
38 from os import environ
39 else:
40 # Mock winreg and environ so the module can be imported on this platform.
41
42 class winreg:
43 HKEY_USERS = None
44 HKEY_CURRENT_USER = None
45 HKEY_LOCAL_MACHINE = None
46 HKEY_CLASSES_ROOT = None
47
48 environ = dict()
49
50 _msvc9_suppress_errors = (
51 # msvc9compiler isn't available on some platforms
52 ImportError,
53
54 # msvc9compiler raises DistutilsPlatformError in some
55 # environments. See #1118.
56 distutils.errors.DistutilsPlatformError,
57 )
58
59 try:
60 from distutils.msvc9compiler import Reg
61 except _msvc9_suppress_errors:
62 pass
63
64
65 def 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).
70
71 Fall back to original behavior when the standalone compiler is not
72 available.
73
74 Redirect the path of "vcvarsall.bat".
75
76 Parameters
77 ----------
78 version: float
79 Required Microsoft Visual C++ version.
80
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
98
99 if productdir:
100 vcvarsall = join(productdir, "vcvarsall.bat")
101 if isfile(vcvarsall):
102 return vcvarsall
103
104 return get_unpatched(msvc9_find_vcvarsall)(version)
105
106
107 def 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.
111
112 Set environment without use of "vcvarsall.bat".
113
114 Parameters
115 ----------
116 ver: float
117 Required Microsoft Visual C++ version.
118 arch: str
119 Target architecture.
120
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
136
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
143
144
145 def _msvc14_find_vc2015():
146 """Python 3.8 "distutils/_msvccompiler.py" backport"""
147 try:
148 key = winreg.OpenKey(
149 winreg.HKEY_LOCAL_MACHINE,
150 r"Software\Microsoft\VisualStudio\SxS\VC7",
151 0,
152 winreg.KEY_READ | winreg.KEY_WOW64_32KEY
153 )
154 except OSError:
155 return None, None
156
157 best_version = 0
158 best_dir = None
159 with key:
160 for i in itertools.count():
161 try:
162 v, vc_dir, vt = winreg.EnumValue(key, i)
163 except OSError:
164 break
165 if v and vt == winreg.REG_SZ and isdir(vc_dir):
166 try:
167 version = int(float(v))
168 except (ValueError, TypeError):
169 continue
170 if version >= 14 and version > best_version:
171 best_version, best_dir = version, vc_dir
172 return best_version, best_dir
173
174
175 def _msvc14_find_vc2017():
176 """Python 3.8 "distutils/_msvccompiler.py" backport
177
178 Returns "15, path" based on the result of invoking vswhere.exe
179 If no install is found, returns "None, None"
180
181 The version is returned to avoid unnecessarily changing the function
182 result. It may be ignored when the path is not None.
183
184 If vswhere.exe is not available, by definition, VS 2017 is not
185 installed.
186 """
187 root = environ.get("ProgramFiles(x86)") or environ.get("ProgramFiles")
188 if not root:
189 return None, None
190
191 try:
192 path = subprocess.check_output([
193 join(root, "Microsoft Visual Studio", "Installer", "vswhere.exe"),
194 "-latest",
195 "-prerelease",
196 "-requires", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64",
197 "-property", "installationPath",
198 "-products", "*",
199 ]).decode(encoding="mbcs", errors="strict").strip()
200 except (subprocess.CalledProcessError, OSError, UnicodeDecodeError):
201 return None, None
202
203 path = join(path, "VC", "Auxiliary", "Build")
204 if isdir(path):
205 return 15, path
206
207 return None, None
208
209
210 PLAT_SPEC_TO_RUNTIME = {
211 'x86': 'x86',
212 'x86_amd64': 'x64',
213 'x86_arm': 'arm',
214 'x86_arm64': 'arm64'
215 }
216
217
218 def _msvc14_find_vcvarsall(plat_spec):
219 """Python 3.8 "distutils/_msvccompiler.py" backport"""
220 _, best_dir = _msvc14_find_vc2017()
221 vcruntime = None
222
223 if plat_spec in PLAT_SPEC_TO_RUNTIME:
224 vcruntime_plat = PLAT_SPEC_TO_RUNTIME[plat_spec]
225 else:
226 vcruntime_plat = 'x64' if 'amd64' in plat_spec else 'x86'
227
228 if best_dir:
229 vcredist = join(best_dir, "..", "..", "redist", "MSVC", "**",
230 vcruntime_plat, "Microsoft.VC14*.CRT",
231 "vcruntime140.dll")
232 try:
233 import glob
234 vcruntime = glob.glob(vcredist, recursive=True)[-1]
235 except (ImportError, OSError, LookupError):
236 vcruntime = None
237
238 if not best_dir:
239 best_version, best_dir = _msvc14_find_vc2015()
240 if best_version:
241 vcruntime = join(best_dir, 'redist', vcruntime_plat,
242 "Microsoft.VC140.CRT", "vcruntime140.dll")
243
244 if not best_dir:
245 return None, None
246
247 vcvarsall = join(best_dir, "vcvarsall.bat")
248 if not isfile(vcvarsall):
249 return None, None
250
251 if not vcruntime or not isfile(vcruntime):
252 vcruntime = None
253
254 return vcvarsall, vcruntime
255
256
257 def _msvc14_get_vc_env(plat_spec):
258 """Python 3.8 "distutils/_msvccompiler.py" backport"""
259 if "DISTUTILS_USE_SDK" in environ:
260 return {
261 key.lower(): value
262 for key, value in environ.items()
263 }
264
265 vcvarsall, vcruntime = _msvc14_find_vcvarsall(plat_spec)
266 if not vcvarsall:
267 raise distutils.errors.DistutilsPlatformError(
268 "Unable to find vcvarsall.bat"
269 )
270
271 try:
272 out = subprocess.check_output(
273 'cmd /u /c "{}" {} && set'.format(vcvarsall, plat_spec),
274 stderr=subprocess.STDOUT,
275 ).decode('utf-16le', errors='replace')
276 except subprocess.CalledProcessError as exc:
277 raise distutils.errors.DistutilsPlatformError(
278 "Error executing {}".format(exc.cmd)
279 ) from exc
280
281 env = {
282 key.lower(): value
283 for key, _, value in
284 (line.partition('=') for line in out.splitlines())
285 if key and value
286 }
287
288 if vcruntime:
289 env['py_vcruntime_redist'] = vcruntime
290 return env
291
292
293 def msvc14_get_vc_env(plat_spec):
294 """
295 Patched "distutils._msvccompiler._get_vc_env" for support extra
296 Microsoft Visual C++ 14.X compilers.
297
298 Set environment without use of "vcvarsall.bat".
299
300 Parameters
301 ----------
302 plat_spec: str
303 Target architecture.
304
305 Return
306 ------
307 dict
308 environment
309 """
310
311 # Always use backport from CPython 3.8
312 try:
313 return _msvc14_get_vc_env(plat_spec)
314 except distutils.errors.DistutilsPlatformError as exc:
315 _augment_exception(exc, 14.0)
316 raise
317
318
319 def msvc14_gen_lib_options(*args, **kwargs):
320 """
321 Patched "distutils._msvccompiler.gen_lib_options" for fix
322 compatibility between "numpy.distutils" and "distutils._msvccompiler"
323 (for Numpy < 1.11.2)
324 """
325 if "numpy.distutils" in sys.modules:
326 import numpy as np
327 if LegacyVersion(np.__version__) < LegacyVersion('1.11.2'):
328 return np.distutils.ccompiler.gen_lib_options(*args, **kwargs)
329 return get_unpatched(msvc14_gen_lib_options)(*args, **kwargs)
330
331
332 def _augment_exception(exc, version, arch=''):
333 """
334 Add details to the exception message to help guide the user
335 as to what action will resolve it.
336 """
337 # Error if MSVC++ directory not found or environment not set
338 message = exc.args[0]
339
340 if "vcvarsall" in message.lower() or "visual c" in message.lower():
341 # Special error message if MSVC++ not installed
342 tmpl = 'Microsoft Visual C++ {version:0.1f} or greater is required.'
343 message = tmpl.format(**locals())
344 msdownload = 'www.microsoft.com/download/details.aspx?id=%d'
345 if version == 9.0:
346 if arch.lower().find('ia64') > -1:
347 # For VC++ 9.0, if IA64 support is needed, redirect user
348 # to Windows SDK 7.0.
349 # Note: No download link available from Microsoft.
350 message += ' Get it with "Microsoft Windows SDK 7.0"'
351 else:
352 # For VC++ 9.0 redirect user to Vc++ for Python 2.7 :
353 # This redirection link is maintained by Microsoft.
354 # Contact vspython@microsoft.com if it needs updating.
355 message += ' Get it from http://aka.ms/vcpython27'
356 elif version == 10.0:
357 # For VC++ 10.0 Redirect user to Windows SDK 7.1
358 message += ' Get it with "Microsoft Windows SDK 7.1": '
359 message += msdownload % 8279
360 elif version >= 14.0:
361 # For VC++ 14.X Redirect user to latest Visual C++ Build Tools
362 message += (' Get it with "Microsoft C++ Build Tools": '
363 r'https://visualstudio.microsoft.com'
364 r'/visual-cpp-build-tools/')
365
366 exc.args = (message, )
367
368
369 class PlatformInfo:
370 """
371 Current and Target Architectures information.
372
373 Parameters
374 ----------
375 arch: str
376 Target architecture.
377 """
378 current_cpu = environ.get('processor_architecture', '').lower()
379
380 def __init__(self, arch):
381 self.arch = arch.lower().replace('x64', 'amd64')
382
383 @property
384 def target_cpu(self):
385 """
386 Return Target CPU architecture.
387
388 Return
389 ------
390 str
391 Target CPU
392 """
393 return self.arch[self.arch.find('_') + 1:]
394
395 def target_is_x86(self):
396 """
397 Return True if target CPU is x86 32 bits..
398
399 Return
400 ------
401 bool
402 CPU is x86 32 bits
403 """
404 return self.target_cpu == 'x86'
405
406 def current_is_x86(self):
407 """
408 Return True if current CPU is x86 32 bits..
409
410 Return
411 ------
412 bool
413 CPU is x86 32 bits
414 """
415 return self.current_cpu == 'x86'
416
417 def current_dir(self, hidex86=False, x64=False):
418 """
419 Current platform specific subfolder.
420
421 Parameters
422 ----------
423 hidex86: bool
424 return '' and not '\x86' if architecture is x86.
425 x64: bool
426 return '\x64' and not '\amd64' if architecture is amd64.
427
428 Return
429 ------
430 str
431 subfolder: '\target', or '' (see hidex86 parameter)
432 """
433 return (
434 '' if (self.current_cpu == 'x86' and hidex86) else
435 r'\x64' if (self.current_cpu == 'amd64' and x64) else
436 r'\%s' % self.current_cpu
437 )
438
439 def target_dir(self, hidex86=False, x64=False):
440 r"""
441 Target platform specific subfolder.
442
443 Parameters
444 ----------
445 hidex86: bool
446 return '' and not '\x86' if architecture is x86.
447 x64: bool
448 return '\x64' and not '\amd64' if architecture is amd64.
449
450 Return
451 ------
452 str
453 subfolder: '\current', or '' (see hidex86 parameter)
454 """
455 return (
456 '' if (self.target_cpu == 'x86' and hidex86) else
457 r'\x64' if (self.target_cpu == 'amd64' and x64) else
458 r'\%s' % self.target_cpu
459 )
460
461 def cross_dir(self, forcex86=False):
462 r"""
463 Cross platform specific subfolder.
464
465 Parameters
466 ----------
467 forcex86: bool
468 Use 'x86' as current architecture even if current architecture is
469 not x86.
470
471 Return
472 ------
473 str
474 subfolder: '' if target architecture is current architecture,
475 '\current_target' if not.
476 """
477 current = 'x86' if forcex86 else self.current_cpu
478 return (
479 '' if self.target_cpu == current else
480 self.target_dir().replace('\\', '\\%s_' % current)
481 )
482
483
484 class RegistryInfo:
485 """
486 Microsoft Visual Studio related registry information.
487
488 Parameters
489 ----------
490 platform_info: PlatformInfo
491 "PlatformInfo" instance.
492 """
493 HKEYS = (winreg.HKEY_USERS,
494 winreg.HKEY_CURRENT_USER,
495 winreg.HKEY_LOCAL_MACHINE,
496 winreg.HKEY_CLASSES_ROOT)
497
498 def __init__(self, platform_info):
499 self.pi = platform_info
500
501 @property
502 def visualstudio(self):
503 """
504 Microsoft Visual Studio root registry key.
505
506 Return
507 ------
508 str
509 Registry key
510 """
511 return 'VisualStudio'
512
513 @property
514 def sxs(self):
515 """
516 Microsoft Visual Studio SxS registry key.
517
518 Return
519 ------
520 str
521 Registry key
522 """
523 return join(self.visualstudio, 'SxS')
524
525 @property
526 def vc(self):
527 """
528 Microsoft Visual C++ VC7 registry key.
529
530 Return
531 ------
532 str
533 Registry key
534 """
535 return join(self.sxs, 'VC7')
536
537 @property
538 def vs(self):
539 """
540 Microsoft Visual Studio VS7 registry key.
541
542 Return
543 ------
544 str
545 Registry key
546 """
547 return join(self.sxs, 'VS7')
548
549 @property
550 def vc_for_python(self):
551 """
552 Microsoft Visual C++ for Python registry key.
553
554 Return
555 ------
556 str
557 Registry key
558 """
559 return r'DevDiv\VCForPython'
560
561 @property
562 def microsoft_sdk(self):
563 """
564 Microsoft SDK registry key.
565
566 Return
567 ------
568 str
569 Registry key
570 """
571 return 'Microsoft SDKs'
572
573 @property
574 def windows_sdk(self):
575 """
576 Microsoft Windows/Platform SDK registry key.
577
578 Return
579 ------
580 str
581 Registry key
582 """
583 return join(self.microsoft_sdk, 'Windows')
584
585 @property
586 def netfx_sdk(self):
587 """
588 Microsoft .NET Framework SDK registry key.
589
590 Return
591 ------
592 str
593 Registry key
594 """
595 return join(self.microsoft_sdk, 'NETFXSDK')
596
597 @property
598 def windows_kits_roots(self):
599 """
600 Microsoft Windows Kits Roots registry key.
601
602 Return
603 ------
604 str
605 Registry key
606 """
607 return r'Windows Kits\Installed Roots'
608
609 def microsoft(self, key, x86=False):
610 """
611 Return key in Microsoft software registry.
612
613 Parameters
614 ----------
615 key: str
616 Registry key path where look.
617 x86: str
618 Force x86 software registry.
619
620 Return
621 ------
622 str
623 Registry key
624 """
625 node64 = '' if self.pi.current_is_x86() or x86 else 'Wow6432Node'
626 return join('Software', node64, 'Microsoft', key)
627
628 def lookup(self, key, name):
629 """
630 Look for values in registry in Microsoft software registry.
631
632 Parameters
633 ----------
634 key: str
635 Registry key path where look.
636 name: str
637 Value name to find.
638
639 Return
640 ------
641 str
642 value
643 """
644 key_read = winreg.KEY_READ
645 openkey = winreg.OpenKey
646 closekey = winreg.CloseKey
647 ms = self.microsoft
648 for hkey in self.HKEYS:
649 bkey = None
650 try:
651 bkey = openkey(hkey, ms(key), 0, key_read)
652 except (OSError, IOError):
653 if not self.pi.current_is_x86():
654 try:
655 bkey = openkey(hkey, ms(key, True), 0, key_read)
656 except (OSError, IOError):
657 continue
658 else:
659 continue
660 try:
661 return winreg.QueryValueEx(bkey, name)[0]
662 except (OSError, IOError):
663 pass
664 finally:
665 if bkey:
666 closekey(bkey)
667
668
669 class SystemInfo:
670 """
671 Microsoft Windows and Visual Studio related system information.
672
673 Parameters
674 ----------
675 registry_info: RegistryInfo
676 "RegistryInfo" instance.
677 vc_ver: float
678 Required Microsoft Visual C++ version.
679 """
680
681 # Variables and properties in this class use originals CamelCase variables
682 # names from Microsoft source files for more easy comparison.
683 WinDir = environ.get('WinDir', '')
684 ProgramFiles = environ.get('ProgramFiles', '')
685 ProgramFilesx86 = environ.get('ProgramFiles(x86)', ProgramFiles)
686
687 def __init__(self, registry_info, vc_ver=None):
688 self.ri = registry_info
689 self.pi = self.ri.pi
690
691 self.known_vs_paths = self.find_programdata_vs_vers()
692
693 # Except for VS15+, VC version is aligned with VS version
694 self.vs_ver = self.vc_ver = (
695 vc_ver or self._find_latest_available_vs_ver())
696
697 def _find_latest_available_vs_ver(self):
698 """
699 Find the latest VC version
700
701 Return
702 ------
703 float
704 version
705 """
706 reg_vc_vers = self.find_reg_vs_vers()
707
708 if not (reg_vc_vers or self.known_vs_paths):
709 raise distutils.errors.DistutilsPlatformError(
710 'No Microsoft Visual C++ version found')
711
712 vc_vers = set(reg_vc_vers)
713 vc_vers.update(self.known_vs_paths)
714 return sorted(vc_vers)[-1]
715
716 def find_reg_vs_vers(self):
717 """
718 Find Microsoft Visual Studio versions available in registry.
719
720 Return
721 ------
722 list of float
723 Versions
724 """
725 ms = self.ri.microsoft
726 vckeys = (self.ri.vc, self.ri.vc_for_python, self.ri.vs)
727 vs_vers = []
728 for hkey, key in itertools.product(self.ri.HKEYS, vckeys):
729 try:
730 bkey = winreg.OpenKey(hkey, ms(key), 0, winreg.KEY_READ)
731 except (OSError, IOError):
732 continue
733 with bkey:
734 subkeys, values, _ = winreg.QueryInfoKey(bkey)
735 for i in range(values):
736 with contextlib.suppress(ValueError):
737 ver = float(winreg.EnumValue(bkey, i)[0])
738 if ver not in vs_vers:
739 vs_vers.append(ver)
740 for i in range(subkeys):
741 with contextlib.suppress(ValueError):
742 ver = float(winreg.EnumKey(bkey, i))
743 if ver not in vs_vers:
744 vs_vers.append(ver)
745 return sorted(vs_vers)
746
747 def find_programdata_vs_vers(self):
748 r"""
749 Find Visual studio 2017+ versions from information in
750 "C:\ProgramData\Microsoft\VisualStudio\Packages\_Instances".
751
752 Return
753 ------
754 dict
755 float version as key, path as value.
756 """
757 vs_versions = {}
758 instances_dir = \
759 r'C:\ProgramData\Microsoft\VisualStudio\Packages\_Instances'
760
761 try:
762 hashed_names = listdir(instances_dir)
763
764 except (OSError, IOError):
765 # Directory not exists with all Visual Studio versions
766 return vs_versions
767
768 for name in hashed_names:
769 try:
770 # Get VS installation path from "state.json" file
771 state_path = join(instances_dir, name, 'state.json')
772 with open(state_path, 'rt', encoding='utf-8') as state_file:
773 state = json.load(state_file)
774 vs_path = state['installationPath']
775
776 # Raises OSError if this VS installation does not contain VC
777 listdir(join(vs_path, r'VC\Tools\MSVC'))
778
779 # Store version and path
780 vs_versions[self._as_float_version(
781 state['installationVersion'])] = vs_path
782
783 except (OSError, IOError, KeyError):
784 # Skip if "state.json" file is missing or bad format
785 continue
786
787 return vs_versions
788
789 @staticmethod
790 def _as_float_version(version):
791 """
792 Return a string version as a simplified float version (major.minor)
793
794 Parameters
795 ----------
796 version: str
797 Version.
798
799 Return
800 ------
801 float
802 version
803 """
804 return float('.'.join(version.split('.')[:2]))
805
806 @property
807 def VSInstallDir(self):
808 """
809 Microsoft Visual Studio directory.
810
811 Return
812 ------
813 str
814 path
815 """
816 # Default path
817 default = join(self.ProgramFilesx86,
818 'Microsoft Visual Studio %0.1f' % self.vs_ver)
819
820 # Try to get path from registry, if fail use default path
821 return self.ri.lookup(self.ri.vs, '%0.1f' % self.vs_ver) or default
822
823 @property
824 def VCInstallDir(self):
825 """
826 Microsoft Visual C++ directory.
827
828 Return
829 ------
830 str
831 path
832 """
833 path = self._guess_vc() or self._guess_vc_legacy()
834
835 if not isdir(path):
836 msg = 'Microsoft Visual C++ directory not found'
837 raise distutils.errors.DistutilsPlatformError(msg)
838
839 return path
840
841 def _guess_vc(self):
842 """
843 Locate Visual C++ for VS2017+.
844
845 Return
846 ------
847 str
848 path
849 """
850 if self.vs_ver <= 14.0:
851 return ''
852
853 try:
854 # First search in known VS paths
855 vs_dir = self.known_vs_paths[self.vs_ver]
856 except KeyError:
857 # Else, search with path from registry
858 vs_dir = self.VSInstallDir
859
860 guess_vc = join(vs_dir, r'VC\Tools\MSVC')
861
862 # Subdir with VC exact version as name
863 try:
864 # Update the VC version with real one instead of VS version
865 vc_ver = listdir(guess_vc)[-1]
866 self.vc_ver = self._as_float_version(vc_ver)
867 return join(guess_vc, vc_ver)
868 except (OSError, IOError, IndexError):
869 return ''
870
871 def _guess_vc_legacy(self):
872 """
873 Locate Visual C++ for versions prior to 2017.
874
875 Return
876 ------
877 str
878 path
879 """
880 default = join(self.ProgramFilesx86,
881 r'Microsoft Visual Studio %0.1f\VC' % self.vs_ver)
882
883 # Try to get "VC++ for Python" path from registry as default path
884 reg_path = join(self.ri.vc_for_python, '%0.1f' % self.vs_ver)
885 python_vc = self.ri.lookup(reg_path, 'installdir')
886 default_vc = join(python_vc, 'VC') if python_vc else default
887
888 # Try to get path from registry, if fail use default path
889 return self.ri.lookup(self.ri.vc, '%0.1f' % self.vs_ver) or default_vc
890
891 @property
892 def WindowsSdkVersion(self):
893 """
894 Microsoft Windows SDK versions for specified MSVC++ version.
895
896 Return
897 ------
898 tuple of str
899 versions
900 """
901 if self.vs_ver <= 9.0:
902 return '7.0', '6.1', '6.0a'
903 elif self.vs_ver == 10.0:
904 return '7.1', '7.0a'
905 elif self.vs_ver == 11.0:
906 return '8.0', '8.0a'
907 elif self.vs_ver == 12.0:
908 return '8.1', '8.1a'
909 elif self.vs_ver >= 14.0:
910 return '10.0', '8.1'
911
912 @property
913 def WindowsSdkLastVersion(self):
914 """
915 Microsoft Windows SDK last version.
916
917 Return
918 ------
919 str
920 version
921 """
922 return self._use_last_dir_name(join(self.WindowsSdkDir, 'lib'))
923
924 @property # noqa: C901
925 def WindowsSdkDir(self): # noqa: C901 # is too complex (12) # FIXME
926 """
927 Microsoft Windows SDK directory.
928
929 Return
930 ------
931 str
932 path
933 """
934 sdkdir = ''
935 for ver in self.WindowsSdkVersion:
936 # Try to get it from registry
937 loc = join(self.ri.windows_sdk, 'v%s' % ver)
938 sdkdir = self.ri.lookup(loc, 'installationfolder')
939 if sdkdir:
940 break
941 if not sdkdir or not isdir(sdkdir):
942 # Try to get "VC++ for Python" version from registry
943 path = join(self.ri.vc_for_python, '%0.1f' % self.vc_ver)
944 install_base = self.ri.lookup(path, 'installdir')
945 if install_base:
946 sdkdir = join(install_base, 'WinSDK')
947 if not sdkdir or not isdir(sdkdir):
948 # If fail, use default new path
949 for ver in self.WindowsSdkVersion:
950 intver = ver[:ver.rfind('.')]
951 path = r'Microsoft SDKs\Windows Kits\%s' % intver
952 d = join(self.ProgramFiles, path)
953 if isdir(d):
954 sdkdir = d
955 if not sdkdir or not isdir(sdkdir):
956 # If fail, use default old path
957 for ver in self.WindowsSdkVersion:
958 path = r'Microsoft SDKs\Windows\v%s' % ver
959 d = join(self.ProgramFiles, path)
960 if isdir(d):
961 sdkdir = d
962 if not sdkdir:
963 # If fail, use Platform SDK
964 sdkdir = join(self.VCInstallDir, 'PlatformSDK')
965 return sdkdir
966
967 @property
968 def WindowsSDKExecutablePath(self):
969 """
970 Microsoft Windows SDK executable directory.
971
972 Return
973 ------
974 str
975 path
976 """
977 # Find WinSDK NetFx Tools registry dir name
978 if self.vs_ver <= 11.0:
979 netfxver = 35
980 arch = ''
981 else:
982 netfxver = 40
983 hidex86 = True if self.vs_ver <= 12.0 else False
984 arch = self.pi.current_dir(x64=True, hidex86=hidex86)
985 fx = 'WinSDK-NetFx%dTools%s' % (netfxver, arch.replace('\\', '-'))
986
987 # list all possibles registry paths
988 regpaths = []
989 if self.vs_ver >= 14.0:
990 for ver in self.NetFxSdkVersion:
991 regpaths += [join(self.ri.netfx_sdk, ver, fx)]
992
993 for ver in self.WindowsSdkVersion:
994 regpaths += [join(self.ri.windows_sdk, 'v%sA' % ver, fx)]
995
996 # Return installation folder from the more recent path
997 for path in regpaths:
998 execpath = self.ri.lookup(path, 'installationfolder')
999 if execpath:
1000 return execpath
1001
1002 @property
1003 def FSharpInstallDir(self):
1004 """
1005 Microsoft Visual F# directory.
1006
1007 Return
1008 ------
1009 str
1010 path
1011 """
1012 path = join(self.ri.visualstudio, r'%0.1f\Setup\F#' % self.vs_ver)
1013 return self.ri.lookup(path, 'productdir') or ''
1014
1015 @property
1016 def UniversalCRTSdkDir(self):
1017 """
1018 Microsoft Universal CRT SDK directory.
1019
1020 Return
1021 ------
1022 str
1023 path
1024 """
1025 # Set Kit Roots versions for specified MSVC++ version
1026 vers = ('10', '81') if self.vs_ver >= 14.0 else ()
1027
1028 # Find path of the more recent Kit
1029 for ver in vers:
1030 sdkdir = self.ri.lookup(self.ri.windows_kits_roots,
1031 'kitsroot%s' % ver)
1032 if sdkdir:
1033 return sdkdir or ''
1034
1035 @property
1036 def UniversalCRTSdkLastVersion(self):
1037 """
1038 Microsoft Universal C Runtime SDK last version.
1039
1040 Return
1041 ------
1042 str
1043 version
1044 """
1045 return self._use_last_dir_name(join(self.UniversalCRTSdkDir, 'lib'))
1046
1047 @property
1048 def NetFxSdkVersion(self):
1049 """
1050 Microsoft .NET Framework SDK versions.
1051
1052 Return
1053 ------
1054 tuple of str
1055 versions
1056 """
1057 # Set FxSdk versions for specified VS version
1058 return (('4.7.2', '4.7.1', '4.7',
1059 '4.6.2', '4.6.1', '4.6',
1060 '4.5.2', '4.5.1', '4.5')
1061 if self.vs_ver >= 14.0 else ())
1062
1063 @property
1064 def NetFxSdkDir(self):
1065 """
1066 Microsoft .NET Framework SDK directory.
1067
1068 Return
1069 ------
1070 str
1071 path
1072 """
1073 sdkdir = ''
1074 for ver in self.NetFxSdkVersion:
1075 loc = join(self.ri.netfx_sdk, ver)
1076 sdkdir = self.ri.lookup(loc, 'kitsinstallationfolder')
1077 if sdkdir:
1078 break
1079 return sdkdir
1080
1081 @property
1082 def FrameworkDir32(self):
1083 """
1084 Microsoft .NET Framework 32bit directory.
1085
1086 Return
1087 ------
1088 str
1089 path
1090 """
1091 # Default path
1092 guess_fw = join(self.WinDir, r'Microsoft.NET\Framework')
1093
1094 # Try to get path from registry, if fail use default path
1095 return self.ri.lookup(self.ri.vc, 'frameworkdir32') or guess_fw
1096
1097 @property
1098 def FrameworkDir64(self):
1099 """
1100 Microsoft .NET Framework 64bit directory.
1101
1102 Return
1103 ------
1104 str
1105 path
1106 """
1107 # Default path
1108 guess_fw = join(self.WinDir, r'Microsoft.NET\Framework64')
1109
1110 # Try to get path from registry, if fail use default path
1111 return self.ri.lookup(self.ri.vc, 'frameworkdir64') or guess_fw
1112
1113 @property
1114 def FrameworkVersion32(self):
1115 """
1116 Microsoft .NET Framework 32bit versions.
1117
1118 Return
1119 ------
1120 tuple of str
1121 versions
1122 """
1123 return self._find_dot_net_versions(32)
1124
1125 @property
1126 def FrameworkVersion64(self):
1127 """
1128 Microsoft .NET Framework 64bit versions.
1129
1130 Return
1131 ------
1132 tuple of str
1133 versions
1134 """
1135 return self._find_dot_net_versions(64)
1136
1137 def _find_dot_net_versions(self, bits):
1138 """
1139 Find Microsoft .NET Framework versions.
1140
1141 Parameters
1142 ----------
1143 bits: int
1144 Platform number of bits: 32 or 64.
1145
1146 Return
1147 ------
1148 tuple of str
1149 versions
1150 """
1151 # Find actual .NET version in registry
1152 reg_ver = self.ri.lookup(self.ri.vc, 'frameworkver%d' % bits)
1153 dot_net_dir = getattr(self, 'FrameworkDir%d' % bits)
1154 ver = reg_ver or self._use_last_dir_name(dot_net_dir, 'v') or ''
1155
1156 # Set .NET versions for specified MSVC++ version
1157 if self.vs_ver >= 12.0:
1158 return ver, 'v4.0'
1159 elif self.vs_ver >= 10.0:
1160 return 'v4.0.30319' if ver.lower()[:2] != 'v4' else ver, 'v3.5'
1161 elif self.vs_ver == 9.0:
1162 return 'v3.5', 'v2.0.50727'
1163 elif self.vs_ver == 8.0:
1164 return 'v3.0', 'v2.0.50727'
1165
1166 @staticmethod
1167 def _use_last_dir_name(path, prefix=''):
1168 """
1169 Return name of the last dir in path or '' if no dir found.
1170
1171 Parameters
1172 ----------
1173 path: str
1174 Use dirs in this path
1175 prefix: str
1176 Use only dirs starting by this prefix
1177
1178 Return
1179 ------
1180 str
1181 name
1182 """
1183 matching_dirs = (
1184 dir_name
1185 for dir_name in reversed(listdir(path))
1186 if isdir(join(path, dir_name)) and
1187 dir_name.startswith(prefix)
1188 )
1189 return next(matching_dirs, None) or ''
1190
1191
1192 class EnvironmentInfo:
1193 """
1194 Return environment variables for specified Microsoft Visual C++ version
1195 and platform : Lib, Include, Path and libpath.
1196
1197 This function is compatible with Microsoft Visual C++ 9.0 to 14.X.
1198
1199 Script created by analysing Microsoft environment configuration files like
1200 "vcvars[...].bat", "SetEnv.Cmd", "vcbuildtools.bat", ...
1201
1202 Parameters
1203 ----------
1204 arch: str
1205 Target architecture.
1206 vc_ver: float
1207 Required Microsoft Visual C++ version. If not set, autodetect the last
1208 version.
1209 vc_min_ver: float
1210 Minimum Microsoft Visual C++ version.
1211 """
1212
1213 # Variables and properties in this class use originals CamelCase variables
1214 # names from Microsoft source files for more easy comparison.
1215
1216 def __init__(self, arch, vc_ver=None, vc_min_ver=0):
1217 self.pi = PlatformInfo(arch)
1218 self.ri = RegistryInfo(self.pi)
1219 self.si = SystemInfo(self.ri, vc_ver)
1220
1221 if self.vc_ver < vc_min_ver:
1222 err = 'No suitable Microsoft Visual C++ version found'
1223 raise distutils.errors.DistutilsPlatformError(err)
1224
1225 @property
1226 def vs_ver(self):
1227 """
1228 Microsoft Visual Studio.
1229
1230 Return
1231 ------
1232 float
1233 version
1234 """
1235 return self.si.vs_ver
1236
1237 @property
1238 def vc_ver(self):
1239 """
1240 Microsoft Visual C++ version.
1241
1242 Return
1243 ------
1244 float
1245 version
1246 """
1247 return self.si.vc_ver
1248
1249 @property
1250 def VSTools(self):
1251 """
1252 Microsoft Visual Studio Tools.
1253
1254 Return
1255 ------
1256 list of str
1257 paths
1258 """
1259 paths = [r'Common7\IDE', r'Common7\Tools']
1260
1261 if self.vs_ver >= 14.0:
1262 arch_subdir = self.pi.current_dir(hidex86=True, x64=True)
1263 paths += [r'Common7\IDE\CommonExtensions\Microsoft\TestWindow']
1264 paths += [r'Team Tools\Performance Tools']
1265 paths += [r'Team Tools\Performance Tools%s' % arch_subdir]
1266
1267 return [join(self.si.VSInstallDir, path) for path in paths]
1268
1269 @property
1270 def VCIncludes(self):
1271 """
1272 Microsoft Visual C++ & Microsoft Foundation Class Includes.
1273
1274 Return
1275 ------
1276 list of str
1277 paths
1278 """
1279 return [join(self.si.VCInstallDir, 'Include'),
1280 join(self.si.VCInstallDir, r'ATLMFC\Include')]
1281
1282 @property
1283 def VCLibraries(self):
1284 """
1285 Microsoft Visual C++ & Microsoft Foundation Class Libraries.
1286
1287 Return
1288 ------
1289 list of str
1290 paths
1291 """
1292 if self.vs_ver >= 15.0:
1293 arch_subdir = self.pi.target_dir(x64=True)
1294 else:
1295 arch_subdir = self.pi.target_dir(hidex86=True)
1296 paths = ['Lib%s' % arch_subdir, r'ATLMFC\Lib%s' % arch_subdir]
1297
1298 if self.vs_ver >= 14.0:
1299 paths += [r'Lib\store%s' % arch_subdir]
1300
1301 return [join(self.si.VCInstallDir, path) for path in paths]
1302
1303 @property
1304 def VCStoreRefs(self):
1305 """
1306 Microsoft Visual C++ store references Libraries.
1307
1308 Return
1309 ------
1310 list of str
1311 paths
1312 """
1313 if self.vs_ver < 14.0:
1314 return []
1315 return [join(self.si.VCInstallDir, r'Lib\store\references')]
1316
1317 @property
1318 def VCTools(self):
1319 """
1320 Microsoft Visual C++ Tools.
1321
1322 Return
1323 ------
1324 list of str
1325 paths
1326 """
1327 si = self.si
1328 tools = [join(si.VCInstallDir, 'VCPackages')]
1329
1330 forcex86 = True if self.vs_ver <= 10.0 else False
1331 arch_subdir = self.pi.cross_dir(forcex86)
1332 if arch_subdir:
1333 tools += [join(si.VCInstallDir, 'Bin%s' % arch_subdir)]
1334
1335 if self.vs_ver == 14.0:
1336 path = 'Bin%s' % self.pi.current_dir(hidex86=True)
1337 tools += [join(si.VCInstallDir, path)]
1338
1339 elif self.vs_ver >= 15.0:
1340 host_dir = (r'bin\HostX86%s' if self.pi.current_is_x86() else
1341 r'bin\HostX64%s')
1342 tools += [join(
1343 si.VCInstallDir, host_dir % self.pi.target_dir(x64=True))]
1344
1345 if self.pi.current_cpu != self.pi.target_cpu:
1346 tools += [join(
1347 si.VCInstallDir, host_dir % self.pi.current_dir(x64=True))]
1348
1349 else:
1350 tools += [join(si.VCInstallDir, 'Bin')]
1351
1352 return tools
1353
1354 @property
1355 def OSLibraries(self):
1356 """
1357 Microsoft Windows SDK Libraries.
1358
1359 Return
1360 ------
1361 list of str
1362 paths
1363 """
1364 if self.vs_ver <= 10.0:
1365 arch_subdir = self.pi.target_dir(hidex86=True, x64=True)
1366 return [join(self.si.WindowsSdkDir, 'Lib%s' % arch_subdir)]
1367
1368 else:
1369 arch_subdir = self.pi.target_dir(x64=True)
1370 lib = join(self.si.WindowsSdkDir, 'lib')
1371 libver = self._sdk_subdir
1372 return [join(lib, '%sum%s' % (libver, arch_subdir))]
1373
1374 @property
1375 def OSIncludes(self):
1376 """
1377 Microsoft Windows SDK Include.
1378
1379 Return
1380 ------
1381 list of str
1382 paths
1383 """
1384 include = join(self.si.WindowsSdkDir, 'include')
1385
1386 if self.vs_ver <= 10.0:
1387 return [include, join(include, 'gl')]
1388
1389 else:
1390 if self.vs_ver >= 14.0:
1391 sdkver = self._sdk_subdir
1392 else:
1393 sdkver = ''
1394 return [join(include, '%sshared' % sdkver),
1395 join(include, '%sum' % sdkver),
1396 join(include, '%swinrt' % sdkver)]
1397
1398 @property
1399 def OSLibpath(self):
1400 """
1401 Microsoft Windows SDK Libraries Paths.
1402
1403 Return
1404 ------
1405 list of str
1406 paths
1407 """
1408 ref = join(self.si.WindowsSdkDir, 'References')
1409 libpath = []
1410
1411 if self.vs_ver <= 9.0:
1412 libpath += self.OSLibraries
1413
1414 if self.vs_ver >= 11.0:
1415 libpath += [join(ref, r'CommonConfiguration\Neutral')]
1416
1417 if self.vs_ver >= 14.0:
1418 libpath += [
1419 ref,
1420 join(self.si.WindowsSdkDir, 'UnionMetadata'),
1421 join(
1422 ref, 'Windows.Foundation.UniversalApiContract', '1.0.0.0'),
1423 join(ref, 'Windows.Foundation.FoundationContract', '1.0.0.0'),
1424 join(
1425 ref, 'Windows.Networking.Connectivity.WwanContract',
1426 '1.0.0.0'),
1427 join(
1428 self.si.WindowsSdkDir, 'ExtensionSDKs', 'Microsoft.VCLibs',
1429 '%0.1f' % self.vs_ver, 'References', 'CommonConfiguration',
1430 'neutral'),
1431 ]
1432 return libpath
1433
1434 @property
1435 def SdkTools(self):
1436 """
1437 Microsoft Windows SDK Tools.
1438
1439 Return
1440 ------
1441 list of str
1442 paths
1443 """
1444 return list(self._sdk_tools())
1445
1446 def _sdk_tools(self):
1447 """
1448 Microsoft Windows SDK Tools paths generator.
1449
1450 Return
1451 ------
1452 generator of str
1453 paths
1454 """
1455 if self.vs_ver < 15.0:
1456 bin_dir = 'Bin' if self.vs_ver <= 11.0 else r'Bin\x86'
1457 yield join(self.si.WindowsSdkDir, bin_dir)
1458
1459 if not self.pi.current_is_x86():
1460 arch_subdir = self.pi.current_dir(x64=True)
1461 path = 'Bin%s' % arch_subdir
1462 yield join(self.si.WindowsSdkDir, path)
1463
1464 if self.vs_ver in (10.0, 11.0):
1465 if self.pi.target_is_x86():
1466 arch_subdir = ''
1467 else:
1468 arch_subdir = self.pi.current_dir(hidex86=True, x64=True)
1469 path = r'Bin\NETFX 4.0 Tools%s' % arch_subdir
1470 yield join(self.si.WindowsSdkDir, path)
1471
1472 elif self.vs_ver >= 15.0:
1473 path = join(self.si.WindowsSdkDir, 'Bin')
1474 arch_subdir = self.pi.current_dir(x64=True)
1475 sdkver = self.si.WindowsSdkLastVersion
1476 yield join(path, '%s%s' % (sdkver, arch_subdir))
1477
1478 if self.si.WindowsSDKExecutablePath:
1479 yield self.si.WindowsSDKExecutablePath
1480
1481 @property
1482 def _sdk_subdir(self):
1483 """
1484 Microsoft Windows SDK version subdir.
1485
1486 Return
1487 ------
1488 str
1489 subdir
1490 """
1491 ucrtver = self.si.WindowsSdkLastVersion
1492 return ('%s\\' % ucrtver) if ucrtver else ''
1493
1494 @property
1495 def SdkSetup(self):
1496 """
1497 Microsoft Windows SDK Setup.
1498
1499 Return
1500 ------
1501 list of str
1502 paths
1503 """
1504 if self.vs_ver > 9.0:
1505 return []
1506
1507 return [join(self.si.WindowsSdkDir, 'Setup')]
1508
1509 @property
1510 def FxTools(self):
1511 """
1512 Microsoft .NET Framework Tools.
1513
1514 Return
1515 ------
1516 list of str
1517 paths
1518 """
1519 pi = self.pi
1520 si = self.si
1521
1522 if self.vs_ver <= 10.0:
1523 include32 = True
1524 include64 = not pi.target_is_x86() and not pi.current_is_x86()
1525 else:
1526 include32 = pi.target_is_x86() or pi.current_is_x86()
1527 include64 = pi.current_cpu == 'amd64' or pi.target_cpu == 'amd64'
1528
1529 tools = []
1530 if include32:
1531 tools += [join(si.FrameworkDir32, ver)
1532 for ver in si.FrameworkVersion32]
1533 if include64:
1534 tools += [join(si.FrameworkDir64, ver)
1535 for ver in si.FrameworkVersion64]
1536 return tools
1537
1538 @property
1539 def NetFxSDKLibraries(self):
1540 """
1541 Microsoft .Net Framework SDK Libraries.
1542
1543 Return
1544 ------
1545 list of str
1546 paths
1547 """
1548 if self.vs_ver < 14.0 or not self.si.NetFxSdkDir:
1549 return []
1550
1551 arch_subdir = self.pi.target_dir(x64=True)
1552 return [join(self.si.NetFxSdkDir, r'lib\um%s' % arch_subdir)]
1553
1554 @property
1555 def NetFxSDKIncludes(self):
1556 """
1557 Microsoft .Net Framework SDK Includes.
1558
1559 Return
1560 ------
1561 list of str
1562 paths
1563 """
1564 if self.vs_ver < 14.0 or not self.si.NetFxSdkDir:
1565 return []
1566
1567 return [join(self.si.NetFxSdkDir, r'include\um')]
1568
1569 @property
1570 def VsTDb(self):
1571 """
1572 Microsoft Visual Studio Team System Database.
1573
1574 Return
1575 ------
1576 list of str
1577 paths
1578 """
1579 return [join(self.si.VSInstallDir, r'VSTSDB\Deploy')]
1580
1581 @property
1582 def MSBuild(self):
1583 """
1584 Microsoft Build Engine.
1585
1586 Return
1587 ------
1588 list of str
1589 paths
1590 """
1591 if self.vs_ver < 12.0:
1592 return []
1593 elif self.vs_ver < 15.0:
1594 base_path = self.si.ProgramFilesx86
1595 arch_subdir = self.pi.current_dir(hidex86=True)
1596 else:
1597 base_path = self.si.VSInstallDir
1598 arch_subdir = ''
1599
1600 path = r'MSBuild\%0.1f\bin%s' % (self.vs_ver, arch_subdir)
1601 build = [join(base_path, path)]
1602
1603 if self.vs_ver >= 15.0:
1604 # Add Roslyn C# & Visual Basic Compiler
1605 build += [join(base_path, path, 'Roslyn')]
1606
1607 return build
1608
1609 @property
1610 def HTMLHelpWorkshop(self):
1611 """
1612 Microsoft HTML Help Workshop.
1613
1614 Return
1615 ------
1616 list of str
1617 paths
1618 """
1619 if self.vs_ver < 11.0:
1620 return []
1621
1622 return [join(self.si.ProgramFilesx86, 'HTML Help Workshop')]
1623
1624 @property
1625 def UCRTLibraries(self):
1626 """
1627 Microsoft Universal C Runtime SDK Libraries.
1628
1629 Return
1630 ------
1631 list of str
1632 paths
1633 """
1634 if self.vs_ver < 14.0:
1635 return []
1636
1637 arch_subdir = self.pi.target_dir(x64=True)
1638 lib = join(self.si.UniversalCRTSdkDir, 'lib')
1639 ucrtver = self._ucrt_subdir
1640 return [join(lib, '%sucrt%s' % (ucrtver, arch_subdir))]
1641
1642 @property
1643 def UCRTIncludes(self):
1644 """
1645 Microsoft Universal C Runtime SDK Include.
1646
1647 Return
1648 ------
1649 list of str
1650 paths
1651 """
1652 if self.vs_ver < 14.0:
1653 return []
1654
1655 include = join(self.si.UniversalCRTSdkDir, 'include')
1656 return [join(include, '%sucrt' % self._ucrt_subdir)]
1657
1658 @property
1659 def _ucrt_subdir(self):
1660 """
1661 Microsoft Universal C Runtime SDK version subdir.
1662
1663 Return
1664 ------
1665 str
1666 subdir
1667 """
1668 ucrtver = self.si.UniversalCRTSdkLastVersion
1669 return ('%s\\' % ucrtver) if ucrtver else ''
1670
1671 @property
1672 def FSharp(self):
1673 """
1674 Microsoft Visual F#.
1675
1676 Return
1677 ------
1678 list of str
1679 paths
1680 """
1681 if 11.0 > self.vs_ver > 12.0:
1682 return []
1683
1684 return [self.si.FSharpInstallDir]
1685
1686 @property
1687 def VCRuntimeRedist(self):
1688 """
1689 Microsoft Visual C++ runtime redistributable dll.
1690
1691 Return
1692 ------
1693 str
1694 path
1695 """
1696 vcruntime = 'vcruntime%d0.dll' % self.vc_ver
1697 arch_subdir = self.pi.target_dir(x64=True).strip('\\')
1698
1699 # Installation prefixes candidates
1700 prefixes = []
1701 tools_path = self.si.VCInstallDir
1702 redist_path = dirname(tools_path.replace(r'\Tools', r'\Redist'))
1703 if isdir(redist_path):
1704 # Redist version may not be exactly the same as tools
1705 redist_path = join(redist_path, listdir(redist_path)[-1])
1706 prefixes += [redist_path, join(redist_path, 'onecore')]
1707
1708 prefixes += [join(tools_path, 'redist')] # VS14 legacy path
1709
1710 # CRT directory
1711 crt_dirs = ('Microsoft.VC%d.CRT' % (self.vc_ver * 10),
1712 # Sometime store in directory with VS version instead of VC
1713 'Microsoft.VC%d.CRT' % (int(self.vs_ver) * 10))
1714
1715 # vcruntime path
1716 for prefix, crt_dir in itertools.product(prefixes, crt_dirs):
1717 path = join(prefix, arch_subdir, crt_dir, vcruntime)
1718 if isfile(path):
1719 return path
1720
1721 def return_env(self, exists=True):
1722 """
1723 Return environment dict.
1724
1725 Parameters
1726 ----------
1727 exists: bool
1728 It True, only return existing paths.
1729
1730 Return
1731 ------
1732 dict
1733 environment
1734 """
1735 env = dict(
1736 include=self._build_paths('include',
1737 [self.VCIncludes,
1738 self.OSIncludes,
1739 self.UCRTIncludes,
1740 self.NetFxSDKIncludes],
1741 exists),
1742 lib=self._build_paths('lib',
1743 [self.VCLibraries,
1744 self.OSLibraries,
1745 self.FxTools,
1746 self.UCRTLibraries,
1747 self.NetFxSDKLibraries],
1748 exists),
1749 libpath=self._build_paths('libpath',
1750 [self.VCLibraries,
1751 self.FxTools,
1752 self.VCStoreRefs,
1753 self.OSLibpath],
1754 exists),
1755 path=self._build_paths('path',
1756 [self.VCTools,
1757 self.VSTools,
1758 self.VsTDb,
1759 self.SdkTools,
1760 self.SdkSetup,
1761 self.FxTools,
1762 self.MSBuild,
1763 self.HTMLHelpWorkshop,
1764 self.FSharp],
1765 exists),
1766 )
1767 if self.vs_ver >= 14 and isfile(self.VCRuntimeRedist):
1768 env['py_vcruntime_redist'] = self.VCRuntimeRedist
1769 return env
1770
1771 def _build_paths(self, name, spec_path_lists, exists):
1772 """
1773 Given an environment variable name and specified paths,
1774 return a pathsep-separated string of paths containing
1775 unique, extant, directories from those paths and from
1776 the environment variable. Raise an error if no paths
1777 are resolved.
1778
1779 Parameters
1780 ----------
1781 name: str
1782 Environment variable name
1783 spec_path_lists: list of str
1784 Paths
1785 exists: bool
1786 It True, only return existing paths.
1787
1788 Return
1789 ------
1790 str
1791 Pathsep-separated paths
1792 """
1793 # flatten spec_path_lists
1794 spec_paths = itertools.chain.from_iterable(spec_path_lists)
1795 env_paths = environ.get(name, '').split(pathsep)
1796 paths = itertools.chain(spec_paths, env_paths)
1797 extant_paths = list(filter(isdir, paths)) if exists else paths
1798 if not extant_paths:
1799 msg = "%s environment variable is empty" % name.upper()
1800 raise distutils.errors.DistutilsPlatformError(msg)
1801 unique_paths = self._unique_everseen(extant_paths)
1802 return pathsep.join(unique_paths)
1803
1804 # from Python docs
1805 @staticmethod
1806 def _unique_everseen(iterable, key=None):
1807 """
1808 List unique elements, preserving order.
1809 Remember all elements ever seen.
1810
1811 _unique_everseen('AAAABBBCCDAABBB') --> A B C D
1812
1813 _unique_everseen('ABBCcAD', str.lower) --> A B C D
1814 """
1815 seen = set()
1816 seen_add = seen.add
1817 if key is None:
1818 for element in itertools.filterfalse(seen.__contains__, iterable):
1819 seen_add(element)
1820 yield element
1821 else:
1822 for element in iterable:
1823 k = key(element)
1824 if k not in seen:
1825 seen_add(k)
1826 yield element