comparison lib/python3.8/site-packages/setuptools/msvc.py @ 0:9e54283cc701 draft

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