Mercurial > repos > shellac > sam_consensus_v3
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 |