comparison planemo/lib/python3.7/site-packages/setuptools/msvc.py @ 1:56ad4e20f292 draft

"planemo upload commit 6eee67778febed82ddd413c3ca40b3183a3898f1"
author guerler
date Fri, 31 Jul 2020 00:32:28 -0400
parents
children
comparison
equal deleted inserted replaced
0:d30785e31577 1:56ad4e20f292
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.0:
15 Microsoft Visual C++ Build Tools 2015 (x86, x64, arm)
16 Microsoft Visual Studio 2017 (x86, x64, arm, arm64)
17 Microsoft Visual Studio Build Tools 2017 (x86, x64, arm, arm64)
18 """
19
20 import os
21 import sys
22 import platform
23 import itertools
24 import distutils.errors
25 from setuptools.extern.packaging.version import LegacyVersion
26
27 from setuptools.extern.six.moves import filterfalse
28
29 from .monkey import get_unpatched
30
31 if platform.system() == 'Windows':
32 from setuptools.extern.six.moves import winreg
33 safe_env = os.environ
34 else:
35 """
36 Mock winreg and environ so the module can be imported
37 on this platform.
38 """
39
40 class winreg:
41 HKEY_USERS = None
42 HKEY_CURRENT_USER = None
43 HKEY_LOCAL_MACHINE = None
44 HKEY_CLASSES_ROOT = None
45
46 safe_env = dict()
47
48 _msvc9_suppress_errors = (
49 # msvc9compiler isn't available on some platforms
50 ImportError,
51
52 # msvc9compiler raises DistutilsPlatformError in some
53 # environments. See #1118.
54 distutils.errors.DistutilsPlatformError,
55 )
56
57 try:
58 from distutils.msvc9compiler import Reg
59 except _msvc9_suppress_errors:
60 pass
61
62
63 def msvc9_find_vcvarsall(version):
64 """
65 Patched "distutils.msvc9compiler.find_vcvarsall" to use the standalone
66 compiler build for Python (VCForPython). Fall back to original behavior
67 when the standalone compiler is not available.
68
69 Redirect the path of "vcvarsall.bat".
70
71 Known supported compilers
72 -------------------------
73 Microsoft Visual C++ 9.0:
74 Microsoft Visual C++ Compiler for Python 2.7 (x86, amd64)
75
76 Parameters
77 ----------
78 version: float
79 Required Microsoft Visual C++ version.
80
81 Return
82 ------
83 vcvarsall.bat path: str
84 """
85 VC_BASE = r'Software\%sMicrosoft\DevDiv\VCForPython\%0.1f'
86 key = VC_BASE % ('', version)
87 try:
88 # Per-user installs register the compiler path here
89 productdir = Reg.get_value(key, "installdir")
90 except KeyError:
91 try:
92 # All-user installs on a 64-bit system register here
93 key = VC_BASE % ('Wow6432Node\\', version)
94 productdir = Reg.get_value(key, "installdir")
95 except KeyError:
96 productdir = None
97
98 if productdir:
99 vcvarsall = os.path.os.path.join(productdir, "vcvarsall.bat")
100 if os.path.isfile(vcvarsall):
101 return vcvarsall
102
103 return get_unpatched(msvc9_find_vcvarsall)(version)
104
105
106 def msvc9_query_vcvarsall(ver, arch='x86', *args, **kwargs):
107 """
108 Patched "distutils.msvc9compiler.query_vcvarsall" for support extra
109 compilers.
110
111 Set environment without use of "vcvarsall.bat".
112
113 Known supported compilers
114 -------------------------
115 Microsoft Visual C++ 9.0:
116 Microsoft Visual C++ Compiler for Python 2.7 (x86, amd64)
117 Microsoft Windows SDK 6.1 (x86, x64, ia64)
118 Microsoft Windows SDK 7.0 (x86, x64, ia64)
119
120 Microsoft Visual C++ 10.0:
121 Microsoft Windows SDK 7.1 (x86, x64, ia64)
122
123 Parameters
124 ----------
125 ver: float
126 Required Microsoft Visual C++ version.
127 arch: str
128 Target architecture.
129
130 Return
131 ------
132 environment: dict
133 """
134 # Try to get environement from vcvarsall.bat (Classical way)
135 try:
136 orig = get_unpatched(msvc9_query_vcvarsall)
137 return orig(ver, arch, *args, **kwargs)
138 except distutils.errors.DistutilsPlatformError:
139 # Pass error if Vcvarsall.bat is missing
140 pass
141 except ValueError:
142 # Pass error if environment not set after executing vcvarsall.bat
143 pass
144
145 # If error, try to set environment directly
146 try:
147 return EnvironmentInfo(arch, ver).return_env()
148 except distutils.errors.DistutilsPlatformError as exc:
149 _augment_exception(exc, ver, arch)
150 raise
151
152
153 def msvc14_get_vc_env(plat_spec):
154 """
155 Patched "distutils._msvccompiler._get_vc_env" for support extra
156 compilers.
157
158 Set environment without use of "vcvarsall.bat".
159
160 Known supported compilers
161 -------------------------
162 Microsoft Visual C++ 14.0:
163 Microsoft Visual C++ Build Tools 2015 (x86, x64, arm)
164 Microsoft Visual Studio 2017 (x86, x64, arm, arm64)
165 Microsoft Visual Studio Build Tools 2017 (x86, x64, arm, arm64)
166
167 Parameters
168 ----------
169 plat_spec: str
170 Target architecture.
171
172 Return
173 ------
174 environment: dict
175 """
176 # Try to get environment from vcvarsall.bat (Classical way)
177 try:
178 return get_unpatched(msvc14_get_vc_env)(plat_spec)
179 except distutils.errors.DistutilsPlatformError:
180 # Pass error Vcvarsall.bat is missing
181 pass
182
183 # If error, try to set environment directly
184 try:
185 return EnvironmentInfo(plat_spec, vc_min_ver=14.0).return_env()
186 except distutils.errors.DistutilsPlatformError as exc:
187 _augment_exception(exc, 14.0)
188 raise
189
190
191 def msvc14_gen_lib_options(*args, **kwargs):
192 """
193 Patched "distutils._msvccompiler.gen_lib_options" for fix
194 compatibility between "numpy.distutils" and "distutils._msvccompiler"
195 (for Numpy < 1.11.2)
196 """
197 if "numpy.distutils" in sys.modules:
198 import numpy as np
199 if LegacyVersion(np.__version__) < LegacyVersion('1.11.2'):
200 return np.distutils.ccompiler.gen_lib_options(*args, **kwargs)
201 return get_unpatched(msvc14_gen_lib_options)(*args, **kwargs)
202
203
204 def _augment_exception(exc, version, arch=''):
205 """
206 Add details to the exception message to help guide the user
207 as to what action will resolve it.
208 """
209 # Error if MSVC++ directory not found or environment not set
210 message = exc.args[0]
211
212 if "vcvarsall" in message.lower() or "visual c" in message.lower():
213 # Special error message if MSVC++ not installed
214 tmpl = 'Microsoft Visual C++ {version:0.1f} is required.'
215 message = tmpl.format(**locals())
216 msdownload = 'www.microsoft.com/download/details.aspx?id=%d'
217 if version == 9.0:
218 if arch.lower().find('ia64') > -1:
219 # For VC++ 9.0, if IA64 support is needed, redirect user
220 # to Windows SDK 7.0
221 message += ' Get it with "Microsoft Windows SDK 7.0": '
222 message += msdownload % 3138
223 else:
224 # For VC++ 9.0 redirect user to Vc++ for Python 2.7 :
225 # This redirection link is maintained by Microsoft.
226 # Contact vspython@microsoft.com if it needs updating.
227 message += ' Get it from http://aka.ms/vcpython27'
228 elif version == 10.0:
229 # For VC++ 10.0 Redirect user to Windows SDK 7.1
230 message += ' Get it with "Microsoft Windows SDK 7.1": '
231 message += msdownload % 8279
232 elif version >= 14.0:
233 # For VC++ 14.0 Redirect user to Visual C++ Build Tools
234 message += (' Get it with "Microsoft Visual C++ Build Tools": '
235 r'https://visualstudio.microsoft.com/downloads/')
236
237 exc.args = (message, )
238
239
240 class PlatformInfo:
241 """
242 Current and Target Architectures informations.
243
244 Parameters
245 ----------
246 arch: str
247 Target architecture.
248 """
249 current_cpu = safe_env.get('processor_architecture', '').lower()
250
251 def __init__(self, arch):
252 self.arch = arch.lower().replace('x64', 'amd64')
253
254 @property
255 def target_cpu(self):
256 return self.arch[self.arch.find('_') + 1:]
257
258 def target_is_x86(self):
259 return self.target_cpu == 'x86'
260
261 def current_is_x86(self):
262 return self.current_cpu == 'x86'
263
264 def current_dir(self, hidex86=False, x64=False):
265 """
266 Current platform specific subfolder.
267
268 Parameters
269 ----------
270 hidex86: bool
271 return '' and not '\x86' if architecture is x86.
272 x64: bool
273 return '\x64' and not '\amd64' if architecture is amd64.
274
275 Return
276 ------
277 subfolder: str
278 '\target', or '' (see hidex86 parameter)
279 """
280 return (
281 '' if (self.current_cpu == 'x86' and hidex86) else
282 r'\x64' if (self.current_cpu == 'amd64' and x64) else
283 r'\%s' % self.current_cpu
284 )
285
286 def target_dir(self, hidex86=False, x64=False):
287 r"""
288 Target platform specific subfolder.
289
290 Parameters
291 ----------
292 hidex86: bool
293 return '' and not '\x86' if architecture is x86.
294 x64: bool
295 return '\x64' and not '\amd64' if architecture is amd64.
296
297 Return
298 ------
299 subfolder: str
300 '\current', or '' (see hidex86 parameter)
301 """
302 return (
303 '' if (self.target_cpu == 'x86' and hidex86) else
304 r'\x64' if (self.target_cpu == 'amd64' and x64) else
305 r'\%s' % self.target_cpu
306 )
307
308 def cross_dir(self, forcex86=False):
309 r"""
310 Cross platform specific subfolder.
311
312 Parameters
313 ----------
314 forcex86: bool
315 Use 'x86' as current architecture even if current acritecture is
316 not x86.
317
318 Return
319 ------
320 subfolder: str
321 '' if target architecture is current architecture,
322 '\current_target' if not.
323 """
324 current = 'x86' if forcex86 else self.current_cpu
325 return (
326 '' if self.target_cpu == current else
327 self.target_dir().replace('\\', '\\%s_' % current)
328 )
329
330
331 class RegistryInfo:
332 """
333 Microsoft Visual Studio related registry informations.
334
335 Parameters
336 ----------
337 platform_info: PlatformInfo
338 "PlatformInfo" instance.
339 """
340 HKEYS = (winreg.HKEY_USERS,
341 winreg.HKEY_CURRENT_USER,
342 winreg.HKEY_LOCAL_MACHINE,
343 winreg.HKEY_CLASSES_ROOT)
344
345 def __init__(self, platform_info):
346 self.pi = platform_info
347
348 @property
349 def visualstudio(self):
350 """
351 Microsoft Visual Studio root registry key.
352 """
353 return 'VisualStudio'
354
355 @property
356 def sxs(self):
357 """
358 Microsoft Visual Studio SxS registry key.
359 """
360 return os.path.join(self.visualstudio, 'SxS')
361
362 @property
363 def vc(self):
364 """
365 Microsoft Visual C++ VC7 registry key.
366 """
367 return os.path.join(self.sxs, 'VC7')
368
369 @property
370 def vs(self):
371 """
372 Microsoft Visual Studio VS7 registry key.
373 """
374 return os.path.join(self.sxs, 'VS7')
375
376 @property
377 def vc_for_python(self):
378 """
379 Microsoft Visual C++ for Python registry key.
380 """
381 return r'DevDiv\VCForPython'
382
383 @property
384 def microsoft_sdk(self):
385 """
386 Microsoft SDK registry key.
387 """
388 return 'Microsoft SDKs'
389
390 @property
391 def windows_sdk(self):
392 """
393 Microsoft Windows/Platform SDK registry key.
394 """
395 return os.path.join(self.microsoft_sdk, 'Windows')
396
397 @property
398 def netfx_sdk(self):
399 """
400 Microsoft .NET Framework SDK registry key.
401 """
402 return os.path.join(self.microsoft_sdk, 'NETFXSDK')
403
404 @property
405 def windows_kits_roots(self):
406 """
407 Microsoft Windows Kits Roots registry key.
408 """
409 return r'Windows Kits\Installed Roots'
410
411 def microsoft(self, key, x86=False):
412 """
413 Return key in Microsoft software registry.
414
415 Parameters
416 ----------
417 key: str
418 Registry key path where look.
419 x86: str
420 Force x86 software registry.
421
422 Return
423 ------
424 str: value
425 """
426 node64 = '' if self.pi.current_is_x86() or x86 else 'Wow6432Node'
427 return os.path.join('Software', node64, 'Microsoft', key)
428
429 def lookup(self, key, name):
430 """
431 Look for values in registry in Microsoft software registry.
432
433 Parameters
434 ----------
435 key: str
436 Registry key path where look.
437 name: str
438 Value name to find.
439
440 Return
441 ------
442 str: value
443 """
444 KEY_READ = winreg.KEY_READ
445 openkey = winreg.OpenKey
446 ms = self.microsoft
447 for hkey in self.HKEYS:
448 try:
449 bkey = openkey(hkey, ms(key), 0, KEY_READ)
450 except (OSError, IOError):
451 if not self.pi.current_is_x86():
452 try:
453 bkey = openkey(hkey, ms(key, True), 0, KEY_READ)
454 except (OSError, IOError):
455 continue
456 else:
457 continue
458 try:
459 return winreg.QueryValueEx(bkey, name)[0]
460 except (OSError, IOError):
461 pass
462
463
464 class SystemInfo:
465 """
466 Microsoft Windows and Visual Studio related system inormations.
467
468 Parameters
469 ----------
470 registry_info: RegistryInfo
471 "RegistryInfo" instance.
472 vc_ver: float
473 Required Microsoft Visual C++ version.
474 """
475
476 # Variables and properties in this class use originals CamelCase variables
477 # names from Microsoft source files for more easy comparaison.
478 WinDir = safe_env.get('WinDir', '')
479 ProgramFiles = safe_env.get('ProgramFiles', '')
480 ProgramFilesx86 = safe_env.get('ProgramFiles(x86)', ProgramFiles)
481
482 def __init__(self, registry_info, vc_ver=None):
483 self.ri = registry_info
484 self.pi = self.ri.pi
485 self.vc_ver = vc_ver or self._find_latest_available_vc_ver()
486
487 def _find_latest_available_vc_ver(self):
488 try:
489 return self.find_available_vc_vers()[-1]
490 except IndexError:
491 err = 'No Microsoft Visual C++ version found'
492 raise distutils.errors.DistutilsPlatformError(err)
493
494 def find_available_vc_vers(self):
495 """
496 Find all available Microsoft Visual C++ versions.
497 """
498 ms = self.ri.microsoft
499 vckeys = (self.ri.vc, self.ri.vc_for_python, self.ri.vs)
500 vc_vers = []
501 for hkey in self.ri.HKEYS:
502 for key in vckeys:
503 try:
504 bkey = winreg.OpenKey(hkey, ms(key), 0, winreg.KEY_READ)
505 except (OSError, IOError):
506 continue
507 subkeys, values, _ = winreg.QueryInfoKey(bkey)
508 for i in range(values):
509 try:
510 ver = float(winreg.EnumValue(bkey, i)[0])
511 if ver not in vc_vers:
512 vc_vers.append(ver)
513 except ValueError:
514 pass
515 for i in range(subkeys):
516 try:
517 ver = float(winreg.EnumKey(bkey, i))
518 if ver not in vc_vers:
519 vc_vers.append(ver)
520 except ValueError:
521 pass
522 return sorted(vc_vers)
523
524 @property
525 def VSInstallDir(self):
526 """
527 Microsoft Visual Studio directory.
528 """
529 # Default path
530 name = 'Microsoft Visual Studio %0.1f' % self.vc_ver
531 default = os.path.join(self.ProgramFilesx86, name)
532
533 # Try to get path from registry, if fail use default path
534 return self.ri.lookup(self.ri.vs, '%0.1f' % self.vc_ver) or default
535
536 @property
537 def VCInstallDir(self):
538 """
539 Microsoft Visual C++ directory.
540 """
541 self.VSInstallDir
542
543 guess_vc = self._guess_vc() or self._guess_vc_legacy()
544
545 # Try to get "VC++ for Python" path from registry as default path
546 reg_path = os.path.join(self.ri.vc_for_python, '%0.1f' % self.vc_ver)
547 python_vc = self.ri.lookup(reg_path, 'installdir')
548 default_vc = os.path.join(python_vc, 'VC') if python_vc else guess_vc
549
550 # Try to get path from registry, if fail use default path
551 path = self.ri.lookup(self.ri.vc, '%0.1f' % self.vc_ver) or default_vc
552
553 if not os.path.isdir(path):
554 msg = 'Microsoft Visual C++ directory not found'
555 raise distutils.errors.DistutilsPlatformError(msg)
556
557 return path
558
559 def _guess_vc(self):
560 """
561 Locate Visual C for 2017
562 """
563 if self.vc_ver <= 14.0:
564 return
565
566 default = r'VC\Tools\MSVC'
567 guess_vc = os.path.join(self.VSInstallDir, default)
568 # Subdir with VC exact version as name
569 try:
570 vc_exact_ver = os.listdir(guess_vc)[-1]
571 return os.path.join(guess_vc, vc_exact_ver)
572 except (OSError, IOError, IndexError):
573 pass
574
575 def _guess_vc_legacy(self):
576 """
577 Locate Visual C for versions prior to 2017
578 """
579 default = r'Microsoft Visual Studio %0.1f\VC' % self.vc_ver
580 return os.path.join(self.ProgramFilesx86, default)
581
582 @property
583 def WindowsSdkVersion(self):
584 """
585 Microsoft Windows SDK versions for specified MSVC++ version.
586 """
587 if self.vc_ver <= 9.0:
588 return ('7.0', '6.1', '6.0a')
589 elif self.vc_ver == 10.0:
590 return ('7.1', '7.0a')
591 elif self.vc_ver == 11.0:
592 return ('8.0', '8.0a')
593 elif self.vc_ver == 12.0:
594 return ('8.1', '8.1a')
595 elif self.vc_ver >= 14.0:
596 return ('10.0', '8.1')
597
598 @property
599 def WindowsSdkLastVersion(self):
600 """
601 Microsoft Windows SDK last version
602 """
603 return self._use_last_dir_name(os.path.join(
604 self.WindowsSdkDir, 'lib'))
605
606 @property
607 def WindowsSdkDir(self):
608 """
609 Microsoft Windows SDK directory.
610 """
611 sdkdir = ''
612 for ver in self.WindowsSdkVersion:
613 # Try to get it from registry
614 loc = os.path.join(self.ri.windows_sdk, 'v%s' % ver)
615 sdkdir = self.ri.lookup(loc, 'installationfolder')
616 if sdkdir:
617 break
618 if not sdkdir or not os.path.isdir(sdkdir):
619 # Try to get "VC++ for Python" version from registry
620 path = os.path.join(self.ri.vc_for_python, '%0.1f' % self.vc_ver)
621 install_base = self.ri.lookup(path, 'installdir')
622 if install_base:
623 sdkdir = os.path.join(install_base, 'WinSDK')
624 if not sdkdir or not os.path.isdir(sdkdir):
625 # If fail, use default new path
626 for ver in self.WindowsSdkVersion:
627 intver = ver[:ver.rfind('.')]
628 path = r'Microsoft SDKs\Windows Kits\%s' % (intver)
629 d = os.path.join(self.ProgramFiles, path)
630 if os.path.isdir(d):
631 sdkdir = d
632 if not sdkdir or not os.path.isdir(sdkdir):
633 # If fail, use default old path
634 for ver in self.WindowsSdkVersion:
635 path = r'Microsoft SDKs\Windows\v%s' % ver
636 d = os.path.join(self.ProgramFiles, path)
637 if os.path.isdir(d):
638 sdkdir = d
639 if not sdkdir:
640 # If fail, use Platform SDK
641 sdkdir = os.path.join(self.VCInstallDir, 'PlatformSDK')
642 return sdkdir
643
644 @property
645 def WindowsSDKExecutablePath(self):
646 """
647 Microsoft Windows SDK executable directory.
648 """
649 # Find WinSDK NetFx Tools registry dir name
650 if self.vc_ver <= 11.0:
651 netfxver = 35
652 arch = ''
653 else:
654 netfxver = 40
655 hidex86 = True if self.vc_ver <= 12.0 else False
656 arch = self.pi.current_dir(x64=True, hidex86=hidex86)
657 fx = 'WinSDK-NetFx%dTools%s' % (netfxver, arch.replace('\\', '-'))
658
659 # liste all possibles registry paths
660 regpaths = []
661 if self.vc_ver >= 14.0:
662 for ver in self.NetFxSdkVersion:
663 regpaths += [os.path.join(self.ri.netfx_sdk, ver, fx)]
664
665 for ver in self.WindowsSdkVersion:
666 regpaths += [os.path.join(self.ri.windows_sdk, 'v%sA' % ver, fx)]
667
668 # Return installation folder from the more recent path
669 for path in regpaths:
670 execpath = self.ri.lookup(path, 'installationfolder')
671 if execpath:
672 break
673 return execpath
674
675 @property
676 def FSharpInstallDir(self):
677 """
678 Microsoft Visual F# directory.
679 """
680 path = r'%0.1f\Setup\F#' % self.vc_ver
681 path = os.path.join(self.ri.visualstudio, path)
682 return self.ri.lookup(path, 'productdir') or ''
683
684 @property
685 def UniversalCRTSdkDir(self):
686 """
687 Microsoft Universal CRT SDK directory.
688 """
689 # Set Kit Roots versions for specified MSVC++ version
690 if self.vc_ver >= 14.0:
691 vers = ('10', '81')
692 else:
693 vers = ()
694
695 # Find path of the more recent Kit
696 for ver in vers:
697 sdkdir = self.ri.lookup(self.ri.windows_kits_roots,
698 'kitsroot%s' % ver)
699 if sdkdir:
700 break
701 return sdkdir or ''
702
703 @property
704 def UniversalCRTSdkLastVersion(self):
705 """
706 Microsoft Universal C Runtime SDK last version
707 """
708 return self._use_last_dir_name(os.path.join(
709 self.UniversalCRTSdkDir, 'lib'))
710
711 @property
712 def NetFxSdkVersion(self):
713 """
714 Microsoft .NET Framework SDK versions.
715 """
716 # Set FxSdk versions for specified MSVC++ version
717 if self.vc_ver >= 14.0:
718 return ('4.6.1', '4.6')
719 else:
720 return ()
721
722 @property
723 def NetFxSdkDir(self):
724 """
725 Microsoft .NET Framework SDK directory.
726 """
727 for ver in self.NetFxSdkVersion:
728 loc = os.path.join(self.ri.netfx_sdk, ver)
729 sdkdir = self.ri.lookup(loc, 'kitsinstallationfolder')
730 if sdkdir:
731 break
732 return sdkdir or ''
733
734 @property
735 def FrameworkDir32(self):
736 """
737 Microsoft .NET Framework 32bit directory.
738 """
739 # Default path
740 guess_fw = os.path.join(self.WinDir, r'Microsoft.NET\Framework')
741
742 # Try to get path from registry, if fail use default path
743 return self.ri.lookup(self.ri.vc, 'frameworkdir32') or guess_fw
744
745 @property
746 def FrameworkDir64(self):
747 """
748 Microsoft .NET Framework 64bit directory.
749 """
750 # Default path
751 guess_fw = os.path.join(self.WinDir, r'Microsoft.NET\Framework64')
752
753 # Try to get path from registry, if fail use default path
754 return self.ri.lookup(self.ri.vc, 'frameworkdir64') or guess_fw
755
756 @property
757 def FrameworkVersion32(self):
758 """
759 Microsoft .NET Framework 32bit versions.
760 """
761 return self._find_dot_net_versions(32)
762
763 @property
764 def FrameworkVersion64(self):
765 """
766 Microsoft .NET Framework 64bit versions.
767 """
768 return self._find_dot_net_versions(64)
769
770 def _find_dot_net_versions(self, bits):
771 """
772 Find Microsoft .NET Framework versions.
773
774 Parameters
775 ----------
776 bits: int
777 Platform number of bits: 32 or 64.
778 """
779 # Find actual .NET version in registry
780 reg_ver = self.ri.lookup(self.ri.vc, 'frameworkver%d' % bits)
781 dot_net_dir = getattr(self, 'FrameworkDir%d' % bits)
782 ver = reg_ver or self._use_last_dir_name(dot_net_dir, 'v') or ''
783
784 # Set .NET versions for specified MSVC++ version
785 if self.vc_ver >= 12.0:
786 frameworkver = (ver, 'v4.0')
787 elif self.vc_ver >= 10.0:
788 frameworkver = ('v4.0.30319' if ver.lower()[:2] != 'v4' else ver,
789 'v3.5')
790 elif self.vc_ver == 9.0:
791 frameworkver = ('v3.5', 'v2.0.50727')
792 if self.vc_ver == 8.0:
793 frameworkver = ('v3.0', 'v2.0.50727')
794 return frameworkver
795
796 def _use_last_dir_name(self, path, prefix=''):
797 """
798 Return name of the last dir in path or '' if no dir found.
799
800 Parameters
801 ----------
802 path: str
803 Use dirs in this path
804 prefix: str
805 Use only dirs startings by this prefix
806 """
807 matching_dirs = (
808 dir_name
809 for dir_name in reversed(os.listdir(path))
810 if os.path.isdir(os.path.join(path, dir_name)) and
811 dir_name.startswith(prefix)
812 )
813 return next(matching_dirs, None) or ''
814
815
816 class EnvironmentInfo:
817 """
818 Return environment variables for specified Microsoft Visual C++ version
819 and platform : Lib, Include, Path and libpath.
820
821 This function is compatible with Microsoft Visual C++ 9.0 to 14.0.
822
823 Script created by analysing Microsoft environment configuration files like
824 "vcvars[...].bat", "SetEnv.Cmd", "vcbuildtools.bat", ...
825
826 Parameters
827 ----------
828 arch: str
829 Target architecture.
830 vc_ver: float
831 Required Microsoft Visual C++ version. If not set, autodetect the last
832 version.
833 vc_min_ver: float
834 Minimum Microsoft Visual C++ version.
835 """
836
837 # Variables and properties in this class use originals CamelCase variables
838 # names from Microsoft source files for more easy comparaison.
839
840 def __init__(self, arch, vc_ver=None, vc_min_ver=0):
841 self.pi = PlatformInfo(arch)
842 self.ri = RegistryInfo(self.pi)
843 self.si = SystemInfo(self.ri, vc_ver)
844
845 if self.vc_ver < vc_min_ver:
846 err = 'No suitable Microsoft Visual C++ version found'
847 raise distutils.errors.DistutilsPlatformError(err)
848
849 @property
850 def vc_ver(self):
851 """
852 Microsoft Visual C++ version.
853 """
854 return self.si.vc_ver
855
856 @property
857 def VSTools(self):
858 """
859 Microsoft Visual Studio Tools
860 """
861 paths = [r'Common7\IDE', r'Common7\Tools']
862
863 if self.vc_ver >= 14.0:
864 arch_subdir = self.pi.current_dir(hidex86=True, x64=True)
865 paths += [r'Common7\IDE\CommonExtensions\Microsoft\TestWindow']
866 paths += [r'Team Tools\Performance Tools']
867 paths += [r'Team Tools\Performance Tools%s' % arch_subdir]
868
869 return [os.path.join(self.si.VSInstallDir, path) for path in paths]
870
871 @property
872 def VCIncludes(self):
873 """
874 Microsoft Visual C++ & Microsoft Foundation Class Includes
875 """
876 return [os.path.join(self.si.VCInstallDir, 'Include'),
877 os.path.join(self.si.VCInstallDir, r'ATLMFC\Include')]
878
879 @property
880 def VCLibraries(self):
881 """
882 Microsoft Visual C++ & Microsoft Foundation Class Libraries
883 """
884 if self.vc_ver >= 15.0:
885 arch_subdir = self.pi.target_dir(x64=True)
886 else:
887 arch_subdir = self.pi.target_dir(hidex86=True)
888 paths = ['Lib%s' % arch_subdir, r'ATLMFC\Lib%s' % arch_subdir]
889
890 if self.vc_ver >= 14.0:
891 paths += [r'Lib\store%s' % arch_subdir]
892
893 return [os.path.join(self.si.VCInstallDir, path) for path in paths]
894
895 @property
896 def VCStoreRefs(self):
897 """
898 Microsoft Visual C++ store references Libraries
899 """
900 if self.vc_ver < 14.0:
901 return []
902 return [os.path.join(self.si.VCInstallDir, r'Lib\store\references')]
903
904 @property
905 def VCTools(self):
906 """
907 Microsoft Visual C++ Tools
908 """
909 si = self.si
910 tools = [os.path.join(si.VCInstallDir, 'VCPackages')]
911
912 forcex86 = True if self.vc_ver <= 10.0 else False
913 arch_subdir = self.pi.cross_dir(forcex86)
914 if arch_subdir:
915 tools += [os.path.join(si.VCInstallDir, 'Bin%s' % arch_subdir)]
916
917 if self.vc_ver == 14.0:
918 path = 'Bin%s' % self.pi.current_dir(hidex86=True)
919 tools += [os.path.join(si.VCInstallDir, path)]
920
921 elif self.vc_ver >= 15.0:
922 host_dir = (r'bin\HostX86%s' if self.pi.current_is_x86() else
923 r'bin\HostX64%s')
924 tools += [os.path.join(
925 si.VCInstallDir, host_dir % self.pi.target_dir(x64=True))]
926
927 if self.pi.current_cpu != self.pi.target_cpu:
928 tools += [os.path.join(
929 si.VCInstallDir, host_dir % self.pi.current_dir(x64=True))]
930
931 else:
932 tools += [os.path.join(si.VCInstallDir, 'Bin')]
933
934 return tools
935
936 @property
937 def OSLibraries(self):
938 """
939 Microsoft Windows SDK Libraries
940 """
941 if self.vc_ver <= 10.0:
942 arch_subdir = self.pi.target_dir(hidex86=True, x64=True)
943 return [os.path.join(self.si.WindowsSdkDir, 'Lib%s' % arch_subdir)]
944
945 else:
946 arch_subdir = self.pi.target_dir(x64=True)
947 lib = os.path.join(self.si.WindowsSdkDir, 'lib')
948 libver = self._sdk_subdir
949 return [os.path.join(lib, '%sum%s' % (libver , arch_subdir))]
950
951 @property
952 def OSIncludes(self):
953 """
954 Microsoft Windows SDK Include
955 """
956 include = os.path.join(self.si.WindowsSdkDir, 'include')
957
958 if self.vc_ver <= 10.0:
959 return [include, os.path.join(include, 'gl')]
960
961 else:
962 if self.vc_ver >= 14.0:
963 sdkver = self._sdk_subdir
964 else:
965 sdkver = ''
966 return [os.path.join(include, '%sshared' % sdkver),
967 os.path.join(include, '%sum' % sdkver),
968 os.path.join(include, '%swinrt' % sdkver)]
969
970 @property
971 def OSLibpath(self):
972 """
973 Microsoft Windows SDK Libraries Paths
974 """
975 ref = os.path.join(self.si.WindowsSdkDir, 'References')
976 libpath = []
977
978 if self.vc_ver <= 9.0:
979 libpath += self.OSLibraries
980
981 if self.vc_ver >= 11.0:
982 libpath += [os.path.join(ref, r'CommonConfiguration\Neutral')]
983
984 if self.vc_ver >= 14.0:
985 libpath += [
986 ref,
987 os.path.join(self.si.WindowsSdkDir, 'UnionMetadata'),
988 os.path.join(
989 ref,
990 'Windows.Foundation.UniversalApiContract',
991 '1.0.0.0',
992 ),
993 os.path.join(
994 ref,
995 'Windows.Foundation.FoundationContract',
996 '1.0.0.0',
997 ),
998 os.path.join(
999 ref,
1000 'Windows.Networking.Connectivity.WwanContract',
1001 '1.0.0.0',
1002 ),
1003 os.path.join(
1004 self.si.WindowsSdkDir,
1005 'ExtensionSDKs',
1006 'Microsoft.VCLibs',
1007 '%0.1f' % self.vc_ver,
1008 'References',
1009 'CommonConfiguration',
1010 'neutral',
1011 ),
1012 ]
1013 return libpath
1014
1015 @property
1016 def SdkTools(self):
1017 """
1018 Microsoft Windows SDK Tools
1019 """
1020 return list(self._sdk_tools())
1021
1022 def _sdk_tools(self):
1023 """
1024 Microsoft Windows SDK Tools paths generator
1025 """
1026 if self.vc_ver < 15.0:
1027 bin_dir = 'Bin' if self.vc_ver <= 11.0 else r'Bin\x86'
1028 yield os.path.join(self.si.WindowsSdkDir, bin_dir)
1029
1030 if not self.pi.current_is_x86():
1031 arch_subdir = self.pi.current_dir(x64=True)
1032 path = 'Bin%s' % arch_subdir
1033 yield os.path.join(self.si.WindowsSdkDir, path)
1034
1035 if self.vc_ver == 10.0 or self.vc_ver == 11.0:
1036 if self.pi.target_is_x86():
1037 arch_subdir = ''
1038 else:
1039 arch_subdir = self.pi.current_dir(hidex86=True, x64=True)
1040 path = r'Bin\NETFX 4.0 Tools%s' % arch_subdir
1041 yield os.path.join(self.si.WindowsSdkDir, path)
1042
1043 elif self.vc_ver >= 15.0:
1044 path = os.path.join(self.si.WindowsSdkDir, 'Bin')
1045 arch_subdir = self.pi.current_dir(x64=True)
1046 sdkver = self.si.WindowsSdkLastVersion
1047 yield os.path.join(path, '%s%s' % (sdkver, arch_subdir))
1048
1049 if self.si.WindowsSDKExecutablePath:
1050 yield self.si.WindowsSDKExecutablePath
1051
1052 @property
1053 def _sdk_subdir(self):
1054 """
1055 Microsoft Windows SDK version subdir
1056 """
1057 ucrtver = self.si.WindowsSdkLastVersion
1058 return ('%s\\' % ucrtver) if ucrtver else ''
1059
1060 @property
1061 def SdkSetup(self):
1062 """
1063 Microsoft Windows SDK Setup
1064 """
1065 if self.vc_ver > 9.0:
1066 return []
1067
1068 return [os.path.join(self.si.WindowsSdkDir, 'Setup')]
1069
1070 @property
1071 def FxTools(self):
1072 """
1073 Microsoft .NET Framework Tools
1074 """
1075 pi = self.pi
1076 si = self.si
1077
1078 if self.vc_ver <= 10.0:
1079 include32 = True
1080 include64 = not pi.target_is_x86() and not pi.current_is_x86()
1081 else:
1082 include32 = pi.target_is_x86() or pi.current_is_x86()
1083 include64 = pi.current_cpu == 'amd64' or pi.target_cpu == 'amd64'
1084
1085 tools = []
1086 if include32:
1087 tools += [os.path.join(si.FrameworkDir32, ver)
1088 for ver in si.FrameworkVersion32]
1089 if include64:
1090 tools += [os.path.join(si.FrameworkDir64, ver)
1091 for ver in si.FrameworkVersion64]
1092 return tools
1093
1094 @property
1095 def NetFxSDKLibraries(self):
1096 """
1097 Microsoft .Net Framework SDK Libraries
1098 """
1099 if self.vc_ver < 14.0 or not self.si.NetFxSdkDir:
1100 return []
1101
1102 arch_subdir = self.pi.target_dir(x64=True)
1103 return [os.path.join(self.si.NetFxSdkDir, r'lib\um%s' % arch_subdir)]
1104
1105 @property
1106 def NetFxSDKIncludes(self):
1107 """
1108 Microsoft .Net Framework SDK Includes
1109 """
1110 if self.vc_ver < 14.0 or not self.si.NetFxSdkDir:
1111 return []
1112
1113 return [os.path.join(self.si.NetFxSdkDir, r'include\um')]
1114
1115 @property
1116 def VsTDb(self):
1117 """
1118 Microsoft Visual Studio Team System Database
1119 """
1120 return [os.path.join(self.si.VSInstallDir, r'VSTSDB\Deploy')]
1121
1122 @property
1123 def MSBuild(self):
1124 """
1125 Microsoft Build Engine
1126 """
1127 if self.vc_ver < 12.0:
1128 return []
1129 elif self.vc_ver < 15.0:
1130 base_path = self.si.ProgramFilesx86
1131 arch_subdir = self.pi.current_dir(hidex86=True)
1132 else:
1133 base_path = self.si.VSInstallDir
1134 arch_subdir = ''
1135
1136 path = r'MSBuild\%0.1f\bin%s' % (self.vc_ver, arch_subdir)
1137 build = [os.path.join(base_path, path)]
1138
1139 if self.vc_ver >= 15.0:
1140 # Add Roslyn C# & Visual Basic Compiler
1141 build += [os.path.join(base_path, path, 'Roslyn')]
1142
1143 return build
1144
1145 @property
1146 def HTMLHelpWorkshop(self):
1147 """
1148 Microsoft HTML Help Workshop
1149 """
1150 if self.vc_ver < 11.0:
1151 return []
1152
1153 return [os.path.join(self.si.ProgramFilesx86, 'HTML Help Workshop')]
1154
1155 @property
1156 def UCRTLibraries(self):
1157 """
1158 Microsoft Universal C Runtime SDK Libraries
1159 """
1160 if self.vc_ver < 14.0:
1161 return []
1162
1163 arch_subdir = self.pi.target_dir(x64=True)
1164 lib = os.path.join(self.si.UniversalCRTSdkDir, 'lib')
1165 ucrtver = self._ucrt_subdir
1166 return [os.path.join(lib, '%sucrt%s' % (ucrtver, arch_subdir))]
1167
1168 @property
1169 def UCRTIncludes(self):
1170 """
1171 Microsoft Universal C Runtime SDK Include
1172 """
1173 if self.vc_ver < 14.0:
1174 return []
1175
1176 include = os.path.join(self.si.UniversalCRTSdkDir, 'include')
1177 return [os.path.join(include, '%sucrt' % self._ucrt_subdir)]
1178
1179 @property
1180 def _ucrt_subdir(self):
1181 """
1182 Microsoft Universal C Runtime SDK version subdir
1183 """
1184 ucrtver = self.si.UniversalCRTSdkLastVersion
1185 return ('%s\\' % ucrtver) if ucrtver else ''
1186
1187 @property
1188 def FSharp(self):
1189 """
1190 Microsoft Visual F#
1191 """
1192 if self.vc_ver < 11.0 and self.vc_ver > 12.0:
1193 return []
1194
1195 return self.si.FSharpInstallDir
1196
1197 @property
1198 def VCRuntimeRedist(self):
1199 """
1200 Microsoft Visual C++ runtime redistribuable dll
1201 """
1202 arch_subdir = self.pi.target_dir(x64=True)
1203 if self.vc_ver < 15:
1204 redist_path = self.si.VCInstallDir
1205 vcruntime = 'redist%s\\Microsoft.VC%d0.CRT\\vcruntime%d0.dll'
1206 else:
1207 redist_path = self.si.VCInstallDir.replace('\\Tools', '\\Redist')
1208 vcruntime = 'onecore%s\\Microsoft.VC%d0.CRT\\vcruntime%d0.dll'
1209
1210 # Visual Studio 2017 is still Visual C++ 14.0
1211 dll_ver = 14.0 if self.vc_ver == 15 else self.vc_ver
1212
1213 vcruntime = vcruntime % (arch_subdir, self.vc_ver, dll_ver)
1214 return os.path.join(redist_path, vcruntime)
1215
1216 def return_env(self, exists=True):
1217 """
1218 Return environment dict.
1219
1220 Parameters
1221 ----------
1222 exists: bool
1223 It True, only return existing paths.
1224 """
1225 env = dict(
1226 include=self._build_paths('include',
1227 [self.VCIncludes,
1228 self.OSIncludes,
1229 self.UCRTIncludes,
1230 self.NetFxSDKIncludes],
1231 exists),
1232 lib=self._build_paths('lib',
1233 [self.VCLibraries,
1234 self.OSLibraries,
1235 self.FxTools,
1236 self.UCRTLibraries,
1237 self.NetFxSDKLibraries],
1238 exists),
1239 libpath=self._build_paths('libpath',
1240 [self.VCLibraries,
1241 self.FxTools,
1242 self.VCStoreRefs,
1243 self.OSLibpath],
1244 exists),
1245 path=self._build_paths('path',
1246 [self.VCTools,
1247 self.VSTools,
1248 self.VsTDb,
1249 self.SdkTools,
1250 self.SdkSetup,
1251 self.FxTools,
1252 self.MSBuild,
1253 self.HTMLHelpWorkshop,
1254 self.FSharp],
1255 exists),
1256 )
1257 if self.vc_ver >= 14 and os.path.isfile(self.VCRuntimeRedist):
1258 env['py_vcruntime_redist'] = self.VCRuntimeRedist
1259 return env
1260
1261 def _build_paths(self, name, spec_path_lists, exists):
1262 """
1263 Given an environment variable name and specified paths,
1264 return a pathsep-separated string of paths containing
1265 unique, extant, directories from those paths and from
1266 the environment variable. Raise an error if no paths
1267 are resolved.
1268 """
1269 # flatten spec_path_lists
1270 spec_paths = itertools.chain.from_iterable(spec_path_lists)
1271 env_paths = safe_env.get(name, '').split(os.pathsep)
1272 paths = itertools.chain(spec_paths, env_paths)
1273 extant_paths = list(filter(os.path.isdir, paths)) if exists else paths
1274 if not extant_paths:
1275 msg = "%s environment variable is empty" % name.upper()
1276 raise distutils.errors.DistutilsPlatformError(msg)
1277 unique_paths = self._unique_everseen(extant_paths)
1278 return os.pathsep.join(unique_paths)
1279
1280 # from Python docs
1281 def _unique_everseen(self, iterable, key=None):
1282 """
1283 List unique elements, preserving order.
1284 Remember all elements ever seen.
1285
1286 _unique_everseen('AAAABBBCCDAABBB') --> A B C D
1287
1288 _unique_everseen('ABBCcAD', str.lower) --> A B C D
1289 """
1290 seen = set()
1291 seen_add = seen.add
1292 if key is None:
1293 for element in filterfalse(seen.__contains__, iterable):
1294 seen_add(element)
1295 yield element
1296 else:
1297 for element in iterable:
1298 k = key(element)
1299 if k not in seen:
1300 seen_add(k)
1301 yield element