Mercurial > repos > rliterman > csp2
comparison CSP2/CSP2_env/env-d9b9114564458d9d-741b3de822f2aaca6c6caa4325c4afce/lib/python3.8/site-packages/setuptools/msvc.py @ 68:5028fdace37b
planemo upload commit 2e9511a184a1ca667c7be0c6321a36dc4e3d116d
author | jpayne |
---|---|
date | Tue, 18 Mar 2025 16:23:26 -0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
67:0e9998148a16 | 68:5028fdace37b |
---|---|
1 """ | |
2 Environment info about Microsoft Compilers. | |
3 | |
4 >>> getfixture('windows_only') | |
5 >>> ei = EnvironmentInfo('amd64') | |
6 """ | |
7 | |
8 from __future__ import annotations | |
9 | |
10 import contextlib | |
11 import itertools | |
12 import json | |
13 import os | |
14 import os.path | |
15 import platform | |
16 from typing import TYPE_CHECKING | |
17 | |
18 from more_itertools import unique_everseen | |
19 | |
20 import distutils.errors | |
21 | |
22 # https://github.com/python/mypy/issues/8166 | |
23 if not TYPE_CHECKING and platform.system() == 'Windows': | |
24 import winreg | |
25 from os import environ | |
26 else: | |
27 # Mock winreg and environ so the module can be imported on this platform. | |
28 | |
29 class winreg: | |
30 HKEY_USERS = None | |
31 HKEY_CURRENT_USER = None | |
32 HKEY_LOCAL_MACHINE = None | |
33 HKEY_CLASSES_ROOT = None | |
34 | |
35 environ: dict[str, str] = dict() | |
36 | |
37 | |
38 class PlatformInfo: | |
39 """ | |
40 Current and Target Architectures information. | |
41 | |
42 Parameters | |
43 ---------- | |
44 arch: str | |
45 Target architecture. | |
46 """ | |
47 | |
48 current_cpu = environ.get('processor_architecture', '').lower() | |
49 | |
50 def __init__(self, arch): | |
51 self.arch = arch.lower().replace('x64', 'amd64') | |
52 | |
53 @property | |
54 def target_cpu(self): | |
55 """ | |
56 Return Target CPU architecture. | |
57 | |
58 Return | |
59 ------ | |
60 str | |
61 Target CPU | |
62 """ | |
63 return self.arch[self.arch.find('_') + 1 :] | |
64 | |
65 def target_is_x86(self): | |
66 """ | |
67 Return True if target CPU is x86 32 bits.. | |
68 | |
69 Return | |
70 ------ | |
71 bool | |
72 CPU is x86 32 bits | |
73 """ | |
74 return self.target_cpu == 'x86' | |
75 | |
76 def current_is_x86(self): | |
77 """ | |
78 Return True if current CPU is x86 32 bits.. | |
79 | |
80 Return | |
81 ------ | |
82 bool | |
83 CPU is x86 32 bits | |
84 """ | |
85 return self.current_cpu == 'x86' | |
86 | |
87 def current_dir(self, hidex86=False, x64=False): | |
88 """ | |
89 Current platform specific subfolder. | |
90 | |
91 Parameters | |
92 ---------- | |
93 hidex86: bool | |
94 return '' and not '\x86' if architecture is x86. | |
95 x64: bool | |
96 return '\x64' and not '\amd64' if architecture is amd64. | |
97 | |
98 Return | |
99 ------ | |
100 str | |
101 subfolder: '\target', or '' (see hidex86 parameter) | |
102 """ | |
103 return ( | |
104 '' | |
105 if (self.current_cpu == 'x86' and hidex86) | |
106 else r'\x64' | |
107 if (self.current_cpu == 'amd64' and x64) | |
108 else r'\%s' % self.current_cpu | |
109 ) | |
110 | |
111 def target_dir(self, hidex86=False, x64=False): | |
112 r""" | |
113 Target platform specific subfolder. | |
114 | |
115 Parameters | |
116 ---------- | |
117 hidex86: bool | |
118 return '' and not '\x86' if architecture is x86. | |
119 x64: bool | |
120 return '\x64' and not '\amd64' if architecture is amd64. | |
121 | |
122 Return | |
123 ------ | |
124 str | |
125 subfolder: '\current', or '' (see hidex86 parameter) | |
126 """ | |
127 return ( | |
128 '' | |
129 if (self.target_cpu == 'x86' and hidex86) | |
130 else r'\x64' | |
131 if (self.target_cpu == 'amd64' and x64) | |
132 else r'\%s' % self.target_cpu | |
133 ) | |
134 | |
135 def cross_dir(self, forcex86=False): | |
136 r""" | |
137 Cross platform specific subfolder. | |
138 | |
139 Parameters | |
140 ---------- | |
141 forcex86: bool | |
142 Use 'x86' as current architecture even if current architecture is | |
143 not x86. | |
144 | |
145 Return | |
146 ------ | |
147 str | |
148 subfolder: '' if target architecture is current architecture, | |
149 '\current_target' if not. | |
150 """ | |
151 current = 'x86' if forcex86 else self.current_cpu | |
152 return ( | |
153 '' | |
154 if self.target_cpu == current | |
155 else self.target_dir().replace('\\', '\\%s_' % current) | |
156 ) | |
157 | |
158 | |
159 class RegistryInfo: | |
160 """ | |
161 Microsoft Visual Studio related registry information. | |
162 | |
163 Parameters | |
164 ---------- | |
165 platform_info: PlatformInfo | |
166 "PlatformInfo" instance. | |
167 """ | |
168 | |
169 HKEYS = ( | |
170 winreg.HKEY_USERS, | |
171 winreg.HKEY_CURRENT_USER, | |
172 winreg.HKEY_LOCAL_MACHINE, | |
173 winreg.HKEY_CLASSES_ROOT, | |
174 ) | |
175 | |
176 def __init__(self, platform_info): | |
177 self.pi = platform_info | |
178 | |
179 @property | |
180 def visualstudio(self): | |
181 """ | |
182 Microsoft Visual Studio root registry key. | |
183 | |
184 Return | |
185 ------ | |
186 str | |
187 Registry key | |
188 """ | |
189 return 'VisualStudio' | |
190 | |
191 @property | |
192 def sxs(self): | |
193 """ | |
194 Microsoft Visual Studio SxS registry key. | |
195 | |
196 Return | |
197 ------ | |
198 str | |
199 Registry key | |
200 """ | |
201 return os.path.join(self.visualstudio, 'SxS') | |
202 | |
203 @property | |
204 def vc(self): | |
205 """ | |
206 Microsoft Visual C++ VC7 registry key. | |
207 | |
208 Return | |
209 ------ | |
210 str | |
211 Registry key | |
212 """ | |
213 return os.path.join(self.sxs, 'VC7') | |
214 | |
215 @property | |
216 def vs(self): | |
217 """ | |
218 Microsoft Visual Studio VS7 registry key. | |
219 | |
220 Return | |
221 ------ | |
222 str | |
223 Registry key | |
224 """ | |
225 return os.path.join(self.sxs, 'VS7') | |
226 | |
227 @property | |
228 def vc_for_python(self): | |
229 """ | |
230 Microsoft Visual C++ for Python registry key. | |
231 | |
232 Return | |
233 ------ | |
234 str | |
235 Registry key | |
236 """ | |
237 return r'DevDiv\VCForPython' | |
238 | |
239 @property | |
240 def microsoft_sdk(self): | |
241 """ | |
242 Microsoft SDK registry key. | |
243 | |
244 Return | |
245 ------ | |
246 str | |
247 Registry key | |
248 """ | |
249 return 'Microsoft SDKs' | |
250 | |
251 @property | |
252 def windows_sdk(self): | |
253 """ | |
254 Microsoft Windows/Platform SDK registry key. | |
255 | |
256 Return | |
257 ------ | |
258 str | |
259 Registry key | |
260 """ | |
261 return os.path.join(self.microsoft_sdk, 'Windows') | |
262 | |
263 @property | |
264 def netfx_sdk(self): | |
265 """ | |
266 Microsoft .NET Framework SDK registry key. | |
267 | |
268 Return | |
269 ------ | |
270 str | |
271 Registry key | |
272 """ | |
273 return os.path.join(self.microsoft_sdk, 'NETFXSDK') | |
274 | |
275 @property | |
276 def windows_kits_roots(self): | |
277 """ | |
278 Microsoft Windows Kits Roots registry key. | |
279 | |
280 Return | |
281 ------ | |
282 str | |
283 Registry key | |
284 """ | |
285 return r'Windows Kits\Installed Roots' | |
286 | |
287 def microsoft(self, key, x86=False): | |
288 """ | |
289 Return key in Microsoft software registry. | |
290 | |
291 Parameters | |
292 ---------- | |
293 key: str | |
294 Registry key path where look. | |
295 x86: str | |
296 Force x86 software registry. | |
297 | |
298 Return | |
299 ------ | |
300 str | |
301 Registry key | |
302 """ | |
303 node64 = '' if self.pi.current_is_x86() or x86 else 'Wow6432Node' | |
304 return os.path.join('Software', node64, 'Microsoft', key) | |
305 | |
306 def lookup(self, key, name): | |
307 """ | |
308 Look for values in registry in Microsoft software registry. | |
309 | |
310 Parameters | |
311 ---------- | |
312 key: str | |
313 Registry key path where look. | |
314 name: str | |
315 Value name to find. | |
316 | |
317 Return | |
318 ------ | |
319 str | |
320 value | |
321 """ | |
322 key_read = winreg.KEY_READ | |
323 openkey = winreg.OpenKey | |
324 closekey = winreg.CloseKey | |
325 ms = self.microsoft | |
326 for hkey in self.HKEYS: | |
327 bkey = None | |
328 try: | |
329 bkey = openkey(hkey, ms(key), 0, key_read) | |
330 except OSError: | |
331 if not self.pi.current_is_x86(): | |
332 try: | |
333 bkey = openkey(hkey, ms(key, True), 0, key_read) | |
334 except OSError: | |
335 continue | |
336 else: | |
337 continue | |
338 try: | |
339 return winreg.QueryValueEx(bkey, name)[0] | |
340 except OSError: | |
341 pass | |
342 finally: | |
343 if bkey: | |
344 closekey(bkey) | |
345 return None | |
346 | |
347 | |
348 class SystemInfo: | |
349 """ | |
350 Microsoft Windows and Visual Studio related system information. | |
351 | |
352 Parameters | |
353 ---------- | |
354 registry_info: RegistryInfo | |
355 "RegistryInfo" instance. | |
356 vc_ver: float | |
357 Required Microsoft Visual C++ version. | |
358 """ | |
359 | |
360 # Variables and properties in this class use originals CamelCase variables | |
361 # names from Microsoft source files for more easy comparison. | |
362 WinDir = environ.get('WinDir', '') | |
363 ProgramFiles = environ.get('ProgramFiles', '') | |
364 ProgramFilesx86 = environ.get('ProgramFiles(x86)', ProgramFiles) | |
365 | |
366 def __init__(self, registry_info, vc_ver=None): | |
367 self.ri = registry_info | |
368 self.pi = self.ri.pi | |
369 | |
370 self.known_vs_paths = self.find_programdata_vs_vers() | |
371 | |
372 # Except for VS15+, VC version is aligned with VS version | |
373 self.vs_ver = self.vc_ver = vc_ver or self._find_latest_available_vs_ver() | |
374 | |
375 def _find_latest_available_vs_ver(self): | |
376 """ | |
377 Find the latest VC version | |
378 | |
379 Return | |
380 ------ | |
381 float | |
382 version | |
383 """ | |
384 reg_vc_vers = self.find_reg_vs_vers() | |
385 | |
386 if not (reg_vc_vers or self.known_vs_paths): | |
387 raise distutils.errors.DistutilsPlatformError( | |
388 'No Microsoft Visual C++ version found' | |
389 ) | |
390 | |
391 vc_vers = set(reg_vc_vers) | |
392 vc_vers.update(self.known_vs_paths) | |
393 return sorted(vc_vers)[-1] | |
394 | |
395 def find_reg_vs_vers(self): | |
396 """ | |
397 Find Microsoft Visual Studio versions available in registry. | |
398 | |
399 Return | |
400 ------ | |
401 list of float | |
402 Versions | |
403 """ | |
404 ms = self.ri.microsoft | |
405 vckeys = (self.ri.vc, self.ri.vc_for_python, self.ri.vs) | |
406 vs_vers = [] | |
407 for hkey, key in itertools.product(self.ri.HKEYS, vckeys): | |
408 try: | |
409 bkey = winreg.OpenKey(hkey, ms(key), 0, winreg.KEY_READ) | |
410 except OSError: | |
411 continue | |
412 with bkey: | |
413 subkeys, values, _ = winreg.QueryInfoKey(bkey) | |
414 for i in range(values): | |
415 with contextlib.suppress(ValueError): | |
416 ver = float(winreg.EnumValue(bkey, i)[0]) | |
417 if ver not in vs_vers: | |
418 vs_vers.append(ver) | |
419 for i in range(subkeys): | |
420 with contextlib.suppress(ValueError): | |
421 ver = float(winreg.EnumKey(bkey, i)) | |
422 if ver not in vs_vers: | |
423 vs_vers.append(ver) | |
424 return sorted(vs_vers) | |
425 | |
426 def find_programdata_vs_vers(self): | |
427 r""" | |
428 Find Visual studio 2017+ versions from information in | |
429 "C:\ProgramData\Microsoft\VisualStudio\Packages\_Instances". | |
430 | |
431 Return | |
432 ------ | |
433 dict | |
434 float version as key, path as value. | |
435 """ | |
436 vs_versions = {} | |
437 instances_dir = r'C:\ProgramData\Microsoft\VisualStudio\Packages\_Instances' | |
438 | |
439 try: | |
440 hashed_names = os.listdir(instances_dir) | |
441 | |
442 except OSError: | |
443 # Directory not exists with all Visual Studio versions | |
444 return vs_versions | |
445 | |
446 for name in hashed_names: | |
447 try: | |
448 # Get VS installation path from "state.json" file | |
449 state_path = os.path.join(instances_dir, name, 'state.json') | |
450 with open(state_path, 'rt', encoding='utf-8') as state_file: | |
451 state = json.load(state_file) | |
452 vs_path = state['installationPath'] | |
453 | |
454 # Raises OSError if this VS installation does not contain VC | |
455 os.listdir(os.path.join(vs_path, r'VC\Tools\MSVC')) | |
456 | |
457 # Store version and path | |
458 vs_versions[self._as_float_version(state['installationVersion'])] = ( | |
459 vs_path | |
460 ) | |
461 | |
462 except (OSError, KeyError): | |
463 # Skip if "state.json" file is missing or bad format | |
464 continue | |
465 | |
466 return vs_versions | |
467 | |
468 @staticmethod | |
469 def _as_float_version(version): | |
470 """ | |
471 Return a string version as a simplified float version (major.minor) | |
472 | |
473 Parameters | |
474 ---------- | |
475 version: str | |
476 Version. | |
477 | |
478 Return | |
479 ------ | |
480 float | |
481 version | |
482 """ | |
483 return float('.'.join(version.split('.')[:2])) | |
484 | |
485 @property | |
486 def VSInstallDir(self): | |
487 """ | |
488 Microsoft Visual Studio directory. | |
489 | |
490 Return | |
491 ------ | |
492 str | |
493 path | |
494 """ | |
495 # Default path | |
496 default = os.path.join( | |
497 self.ProgramFilesx86, 'Microsoft Visual Studio %0.1f' % self.vs_ver | |
498 ) | |
499 | |
500 # Try to get path from registry, if fail use default path | |
501 return self.ri.lookup(self.ri.vs, '%0.1f' % self.vs_ver) or default | |
502 | |
503 @property | |
504 def VCInstallDir(self): | |
505 """ | |
506 Microsoft Visual C++ directory. | |
507 | |
508 Return | |
509 ------ | |
510 str | |
511 path | |
512 """ | |
513 path = self._guess_vc() or self._guess_vc_legacy() | |
514 | |
515 if not os.path.isdir(path): | |
516 msg = 'Microsoft Visual C++ directory not found' | |
517 raise distutils.errors.DistutilsPlatformError(msg) | |
518 | |
519 return path | |
520 | |
521 def _guess_vc(self): | |
522 """ | |
523 Locate Visual C++ for VS2017+. | |
524 | |
525 Return | |
526 ------ | |
527 str | |
528 path | |
529 """ | |
530 if self.vs_ver <= 14.0: | |
531 return '' | |
532 | |
533 try: | |
534 # First search in known VS paths | |
535 vs_dir = self.known_vs_paths[self.vs_ver] | |
536 except KeyError: | |
537 # Else, search with path from registry | |
538 vs_dir = self.VSInstallDir | |
539 | |
540 guess_vc = os.path.join(vs_dir, r'VC\Tools\MSVC') | |
541 | |
542 # Subdir with VC exact version as name | |
543 try: | |
544 # Update the VC version with real one instead of VS version | |
545 vc_ver = os.listdir(guess_vc)[-1] | |
546 self.vc_ver = self._as_float_version(vc_ver) | |
547 return os.path.join(guess_vc, vc_ver) | |
548 except (OSError, IndexError): | |
549 return '' | |
550 | |
551 def _guess_vc_legacy(self): | |
552 """ | |
553 Locate Visual C++ for versions prior to 2017. | |
554 | |
555 Return | |
556 ------ | |
557 str | |
558 path | |
559 """ | |
560 default = os.path.join( | |
561 self.ProgramFilesx86, r'Microsoft Visual Studio %0.1f\VC' % self.vs_ver | |
562 ) | |
563 | |
564 # Try to get "VC++ for Python" path from registry as default path | |
565 reg_path = os.path.join(self.ri.vc_for_python, '%0.1f' % self.vs_ver) | |
566 python_vc = self.ri.lookup(reg_path, 'installdir') | |
567 default_vc = os.path.join(python_vc, 'VC') if python_vc else default | |
568 | |
569 # Try to get path from registry, if fail use default path | |
570 return self.ri.lookup(self.ri.vc, '%0.1f' % self.vs_ver) or default_vc | |
571 | |
572 @property | |
573 def WindowsSdkVersion(self): | |
574 """ | |
575 Microsoft Windows SDK versions for specified MSVC++ version. | |
576 | |
577 Return | |
578 ------ | |
579 tuple of str | |
580 versions | |
581 """ | |
582 if self.vs_ver <= 9.0: | |
583 return '7.0', '6.1', '6.0a' | |
584 elif self.vs_ver == 10.0: | |
585 return '7.1', '7.0a' | |
586 elif self.vs_ver == 11.0: | |
587 return '8.0', '8.0a' | |
588 elif self.vs_ver == 12.0: | |
589 return '8.1', '8.1a' | |
590 elif self.vs_ver >= 14.0: | |
591 return '10.0', '8.1' | |
592 return None | |
593 | |
594 @property | |
595 def WindowsSdkLastVersion(self): | |
596 """ | |
597 Microsoft Windows SDK last version. | |
598 | |
599 Return | |
600 ------ | |
601 str | |
602 version | |
603 """ | |
604 return self._use_last_dir_name(os.path.join(self.WindowsSdkDir, 'lib')) | |
605 | |
606 @property | |
607 def WindowsSdkDir(self): # noqa: C901 # is too complex (12) # FIXME | |
608 """ | |
609 Microsoft Windows SDK directory. | |
610 | |
611 Return | |
612 ------ | |
613 str | |
614 path | |
615 """ | |
616 sdkdir = '' | |
617 for ver in self.WindowsSdkVersion: | |
618 # Try to get it from registry | |
619 loc = os.path.join(self.ri.windows_sdk, 'v%s' % ver) | |
620 sdkdir = self.ri.lookup(loc, 'installationfolder') | |
621 if sdkdir: | |
622 break | |
623 if not sdkdir or not os.path.isdir(sdkdir): | |
624 # Try to get "VC++ for Python" version from registry | |
625 path = os.path.join(self.ri.vc_for_python, '%0.1f' % self.vc_ver) | |
626 install_base = self.ri.lookup(path, 'installdir') | |
627 if install_base: | |
628 sdkdir = os.path.join(install_base, 'WinSDK') | |
629 if not sdkdir or not os.path.isdir(sdkdir): | |
630 # If fail, use default new path | |
631 for ver in self.WindowsSdkVersion: | |
632 intver = ver[: ver.rfind('.')] | |
633 path = r'Microsoft SDKs\Windows Kits\%s' % intver | |
634 d = os.path.join(self.ProgramFiles, path) | |
635 if os.path.isdir(d): | |
636 sdkdir = d | |
637 if not sdkdir or not os.path.isdir(sdkdir): | |
638 # If fail, use default old path | |
639 for ver in self.WindowsSdkVersion: | |
640 path = r'Microsoft SDKs\Windows\v%s' % ver | |
641 d = os.path.join(self.ProgramFiles, path) | |
642 if os.path.isdir(d): | |
643 sdkdir = d | |
644 if not sdkdir: | |
645 # If fail, use Platform SDK | |
646 sdkdir = os.path.join(self.VCInstallDir, 'PlatformSDK') | |
647 return sdkdir | |
648 | |
649 @property | |
650 def WindowsSDKExecutablePath(self): | |
651 """ | |
652 Microsoft Windows SDK executable directory. | |
653 | |
654 Return | |
655 ------ | |
656 str | |
657 path | |
658 """ | |
659 # Find WinSDK NetFx Tools registry dir name | |
660 if self.vs_ver <= 11.0: | |
661 netfxver = 35 | |
662 arch = '' | |
663 else: | |
664 netfxver = 40 | |
665 hidex86 = True if self.vs_ver <= 12.0 else False | |
666 arch = self.pi.current_dir(x64=True, hidex86=hidex86) | |
667 fx = 'WinSDK-NetFx%dTools%s' % (netfxver, arch.replace('\\', '-')) | |
668 | |
669 # list all possibles registry paths | |
670 regpaths = [] | |
671 if self.vs_ver >= 14.0: | |
672 for ver in self.NetFxSdkVersion: | |
673 regpaths += [os.path.join(self.ri.netfx_sdk, ver, fx)] | |
674 | |
675 for ver in self.WindowsSdkVersion: | |
676 regpaths += [os.path.join(self.ri.windows_sdk, 'v%sA' % ver, fx)] | |
677 | |
678 # Return installation folder from the more recent path | |
679 for path in regpaths: | |
680 execpath = self.ri.lookup(path, 'installationfolder') | |
681 if execpath: | |
682 return execpath | |
683 | |
684 return None | |
685 | |
686 @property | |
687 def FSharpInstallDir(self): | |
688 """ | |
689 Microsoft Visual F# directory. | |
690 | |
691 Return | |
692 ------ | |
693 str | |
694 path | |
695 """ | |
696 path = os.path.join(self.ri.visualstudio, r'%0.1f\Setup\F#' % self.vs_ver) | |
697 return self.ri.lookup(path, 'productdir') or '' | |
698 | |
699 @property | |
700 def UniversalCRTSdkDir(self): | |
701 """ | |
702 Microsoft Universal CRT SDK directory. | |
703 | |
704 Return | |
705 ------ | |
706 str | |
707 path | |
708 """ | |
709 # Set Kit Roots versions for specified MSVC++ version | |
710 vers = ('10', '81') if self.vs_ver >= 14.0 else () | |
711 | |
712 # Find path of the more recent Kit | |
713 for ver in vers: | |
714 sdkdir = self.ri.lookup(self.ri.windows_kits_roots, 'kitsroot%s' % ver) | |
715 if sdkdir: | |
716 return sdkdir or '' | |
717 | |
718 return None | |
719 | |
720 @property | |
721 def UniversalCRTSdkLastVersion(self): | |
722 """ | |
723 Microsoft Universal C Runtime SDK last version. | |
724 | |
725 Return | |
726 ------ | |
727 str | |
728 version | |
729 """ | |
730 return self._use_last_dir_name(os.path.join(self.UniversalCRTSdkDir, 'lib')) | |
731 | |
732 @property | |
733 def NetFxSdkVersion(self): | |
734 """ | |
735 Microsoft .NET Framework SDK versions. | |
736 | |
737 Return | |
738 ------ | |
739 tuple of str | |
740 versions | |
741 """ | |
742 # Set FxSdk versions for specified VS version | |
743 return ( | |
744 ('4.7.2', '4.7.1', '4.7', '4.6.2', '4.6.1', '4.6', '4.5.2', '4.5.1', '4.5') | |
745 if self.vs_ver >= 14.0 | |
746 else () | |
747 ) | |
748 | |
749 @property | |
750 def NetFxSdkDir(self): | |
751 """ | |
752 Microsoft .NET Framework SDK directory. | |
753 | |
754 Return | |
755 ------ | |
756 str | |
757 path | |
758 """ | |
759 sdkdir = '' | |
760 for ver in self.NetFxSdkVersion: | |
761 loc = os.path.join(self.ri.netfx_sdk, ver) | |
762 sdkdir = self.ri.lookup(loc, 'kitsinstallationfolder') | |
763 if sdkdir: | |
764 break | |
765 return sdkdir | |
766 | |
767 @property | |
768 def FrameworkDir32(self): | |
769 """ | |
770 Microsoft .NET Framework 32bit directory. | |
771 | |
772 Return | |
773 ------ | |
774 str | |
775 path | |
776 """ | |
777 # Default path | |
778 guess_fw = os.path.join(self.WinDir, r'Microsoft.NET\Framework') | |
779 | |
780 # Try to get path from registry, if fail use default path | |
781 return self.ri.lookup(self.ri.vc, 'frameworkdir32') or guess_fw | |
782 | |
783 @property | |
784 def FrameworkDir64(self): | |
785 """ | |
786 Microsoft .NET Framework 64bit directory. | |
787 | |
788 Return | |
789 ------ | |
790 str | |
791 path | |
792 """ | |
793 # Default path | |
794 guess_fw = os.path.join(self.WinDir, r'Microsoft.NET\Framework64') | |
795 | |
796 # Try to get path from registry, if fail use default path | |
797 return self.ri.lookup(self.ri.vc, 'frameworkdir64') or guess_fw | |
798 | |
799 @property | |
800 def FrameworkVersion32(self): | |
801 """ | |
802 Microsoft .NET Framework 32bit versions. | |
803 | |
804 Return | |
805 ------ | |
806 tuple of str | |
807 versions | |
808 """ | |
809 return self._find_dot_net_versions(32) | |
810 | |
811 @property | |
812 def FrameworkVersion64(self): | |
813 """ | |
814 Microsoft .NET Framework 64bit versions. | |
815 | |
816 Return | |
817 ------ | |
818 tuple of str | |
819 versions | |
820 """ | |
821 return self._find_dot_net_versions(64) | |
822 | |
823 def _find_dot_net_versions(self, bits): | |
824 """ | |
825 Find Microsoft .NET Framework versions. | |
826 | |
827 Parameters | |
828 ---------- | |
829 bits: int | |
830 Platform number of bits: 32 or 64. | |
831 | |
832 Return | |
833 ------ | |
834 tuple of str | |
835 versions | |
836 """ | |
837 # Find actual .NET version in registry | |
838 reg_ver = self.ri.lookup(self.ri.vc, 'frameworkver%d' % bits) | |
839 dot_net_dir = getattr(self, 'FrameworkDir%d' % bits) | |
840 ver = reg_ver or self._use_last_dir_name(dot_net_dir, 'v') or '' | |
841 | |
842 # Set .NET versions for specified MSVC++ version | |
843 if self.vs_ver >= 12.0: | |
844 return ver, 'v4.0' | |
845 elif self.vs_ver >= 10.0: | |
846 return 'v4.0.30319' if ver.lower()[:2] != 'v4' else ver, 'v3.5' | |
847 elif self.vs_ver == 9.0: | |
848 return 'v3.5', 'v2.0.50727' | |
849 elif self.vs_ver == 8.0: | |
850 return 'v3.0', 'v2.0.50727' | |
851 return None | |
852 | |
853 @staticmethod | |
854 def _use_last_dir_name(path, prefix=''): | |
855 """ | |
856 Return name of the last dir in path or '' if no dir found. | |
857 | |
858 Parameters | |
859 ---------- | |
860 path: str | |
861 Use dirs in this path | |
862 prefix: str | |
863 Use only dirs starting by this prefix | |
864 | |
865 Return | |
866 ------ | |
867 str | |
868 name | |
869 """ | |
870 matching_dirs = ( | |
871 dir_name | |
872 for dir_name in reversed(os.listdir(path)) | |
873 if os.path.isdir(os.path.join(path, dir_name)) | |
874 and dir_name.startswith(prefix) | |
875 ) | |
876 return next(matching_dirs, None) or '' | |
877 | |
878 | |
879 class EnvironmentInfo: | |
880 """ | |
881 Return environment variables for specified Microsoft Visual C++ version | |
882 and platform : Lib, Include, Path and libpath. | |
883 | |
884 This function is compatible with Microsoft Visual C++ 9.0 to 14.X. | |
885 | |
886 Script created by analysing Microsoft environment configuration files like | |
887 "vcvars[...].bat", "SetEnv.Cmd", "vcbuildtools.bat", ... | |
888 | |
889 Parameters | |
890 ---------- | |
891 arch: str | |
892 Target architecture. | |
893 vc_ver: float | |
894 Required Microsoft Visual C++ version. If not set, autodetect the last | |
895 version. | |
896 vc_min_ver: float | |
897 Minimum Microsoft Visual C++ version. | |
898 """ | |
899 | |
900 # Variables and properties in this class use originals CamelCase variables | |
901 # names from Microsoft source files for more easy comparison. | |
902 | |
903 def __init__(self, arch, vc_ver=None, vc_min_ver=0): | |
904 self.pi = PlatformInfo(arch) | |
905 self.ri = RegistryInfo(self.pi) | |
906 self.si = SystemInfo(self.ri, vc_ver) | |
907 | |
908 if self.vc_ver < vc_min_ver: | |
909 err = 'No suitable Microsoft Visual C++ version found' | |
910 raise distutils.errors.DistutilsPlatformError(err) | |
911 | |
912 @property | |
913 def vs_ver(self): | |
914 """ | |
915 Microsoft Visual Studio. | |
916 | |
917 Return | |
918 ------ | |
919 float | |
920 version | |
921 """ | |
922 return self.si.vs_ver | |
923 | |
924 @property | |
925 def vc_ver(self): | |
926 """ | |
927 Microsoft Visual C++ version. | |
928 | |
929 Return | |
930 ------ | |
931 float | |
932 version | |
933 """ | |
934 return self.si.vc_ver | |
935 | |
936 @property | |
937 def VSTools(self): | |
938 """ | |
939 Microsoft Visual Studio Tools. | |
940 | |
941 Return | |
942 ------ | |
943 list of str | |
944 paths | |
945 """ | |
946 paths = [r'Common7\IDE', r'Common7\Tools'] | |
947 | |
948 if self.vs_ver >= 14.0: | |
949 arch_subdir = self.pi.current_dir(hidex86=True, x64=True) | |
950 paths += [r'Common7\IDE\CommonExtensions\Microsoft\TestWindow'] | |
951 paths += [r'Team Tools\Performance Tools'] | |
952 paths += [r'Team Tools\Performance Tools%s' % arch_subdir] | |
953 | |
954 return [os.path.join(self.si.VSInstallDir, path) for path in paths] | |
955 | |
956 @property | |
957 def VCIncludes(self): | |
958 """ | |
959 Microsoft Visual C++ & Microsoft Foundation Class Includes. | |
960 | |
961 Return | |
962 ------ | |
963 list of str | |
964 paths | |
965 """ | |
966 return [ | |
967 os.path.join(self.si.VCInstallDir, 'Include'), | |
968 os.path.join(self.si.VCInstallDir, r'ATLMFC\Include'), | |
969 ] | |
970 | |
971 @property | |
972 def VCLibraries(self): | |
973 """ | |
974 Microsoft Visual C++ & Microsoft Foundation Class Libraries. | |
975 | |
976 Return | |
977 ------ | |
978 list of str | |
979 paths | |
980 """ | |
981 if self.vs_ver >= 15.0: | |
982 arch_subdir = self.pi.target_dir(x64=True) | |
983 else: | |
984 arch_subdir = self.pi.target_dir(hidex86=True) | |
985 paths = ['Lib%s' % arch_subdir, r'ATLMFC\Lib%s' % arch_subdir] | |
986 | |
987 if self.vs_ver >= 14.0: | |
988 paths += [r'Lib\store%s' % arch_subdir] | |
989 | |
990 return [os.path.join(self.si.VCInstallDir, path) for path in paths] | |
991 | |
992 @property | |
993 def VCStoreRefs(self): | |
994 """ | |
995 Microsoft Visual C++ store references Libraries. | |
996 | |
997 Return | |
998 ------ | |
999 list of str | |
1000 paths | |
1001 """ | |
1002 if self.vs_ver < 14.0: | |
1003 return [] | |
1004 return [os.path.join(self.si.VCInstallDir, r'Lib\store\references')] | |
1005 | |
1006 @property | |
1007 def VCTools(self): | |
1008 """ | |
1009 Microsoft Visual C++ Tools. | |
1010 | |
1011 Return | |
1012 ------ | |
1013 list of str | |
1014 paths | |
1015 """ | |
1016 si = self.si | |
1017 tools = [os.path.join(si.VCInstallDir, 'VCPackages')] | |
1018 | |
1019 forcex86 = True if self.vs_ver <= 10.0 else False | |
1020 arch_subdir = self.pi.cross_dir(forcex86) | |
1021 if arch_subdir: | |
1022 tools += [os.path.join(si.VCInstallDir, 'Bin%s' % arch_subdir)] | |
1023 | |
1024 if self.vs_ver == 14.0: | |
1025 path = 'Bin%s' % self.pi.current_dir(hidex86=True) | |
1026 tools += [os.path.join(si.VCInstallDir, path)] | |
1027 | |
1028 elif self.vs_ver >= 15.0: | |
1029 host_dir = ( | |
1030 r'bin\HostX86%s' if self.pi.current_is_x86() else r'bin\HostX64%s' | |
1031 ) | |
1032 tools += [ | |
1033 os.path.join(si.VCInstallDir, host_dir % self.pi.target_dir(x64=True)) | |
1034 ] | |
1035 | |
1036 if self.pi.current_cpu != self.pi.target_cpu: | |
1037 tools += [ | |
1038 os.path.join( | |
1039 si.VCInstallDir, host_dir % self.pi.current_dir(x64=True) | |
1040 ) | |
1041 ] | |
1042 | |
1043 else: | |
1044 tools += [os.path.join(si.VCInstallDir, 'Bin')] | |
1045 | |
1046 return tools | |
1047 | |
1048 @property | |
1049 def OSLibraries(self): | |
1050 """ | |
1051 Microsoft Windows SDK Libraries. | |
1052 | |
1053 Return | |
1054 ------ | |
1055 list of str | |
1056 paths | |
1057 """ | |
1058 if self.vs_ver <= 10.0: | |
1059 arch_subdir = self.pi.target_dir(hidex86=True, x64=True) | |
1060 return [os.path.join(self.si.WindowsSdkDir, 'Lib%s' % arch_subdir)] | |
1061 | |
1062 else: | |
1063 arch_subdir = self.pi.target_dir(x64=True) | |
1064 lib = os.path.join(self.si.WindowsSdkDir, 'lib') | |
1065 libver = self._sdk_subdir | |
1066 return [os.path.join(lib, '%sum%s' % (libver, arch_subdir))] | |
1067 | |
1068 @property | |
1069 def OSIncludes(self): | |
1070 """ | |
1071 Microsoft Windows SDK Include. | |
1072 | |
1073 Return | |
1074 ------ | |
1075 list of str | |
1076 paths | |
1077 """ | |
1078 include = os.path.join(self.si.WindowsSdkDir, 'include') | |
1079 | |
1080 if self.vs_ver <= 10.0: | |
1081 return [include, os.path.join(include, 'gl')] | |
1082 | |
1083 else: | |
1084 if self.vs_ver >= 14.0: | |
1085 sdkver = self._sdk_subdir | |
1086 else: | |
1087 sdkver = '' | |
1088 return [ | |
1089 os.path.join(include, '%sshared' % sdkver), | |
1090 os.path.join(include, '%sum' % sdkver), | |
1091 os.path.join(include, '%swinrt' % sdkver), | |
1092 ] | |
1093 | |
1094 @property | |
1095 def OSLibpath(self): | |
1096 """ | |
1097 Microsoft Windows SDK Libraries Paths. | |
1098 | |
1099 Return | |
1100 ------ | |
1101 list of str | |
1102 paths | |
1103 """ | |
1104 ref = os.path.join(self.si.WindowsSdkDir, 'References') | |
1105 libpath = [] | |
1106 | |
1107 if self.vs_ver <= 9.0: | |
1108 libpath += self.OSLibraries | |
1109 | |
1110 if self.vs_ver >= 11.0: | |
1111 libpath += [os.path.join(ref, r'CommonConfiguration\Neutral')] | |
1112 | |
1113 if self.vs_ver >= 14.0: | |
1114 libpath += [ | |
1115 ref, | |
1116 os.path.join(self.si.WindowsSdkDir, 'UnionMetadata'), | |
1117 os.path.join(ref, 'Windows.Foundation.UniversalApiContract', '1.0.0.0'), | |
1118 os.path.join(ref, 'Windows.Foundation.FoundationContract', '1.0.0.0'), | |
1119 os.path.join( | |
1120 ref, 'Windows.Networking.Connectivity.WwanContract', '1.0.0.0' | |
1121 ), | |
1122 os.path.join( | |
1123 self.si.WindowsSdkDir, | |
1124 'ExtensionSDKs', | |
1125 'Microsoft.VCLibs', | |
1126 '%0.1f' % self.vs_ver, | |
1127 'References', | |
1128 'CommonConfiguration', | |
1129 'neutral', | |
1130 ), | |
1131 ] | |
1132 return libpath | |
1133 | |
1134 @property | |
1135 def SdkTools(self): | |
1136 """ | |
1137 Microsoft Windows SDK Tools. | |
1138 | |
1139 Return | |
1140 ------ | |
1141 list of str | |
1142 paths | |
1143 """ | |
1144 return list(self._sdk_tools()) | |
1145 | |
1146 def _sdk_tools(self): | |
1147 """ | |
1148 Microsoft Windows SDK Tools paths generator. | |
1149 | |
1150 Return | |
1151 ------ | |
1152 generator of str | |
1153 paths | |
1154 """ | |
1155 if self.vs_ver < 15.0: | |
1156 bin_dir = 'Bin' if self.vs_ver <= 11.0 else r'Bin\x86' | |
1157 yield os.path.join(self.si.WindowsSdkDir, bin_dir) | |
1158 | |
1159 if not self.pi.current_is_x86(): | |
1160 arch_subdir = self.pi.current_dir(x64=True) | |
1161 path = 'Bin%s' % arch_subdir | |
1162 yield os.path.join(self.si.WindowsSdkDir, path) | |
1163 | |
1164 if self.vs_ver in (10.0, 11.0): | |
1165 if self.pi.target_is_x86(): | |
1166 arch_subdir = '' | |
1167 else: | |
1168 arch_subdir = self.pi.current_dir(hidex86=True, x64=True) | |
1169 path = r'Bin\NETFX 4.0 Tools%s' % arch_subdir | |
1170 yield os.path.join(self.si.WindowsSdkDir, path) | |
1171 | |
1172 elif self.vs_ver >= 15.0: | |
1173 path = os.path.join(self.si.WindowsSdkDir, 'Bin') | |
1174 arch_subdir = self.pi.current_dir(x64=True) | |
1175 sdkver = self.si.WindowsSdkLastVersion | |
1176 yield os.path.join(path, '%s%s' % (sdkver, arch_subdir)) | |
1177 | |
1178 if self.si.WindowsSDKExecutablePath: | |
1179 yield self.si.WindowsSDKExecutablePath | |
1180 | |
1181 @property | |
1182 def _sdk_subdir(self): | |
1183 """ | |
1184 Microsoft Windows SDK version subdir. | |
1185 | |
1186 Return | |
1187 ------ | |
1188 str | |
1189 subdir | |
1190 """ | |
1191 ucrtver = self.si.WindowsSdkLastVersion | |
1192 return ('%s\\' % ucrtver) if ucrtver else '' | |
1193 | |
1194 @property | |
1195 def SdkSetup(self): | |
1196 """ | |
1197 Microsoft Windows SDK Setup. | |
1198 | |
1199 Return | |
1200 ------ | |
1201 list of str | |
1202 paths | |
1203 """ | |
1204 if self.vs_ver > 9.0: | |
1205 return [] | |
1206 | |
1207 return [os.path.join(self.si.WindowsSdkDir, 'Setup')] | |
1208 | |
1209 @property | |
1210 def FxTools(self): | |
1211 """ | |
1212 Microsoft .NET Framework Tools. | |
1213 | |
1214 Return | |
1215 ------ | |
1216 list of str | |
1217 paths | |
1218 """ | |
1219 pi = self.pi | |
1220 si = self.si | |
1221 | |
1222 if self.vs_ver <= 10.0: | |
1223 include32 = True | |
1224 include64 = not pi.target_is_x86() and not pi.current_is_x86() | |
1225 else: | |
1226 include32 = pi.target_is_x86() or pi.current_is_x86() | |
1227 include64 = pi.current_cpu == 'amd64' or pi.target_cpu == 'amd64' | |
1228 | |
1229 tools = [] | |
1230 if include32: | |
1231 tools += [ | |
1232 os.path.join(si.FrameworkDir32, ver) for ver in si.FrameworkVersion32 | |
1233 ] | |
1234 if include64: | |
1235 tools += [ | |
1236 os.path.join(si.FrameworkDir64, ver) for ver in si.FrameworkVersion64 | |
1237 ] | |
1238 return tools | |
1239 | |
1240 @property | |
1241 def NetFxSDKLibraries(self): | |
1242 """ | |
1243 Microsoft .Net Framework SDK Libraries. | |
1244 | |
1245 Return | |
1246 ------ | |
1247 list of str | |
1248 paths | |
1249 """ | |
1250 if self.vs_ver < 14.0 or not self.si.NetFxSdkDir: | |
1251 return [] | |
1252 | |
1253 arch_subdir = self.pi.target_dir(x64=True) | |
1254 return [os.path.join(self.si.NetFxSdkDir, r'lib\um%s' % arch_subdir)] | |
1255 | |
1256 @property | |
1257 def NetFxSDKIncludes(self): | |
1258 """ | |
1259 Microsoft .Net Framework SDK Includes. | |
1260 | |
1261 Return | |
1262 ------ | |
1263 list of str | |
1264 paths | |
1265 """ | |
1266 if self.vs_ver < 14.0 or not self.si.NetFxSdkDir: | |
1267 return [] | |
1268 | |
1269 return [os.path.join(self.si.NetFxSdkDir, r'include\um')] | |
1270 | |
1271 @property | |
1272 def VsTDb(self): | |
1273 """ | |
1274 Microsoft Visual Studio Team System Database. | |
1275 | |
1276 Return | |
1277 ------ | |
1278 list of str | |
1279 paths | |
1280 """ | |
1281 return [os.path.join(self.si.VSInstallDir, r'VSTSDB\Deploy')] | |
1282 | |
1283 @property | |
1284 def MSBuild(self): | |
1285 """ | |
1286 Microsoft Build Engine. | |
1287 | |
1288 Return | |
1289 ------ | |
1290 list of str | |
1291 paths | |
1292 """ | |
1293 if self.vs_ver < 12.0: | |
1294 return [] | |
1295 elif self.vs_ver < 15.0: | |
1296 base_path = self.si.ProgramFilesx86 | |
1297 arch_subdir = self.pi.current_dir(hidex86=True) | |
1298 else: | |
1299 base_path = self.si.VSInstallDir | |
1300 arch_subdir = '' | |
1301 | |
1302 path = r'MSBuild\%0.1f\bin%s' % (self.vs_ver, arch_subdir) | |
1303 build = [os.path.join(base_path, path)] | |
1304 | |
1305 if self.vs_ver >= 15.0: | |
1306 # Add Roslyn C# & Visual Basic Compiler | |
1307 build += [os.path.join(base_path, path, 'Roslyn')] | |
1308 | |
1309 return build | |
1310 | |
1311 @property | |
1312 def HTMLHelpWorkshop(self): | |
1313 """ | |
1314 Microsoft HTML Help Workshop. | |
1315 | |
1316 Return | |
1317 ------ | |
1318 list of str | |
1319 paths | |
1320 """ | |
1321 if self.vs_ver < 11.0: | |
1322 return [] | |
1323 | |
1324 return [os.path.join(self.si.ProgramFilesx86, 'HTML Help Workshop')] | |
1325 | |
1326 @property | |
1327 def UCRTLibraries(self): | |
1328 """ | |
1329 Microsoft Universal C Runtime SDK Libraries. | |
1330 | |
1331 Return | |
1332 ------ | |
1333 list of str | |
1334 paths | |
1335 """ | |
1336 if self.vs_ver < 14.0: | |
1337 return [] | |
1338 | |
1339 arch_subdir = self.pi.target_dir(x64=True) | |
1340 lib = os.path.join(self.si.UniversalCRTSdkDir, 'lib') | |
1341 ucrtver = self._ucrt_subdir | |
1342 return [os.path.join(lib, '%sucrt%s' % (ucrtver, arch_subdir))] | |
1343 | |
1344 @property | |
1345 def UCRTIncludes(self): | |
1346 """ | |
1347 Microsoft Universal C Runtime SDK Include. | |
1348 | |
1349 Return | |
1350 ------ | |
1351 list of str | |
1352 paths | |
1353 """ | |
1354 if self.vs_ver < 14.0: | |
1355 return [] | |
1356 | |
1357 include = os.path.join(self.si.UniversalCRTSdkDir, 'include') | |
1358 return [os.path.join(include, '%sucrt' % self._ucrt_subdir)] | |
1359 | |
1360 @property | |
1361 def _ucrt_subdir(self): | |
1362 """ | |
1363 Microsoft Universal C Runtime SDK version subdir. | |
1364 | |
1365 Return | |
1366 ------ | |
1367 str | |
1368 subdir | |
1369 """ | |
1370 ucrtver = self.si.UniversalCRTSdkLastVersion | |
1371 return ('%s\\' % ucrtver) if ucrtver else '' | |
1372 | |
1373 @property | |
1374 def FSharp(self): | |
1375 """ | |
1376 Microsoft Visual F#. | |
1377 | |
1378 Return | |
1379 ------ | |
1380 list of str | |
1381 paths | |
1382 """ | |
1383 if 11.0 > self.vs_ver > 12.0: | |
1384 return [] | |
1385 | |
1386 return [self.si.FSharpInstallDir] | |
1387 | |
1388 @property | |
1389 def VCRuntimeRedist(self) -> str | None: | |
1390 """ | |
1391 Microsoft Visual C++ runtime redistributable dll. | |
1392 | |
1393 Returns the first suitable path found or None. | |
1394 """ | |
1395 vcruntime = 'vcruntime%d0.dll' % self.vc_ver | |
1396 arch_subdir = self.pi.target_dir(x64=True).strip('\\') | |
1397 | |
1398 # Installation prefixes candidates | |
1399 prefixes = [] | |
1400 tools_path = self.si.VCInstallDir | |
1401 redist_path = os.path.dirname(tools_path.replace(r'\Tools', r'\Redist')) | |
1402 if os.path.isdir(redist_path): | |
1403 # Redist version may not be exactly the same as tools | |
1404 redist_path = os.path.join(redist_path, os.listdir(redist_path)[-1]) | |
1405 prefixes += [redist_path, os.path.join(redist_path, 'onecore')] | |
1406 | |
1407 prefixes += [os.path.join(tools_path, 'redist')] # VS14 legacy path | |
1408 | |
1409 # CRT directory | |
1410 crt_dirs = ( | |
1411 'Microsoft.VC%d.CRT' % (self.vc_ver * 10), | |
1412 # Sometime store in directory with VS version instead of VC | |
1413 'Microsoft.VC%d.CRT' % (int(self.vs_ver) * 10), | |
1414 ) | |
1415 | |
1416 # vcruntime path | |
1417 candidate_paths = ( | |
1418 os.path.join(prefix, arch_subdir, crt_dir, vcruntime) | |
1419 for (prefix, crt_dir) in itertools.product(prefixes, crt_dirs) | |
1420 ) | |
1421 return next(filter(os.path.isfile, candidate_paths), None) # type: ignore[arg-type] #python/mypy#12682 | |
1422 | |
1423 def return_env(self, exists=True): | |
1424 """ | |
1425 Return environment dict. | |
1426 | |
1427 Parameters | |
1428 ---------- | |
1429 exists: bool | |
1430 It True, only return existing paths. | |
1431 | |
1432 Return | |
1433 ------ | |
1434 dict | |
1435 environment | |
1436 """ | |
1437 env = dict( | |
1438 include=self._build_paths( | |
1439 'include', | |
1440 [ | |
1441 self.VCIncludes, | |
1442 self.OSIncludes, | |
1443 self.UCRTIncludes, | |
1444 self.NetFxSDKIncludes, | |
1445 ], | |
1446 exists, | |
1447 ), | |
1448 lib=self._build_paths( | |
1449 'lib', | |
1450 [ | |
1451 self.VCLibraries, | |
1452 self.OSLibraries, | |
1453 self.FxTools, | |
1454 self.UCRTLibraries, | |
1455 self.NetFxSDKLibraries, | |
1456 ], | |
1457 exists, | |
1458 ), | |
1459 libpath=self._build_paths( | |
1460 'libpath', | |
1461 [self.VCLibraries, self.FxTools, self.VCStoreRefs, self.OSLibpath], | |
1462 exists, | |
1463 ), | |
1464 path=self._build_paths( | |
1465 'path', | |
1466 [ | |
1467 self.VCTools, | |
1468 self.VSTools, | |
1469 self.VsTDb, | |
1470 self.SdkTools, | |
1471 self.SdkSetup, | |
1472 self.FxTools, | |
1473 self.MSBuild, | |
1474 self.HTMLHelpWorkshop, | |
1475 self.FSharp, | |
1476 ], | |
1477 exists, | |
1478 ), | |
1479 ) | |
1480 if self.vs_ver >= 14 and self.VCRuntimeRedist: | |
1481 env['py_vcruntime_redist'] = self.VCRuntimeRedist | |
1482 return env | |
1483 | |
1484 def _build_paths(self, name, spec_path_lists, exists): | |
1485 """ | |
1486 Given an environment variable name and specified paths, | |
1487 return a pathsep-separated string of paths containing | |
1488 unique, extant, directories from those paths and from | |
1489 the environment variable. Raise an error if no paths | |
1490 are resolved. | |
1491 | |
1492 Parameters | |
1493 ---------- | |
1494 name: str | |
1495 Environment variable name | |
1496 spec_path_lists: list of str | |
1497 Paths | |
1498 exists: bool | |
1499 It True, only return existing paths. | |
1500 | |
1501 Return | |
1502 ------ | |
1503 str | |
1504 Pathsep-separated paths | |
1505 """ | |
1506 # flatten spec_path_lists | |
1507 spec_paths = itertools.chain.from_iterable(spec_path_lists) | |
1508 env_paths = environ.get(name, '').split(os.pathsep) | |
1509 paths = itertools.chain(spec_paths, env_paths) | |
1510 extant_paths = list(filter(os.path.isdir, paths)) if exists else paths | |
1511 if not extant_paths: | |
1512 msg = "%s environment variable is empty" % name.upper() | |
1513 raise distutils.errors.DistutilsPlatformError(msg) | |
1514 unique_paths = unique_everseen(extant_paths) | |
1515 return os.pathsep.join(unique_paths) |