Mercurial > repos > guerler > hhblits
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 |